UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

1 lines 3.33 kB
{"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 return;\n }\n\n if (ref.current === node) {\n return;\n }\n\n if (node) {\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 } else {\n ref.current = null;\n }\n },\n [active]\n );\n\n useEffect(() => {\n if (!active) {\n return undefined;\n }\n\n ref.current && setTimeout(() => focusNode(ref.current!));\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,KAAK;CAErC,MAAM,aAAa,SAAsB;EACvC,IAAI,eAAmC,KAAK,cAAc,mBAAmB;AAE7E,MAAI,CAAC,cAAc;GACjB,MAAM,WAAW,MAAM,KAAkB,KAAK,iBAAiB,eAAe,CAAC;AAC/E,kBAAe,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,UAAU,IAAI;AACtE,OAAI,CAAC,gBAAgB,UAAU,KAAK,CAClC,gBAAe;;AAInB,MAAI,aACF,cAAa,MAAM,EAAE,eAAe,MAAM,CAAC;MAG3C,SAAQ,KACN,yFACA,KACD;;CAIL,MAAM,SAAS,aACZ,SAA6B;AAC5B,MAAI,CAAC,OACH;AAGF,MAAI,SAAS,KACX;AAGF,MAAI,IAAI,YAAY,KAClB;AAGF,MAAI,MAAM;AAER,oBAAiB;AACf,QAAI,KAAK,aAAa,CACpB,WAAU,KAAK;QAGf,SAAQ,KAAK,mEAAmE,KAAK;KAEvF;AAEF,OAAI,UAAU;QAEd,KAAI,UAAU;IAGlB,CAAC,OAAO,CACT;AAED,iBAAgB;AACd,MAAI,CAAC,OACH;AAGF,MAAI,WAAW,iBAAiB,UAAU,IAAI,QAAS,CAAC;EAExD,MAAM,iBAAiB,UAAyB;AAC9C,OAAI,MAAM,QAAQ,SAAS,IAAI,QAC7B,UAAS,IAAI,SAAS,MAAM;;AAIhC,WAAS,iBAAiB,WAAW,cAAc;AACnD,eAAa,SAAS,oBAAoB,WAAW,cAAc;IAClE,CAAC,OAAO,CAAC;AAEZ,QAAO"}