UNPKG

@oruga-ui/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

1 lines 5.17 kB
{"version":3,"file":"useTrapFocus-DXSX2lWD.cjs","sources":["../../src/composables/useTrapFocus.ts"],"sourcesContent":["/*****************************\n * vue v-trap-focus directive\n *****************************/\n\nimport type { DirectiveHook, ObjectDirective } from \"vue\";\n\nfunction findFocusable(element: HTMLElement): NodeListOf<HTMLElement> | null {\n if (!element) return null;\n return element.querySelectorAll(`a[href]:not([tabindex=\"-1\"]),\n area[href],\n input:not([disabled]):not([type=\"hidden\"]),\n select:not([disabled]),\n textarea:not([disabled]),\n button:not([disabled]),\n iframe,\n object,\n embed,\n *[tabindex]:not([tabindex=\"-1\"]):not([disabled]),\n *[contenteditable]`);\n}\n\nexport function useTrapFocus(): {\n /** vue directive - trap focus on the current element */\n vTrapFocus: ObjectDirective<HTMLElement>;\n} {\n /** keydown event, which compares event target with trap element */\n let onKeyDown: ((event: KeyboardEvent) => void) | null = null;\n\n function applyHandler(el: HTMLElement, value: boolean): void {\n if (value) {\n // move focus inside the root element\n el.focus({ preventScroll: true });\n\n // set keydown event listener\n if (typeof onKeyDown === \"function\")\n el.addEventListener(\"keydown\", onKeyDown);\n } else {\n // remove keydown event listener\n if (typeof onKeyDown === \"function\")\n el.removeEventListener(\"keydown\", onKeyDown);\n }\n }\n\n const onMounted: DirectiveHook<HTMLElement> = (el, { value }) => {\n // create onKeyDown event listener\n onKeyDown = (event: KeyboardEvent): void => {\n const target = event.target as HTMLElement;\n if (!target) return;\n\n // Need to get focusable each time since it can change between key events\n // ex. changing month in a datepicker\n const focusable = findFocusable(el);\n if (!focusable?.length) {\n event.preventDefault();\n return;\n }\n\n const firstFocusable = focusable[0];\n const lastFocusable = focusable[focusable.length - 1];\n\n if (\n target === firstFocusable &&\n event.shiftKey &&\n event.key === \"Tab\"\n ) {\n // prevent moving focus outside by setting the focus to last focusable element\n event.preventDefault();\n lastFocusable.focus();\n } else if (\n target === lastFocusable &&\n !event.shiftKey &&\n event.key === \"Tab\"\n ) {\n // prevent moving focus outside by setting the focus to first focusable element\n event.preventDefault();\n firstFocusable.focus();\n }\n };\n\n // apply handler when binding value is already true\n if (value) applyHandler(el, value);\n };\n\n /** cleanup on beforeUnmount */\n const onBeforeUnmount: DirectiveHook<HTMLElement> = (el) => {\n // remove handler\n applyHandler(el, false);\n onKeyDown = null;\n };\n\n const onUpdate: DirectiveHook<HTMLElement> = (el, { value, oldValue }) => {\n // check if binding value has changed\n if (value !== oldValue)\n // update handler based on binding value\n applyHandler(el, value);\n };\n\n return {\n vTrapFocus: {\n mounted: onMounted,\n beforeUnmount: onBeforeUnmount,\n updated: onUpdate,\n },\n };\n}\n"],"names":[],"mappings":";;AAMA,SAAS,cAAc,SAAsD;AACrE,MAAA,CAAC,QAAgB,QAAA;AACrB,SAAO,QAAQ,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oDAUgB;AACpD;AAEO,SAAS,eAGd;AAEE,MAAI,YAAqD;AAEhD,WAAA,aAAa,IAAiB,OAAsB;AACzD,QAAI,OAAO;AAEP,SAAG,MAAM,EAAE,eAAe,KAAA,CAAM;AAGhC,UAAI,OAAO,cAAc;AAClB,WAAA,iBAAiB,WAAW,SAAS;AAAA,IAAA,OACzC;AAEH,UAAI,OAAO,cAAc;AAClB,WAAA,oBAAoB,WAAW,SAAS;AAAA,IAAA;AAAA,EACnD;AAGJ,QAAM,YAAwC,CAAC,IAAI,EAAE,YAAY;AAE7D,gBAAY,CAAC,UAA+B;AACxC,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AAIP,YAAA,YAAY,cAAc,EAAE;AAC9B,UAAA,EAAC,uCAAW,SAAQ;AACpB,cAAM,eAAe;AACrB;AAAA,MAAA;AAGE,YAAA,iBAAiB,UAAU,CAAC;AAClC,YAAM,gBAAgB,UAAU,UAAU,SAAS,CAAC;AAEpD,UACI,WAAW,kBACX,MAAM,YACN,MAAM,QAAQ,OAChB;AAEE,cAAM,eAAe;AACrB,sBAAc,MAAM;AAAA,MAAA,WAEpB,WAAW,iBACX,CAAC,MAAM,YACP,MAAM,QAAQ,OAChB;AAEE,cAAM,eAAe;AACrB,uBAAe,MAAM;AAAA,MAAA;AAAA,IAE7B;AAGI,QAAA,MAAoB,cAAA,IAAI,KAAK;AAAA,EACrC;AAGM,QAAA,kBAA8C,CAAC,OAAO;AAExD,iBAAa,IAAI,KAAK;AACV,gBAAA;AAAA,EAChB;AAEA,QAAM,WAAuC,CAAC,IAAI,EAAE,OAAO,eAAe;AAEtE,QAAI,UAAU;AAEV,mBAAa,IAAI,KAAK;AAAA,EAC9B;AAEO,SAAA;AAAA,IACH,YAAY;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,SAAS;AAAA,IAAA;AAAA,EAEjB;AACJ;;"}