mirror of
https://github.com/element-plus/element-plus.git
synced 2024-11-21 01:02:59 +08:00
feat: integrate use popper (#11045)
* feat: integrate use popper * Integrate popper with use popper hook. * Reorganize code for better readabilities. * fix: contentStyle typing * fix: test failure * fix: slider placement testing * refactor: slider test case refactoring * fix: virtual triggering --------- Co-authored-by: JeremyWuuuuu <15975785+JeremyWuuuuu@users.noreply.github.com>
This commit is contained in:
parent
15e09cebfa
commit
e8bbdf974b
@ -1,4 +1,4 @@
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { defineComponent, nextTick, ref } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { POPPER_INJECTION_KEY } from '@element-plus/tokens'
|
||||
@ -14,6 +14,21 @@ const popperInjection = {
|
||||
contentRef: ref(),
|
||||
}
|
||||
|
||||
const TestComponent = defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
contentRef: ref(),
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<ElContent ref="contentRef" {...this.$attrs}>
|
||||
{AXIOM}
|
||||
</ElContent>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const mountContent = (props = {}) =>
|
||||
mount(<ElContent {...props}>{AXIOM}</ElContent>, {
|
||||
global: {
|
||||
@ -23,6 +38,15 @@ const mountContent = (props = {}) =>
|
||||
},
|
||||
})
|
||||
|
||||
const mountWrappedContent = (props = {}) =>
|
||||
mount(<TestComponent {...props} />, {
|
||||
global: {
|
||||
provide: {
|
||||
[POPPER_INJECTION_KEY as symbol]: popperInjection,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('<ElPopperContent />', () => {
|
||||
describe('with triggerRef provided', () => {
|
||||
const triggerKls = 'el-popper__trigger'
|
||||
@ -48,8 +72,16 @@ describe('<ElPopperContent />', () => {
|
||||
expect(wrapper.html()).toContain(AXIOM)
|
||||
expect(popperInjection.popperInstanceRef.value).toBeDefined()
|
||||
expect(wrapper.classes()).toEqual(['el-popper', 'is-dark'])
|
||||
expect(wrapper.vm.contentStyle).toHaveLength(3)
|
||||
expect(wrapper.vm.contentStyle[0]).toHaveProperty('zIndex')
|
||||
expect(wrapper.vm.contentStyle[1]).toBeUndefined()
|
||||
expect(wrapper.vm.contentStyle[1]).toEqual({})
|
||||
expect(wrapper.vm.contentStyle[2]).toEqual(
|
||||
expect.objectContaining({
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('should be able to be pure and themed', async () => {
|
||||
@ -94,7 +126,6 @@ describe('<ElPopperContent />', () => {
|
||||
|
||||
describe('instantiate popper instance', () => {
|
||||
it('should be able to update the current instance', async () => {
|
||||
wrapper = mountContent()
|
||||
await nextTick()
|
||||
|
||||
vi.spyOn(
|
||||
@ -113,10 +144,11 @@ describe('<ElPopperContent />', () => {
|
||||
})
|
||||
|
||||
it('should be able to update the reference node', async () => {
|
||||
wrapper = mountContent()
|
||||
const w = mountWrappedContent()
|
||||
await nextTick()
|
||||
|
||||
const oldInstance = wrapper.vm.popperInstanceRef
|
||||
const { contentRef } = w.vm
|
||||
const oldInstance = contentRef.popperInstanceRef
|
||||
|
||||
const newRef = document.createElement('div')
|
||||
newRef.classList.add('new-ref')
|
||||
@ -124,13 +156,13 @@ describe('<ElPopperContent />', () => {
|
||||
popperInjection.triggerRef.value = newRef
|
||||
await nextTick()
|
||||
|
||||
expect(wrapper.vm.popperInstanceRef).not.toStrictEqual(oldInstance)
|
||||
expect(contentRef.popperInstanceRef).not.toStrictEqual(oldInstance)
|
||||
|
||||
popperInjection.triggerRef.value = undefined
|
||||
|
||||
await nextTick()
|
||||
|
||||
expect(wrapper.vm.popperInstanceRef).toBeUndefined()
|
||||
expect(contentRef.popperInstanceRef).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,10 @@
|
||||
<template>
|
||||
<span ref="arrowRef" :class="ns.e('arrow')" data-popper-arrow="" />
|
||||
<span
|
||||
ref="arrowRef"
|
||||
:class="ns.e('arrow')"
|
||||
:style="arrowStyle"
|
||||
data-popper-arrow
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -16,7 +21,7 @@ defineOptions({
|
||||
const props = defineProps(popperArrowProps)
|
||||
|
||||
const ns = useNamespace('popper')
|
||||
const { arrowOffset, arrowRef } = inject(
|
||||
const { arrowOffset, arrowRef, arrowStyle } = inject(
|
||||
POPPER_CONTENT_INJECTION_KEY,
|
||||
undefined
|
||||
)!
|
||||
|
3
packages/components/popper/src/composables/index.ts
Normal file
3
packages/components/popper/src/composables/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './use-content'
|
||||
export * from './use-content-dom'
|
||||
export * from './use-focus-trap'
|
@ -0,0 +1,59 @@
|
||||
import { computed, ref, unref } from 'vue'
|
||||
import { useNamespace, useZIndex } from '@element-plus/hooks'
|
||||
|
||||
import type { CSSProperties, StyleValue } from 'vue'
|
||||
import type { UsePopperReturn } from '@element-plus/hooks'
|
||||
import type { UsePopperContentReturn } from './use-content'
|
||||
import type { PopperContentProps } from '../content'
|
||||
|
||||
export const usePopperContentDOM = (
|
||||
props: PopperContentProps,
|
||||
{
|
||||
attributes,
|
||||
styles,
|
||||
role,
|
||||
}: Pick<UsePopperReturn, 'attributes' | 'styles'> &
|
||||
Pick<UsePopperContentReturn, 'role'>
|
||||
) => {
|
||||
const { nextZIndex } = useZIndex()
|
||||
const ns = useNamespace('popper')
|
||||
|
||||
const contentAttrs = computed(() => unref(attributes).popper)
|
||||
const contentZIndex = ref<number>(props.zIndex || nextZIndex())
|
||||
const contentClass = computed(() => [
|
||||
ns.b(),
|
||||
ns.is('pure', props.pure),
|
||||
ns.is(props.effect),
|
||||
props.popperClass,
|
||||
])
|
||||
const contentStyle = computed<StyleValue[]>(() => {
|
||||
return [
|
||||
{ zIndex: unref(contentZIndex) } as CSSProperties,
|
||||
props.popperStyle || {},
|
||||
unref(styles).popper as CSSProperties,
|
||||
]
|
||||
})
|
||||
const ariaModal = computed<string | undefined>(() =>
|
||||
role.value === 'dialog' ? 'false' : undefined
|
||||
)
|
||||
const arrowStyle = computed(
|
||||
() => (unref(styles).arrow || {}) as CSSProperties
|
||||
)
|
||||
|
||||
const updateZIndex = () => {
|
||||
contentZIndex.value = props.zIndex || nextZIndex()
|
||||
}
|
||||
|
||||
return {
|
||||
ariaModal,
|
||||
arrowStyle,
|
||||
contentAttrs,
|
||||
contentClass,
|
||||
contentStyle,
|
||||
contentZIndex,
|
||||
|
||||
updateZIndex,
|
||||
}
|
||||
}
|
||||
|
||||
export type UsePopperContentDOMReturn = ReturnType<typeof usePopperContentDOM>
|
89
packages/components/popper/src/composables/use-content.ts
Normal file
89
packages/components/popper/src/composables/use-content.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { computed, inject, onMounted, ref, unref, watch } from 'vue'
|
||||
import { isUndefined } from 'lodash-unified'
|
||||
import { usePopper } from '@element-plus/hooks'
|
||||
import { POPPER_INJECTION_KEY } from '@element-plus/tokens'
|
||||
import { buildPopperOptions, unwrapMeasurableEl } from '../utils'
|
||||
import type { Modifier } from '@popperjs/core'
|
||||
|
||||
import type { PartialOptions } from '@element-plus/hooks'
|
||||
import type { PopperContentProps } from '../content'
|
||||
|
||||
const DEFAULT_ARROW_OFFSET = 0
|
||||
|
||||
export const usePopperContent = (props: PopperContentProps) => {
|
||||
const { popperInstanceRef, contentRef, triggerRef, role } = inject(
|
||||
POPPER_INJECTION_KEY,
|
||||
undefined
|
||||
)!
|
||||
|
||||
const arrowRef = ref<HTMLElement>()
|
||||
const arrowOffset = ref<number>()
|
||||
|
||||
const eventListenerModifier = computed(() => {
|
||||
return {
|
||||
name: 'eventListeners',
|
||||
enabled: !!props.visible,
|
||||
} as Modifier<'eventListeners', any>
|
||||
})
|
||||
|
||||
const arrowModifier = computed(() => {
|
||||
const arrowEl = unref(arrowRef)
|
||||
const offset = unref(arrowOffset) ?? DEFAULT_ARROW_OFFSET
|
||||
// Seems like the `phase` and `fn` is required by Modifier type
|
||||
// But on its documentation they didn't specify that.
|
||||
// Refer to https://popper.js.org/docs/v2/modifiers/arrow/
|
||||
return {
|
||||
name: 'arrow',
|
||||
enabled: !isUndefined(arrowEl),
|
||||
options: {
|
||||
element: arrowEl,
|
||||
padding: offset,
|
||||
},
|
||||
} as any
|
||||
})
|
||||
|
||||
const options = computed<PartialOptions>(() => {
|
||||
return {
|
||||
onFirstUpdate: () => {
|
||||
update()
|
||||
},
|
||||
...buildPopperOptions(props, [
|
||||
unref(arrowModifier),
|
||||
unref(eventListenerModifier),
|
||||
]),
|
||||
}
|
||||
})
|
||||
|
||||
const computedReference = computed(
|
||||
() => unwrapMeasurableEl(props.referenceEl) || unref(triggerRef)
|
||||
)
|
||||
|
||||
const { attributes, state, styles, update, forceUpdate, instanceRef } =
|
||||
usePopper(computedReference, contentRef, options)
|
||||
|
||||
watch(instanceRef, (instance) => (popperInstanceRef.value = instance))
|
||||
|
||||
onMounted(() => {
|
||||
watch(
|
||||
() => unref(computedReference)?.getBoundingClientRect(),
|
||||
() => {
|
||||
update()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return {
|
||||
attributes,
|
||||
arrowRef,
|
||||
contentRef,
|
||||
instanceRef,
|
||||
state,
|
||||
styles,
|
||||
role,
|
||||
|
||||
forceUpdate,
|
||||
update,
|
||||
}
|
||||
}
|
||||
|
||||
export type UsePopperContentReturn = ReturnType<typeof usePopperContent>
|
61
packages/components/popper/src/composables/use-focus-trap.ts
Normal file
61
packages/components/popper/src/composables/use-focus-trap.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
import type { SetupContext } from 'vue'
|
||||
import type { PopperContentEmits, PopperContentProps } from '../content'
|
||||
|
||||
export const usePopperContentFocusTrap = (
|
||||
props: PopperContentProps,
|
||||
emit: SetupContext<PopperContentEmits>['emit']
|
||||
) => {
|
||||
const trapped = ref(false)
|
||||
const focusStartRef = ref<'container' | 'first' | HTMLElement>()
|
||||
|
||||
const onFocusAfterTrapped = () => {
|
||||
emit('focus')
|
||||
}
|
||||
|
||||
const onFocusAfterReleased = (event: CustomEvent) => {
|
||||
if (event.detail?.focusReason !== 'pointer') {
|
||||
focusStartRef.value = 'first'
|
||||
emit('blur')
|
||||
}
|
||||
}
|
||||
|
||||
const onFocusInTrap = (event: FocusEvent) => {
|
||||
if (props.visible && !trapped.value) {
|
||||
if (event.target) {
|
||||
focusStartRef.value = event.target as typeof focusStartRef.value
|
||||
}
|
||||
trapped.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const onFocusoutPrevented = (event: CustomEvent) => {
|
||||
if (!props.trapping) {
|
||||
if (event.detail.focusReason === 'pointer') {
|
||||
event.preventDefault()
|
||||
}
|
||||
trapped.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onReleaseRequested = () => {
|
||||
trapped.value = false
|
||||
emit('close')
|
||||
}
|
||||
|
||||
return {
|
||||
focusStartRef,
|
||||
trapped,
|
||||
|
||||
onFocusAfterReleased,
|
||||
onFocusAfterTrapped,
|
||||
onFocusInTrap,
|
||||
onFocusoutPrevented,
|
||||
onReleaseRequested,
|
||||
}
|
||||
}
|
||||
|
||||
export type UsePopperContentFocusTrapReturn = ReturnType<
|
||||
typeof usePopperContentFocusTrap
|
||||
>
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
ref="popperContentRef"
|
||||
ref="contentRef"
|
||||
v-bind="contentAttrs"
|
||||
:style="contentStyle"
|
||||
:class="contentClass"
|
||||
tabindex="-1"
|
||||
@ -25,7 +26,6 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
computed,
|
||||
inject,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
@ -36,20 +36,20 @@ import {
|
||||
} from 'vue'
|
||||
import { NOOP } from '@vue/shared'
|
||||
import { isNil } from 'lodash-unified'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import ElFocusTrap from '@element-plus/components/focus-trap'
|
||||
import { useNamespace, useZIndex } from '@element-plus/hooks'
|
||||
import {
|
||||
POPPER_CONTENT_INJECTION_KEY,
|
||||
POPPER_INJECTION_KEY,
|
||||
formItemContextKey,
|
||||
} from '@element-plus/tokens'
|
||||
import { isElement } from '@element-plus/utils'
|
||||
import { popperContentEmits, popperContentProps } from './content'
|
||||
import { buildPopperOptions, unwrapMeasurableEl } from './utils'
|
||||
import {
|
||||
usePopperContent,
|
||||
usePopperContentDOM,
|
||||
usePopperContentFocusTrap,
|
||||
} from './composables'
|
||||
|
||||
import type { WatchStopHandle } from 'vue'
|
||||
import type { CreatePopperInstanceParams } from './content'
|
||||
|
||||
defineOptions({
|
||||
name: 'ElPopperContent',
|
||||
@ -59,18 +59,39 @@ const emit = defineEmits(popperContentEmits)
|
||||
|
||||
const props = defineProps(popperContentProps)
|
||||
|
||||
const { popperInstanceRef, contentRef, triggerRef, role } = inject(
|
||||
POPPER_INJECTION_KEY,
|
||||
undefined
|
||||
)!
|
||||
const {
|
||||
focusStartRef,
|
||||
trapped,
|
||||
|
||||
onFocusAfterReleased,
|
||||
onFocusAfterTrapped,
|
||||
onFocusInTrap,
|
||||
onFocusoutPrevented,
|
||||
onReleaseRequested,
|
||||
} = usePopperContentFocusTrap(props, emit)
|
||||
|
||||
const { attributes, arrowRef, contentRef, styles, instanceRef, role, update } =
|
||||
usePopperContent(props)
|
||||
|
||||
const {
|
||||
ariaModal,
|
||||
arrowStyle,
|
||||
contentAttrs,
|
||||
contentClass,
|
||||
contentStyle,
|
||||
updateZIndex,
|
||||
} = usePopperContentDOM(props, {
|
||||
styles,
|
||||
attributes,
|
||||
role,
|
||||
})
|
||||
|
||||
const formItemContext = inject(formItemContextKey, undefined)
|
||||
const { nextZIndex } = useZIndex()
|
||||
const ns = useNamespace('popper')
|
||||
const popperContentRef = ref<HTMLElement>()
|
||||
const focusStartRef = ref<'container' | 'first' | HTMLElement>('first')
|
||||
const arrowRef = ref<HTMLElement>()
|
||||
const arrowOffset = ref<number>()
|
||||
|
||||
provide(POPPER_CONTENT_INJECTION_KEY, {
|
||||
arrowStyle,
|
||||
arrowRef,
|
||||
arrowOffset,
|
||||
})
|
||||
@ -87,54 +108,14 @@ if (
|
||||
})
|
||||
}
|
||||
|
||||
const contentZIndex = ref<number>(props.zIndex || nextZIndex())
|
||||
const trapped = ref<boolean>(false)
|
||||
|
||||
let triggerTargetAriaStopWatch: WatchStopHandle | undefined = undefined
|
||||
|
||||
const computedReference = computed(
|
||||
() => unwrapMeasurableEl(props.referenceEl) || unref(triggerRef)
|
||||
)
|
||||
|
||||
const contentStyle = computed(
|
||||
() => [{ zIndex: unref(contentZIndex) }, props.popperStyle] as any
|
||||
)
|
||||
|
||||
const contentClass = computed(() => [
|
||||
ns.b(),
|
||||
ns.is('pure', props.pure),
|
||||
ns.is(props.effect),
|
||||
props.popperClass,
|
||||
])
|
||||
|
||||
const ariaModal = computed<string | undefined>(() => {
|
||||
return role && role.value === 'dialog' ? 'false' : undefined
|
||||
})
|
||||
|
||||
const createPopperInstance = ({
|
||||
referenceEl,
|
||||
popperContentEl,
|
||||
arrowEl,
|
||||
}: CreatePopperInstanceParams) => {
|
||||
const options = buildPopperOptions(props, {
|
||||
arrowEl,
|
||||
arrowOffset: unref(arrowOffset),
|
||||
})
|
||||
|
||||
return createPopper(referenceEl, popperContentEl, options)
|
||||
}
|
||||
|
||||
const updatePopper = (shouldUpdateZIndex = true) => {
|
||||
unref(popperInstanceRef)?.update()
|
||||
shouldUpdateZIndex && (contentZIndex.value = props.zIndex || nextZIndex())
|
||||
update()
|
||||
shouldUpdateZIndex && updateZIndex()
|
||||
}
|
||||
|
||||
const togglePopperAlive = () => {
|
||||
const monitorable = { name: 'eventListeners', enabled: props.visible }
|
||||
unref(popperInstanceRef)?.setOptions?.((options) => ({
|
||||
...options,
|
||||
modifiers: [...(options.modifiers || []), monitorable],
|
||||
}))
|
||||
updatePopper(false)
|
||||
if (props.visible && props.focusOnShow) {
|
||||
trapped.value = true
|
||||
@ -143,74 +124,7 @@ const togglePopperAlive = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const onFocusAfterTrapped = () => {
|
||||
emit('focus')
|
||||
}
|
||||
|
||||
const onFocusAfterReleased = (event: CustomEvent) => {
|
||||
if (event.detail?.focusReason !== 'pointer') {
|
||||
focusStartRef.value = 'first'
|
||||
emit('blur')
|
||||
}
|
||||
}
|
||||
|
||||
const onFocusInTrap = (event: FocusEvent) => {
|
||||
if (props.visible && !trapped.value) {
|
||||
if (event.target) {
|
||||
focusStartRef.value = event.target as typeof focusStartRef.value
|
||||
}
|
||||
trapped.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const onFocusoutPrevented = (event: CustomEvent) => {
|
||||
if (!props.trapping) {
|
||||
if (event.detail.focusReason === 'pointer') {
|
||||
event.preventDefault()
|
||||
}
|
||||
trapped.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const onReleaseRequested = () => {
|
||||
trapped.value = false
|
||||
emit('close')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
let updateHandle: WatchStopHandle
|
||||
watch(
|
||||
computedReference,
|
||||
(referenceEl) => {
|
||||
updateHandle?.()
|
||||
const popperInstance = unref(popperInstanceRef)
|
||||
popperInstance?.destroy?.()
|
||||
if (referenceEl) {
|
||||
const popperContentEl = unref(popperContentRef)!
|
||||
contentRef.value = popperContentEl
|
||||
|
||||
popperInstanceRef.value = createPopperInstance({
|
||||
referenceEl,
|
||||
popperContentEl,
|
||||
arrowEl: unref(arrowRef),
|
||||
})
|
||||
|
||||
updateHandle = watch(
|
||||
() => referenceEl.getBoundingClientRect(),
|
||||
() => updatePopper(),
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
popperInstanceRef.value = undefined
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.triggerTargetEl,
|
||||
(triggerTargetEl, prevTriggerTargetEl) => {
|
||||
@ -243,15 +157,6 @@ onMounted(() => {
|
||||
)
|
||||
|
||||
watch(() => props.visible, togglePopperAlive, { immediate: true })
|
||||
|
||||
watch(
|
||||
() =>
|
||||
buildPopperOptions(props, {
|
||||
arrowEl: unref(arrowRef),
|
||||
arrowOffset: unref(arrowOffset),
|
||||
}),
|
||||
(option) => popperInstanceRef.value?.setOptions(option)
|
||||
)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@ -267,7 +172,7 @@ defineExpose({
|
||||
/**
|
||||
* @description popperjs instance
|
||||
*/
|
||||
popperInstanceRef,
|
||||
popperInstanceRef: instanceRef,
|
||||
/**
|
||||
* @description method for updating popper
|
||||
*/
|
||||
|
@ -2,27 +2,22 @@ import { isClient, unrefElement } from '@vueuse/core'
|
||||
|
||||
import type { ComponentPublicInstance } from 'vue'
|
||||
import type { MaybeRef } from '@vueuse/core'
|
||||
import type { Modifier } from '@popperjs/core'
|
||||
import type { Measurable } from '@element-plus/tokens'
|
||||
import type { PopperCoreConfigProps } from './content'
|
||||
|
||||
type ArrowProps = {
|
||||
arrowEl: HTMLElement | undefined
|
||||
arrowOffset: number | undefined
|
||||
}
|
||||
|
||||
export const buildPopperOptions = (
|
||||
props: PopperCoreConfigProps,
|
||||
arrowProps: ArrowProps
|
||||
modifiers: Modifier<any, any>[] = []
|
||||
) => {
|
||||
const { placement, strategy, popperOptions } = props
|
||||
const options = {
|
||||
placement,
|
||||
strategy,
|
||||
...popperOptions,
|
||||
modifiers: genModifiers(props),
|
||||
modifiers: [...genModifiers(props), ...modifiers],
|
||||
}
|
||||
|
||||
attachArrow(options, arrowProps)
|
||||
deriveExtraModifiers(options, popperOptions?.modifiers)
|
||||
return options
|
||||
}
|
||||
@ -70,16 +65,6 @@ function genModifiers(options: PopperCoreConfigProps) {
|
||||
]
|
||||
}
|
||||
|
||||
function attachArrow(options: any, { arrowEl, arrowOffset }: ArrowProps) {
|
||||
options.modifiers.push({
|
||||
name: 'arrow',
|
||||
options: {
|
||||
element: arrowEl,
|
||||
padding: arrowOffset ?? 5,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
|
||||
function deriveExtraModifiers(
|
||||
options: any,
|
||||
modifiers: PopperCoreConfigProps['popperOptions']['modifiers']
|
||||
|
@ -90,11 +90,12 @@ describe('Slider', () => {
|
||||
|
||||
it('placement', async () => {
|
||||
const TOOLTIP_CLASS = 'custom_tooltip'
|
||||
const PLACEMENT = 'right'
|
||||
const PLACEMENT = 'left'
|
||||
|
||||
mount(() => <Slider tooltip-class={TOOLTIP_CLASS} placement={PLACEMENT} />)
|
||||
|
||||
await nextTick()
|
||||
await nextTick() // here
|
||||
|
||||
expect(
|
||||
(document.querySelector(`.${TOOLTIP_CLASS}`) as HTMLElement).dataset
|
||||
|
@ -101,7 +101,7 @@ export const usePopper = (
|
||||
})
|
||||
|
||||
return {
|
||||
state: computed(() => unref(instanceRef)?.state),
|
||||
state: computed(() => ({ ...(unref(instanceRef)?.state || {}) })),
|
||||
styles: computed(() => unref(states).styles),
|
||||
attributes: computed(() => unref(states).attributes),
|
||||
update: () => unref(instanceRef)?.update(),
|
||||
@ -119,7 +119,7 @@ function deriveState(state: State) {
|
||||
const styles = fromPairs(
|
||||
elements.map(
|
||||
(element) =>
|
||||
[element, state.elements[element] || {}] as [
|
||||
[element, state.styles[element] || {}] as [
|
||||
string,
|
||||
State['styles'][keyof State['styles']]
|
||||
]
|
||||
@ -141,3 +141,5 @@ function deriveState(state: State) {
|
||||
attributes,
|
||||
}
|
||||
}
|
||||
|
||||
export type UsePopperReturn = ReturnType<typeof usePopper>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { ComputedRef, InjectionKey, Ref } from 'vue'
|
||||
import type { CSSProperties, ComputedRef, InjectionKey, Ref } from 'vue'
|
||||
import type { Instance } from '@popperjs/core'
|
||||
|
||||
export type Measurable = {
|
||||
@ -21,6 +21,7 @@ export type ElPopperInjectionContext = {
|
||||
export type ElPopperContentInjectionContext = {
|
||||
arrowRef: Ref<HTMLElement | undefined>
|
||||
arrowOffset: Ref<number | undefined>
|
||||
arrowStyle: ComputedRef<CSSProperties>
|
||||
}
|
||||
|
||||
export const POPPER_INJECTION_KEY: InjectionKey<ElPopperInjectionContext> =
|
||||
|
Loading…
Reference in New Issue
Block a user