{"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;;"}