@oruga-ui/oruga-next
Version:
UI components for Vue.js and CSS framework agnostic
1 lines • 5.35 kB
Source Map (JSON)
{"version":3,"file":"useTrapFocus-DIWsoVDu.cjs","sources":["../../src/composables/useTrapFocus.ts"],"sourcesContent":["import {\n toValue,\n type DirectiveHook,\n type MaybeRefOrGetter,\n type ObjectDirective,\n} from \"vue\";\n\n/**\n * Returns all focusable elements inside the given element\n */\nexport function findFocusable(\n element: MaybeRefOrGetter<HTMLElement>,\n): NodeListOf<HTMLElement> {\n return toValue(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\n/**\n * Creates a vue v-trap-focus directive which sets the focus on the given element when mounted\n * and traps the focus inside the element.\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":["toValue"],"mappings":";;;AAUO,SAAS,cACZ,SACuB;AACvB,SAAOA,YAAQ,OAAO,EAAE,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oDAUO;AACpD;AAMO,SAAS,eAGd;AAEE,MAAI,YAAqD;AAEzD,WAAS,aAAa,IAAiB,OAAsB;AACzD,QAAI,OAAO;AAEP,SAAG,MAAM,EAAE,eAAe,KAAA,CAAM;AAGhC,UAAI,OAAO,cAAc;AACrB,WAAG,iBAAiB,WAAW,SAAS;AAAA,IAChD,OAAO;AAEH,UAAI,OAAO,cAAc;AACrB,WAAG,oBAAoB,WAAW,SAAS;AAAA,IACnD;AAAA,EACJ;AAEA,QAAM,YAAwC,CAAC,IAAI,EAAE,YAAY;AAE7D,gBAAY,CAAC,UAA+B;AACxC,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AAIb,YAAM,YAAY,cAAc,EAAE;AAClC,UAAI,CAAC,WAAW,QAAQ;AACpB,cAAM,eAAA;AACN;AAAA,MACJ;AAEA,YAAM,iBAAiB,UAAU,CAAC;AAClC,YAAM,gBAAgB,UAAU,UAAU,SAAS,CAAC;AAEpD,UACI,WAAW,kBACX,MAAM,YACN,MAAM,QAAQ,OAChB;AAEE,cAAM,eAAA;AACN,sBAAc,MAAA;AAAA,MAClB,WACI,WAAW,iBACX,CAAC,MAAM,YACP,MAAM,QAAQ,OAChB;AAEE,cAAM,eAAA;AACN,uBAAe,MAAA;AAAA,MACnB;AAAA,IACJ;AAGA,QAAI,MAAO,cAAa,IAAI,KAAK;AAAA,EACrC;AAGA,QAAM,kBAA8C,CAAC,OAAO;AAExD,iBAAa,IAAI,KAAK;AACtB,gBAAY;AAAA,EAChB;AAEA,QAAM,WAAuC,CAAC,IAAI,EAAE,OAAO,eAAe;AAEtE,QAAI,UAAU;AAEV,mBAAa,IAAI,KAAK;AAAA,EAC9B;AAEA,SAAO;AAAA,IACH,YAAY;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,SAAS;AAAA,IAAA;AAAA,EACb;AAER;;"}