UNPKG

element-plus

Version:

A Component Library for Vue 3

1 lines 7.79 kB
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../../../../packages/components/focus-trap/src/utils.ts"],"sourcesContent":["import { onBeforeUnmount, onMounted, ref } from 'vue'\nimport { focusElement } from '@element-plus/utils'\nimport { FOCUSOUT_PREVENTED, FOCUSOUT_PREVENTED_OPTS } from './tokens'\n\nconst focusReason = ref<'pointer' | 'keyboard'>()\nconst lastUserFocusTimestamp = ref<number>(0)\nconst lastAutomatedFocusTimestamp = ref<number>(0)\nlet focusReasonUserCount = 0\n\nexport type FocusLayer = {\n paused: boolean\n pause: () => void\n resume: () => void\n}\n\nexport type FocusStack = FocusLayer[]\n\nexport const obtainAllFocusableElements = (\n element: HTMLElement\n): HTMLElement[] => {\n const nodes: HTMLElement[] = []\n const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (\n node: Element & {\n disabled: boolean\n hidden: boolean\n type: string\n tabIndex: number\n }\n ) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden'\n if (node.disabled || node.hidden || isHiddenInput)\n return NodeFilter.FILTER_SKIP\n return node.tabIndex >= 0 || node === document.activeElement\n ? NodeFilter.FILTER_ACCEPT\n : NodeFilter.FILTER_SKIP\n },\n })\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement)\n\n return nodes\n}\n\nexport const getVisibleElement = (\n elements: HTMLElement[],\n container: HTMLElement\n) => {\n for (const element of elements) {\n if (!isHidden(element, container)) return element\n }\n}\n\nexport const isHidden = (element: HTMLElement, container: HTMLElement) => {\n if (process.env.NODE_ENV === 'test') return false\n if (getComputedStyle(element).visibility === 'hidden') return true\n\n while (element) {\n if (container && element === container) return false\n if (getComputedStyle(element).display === 'none') return true\n element = element.parentElement as HTMLElement\n }\n\n return false\n}\n\nexport const getEdges = (container: HTMLElement) => {\n const focusable = obtainAllFocusableElements(container)\n const first = getVisibleElement(focusable, container)\n const last = getVisibleElement(focusable.reverse(), container)\n return [first, last]\n}\n\nconst isSelectable = (\n element: any\n): element is HTMLInputElement & { select: () => void } => {\n return element instanceof HTMLInputElement && 'select' in element\n}\n\nexport const tryFocus = (\n element?: HTMLElement | { focus: () => void } | null,\n shouldSelect?: boolean\n) => {\n if (element) {\n const prevFocusedElement = document.activeElement\n\n focusElement(element, { preventScroll: true })\n lastAutomatedFocusTimestamp.value = window.performance.now()\n\n if (\n element !== prevFocusedElement &&\n isSelectable(element) &&\n shouldSelect\n ) {\n element.select()\n }\n }\n}\n\nfunction removeFromStack<T>(list: T[], item: T) {\n const copy = [...list]\n\n const idx = list.indexOf(item)\n\n if (idx !== -1) {\n copy.splice(idx, 1)\n }\n return copy\n}\n\nconst createFocusableStack = () => {\n let stack = [] as FocusStack\n\n const push = (layer: FocusLayer) => {\n const currentLayer = stack[0]\n\n if (currentLayer && layer !== currentLayer) {\n currentLayer.pause()\n }\n\n stack = removeFromStack(stack, layer)\n stack.unshift(layer)\n }\n\n const remove = (layer: FocusLayer) => {\n stack = removeFromStack(stack, layer)\n stack[0]?.resume?.()\n }\n\n return {\n push,\n remove,\n }\n}\n\nexport const focusFirstDescendant = (\n elements: HTMLElement[],\n shouldSelect = false\n) => {\n const prevFocusedElement = document.activeElement\n for (const element of elements) {\n tryFocus(element, shouldSelect)\n if (document.activeElement !== prevFocusedElement) return\n }\n}\n\nexport const focusableStack = createFocusableStack()\n\nexport const isFocusCausedByUserEvent = (): boolean => {\n return lastUserFocusTimestamp.value > lastAutomatedFocusTimestamp.value\n}\n\nconst notifyFocusReasonPointer = () => {\n focusReason.value = 'pointer'\n lastUserFocusTimestamp.value = window.performance.now()\n}\n\nconst notifyFocusReasonKeydown = () => {\n focusReason.value = 'keyboard'\n lastUserFocusTimestamp.value = window.performance.now()\n}\n\nexport const useFocusReason = (): {\n focusReason: typeof focusReason\n lastUserFocusTimestamp: typeof lastUserFocusTimestamp\n lastAutomatedFocusTimestamp: typeof lastAutomatedFocusTimestamp\n} => {\n onMounted(() => {\n if (focusReasonUserCount === 0) {\n document.addEventListener('mousedown', notifyFocusReasonPointer)\n document.addEventListener('touchstart', notifyFocusReasonPointer)\n document.addEventListener('keydown', notifyFocusReasonKeydown)\n }\n focusReasonUserCount++\n })\n\n onBeforeUnmount(() => {\n focusReasonUserCount--\n if (focusReasonUserCount <= 0) {\n document.removeEventListener('mousedown', notifyFocusReasonPointer)\n document.removeEventListener('touchstart', notifyFocusReasonPointer)\n document.removeEventListener('keydown', notifyFocusReasonKeydown)\n }\n })\n\n return {\n focusReason,\n lastUserFocusTimestamp,\n lastAutomatedFocusTimestamp,\n }\n}\n\nexport const createFocusOutPreventedEvent = (\n detail: CustomEventInit['detail']\n) => {\n return new CustomEvent(FOCUSOUT_PREVENTED, {\n ...FOCUSOUT_PREVENTED_OPTS,\n detail,\n })\n}\n"],"mappings":"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"}