element-plus
Version:
A Component Library for Vue 3
1 lines • 13.4 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../../../../../packages/hooks/use-popper/index.ts"],"sourcesContent":["import {\n cloneVNode,\n computed,\n Fragment,\n getCurrentInstance,\n h,\n nextTick,\n toDisplayString,\n toRef,\n Transition,\n ref,\n renderSlot,\n withDirectives,\n} from 'vue'\nimport { NOOP } from '@vue/shared'\nimport { createPopper } from '@popperjs/core'\nimport { ClickOutside } from '@element-plus/directives'\nimport {\n generateId,\n isHTMLElement,\n isString,\n refAttacher,\n} from '@element-plus/utils/util'\nimport { getFirstValidNode } from '@element-plus/utils/vnode'\nimport { stop } from '@element-plus/utils/dom'\nimport PopupManager from '@element-plus/utils/popup-manager'\nimport { throwError } from '@element-plus/utils/error'\n\nimport useTeleport from '../use-teleport'\nimport useTimeout from '../use-timeout'\nimport { useModelToggle } from '../use-model-toggle'\nimport { useTransitionFallthrough } from '../use-transition-fallthrough'\nimport { defaultPopperOptions, defaultModifiers } from './use-popper-options'\nimport { useTargetEvents, DEFAULT_TRIGGER } from './use-target-events'\n\nimport type {\n CSSProperties,\n ComponentPublicInstance,\n ExtractPropTypes,\n PropType,\n} from 'vue'\nimport type {\n Instance as PopperInstance,\n StrictModifiers,\n} from '@popperjs/core'\nimport type { RefElement, Nullable } from '@element-plus/utils/types'\nimport type { Trigger } from './use-target-events'\n\nexport type PopperEffect = 'light' | 'dark'\nexport type Offset = [number, number] | number\n\ntype ElementType = ComponentPublicInstance | HTMLElement\n\nexport const DARK_EFFECT = 'dark'\nexport const LIGHT_EFFECT = 'light'\n\nexport const usePopperControlProps = {\n appendToBody: {\n type: Boolean,\n default: true,\n },\n arrowOffset: {\n type: Number,\n },\n popperOptions: defaultPopperOptions,\n popperClass: {\n type: String,\n default: '',\n },\n}\n\nexport const usePopperProps = {\n ...usePopperControlProps,\n // the arrow size is an equailateral triangle with 10px side length, the 3rd side length ~ 14.1px\n // adding a offset to the ceil of 4.1 should be 5 this resolves the problem of arrow overflowing out of popper.\n autoClose: {\n type: Number,\n default: 0,\n },\n content: {\n type: String,\n default: '',\n },\n class: String,\n style: Object,\n hideAfter: {\n type: Number,\n default: 200,\n },\n disabled: {\n type: Boolean,\n default: false,\n },\n effect: {\n type: String as PropType<PopperEffect>,\n default: DARK_EFFECT,\n },\n enterable: {\n type: Boolean,\n default: true,\n },\n manualMode: {\n type: Boolean,\n default: false,\n },\n showAfter: {\n type: Number,\n default: 0,\n },\n pure: {\n type: Boolean,\n default: false,\n },\n showArrow: {\n type: Boolean,\n default: true,\n },\n transition: {\n type: String,\n default: 'el-fade-in-linear',\n },\n trigger: {\n type: [String, Array] as PropType<Trigger>,\n default: DEFAULT_TRIGGER,\n },\n visible: {\n type: Boolean,\n default: undefined,\n },\n stopPopperMouseEvent: {\n type: Boolean,\n default: true,\n },\n}\n\nexport const usePopperHook = () => {\n const vm = getCurrentInstance()!\n const props: ExtractPropTypes<typeof usePopperProps> = vm.proxy?.$props as any\n const { slots } = vm\n\n const arrowRef = ref<RefElement>(null)\n const triggerRef = ref<ElementType>(null)\n const popperRef = ref<RefElement>(null)\n\n const popperStyle = ref<CSSProperties>({ zIndex: PopupManager.nextZIndex() })\n const visible = ref(false)\n const isManual = computed(\n () => props.manualMode || props.trigger === 'manual'\n )\n\n const popperId = `el-popper-${generateId()}`\n let popperInstance: Nullable<PopperInstance> = null\n\n const { renderTeleport, showTeleport, hideTeleport } = useTeleport(\n popupRenderer,\n toRef(props, 'appendToBody')\n )\n\n const { show, hide } = useModelToggle({\n indicator: visible,\n onShow,\n onHide,\n })\n\n const { registerTimeout, cancelTimeout } = useTimeout()\n\n // event handlers\n\n function onShow() {\n popperStyle.value.zIndex = PopupManager.nextZIndex()\n nextTick(initializePopper)\n }\n\n function onHide() {\n hideTeleport()\n nextTick(detachPopper)\n }\n\n /**\n * The calling mechanism here is:\n * when the visibility gets changed, let's say we change it to true\n * the delayShow gets called which initializes a global root node for the popper content\n * to insert in, then it register a timer for calling the show method, which changes the flag to\n * true, then calls onShow method.\n * So the order of invocation is: delayedShow -> timer(show) -> set the indicator to true -> onShow\n */\n\n function delayShow() {\n if (isManual.value || props.disabled) return\n // renders out the teleport element root.\n showTeleport()\n registerTimeout(show, props.showAfter)\n }\n\n function delayHide() {\n if (isManual.value) return\n registerTimeout(hide, props.hideAfter)\n }\n\n function onToggle() {\n if (visible.value) {\n delayShow()\n } else {\n delayHide()\n }\n }\n\n function detachPopper() {\n popperInstance?.destroy?.()\n popperInstance = null\n }\n\n function onPopperMouseEnter() {\n // if trigger is click, user won't be able to close popper when\n // user tries to move the mouse over popper contents\n if (props.enterable && props.trigger !== 'click') {\n cancelTimeout()\n }\n }\n\n function onPopperMouseLeave() {\n const { trigger } = props\n const shouldPrevent =\n (isString(trigger) && (trigger === 'click' || trigger === 'focus')) ||\n // we'd like to test array type trigger here, but the only case we need to cover is trigger === 'click' or\n // trigger === 'focus', because that when trigger is string\n // trigger.length === 1 and trigger[0] === 5 chars string is mutually exclusive.\n // so there will be no need to test if trigger is array type.\n (trigger.length === 1 &&\n (trigger[0] === 'click' || trigger[0] === 'focus'))\n if (shouldPrevent) return\n delayHide()\n }\n\n function initializePopper() {\n if (!visible.value || popperInstance !== null) {\n return\n }\n const unwrappedTrigger = triggerRef.value\n const $el = isHTMLElement(unwrappedTrigger)\n ? unwrappedTrigger\n : (unwrappedTrigger as ComponentPublicInstance).$el\n\n popperInstance = createPopper($el, popperRef.value, buildPopperOptions())\n popperInstance.update()\n }\n\n function buildPopperOptions() {\n const modifiers = [...defaultModifiers, ...props.popperOptions.modifiers]\n\n if (props.showArrow) {\n modifiers.push({\n name: 'arrow',\n options: {\n padding: props.arrowOffset || 5,\n element: arrowRef.value,\n },\n } as StrictModifiers)\n }\n\n return {\n ...props.popperOptions,\n modifiers,\n }\n }\n\n const { onAfterEnter, onAfterLeave, onBeforeEnter, onBeforeLeave } =\n useTransitionFallthrough()\n\n const events = useTargetEvents(delayShow, delayHide, onToggle)\n\n const arrowRefAttacher = refAttacher(arrowRef)\n const popperRefAttacher = refAttacher(popperRef)\n const triggerRefAttacher = refAttacher(triggerRef)\n\n // renderers\n function popupRenderer() {\n const mouseUpAndDown = props.stopPopperMouseEvent ? stop : NOOP\n return h(\n Transition as any,\n {\n name: props.transition,\n onAfterEnter,\n onAfterLeave,\n onBeforeEnter,\n onBeforeLeave,\n },\n {\n default: () => () =>\n visible.value\n ? h(\n 'div',\n {\n 'aria-hidden': false,\n class: [\n props.popperClass,\n 'el-popper',\n `is-${props.effect}`,\n props.pure ? 'is-pure' : '',\n ],\n style: popperStyle.value,\n id: popperId,\n ref: popperRefAttacher,\n role: 'tooltip',\n onMouseenter: onPopperMouseEnter,\n onMouseleave: onPopperMouseLeave,\n onClick: stop,\n onMousedown: mouseUpAndDown,\n onMouseup: mouseUpAndDown,\n },\n [\n renderSlot(slots, 'default', {}, () => [\n toDisplayString(props.content),\n ]),\n arrowRenderer(),\n ]\n )\n : null,\n }\n )\n }\n\n function arrowRenderer() {\n return props.showArrow\n ? h(\n 'div',\n {\n ref: arrowRefAttacher,\n class: 'el-popper__arrow',\n 'data-popper-arrow': '',\n },\n null\n )\n : null\n }\n\n function triggerRenderer(triggerProps) {\n const trigger = slots.trigger?.()\n const firstElement = getFirstValidNode(trigger, 1)\n if (!firstElement)\n throwError('renderTrigger', 'trigger expects single rooted node')\n return cloneVNode(firstElement, triggerProps, true)\n }\n\n function render() {\n const trigger = triggerRenderer({\n 'aria-describedby': popperId,\n class: props.class,\n style: props.style,\n ref: triggerRefAttacher,\n ...events,\n })\n return h(Fragment, null, [\n isManual.value\n ? trigger\n : withDirectives(trigger, [[ClickOutside, delayHide]]),\n renderTeleport(),\n ])\n }\n\n return {\n render,\n }\n}\n"],"names":[],"mappings":"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"}