feat(components): [tooltip-v2] add new component (#6838)

* feat(components): [tooltip-v2] add new component

- Init component

* Implement trigger and only child

* Fix typing issue
This commit is contained in:
JeremyWuuuuu 2022-03-26 13:33:24 +08:00 committed by GitHub
parent f6da924b21
commit 9919e0a867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 106 additions and 3 deletions

View File

View File

@ -0,0 +1,38 @@
import { Fragment, defineComponent, ref } from 'vue'
import {
buildProps,
composeRefs,
definePropType,
ensureOnlyChild,
} from '@element-plus/utils'
import type { ExtractPropTypes, VNodeArrayChildren } from 'vue'
export type RefSetter = (el: HTMLElement | null) => void
export const forwardRefProps = buildProps({
setRef: { type: definePropType<RefSetter>(Function), required: true },
} as const)
export type ForwardRefProps = ExtractPropTypes<typeof forwardRefProps>
// TODO: consider make this component a reusable component without the only child feature.
export default defineComponent({
props: forwardRefProps,
setup(props, { slots }) {
const fragmentRef = ref()
const setRef = composeRefs(fragmentRef, (el) => {
// vue fragments is represented as a text element.
// The first element sibling should be the first element children of fragment.
// This is how we get the element.
props.setRef((el as HTMLElement).nextElementSibling as HTMLElement | null)
})
return () => {
const [firstChild] = slots.default?.() || []
const child = ensureOnlyChild(firstChild.children as VNodeArrayChildren)
// Dunno why the ref for jsx complains about the typing issue which was not
// in template
return <Fragment ref={setRef as any}>{child}</Fragment>
}
},
})

View File

@ -0,0 +1,9 @@
<template>
<slot />
</template>
<script setup lang="ts">
// import { computed, ref } from 'vue'
// import { useFloating } from '@element-plus/hooks'
</script>

View File

@ -0,0 +1,11 @@
import { buildProps } from '@element-plus/utils'
import type { ExtractPropTypes } from 'vue'
export const tooltipTriggerV2Props = buildProps({
asChild: Boolean,
} as const)
export type TooltipTriggerV2Props = ExtractPropTypes<
typeof tooltipTriggerV2Props
>

View File

@ -0,0 +1,20 @@
<template>
<only-child v-if="asChild" :set-ref="setTriggerRef">
<slot />
</only-child>
<button v-else ref="triggerRef">
<slot />
</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import OnlyChild from './only-child'
import { tooltipTriggerV2Props } from './trigger'
defineProps(tooltipTriggerV2Props)
const triggerRef = ref<HTMLElement | null>(null)
const setTriggerRef = (el: HTMLElement | null) => {
triggerRef.value = el
}
</script>

View File

@ -1,9 +1,21 @@
import { isFunction } from '@element-plus/utils'
import type { ComponentPublicInstance, Ref } from 'vue'
export const composeRefs = (...refs: Ref<HTMLElement | undefined>[]) => {
export type RefSetter = (
el: Element | ComponentPublicInstance | undefined
) => void
export const composeRefs = (
...refs: (Ref<HTMLElement | undefined> | RefSetter)[]
) => {
return (el: Element | ComponentPublicInstance | null) => {
refs.forEach((ref) => {
ref.value = el as HTMLElement | undefined
if (isFunction(ref)) {
ref(el as Element | ComponentPublicInstance)
} else {
ref.value = el as HTMLElement | undefined
}
})
}
}

View File

@ -8,9 +8,15 @@ import {
isVNode,
openBlock,
} from 'vue'
import { isArray } from '@vue/shared'
import { hasOwn } from '../objects'
import { debugWarn } from '../error'
import type { VNode, VNodeChild, VNodeNormalizedChildren } from 'vue'
import type {
VNode,
VNodeArrayChildren,
VNodeChild,
VNodeNormalizedChildren,
} from 'vue'
const SCOPE = 'utils/vue/vnode'
@ -125,3 +131,10 @@ export const getNormalizedProps = (node: VNode) => {
return props
}
export const ensureOnlyChild = (children: VNodeArrayChildren | undefined) => {
if (!isArray(children) || children.length > 1) {
throw new Error('expect to receive a single Vue element child')
}
return children[0]
}