Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions playground/pages/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,26 @@
</UiButton>
</div>

<div class="mt-6">
<span class="font-semibold mr-4">useTippy + plain string:</span>
<button
class="text-sm py-2 px-3 bg-gray-900 text-white rounded-lg"
ref="buttonText"
>
My Button with plain string
</button>
</div>

<div class="mt-6">
<span class="font-semibold mr-4">useTippy + computed string:</span>
<button
class="text-sm py-2 px-3 bg-gray-900 text-white rounded-lg ml-3"
ref="buttonTextComputed"
>
My Button with computed string
</button>
</div>

<div class="mt-6">
<span class="font-semibold mr-4"
>Tippy component + change content and props realtime using component
Expand Down Expand Up @@ -413,6 +433,15 @@ export default defineComponent({
content: 'Test Vue component',
})

const buttonText = ref<HTMLButtonElement>()

useTippy(buttonText, 'String content')

const buttonTextComputed = ref<HTMLButtonElement>()
const computedText = computed(() => `Counter: ${counter.value}`)

useTippy(buttonTextComputed, computedText);

const { x, y } = useMousePosition()

const { tippy } = useTippy(() => document.body, {
Expand Down Expand Up @@ -494,6 +523,8 @@ export default defineComponent({
button7,
target7,
vueComponentButton,
buttonText,
buttonTextComputed,
tippyComponent1,
log: console.log,
}
Expand Down
54 changes: 34 additions & 20 deletions src/composables/useTippy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import tippy, { Instance, Props, Content } from 'tippy.js'
import {
ref,
onMounted,
Ref,
isRef,
isReactive,
isVNode,
Expand All @@ -15,7 +14,8 @@ import {
shallowRef,
App,
} from 'vue'
import { TippyOptions, TippyContent } from '../types'
import { TippyOptions, TippyContent, MaybeRef, MaybeRefOrGetter } from '../types'
import { isObject, isString, toValue, isComponentInstance } from '../utils'

tippy.setDefaultProps({
//@ts-ignore
Expand All @@ -24,15 +24,11 @@ tippy.setDefaultProps({
},
})

const isComponentInstance = (value: any): value is { $el: any } => {
return value instanceof Object && '$' in value && '$el' in value
}

type TippyElement = Element | any // TODO: use ComponentPublicInstance

export function useTippy(
el: TippyElement | (() => TippyElement) | Ref<TippyElement> | Ref<TippyElement | undefined>,
opts: TippyOptions = {},
el: MaybeRefOrGetter<TippyElement>,
opts: TippyOptions | MaybeRef<string> = {},
settings: {
mount: boolean,
appName: string,
Expand Down Expand Up @@ -109,15 +105,15 @@ export function useTippy(
return newContent!
}

const getProps = (opts: TippyOptions): Partial<Props> => {
const getProps = (opts: TippyOptions | MaybeRef<string>): Partial<Props> => {
const value = toValue(opts);

let options: any = {}

if (isRef(opts)) {
options = opts.value || {}
} else if (isReactive(opts)) {
options = { ...opts }
if (isString(value)) {
options = { content: value }
} else {
options = { ...opts }
options = { ...value }
}

if (options.content) {
Expand Down Expand Up @@ -173,23 +169,41 @@ export function useTippy(
return options as Props
}

const getOptsContent = () => {
const value = toValue(opts)

if (isString(value)) {
return value
}

return value?.content
}

const refresh = () => {
if (!instance.value) return

instance.value.setProps(getProps(opts))
if (isObject(toValue(opts))) {
instance.value.setProps(getProps(opts))
} else {
refreshContent()
}
}

const refreshContent = () => {
if (!instance.value || !opts.content) return
if (!instance.value) return

const content = getOptsContent()

if (!content) return

instance.value.setContent(getContent(opts.content))
instance.value.setContent(getContent(content))
}

const setContent = (value: TippyContent) => {
instance.value?.setContent(getContent(value))
}

const setProps = (value: TippyOptions) => {
const setProps = (value: TippyOptions | MaybeRef<string>) => {
instance.value?.setProps(getProps(value))
}

Expand Down Expand Up @@ -283,9 +297,9 @@ export function useTippy(
})
}

if (isRef(opts) || isReactive(opts)) {
if ((isRef(opts) || isReactive(opts)) && !isString(opts)) {
watch(opts, refresh, { immediate: false })
} else if (isRef(opts.content)) {
} else if (isObject(opts) && isRef(opts.content)) {
watch(opts.content, refreshContent, { immediate: false })
}

Expand Down
10 changes: 9 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Tippy from '../components/Tippy'
import { useTippy } from '../composables'
import { Props, Content, DefaultProps, Instance } from 'tippy.js'
import { VNode, Ref, Component, UnwrapNestedRefs } from 'vue'
import { VNode, Ref, Component, UnwrapNestedRefs, ShallowRef, WritableComputedRef, ComputedRef } from 'vue'

export declare type TippyContent = Content | VNode | Component | Ref
export declare type TippyTarget =
Expand Down Expand Up @@ -35,3 +35,11 @@ export interface TippyPluginOptions {

export type TippyInstance = Instance | Element | undefined
export type TippyInstances = Ref<TippyInstance>[] | Ref<TippyInstance[]> | (() => TippyInstance[])

export type MaybeRef<T = any> =
| T
| Ref<T>
| ShallowRef<T>
| WritableComputedRef<T>

export type MaybeRefOrGetter<T = any> = MaybeRef<T> | ComputedRef<T> | (() => T)
27 changes: 27 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { isRef, unref } from 'vue'
import { MaybeRefOrGetter } from '../types'

export const isComponentInstance = (value: any): value is { $el: any } => {
return value instanceof Object && '$' in value && '$el' in value
}

export const isString = (val: unknown): val is string => typeof val === 'string'

export const isFunction = (val: unknown): val is Function =>
typeof val === 'function'

export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'


export function toValue<T>(source: MaybeRefOrGetter<T>): T {
if (isRef(source)) {
return unref(source)
}

if (isFunction(source)) {
return source()
}

return source;
}