@mantine/hooks
Version:
A collection of 50+ hooks for state and UI management
1 lines • 3.4 kB
Source Map (JSON)
{"version":3,"file":"use-focus-trap.mjs","names":[],"sources":["../../src/use-focus-trap/use-focus-trap.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { scopeTab } from './scope-tab';\nimport { FOCUS_SELECTOR, focusable, tabbable } from './tabbable';\n\nexport function useFocusTrap(active = true): React.RefCallback<HTMLElement | null> {\n const ref = useRef<HTMLElement>(null);\n\n const focusNode = (node: HTMLElement) => {\n let focusElement: HTMLElement | null = node.querySelector('[data-autofocus]');\n\n if (!focusElement) {\n const children = Array.from<HTMLElement>(node.querySelectorAll(FOCUS_SELECTOR));\n focusElement = children.find(tabbable) || children.find(focusable) || null;\n if (!focusElement && focusable(node)) {\n focusElement = node;\n }\n }\n\n if (focusElement) {\n focusElement.focus({ preventScroll: true });\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn(\n '[@mantine/hooks/use-focus-trap] Failed to find focusable element within provided node',\n node\n );\n }\n };\n\n const setRef = useCallback(\n (node: HTMLElement | null) => {\n if (!active) {\n return;\n }\n\n if (node === null) {\n ref.current = null;\n return;\n }\n\n if (ref.current === node) {\n return;\n }\n\n // Delay processing the HTML node by a frame. This ensures focus is assigned correctly.\n setTimeout(() => {\n if (node.getRootNode()) {\n focusNode(node);\n } else if (process.env.NODE_ENV === 'development') {\n // oxlint-disable-next-line no-console\n console.warn('[@mantine/hooks/use-focus-trap] Ref node is not part of the dom', node);\n }\n });\n\n ref.current = node;\n },\n [active]\n );\n\n useEffect(() => {\n if (!active) {\n return undefined;\n }\n\n if (ref.current) {\n setTimeout(() => {\n if (ref.current) {\n focusNode(ref.current);\n }\n });\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Tab' && ref.current) {\n scopeTab(ref.current, event);\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [active]);\n\n return setRef;\n}\n"],"mappings":";;;;;AAIA,SAAgB,aAAa,SAAS,MAA6C;CACjF,MAAM,MAAM,OAAoB,IAAI;CAEpC,MAAM,aAAa,SAAsB;EACvC,IAAI,eAAmC,KAAK,cAAc,kBAAkB;EAE5E,IAAI,CAAC,cAAc;GACjB,MAAM,WAAW,MAAM,KAAkB,KAAK,iBAAiB,cAAc,CAAC;GAC9E,eAAe,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK,SAAS,KAAK;GACtE,IAAI,CAAC,gBAAgB,UAAU,IAAI,GACjC,eAAe;EAEnB;EAEA,IAAI,cACF,aAAa,MAAM,EAAE,eAAe,KAAK,CAAC;OAG1C,QAAQ,KACN,yFACA,IACF;CAEJ;CAEA,MAAM,SAAS,aACZ,SAA6B;EAC5B,IAAI,CAAC,QACH;EAGF,IAAI,SAAS,MAAM;GACjB,IAAI,UAAU;GACd;EACF;EAEA,IAAI,IAAI,YAAY,MAClB;EAIF,iBAAiB;GACf,IAAI,KAAK,YAAY,GACnB,UAAU,IAAI;QAGd,QAAQ,KAAK,mEAAmE,IAAI;EAExF,CAAC;EAED,IAAI,UAAU;CAChB,GACA,CAAC,MAAM,CACT;CAEA,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,IAAI,IAAI,SACN,iBAAiB;GACf,IAAI,IAAI,SACN,UAAU,IAAI,OAAO;EAEzB,CAAC;EAGH,MAAM,iBAAiB,UAAyB;GAC9C,IAAI,MAAM,QAAQ,SAAS,IAAI,SAC7B,SAAS,IAAI,SAAS,KAAK;EAE/B;EAEA,SAAS,iBAAiB,WAAW,aAAa;EAClD,aAAa,SAAS,oBAAoB,WAAW,aAAa;CACpE,GAAG,CAAC,MAAM,CAAC;CAEX,OAAO;AACT"}