🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@radix-ui/react-focus-scope

Package Overview
Dependencies
Maintainers
4
Versions
288
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@radix-ui/react-focus-scope - npm Package Compare versions

Comparing version
1.1.14
to
1.1.15
+36
-1
dist/index.d.mts

@@ -19,2 +19,11 @@ import * as React from 'react';

/**
* A list of nodes that should be treated as part of the focus scope even
* though they don't live within the scope's DOM subtree (eg. portalled
* content of a nested, non-modal layer). When the scope is `trapped`, focus
* is allowed to move into these branches without being reclaimed.
*
* See: https://github.com/radix-ui/primitives/issues/3423
*/
branches?: HTMLElement[];
/**
* Event handler called when auto-focusing on mount.

@@ -31,3 +40,29 @@ * Can be prevented.

declare const FocusScope: React.ForwardRefExoticComponent<FocusScopeProps & React.RefAttributes<HTMLDivElement>>;
/**
* Lets portalled content that is a React descendant of a (modal) layer — but rendered outside of
* that layer's DOM subtree — register itself as a "branch" of the layer's focus scope.
*
* A modal container (eg. `Dialog`) creates a registry via `useFocusScopeBranchRegistry`, renders a
* `FocusScopeBranchProvider` around its children, and passes the collected `nodes` to its trapped
* `FocusScope` (so focus isn't reclaimed from the branch) and, if applicable, to its `RemoveScroll`
* `shards` (so the branch remains scrollable). Nested layers (eg. a non-modal `Popover`) register
* their content node with `useFocusScopeBranch`.
*
* See: https://github.com/radix-ui/primitives/issues/3423
*/
interface FocusScopeBranchRegistry {
add: (node: HTMLElement) => void;
remove: (node: HTMLElement) => void;
}
declare const FocusScopeBranchProvider: React.Provider<FocusScopeBranchRegistry | null>;
declare function useFocusScopeBranchRegistry(): {
readonly nodes: HTMLElement[];
readonly registry: FocusScopeBranchRegistry;
};
/**
* Registers `node` as a branch of the nearest ancestor `FocusScopeBranchProvider`. No-ops when
* there is no ancestor registry (eg. the layer isn't nested inside a modal layer).
*/
declare function useFocusScopeBranch(node: HTMLElement | null): void;
export { FocusScope, type FocusScopeProps, FocusScope as Root };
export { FocusScope, FocusScopeBranchProvider, type FocusScopeBranchRegistry, type FocusScopeProps, FocusScope as Root, useFocusScopeBranch, useFocusScopeBranchRegistry };

@@ -19,2 +19,11 @@ import * as React from 'react';

/**
* A list of nodes that should be treated as part of the focus scope even
* though they don't live within the scope's DOM subtree (eg. portalled
* content of a nested, non-modal layer). When the scope is `trapped`, focus
* is allowed to move into these branches without being reclaimed.
*
* See: https://github.com/radix-ui/primitives/issues/3423
*/
branches?: HTMLElement[];
/**
* Event handler called when auto-focusing on mount.

@@ -31,3 +40,29 @@ * Can be prevented.

declare const FocusScope: React.ForwardRefExoticComponent<FocusScopeProps & React.RefAttributes<HTMLDivElement>>;
/**
* Lets portalled content that is a React descendant of a (modal) layer — but rendered outside of
* that layer's DOM subtree — register itself as a "branch" of the layer's focus scope.
*
* A modal container (eg. `Dialog`) creates a registry via `useFocusScopeBranchRegistry`, renders a
* `FocusScopeBranchProvider` around its children, and passes the collected `nodes` to its trapped
* `FocusScope` (so focus isn't reclaimed from the branch) and, if applicable, to its `RemoveScroll`
* `shards` (so the branch remains scrollable). Nested layers (eg. a non-modal `Popover`) register
* their content node with `useFocusScopeBranch`.
*
* See: https://github.com/radix-ui/primitives/issues/3423
*/
interface FocusScopeBranchRegistry {
add: (node: HTMLElement) => void;
remove: (node: HTMLElement) => void;
}
declare const FocusScopeBranchProvider: React.Provider<FocusScopeBranchRegistry | null>;
declare function useFocusScopeBranchRegistry(): {
readonly nodes: HTMLElement[];
readonly registry: FocusScopeBranchRegistry;
};
/**
* Registers `node` as a branch of the nearest ancestor `FocusScopeBranchProvider`. No-ops when
* there is no ancestor registry (eg. the layer isn't nested inside a modal layer).
*/
declare function useFocusScopeBranch(node: HTMLElement | null): void;
export { FocusScope, type FocusScopeProps, FocusScope as Root };
export { FocusScope, FocusScopeBranchProvider, type FocusScopeBranchRegistry, type FocusScopeProps, FocusScope as Root, useFocusScopeBranch, useFocusScopeBranchRegistry };

@@ -36,3 +36,6 @@ "use strict";

FocusScope: () => FocusScope,
Root: () => FocusScope
FocusScopeBranchProvider: () => FocusScopeBranchProvider,
Root: () => FocusScope,
useFocusScopeBranch: () => useFocusScopeBranch,
useFocusScopeBranchRegistry: () => useFocusScopeBranchRegistry
});

@@ -55,2 +58,3 @@ module.exports = __toCommonJS(index_exports);

trapped = false,
branches,
onMountAutoFocus: onMountAutoFocusProp,

@@ -65,2 +69,14 @@ onUnmountAutoFocus: onUnmountAutoFocusProp,

const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, setContainer);
const branchesRef = React.useRef(branches);
React.useEffect(() => {
branchesRef.current = branches;
});
const isTargetInScope = React.useCallback(
(target) => {
if (!target) return false;
if (container?.contains(target)) return true;
return Boolean(branchesRef.current?.some((branch) => branch.contains(target)));
},
[container]
);
const focusScope = React.useRef({

@@ -80,3 +96,3 @@ paused: false,

const target = event.target;
if (container.contains(target)) {
if (isTargetInScope(target)) {
lastFocusedElementRef.current = target;

@@ -90,3 +106,3 @@ } else {

if (relatedTarget === null) return;
if (!container.contains(relatedTarget)) {
if (!isTargetInScope(relatedTarget)) {
focus(lastFocusedElementRef.current, { select: true });

@@ -115,3 +131,3 @@ }

}
}, [trapped, container, focusScope.paused]);
}, [trapped, container, focusScope.paused, isTargetInScope]);
React.useEffect(() => {

@@ -176,2 +192,25 @@ if (container) {

);
var FocusScopeBranchContext = React.createContext(null);
var FocusScopeBranchProvider = FocusScopeBranchContext.Provider;
function useFocusScopeBranchRegistry() {
const [nodes, setNodes] = React.useState([]);
const registry = React.useMemo(
() => ({
add: /* @__PURE__ */ __name((node) => setNodes((prev) => prev.includes(node) ? prev : [...prev, node]), "add"),
remove: /* @__PURE__ */ __name((node) => setNodes((prev) => prev.filter((current) => current !== node)), "remove")
}),
[]
);
return { nodes, registry };
}
__name(useFocusScopeBranchRegistry, "useFocusScopeBranchRegistry");
function useFocusScopeBranch(node) {
const registry = React.useContext(FocusScopeBranchContext);
React.useEffect(() => {
if (!node || !registry) return;
registry.add(node);
return () => registry.remove(node);
}, [node, registry]);
}
__name(useFocusScopeBranch, "useFocusScopeBranch");
function focusFirst(candidates, { select = false } = {}) {

@@ -178,0 +217,0 @@ const previouslyFocusedElement = document.activeElement;

+2
-2
{
"version": 3,
"sources": ["../src/index.ts", "../src/focus-scope.tsx"],
"sourcesContent": ["'use client';\nexport {\n FocusScope,\n //\n Root,\n} from './focus-scope';\nexport type { FocusScopeProps } from './focus-scope';\n", "import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { Primitive } from '@radix-ui/react-primitive';\nimport { useCallbackRef } from '@radix-ui/react-use-callback-ref';\n\nconst AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nconst AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus(): void };\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeElement = React.ComponentRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface FocusScopeProps extends PrimitiveDivProps {\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n * @defaultValue false\n */\n loop?: boolean;\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n * @defaultValue false\n */\n trapped?: boolean;\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n */\n onMountAutoFocus?: (event: Event) => void;\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n */\n onUnmountAutoFocus?: (event: Event) => void;\n}\n\nconst FocusScope = /* @__PURE__ */ React.forwardRef<FocusScopeElement, FocusScopeProps>(\n function FocusScope(props, forwardedRef) {\n const {\n loop = false,\n trapped = false,\n onMountAutoFocus: onMountAutoFocusProp,\n onUnmountAutoFocus: onUnmountAutoFocusProp,\n ...scopeProps\n } = props;\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);\n const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, setContainer);\n\n const focusScope = React.useRef({\n paused: false,\n pause() {\n this.paused = true;\n },\n resume() {\n this.paused = false;\n },\n }).current;\n\n // Takes care of trapping focus if focus is moved outside programmatically for example\n React.useEffect(() => {\n if (trapped) {\n function handleFocusIn(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const target = event.target as HTMLElement | null;\n if (container.contains(target)) {\n lastFocusedElementRef.current = target;\n } else {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n function handleFocusOut(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at least two cases:\n //\n // 1. When the user switches app/tabs/windows/the browser itself loses focus.\n // 2. In Google Chrome, when the focused element is removed from the DOM.\n //\n // We let the browser do its thing here because:\n //\n // 1. The browser already keeps a memory of what's focused for when the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element (as per below), it\n // throws the CPU to 100%, so we avoid doing anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element (`relatedTarget !== null`)\n // that is outside the container, we move focus to the last valid focused element inside.\n if (!container.contains(relatedTarget)) {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n // When the focused element gets removed from the DOM, browsers move focus\n // back to the document.body. In this case, we move focus to the container\n // to keep focus trapped correctly.\n function handleMutations(mutations: MutationRecord[]) {\n const focusedElement = document.activeElement as HTMLElement | null;\n if (focusedElement !== document.body) return;\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) focus(container);\n }\n }\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) mutationObserver.observe(container, { childList: true, subtree: true });\n\n return () => {\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n };\n }\n }, [trapped, container, focusScope.paused]);\n\n React.useEffect(() => {\n if (container) {\n focusScopesStack.add(focusScope);\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = container.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n container.dispatchEvent(mountEvent);\n if (!mountEvent.defaultPrevented) {\n focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });\n if (document.activeElement === previouslyFocusedElement) {\n focus(container);\n }\n }\n }\n\n return () => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n\n // We hit a react bug (fixed in v17) with focusing in unmount.\n // We need to delay the focus a little to get around it for now.\n // See: https://github.com/facebook/react/issues/17894\n setTimeout(() => {\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n container.dispatchEvent(unmountEvent);\n if (!unmountEvent.defaultPrevented) {\n focus(previouslyFocusedElement ?? document.body, { select: true });\n }\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n\n focusScopesStack.remove(focusScope);\n }, 0);\n };\n }\n }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);\n\n // Takes care of looping focus (when tabbing whilst at the edges)\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!loop && !trapped) return;\n if (focusScope.paused) return;\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (loop) focus(first, { select: true });\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (loop) focus(last, { select: true });\n }\n }\n }\n },\n [loop, trapped, focusScope.paused],\n );\n\n return (\n <Primitive.div tabIndex={-1} {...scopeProps} ref={composedRefs} onKeyDown={handleKeyDown} />\n );\n },\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nfunction focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = document.activeElement;\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (document.activeElement !== previouslyFocusedElement) return;\n }\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nfunction getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates, container);\n const last = findVisible(candidates.reverse(), container);\n return [first, last] as const;\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nfunction getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';\n if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes;\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nfunction findVisible(elements: HTMLElement[], container: HTMLElement) {\n // `checkVisibility` performs a single native check for `visibility: hidden`\n // and `display: none` across the ancestor chain. This avoids the repeated\n // `getComputedStyle` calls per ancestor that the fallback path requires,\n // which reduces the cost of style resolution triggered by earlier DOM writes\n // (e.g. react-remove-scroll, DismissableLayer setting body styles).\n const canUseCheckVisibility =\n typeof container.checkVisibility === 'function' &&\n container.checkVisibility({ checkVisibilityCSS: true });\n\n for (const element of elements) {\n const hidden = canUseCheckVisibility\n ? !element.checkVisibility({ checkVisibilityCSS: true })\n : isHidden(element, { upTo: container });\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!hidden) {\n return element;\n }\n }\n}\n\nfunction isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n if (getComputedStyle(node).visibility === 'hidden') return true;\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo) return false;\n if (getComputedStyle(node).display === 'none') return true;\n node = node.parentElement as HTMLElement;\n }\n return false;\n}\n\nfunction isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nfunction focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = document.activeElement;\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select)\n element.select();\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope stack\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeAPI = { paused: boolean; pause(): void; resume(): void };\nconst focusScopesStack = createFocusScopesStack();\n\nfunction createFocusScopesStack() {\n /** A stack of focus scopes, with the active one at the top */\n let stack: FocusScopeAPI[] = [];\n\n return {\n add(focusScope: FocusScopeAPI) {\n // pause the currently active focus scope (at the top of the stack)\n const activeFocusScope = stack[0];\n if (focusScope !== activeFocusScope) {\n activeFocusScope?.pause();\n }\n // remove in case it already exists (because we'll re-add it at the top of the stack)\n stack = arrayRemove(stack, focusScope);\n stack.unshift(focusScope);\n },\n\n remove(focusScope: FocusScopeAPI) {\n stack = arrayRemove(stack, focusScope);\n stack[0]?.resume();\n },\n };\n}\n\nfunction arrayRemove<T>(array: T[], item: T) {\n const updatedArray = [...array];\n const index = updatedArray.indexOf(item);\n if (index !== -1) {\n updatedArray.splice(index, 1);\n }\n return updatedArray;\n}\n\nfunction removeLinks(items: HTMLElement[]) {\n return items.filter((item) => item.tagName !== 'A');\n}\n\nexport {\n FocusScope,\n //\n FocusScope as Root,\n};\nexport type { FocusScopeProps };\n"],
"mappings": "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",
"sourcesContent": ["'use client';\nexport {\n FocusScope,\n FocusScopeBranchProvider,\n useFocusScopeBranchRegistry,\n useFocusScopeBranch,\n //\n Root,\n} from './focus-scope';\nexport type { FocusScopeProps, FocusScopeBranchRegistry } from './focus-scope';\n", "import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { Primitive } from '@radix-ui/react-primitive';\nimport { useCallbackRef } from '@radix-ui/react-use-callback-ref';\n\nconst AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nconst AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus(): void };\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeElement = React.ComponentRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface FocusScopeProps extends PrimitiveDivProps {\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n * @defaultValue false\n */\n loop?: boolean;\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n * @defaultValue false\n */\n trapped?: boolean;\n\n /**\n * A list of nodes that should be treated as part of the focus scope even\n * though they don't live within the scope's DOM subtree (eg. portalled\n * content of a nested, non-modal layer). When the scope is `trapped`, focus\n * is allowed to move into these branches without being reclaimed.\n *\n * See: https://github.com/radix-ui/primitives/issues/3423\n */\n branches?: HTMLElement[];\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n */\n onMountAutoFocus?: (event: Event) => void;\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n */\n onUnmountAutoFocus?: (event: Event) => void;\n}\n\nconst FocusScope = /* @__PURE__ */ React.forwardRef<FocusScopeElement, FocusScopeProps>(\n function FocusScope(props, forwardedRef) {\n const {\n loop = false,\n trapped = false,\n branches,\n onMountAutoFocus: onMountAutoFocusProp,\n onUnmountAutoFocus: onUnmountAutoFocusProp,\n ...scopeProps\n } = props;\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);\n const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, setContainer);\n\n // Keep the latest branches in a ref so the trap effect below doesn't need to resubscribe its\n // listeners whenever the branch list updates. We sync it in the commit phase (not during render)\n // to stay safe under concurrent rendering, where a render can be discarded or replayed. The trap's\n // focus event handlers only run in response to user interaction (well after commit), so reading\n // the ref from them always sees the committed branch list.\n const branchesRef = React.useRef(branches);\n React.useEffect(() => {\n branchesRef.current = branches;\n });\n const isTargetInScope = React.useCallback(\n (target: HTMLElement | null) => {\n if (!target) return false;\n if (container?.contains(target)) return true;\n return Boolean(branchesRef.current?.some((branch) => branch.contains(target)));\n },\n [container],\n );\n\n const focusScope = React.useRef({\n paused: false,\n pause() {\n this.paused = true;\n },\n resume() {\n this.paused = false;\n },\n }).current;\n\n // Takes care of trapping focus if focus is moved outside programmatically for example\n React.useEffect(() => {\n if (trapped) {\n function handleFocusIn(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const target = event.target as HTMLElement | null;\n if (isTargetInScope(target)) {\n lastFocusedElementRef.current = target;\n } else {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n function handleFocusOut(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at\n // least two cases:\n // 1. When the user switches app/tabs/windows/the browser itself loses\n // focus.\n // 2. In Google Chrome, when the focused element is removed from the\n // DOM.\n //\n // We let the browser do its thing here because:\n // 1. The browser already keeps a memory of what's focused for when\n // the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element\n // (as per below), it throws the CPU to 100%, so we avoid doing\n // anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element\n // (`relatedTarget !== null`) that is outside the container (and any\n // registered branches), we move focus to the last valid focused\n // element inside.\n if (!isTargetInScope(relatedTarget)) {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n // When the focused element gets removed from the DOM, browsers move focus\n // back to the document.body. In this case, we move focus to the container\n // to keep focus trapped correctly.\n function handleMutations(mutations: MutationRecord[]) {\n const focusedElement = document.activeElement as HTMLElement | null;\n if (focusedElement !== document.body) return;\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) focus(container);\n }\n }\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) mutationObserver.observe(container, { childList: true, subtree: true });\n\n return () => {\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n };\n }\n }, [trapped, container, focusScope.paused, isTargetInScope]);\n\n React.useEffect(() => {\n if (container) {\n focusScopesStack.add(focusScope);\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = container.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n container.dispatchEvent(mountEvent);\n if (!mountEvent.defaultPrevented) {\n focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });\n if (document.activeElement === previouslyFocusedElement) {\n focus(container);\n }\n }\n }\n\n return () => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n\n // We hit a react bug (fixed in v17) with focusing in unmount.\n // We need to delay the focus a little to get around it for now.\n // See: https://github.com/facebook/react/issues/17894\n setTimeout(() => {\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n container.dispatchEvent(unmountEvent);\n if (!unmountEvent.defaultPrevented) {\n focus(previouslyFocusedElement ?? document.body, { select: true });\n }\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n\n focusScopesStack.remove(focusScope);\n }, 0);\n };\n }\n }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);\n\n // Takes care of looping focus (when tabbing whilst at the edges)\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!loop && !trapped) return;\n if (focusScope.paused) return;\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (loop) focus(first, { select: true });\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (loop) focus(last, { select: true });\n }\n }\n }\n },\n [loop, trapped, focusScope.paused],\n );\n\n return (\n <Primitive.div tabIndex={-1} {...scopeProps} ref={composedRefs} onKeyDown={handleKeyDown} />\n );\n },\n);\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope branch registry\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Lets portalled content that is a React descendant of a (modal) layer \u2014 but rendered outside of\n * that layer's DOM subtree \u2014 register itself as a \"branch\" of the layer's focus scope.\n *\n * A modal container (eg. `Dialog`) creates a registry via `useFocusScopeBranchRegistry`, renders a\n * `FocusScopeBranchProvider` around its children, and passes the collected `nodes` to its trapped\n * `FocusScope` (so focus isn't reclaimed from the branch) and, if applicable, to its `RemoveScroll`\n * `shards` (so the branch remains scrollable). Nested layers (eg. a non-modal `Popover`) register\n * their content node with `useFocusScopeBranch`.\n *\n * See: https://github.com/radix-ui/primitives/issues/3423\n */\ninterface FocusScopeBranchRegistry {\n add: (node: HTMLElement) => void;\n remove: (node: HTMLElement) => void;\n}\n\nconst FocusScopeBranchContext = React.createContext<FocusScopeBranchRegistry | null>(null);\n\nconst FocusScopeBranchProvider = FocusScopeBranchContext.Provider;\n\nfunction useFocusScopeBranchRegistry() {\n const [nodes, setNodes] = React.useState<HTMLElement[]>([]);\n const registry = React.useMemo<FocusScopeBranchRegistry>(\n () => ({\n add: (node) => setNodes((prev) => (prev.includes(node) ? prev : [...prev, node])),\n remove: (node) => setNodes((prev) => prev.filter((current) => current !== node)),\n }),\n [],\n );\n return { nodes, registry } as const;\n}\n\n/**\n * Registers `node` as a branch of the nearest ancestor `FocusScopeBranchProvider`. No-ops when\n * there is no ancestor registry (eg. the layer isn't nested inside a modal layer).\n */\nfunction useFocusScopeBranch(node: HTMLElement | null) {\n const registry = React.useContext(FocusScopeBranchContext);\n React.useEffect(() => {\n if (!node || !registry) return;\n registry.add(node);\n return () => registry.remove(node);\n }, [node, registry]);\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nfunction focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = document.activeElement;\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (document.activeElement !== previouslyFocusedElement) return;\n }\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nfunction getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates, container);\n const last = findVisible(candidates.reverse(), container);\n return [first, last] as const;\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nfunction getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';\n if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes;\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nfunction findVisible(elements: HTMLElement[], container: HTMLElement) {\n // `checkVisibility` performs a single native check for `visibility: hidden`\n // and `display: none` across the ancestor chain. This avoids the repeated\n // `getComputedStyle` calls per ancestor that the fallback path requires,\n // which reduces the cost of style resolution triggered by earlier DOM writes\n // (e.g. react-remove-scroll, DismissableLayer setting body styles).\n const canUseCheckVisibility =\n typeof container.checkVisibility === 'function' &&\n container.checkVisibility({ checkVisibilityCSS: true });\n\n for (const element of elements) {\n const hidden = canUseCheckVisibility\n ? !element.checkVisibility({ checkVisibilityCSS: true })\n : isHidden(element, { upTo: container });\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!hidden) {\n return element;\n }\n }\n}\n\nfunction isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n if (getComputedStyle(node).visibility === 'hidden') return true;\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo) return false;\n if (getComputedStyle(node).display === 'none') return true;\n node = node.parentElement as HTMLElement;\n }\n return false;\n}\n\nfunction isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nfunction focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = document.activeElement;\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select)\n element.select();\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope stack\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeAPI = { paused: boolean; pause(): void; resume(): void };\nconst focusScopesStack = createFocusScopesStack();\n\nfunction createFocusScopesStack() {\n /** A stack of focus scopes, with the active one at the top */\n let stack: FocusScopeAPI[] = [];\n\n return {\n add(focusScope: FocusScopeAPI) {\n // pause the currently active focus scope (at the top of the stack)\n const activeFocusScope = stack[0];\n if (focusScope !== activeFocusScope) {\n activeFocusScope?.pause();\n }\n // remove in case it already exists (because we'll re-add it at the top of the stack)\n stack = arrayRemove(stack, focusScope);\n stack.unshift(focusScope);\n },\n\n remove(focusScope: FocusScopeAPI) {\n stack = arrayRemove(stack, focusScope);\n stack[0]?.resume();\n },\n };\n}\n\nfunction arrayRemove<T>(array: T[], item: T) {\n const updatedArray = [...array];\n const index = updatedArray.indexOf(item);\n if (index !== -1) {\n updatedArray.splice(index, 1);\n }\n return updatedArray;\n}\n\nfunction removeLinks(items: HTMLElement[]) {\n return items.filter((item) => item.tagName !== 'A');\n}\n\nexport {\n FocusScope,\n FocusScopeBranchProvider,\n useFocusScopeBranchRegistry,\n useFocusScopeBranch,\n //\n FocusScope as Root,\n};\nexport type { FocusScopeProps, FocusScopeBranchRegistry };\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,gCAAgC;AAChC,6BAA0B;AAC1B,oCAA+B;AAyOzB;AAvON,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAC7B,IAAM,gBAAgB,EAAE,SAAS,OAAO,YAAY,KAAK;AAgDzD,IAAM,aAA6B,gBAAM;AAAA,EACvC,gCAASA,YAAW,OAAO,cAAc;AACvC,UAAM;AAAA,MACJ,OAAO;AAAA,MACP,UAAU;AAAA,MACV;AAAA,MACA,kBAAkB;AAAA,MAClB,oBAAoB;AAAA,MACpB,GAAG;AAAA,IACL,IAAI;AACJ,UAAM,CAAC,WAAW,YAAY,IAAU,eAA6B,IAAI;AACzE,UAAM,uBAAmB,8CAAe,oBAAoB;AAC5D,UAAM,yBAAqB,8CAAe,sBAAsB;AAChE,UAAM,wBAA8B,aAA2B,IAAI;AACnE,UAAM,mBAAe,2CAAgB,cAAc,YAAY;AAO/D,UAAM,cAAoB,aAAO,QAAQ;AACzC,IAAM,gBAAU,MAAM;AACpB,kBAAY,UAAU;AAAA,IACxB,CAAC;AACD,UAAM,kBAAwB;AAAA,MAC5B,CAAC,WAA+B;AAC9B,YAAI,CAAC,OAAQ,QAAO;AACpB,YAAI,WAAW,SAAS,MAAM,EAAG,QAAO;AACxC,eAAO,QAAQ,YAAY,SAAS,KAAK,CAAC,WAAW,OAAO,SAAS,MAAM,CAAC,CAAC;AAAA,MAC/E;AAAA,MACA,CAAC,SAAS;AAAA,IACZ;AAEA,UAAM,aAAmB,aAAO;AAAA,MAC9B,QAAQ;AAAA,MACR,QAAQ;AACN,aAAK,SAAS;AAAA,MAChB;AAAA,MACA,SAAS;AACP,aAAK,SAAS;AAAA,MAChB;AAAA,IACF,CAAC,EAAE;AAGH,IAAM,gBAAU,MAAM;AACpB,UAAI,SAAS;AACX,YAASC,iBAAT,SAAuB,OAAmB;AACxC,cAAI,WAAW,UAAU,CAAC,UAAW;AACrC,gBAAM,SAAS,MAAM;AACrB,cAAI,gBAAgB,MAAM,GAAG;AAC3B,kCAAsB,UAAU;AAAA,UAClC,OAAO;AACL,kBAAM,sBAAsB,SAAS,EAAE,QAAQ,KAAK,CAAC;AAAA,UACvD;AAAA,QACF,GAESC,kBAAT,SAAwB,OAAmB;AACzC,cAAI,WAAW,UAAU,CAAC,UAAW;AACrC,gBAAM,gBAAgB,MAAM;AAe5B,cAAI,kBAAkB,KAAM;AAM5B,cAAI,CAAC,gBAAgB,aAAa,GAAG;AACnC,kBAAM,sBAAsB,SAAS,EAAE,QAAQ,KAAK,CAAC;AAAA,UACvD;AAAA,QACF,GAKSC,mBAAT,SAAyB,WAA6B;AACpD,gBAAM,iBAAiB,SAAS;AAChC,cAAI,mBAAmB,SAAS,KAAM;AACtC,qBAAW,YAAY,WAAW;AAChC,gBAAI,SAAS,aAAa,SAAS,EAAG,OAAM,SAAS;AAAA,UACvD;AAAA,QACF;AA/CS,4BAAAF,gBAUA,iBAAAC,iBA+BA,kBAAAC;AAzCA,eAAAF,gBAAA;AAUA,eAAAC,iBAAA;AA+BA,eAAAC,kBAAA;AAQT,iBAAS,iBAAiB,WAAWF,cAAa;AAClD,iBAAS,iBAAiB,YAAYC,eAAc;AACpD,cAAM,mBAAmB,IAAI,iBAAiBC,gBAAe;AAC7D,YAAI,UAAW,kBAAiB,QAAQ,WAAW,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AAErF,eAAO,MAAM;AACX,mBAAS,oBAAoB,WAAWF,cAAa;AACrD,mBAAS,oBAAoB,YAAYC,eAAc;AACvD,2BAAiB,WAAW;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,GAAG,CAAC,SAAS,WAAW,WAAW,QAAQ,eAAe,CAAC;AAE3D,IAAM,gBAAU,MAAM;AACpB,UAAI,WAAW;AACb,yBAAiB,IAAI,UAAU;AAC/B,cAAM,2BAA2B,SAAS;AAC1C,cAAM,sBAAsB,UAAU,SAAS,wBAAwB;AAEvE,YAAI,CAAC,qBAAqB;AACxB,gBAAM,aAAa,IAAI,YAAY,oBAAoB,aAAa;AACpE,oBAAU,iBAAiB,oBAAoB,gBAAgB;AAC/D,oBAAU,cAAc,UAAU;AAClC,cAAI,CAAC,WAAW,kBAAkB;AAChC,uBAAW,YAAY,sBAAsB,SAAS,CAAC,GAAG,EAAE,QAAQ,KAAK,CAAC;AAC1E,gBAAI,SAAS,kBAAkB,0BAA0B;AACvD,oBAAM,SAAS;AAAA,YACjB;AAAA,UACF;AAAA,QACF;AAEA,eAAO,MAAM;AACX,oBAAU,oBAAoB,oBAAoB,gBAAgB;AAKlE,qBAAW,MAAM;AACf,kBAAM,eAAe,IAAI,YAAY,sBAAsB,aAAa;AACxE,sBAAU,iBAAiB,sBAAsB,kBAAkB;AACnE,sBAAU,cAAc,YAAY;AACpC,gBAAI,CAAC,aAAa,kBAAkB;AAClC,oBAAM,4BAA4B,SAAS,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,YACnE;AAEA,sBAAU,oBAAoB,sBAAsB,kBAAkB;AAEtE,6BAAiB,OAAO,UAAU;AAAA,UACpC,GAAG,CAAC;AAAA,QACN;AAAA,MACF;AAAA,IACF,GAAG,CAAC,WAAW,kBAAkB,oBAAoB,UAAU,CAAC;AAGhE,UAAM,gBAAsB;AAAA,MAC1B,CAAC,UAA+B;AAC9B,YAAI,CAAC,QAAQ,CAAC,QAAS;AACvB,YAAI,WAAW,OAAQ;AAEvB,cAAM,WAAW,MAAM,QAAQ,SAAS,CAAC,MAAM,UAAU,CAAC,MAAM,WAAW,CAAC,MAAM;AAClF,cAAM,iBAAiB,SAAS;AAEhC,YAAI,YAAY,gBAAgB;AAC9B,gBAAME,aAAY,MAAM;AACxB,gBAAM,CAAC,OAAO,IAAI,IAAI,iBAAiBA,UAAS;AAChD,gBAAM,4BAA4B,SAAS;AAG3C,cAAI,CAAC,2BAA2B;AAC9B,gBAAI,mBAAmBA,WAAW,OAAM,eAAe;AAAA,UACzD,OAAO;AACL,gBAAI,CAAC,MAAM,YAAY,mBAAmB,MAAM;AAC9C,oBAAM,eAAe;AACrB,kBAAI,KAAM,OAAM,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,YACzC,WAAW,MAAM,YAAY,mBAAmB,OAAO;AACrD,oBAAM,eAAe;AACrB,kBAAI,KAAM,OAAM,MAAM,EAAE,QAAQ,KAAK,CAAC;AAAA,YACxC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,CAAC,MAAM,SAAS,WAAW,MAAM;AAAA,IACnC;AAEA,WACE,4CAAC,iCAAU,KAAV,EAAc,UAAU,IAAK,GAAG,YAAY,KAAK,cAAc,WAAW,eAAe;AAAA,EAE9F,GAtLA;AAuLF;AAuBA,IAAM,0BAAgC,oBAA+C,IAAI;AAEzF,IAAM,2BAA2B,wBAAwB;AAEzD,SAAS,8BAA8B;AACrC,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAwB,CAAC,CAAC;AAC1D,QAAM,WAAiB;AAAA,IACrB,OAAO;AAAA,MACL,KAAK,wBAAC,SAAS,SAAS,CAAC,SAAU,KAAK,SAAS,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,IAAI,CAAE,GAA3E;AAAA,MACL,QAAQ,wBAAC,SAAS,SAAS,CAAC,SAAS,KAAK,OAAO,CAAC,YAAY,YAAY,IAAI,CAAC,GAAvE;AAAA,IACV;AAAA,IACA,CAAC;AAAA,EACH;AACA,SAAO,EAAE,OAAO,SAAS;AAC3B;AAVS;AAgBT,SAAS,oBAAoB,MAA0B;AACrD,QAAM,WAAiB,iBAAW,uBAAuB;AACzD,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,QAAQ,CAAC,SAAU;AACxB,aAAS,IAAI,IAAI;AACjB,WAAO,MAAM,SAAS,OAAO,IAAI;AAAA,EACnC,GAAG,CAAC,MAAM,QAAQ,CAAC;AACrB;AAPS;AAiBT,SAAS,WAAW,YAA2B,EAAE,SAAS,MAAM,IAAI,CAAC,GAAG;AACtE,QAAM,2BAA2B,SAAS;AAC1C,aAAW,aAAa,YAAY;AAClC,UAAM,WAAW,EAAE,OAAO,CAAC;AAC3B,QAAI,SAAS,kBAAkB,yBAA0B;AAAA,EAC3D;AACF;AANS;AAWT,SAAS,iBAAiB,WAAwB;AAChD,QAAM,aAAa,sBAAsB,SAAS;AAClD,QAAM,QAAQ,YAAY,YAAY,SAAS;AAC/C,QAAM,OAAO,YAAY,WAAW,QAAQ,GAAG,SAAS;AACxD,SAAO,CAAC,OAAO,IAAI;AACrB;AALS;AAiBT,SAAS,sBAAsB,WAAwB;AACrD,QAAM,QAAuB,CAAC;AAC9B,QAAM,SAAS,SAAS,iBAAiB,WAAW,WAAW,cAAc;AAAA,IAC3E,YAAY,wBAAC,SAAc;AACzB,YAAM,gBAAgB,KAAK,YAAY,WAAW,KAAK,SAAS;AAChE,UAAI,KAAK,YAAY,KAAK,UAAU,cAAe,QAAO,WAAW;AAIrE,aAAO,KAAK,YAAY,IAAI,WAAW,gBAAgB,WAAW;AAAA,IACpE,GAPY;AAAA,EAQd,CAAC;AACD,SAAO,OAAO,SAAS,EAAG,OAAM,KAAK,OAAO,WAA0B;AAGtE,SAAO;AACT;AAhBS;AAsBT,SAAS,YAAY,UAAyB,WAAwB;AAMpE,QAAM,wBACJ,OAAO,UAAU,oBAAoB,cACrC,UAAU,gBAAgB,EAAE,oBAAoB,KAAK,CAAC;AAExD,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,wBACX,CAAC,QAAQ,gBAAgB,EAAE,oBAAoB,KAAK,CAAC,IACrD,SAAS,SAAS,EAAE,MAAM,UAAU,CAAC;AAEzC,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAnBS;AAqBT,SAAS,SAAS,MAAmB,EAAE,KAAK,GAA2B;AACrE,MAAI,iBAAiB,IAAI,EAAE,eAAe,SAAU,QAAO;AAC3D,SAAO,MAAM;AAEX,QAAI,SAAS,UAAa,SAAS,KAAM,QAAO;AAChD,QAAI,iBAAiB,IAAI,EAAE,YAAY,OAAQ,QAAO;AACtD,WAAO,KAAK;AAAA,EACd;AACA,SAAO;AACT;AATS;AAWT,SAAS,kBAAkB,SAAmE;AAC5F,SAAO,mBAAmB,oBAAoB,YAAY;AAC5D;AAFS;AAIT,SAAS,MAAM,SAAkC,EAAE,SAAS,MAAM,IAAI,CAAC,GAAG;AAExE,MAAI,WAAW,QAAQ,OAAO;AAC5B,UAAM,2BAA2B,SAAS;AAE1C,YAAQ,MAAM,EAAE,eAAe,KAAK,CAAC;AAErC,QAAI,YAAY,4BAA4B,kBAAkB,OAAO,KAAK;AACxE,cAAQ,OAAO;AAAA,EACnB;AACF;AAVS;AAiBT,IAAM,mBAAmB,uBAAuB;AAEhD,SAAS,yBAAyB;AAEhC,MAAI,QAAyB,CAAC;AAE9B,SAAO;AAAA,IACL,IAAI,YAA2B;AAE7B,YAAM,mBAAmB,MAAM,CAAC;AAChC,UAAI,eAAe,kBAAkB;AACnC,0BAAkB,MAAM;AAAA,MAC1B;AAEA,cAAQ,YAAY,OAAO,UAAU;AACrC,YAAM,QAAQ,UAAU;AAAA,IAC1B;AAAA,IAEA,OAAO,YAA2B;AAChC,cAAQ,YAAY,OAAO,UAAU;AACrC,YAAM,CAAC,GAAG,OAAO;AAAA,IACnB;AAAA,EACF;AACF;AArBS;AAuBT,SAAS,YAAe,OAAY,MAAS;AAC3C,QAAM,eAAe,CAAC,GAAG,KAAK;AAC9B,QAAM,QAAQ,aAAa,QAAQ,IAAI;AACvC,MAAI,UAAU,IAAI;AAChB,iBAAa,OAAO,OAAO,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAPS;AAST,SAAS,YAAY,OAAsB;AACzC,SAAO,MAAM,OAAO,CAAC,SAAS,KAAK,YAAY,GAAG;AACpD;AAFS;",
"names": ["FocusScope", "handleFocusIn", "handleFocusOut", "handleMutations", "container"]
}

@@ -19,2 +19,3 @@ "use client";

trapped = false,
branches,
onMountAutoFocus: onMountAutoFocusProp,

@@ -29,2 +30,14 @@ onUnmountAutoFocus: onUnmountAutoFocusProp,

const composedRefs = useComposedRefs(forwardedRef, setContainer);
const branchesRef = React.useRef(branches);
React.useEffect(() => {
branchesRef.current = branches;
});
const isTargetInScope = React.useCallback(
(target) => {
if (!target) return false;
if (container?.contains(target)) return true;
return Boolean(branchesRef.current?.some((branch) => branch.contains(target)));
},
[container]
);
const focusScope = React.useRef({

@@ -44,3 +57,3 @@ paused: false,

const target = event.target;
if (container.contains(target)) {
if (isTargetInScope(target)) {
lastFocusedElementRef.current = target;

@@ -54,3 +67,3 @@ } else {

if (relatedTarget === null) return;
if (!container.contains(relatedTarget)) {
if (!isTargetInScope(relatedTarget)) {
focus(lastFocusedElementRef.current, { select: true });

@@ -79,3 +92,3 @@ }

}
}, [trapped, container, focusScope.paused]);
}, [trapped, container, focusScope.paused, isTargetInScope]);
React.useEffect(() => {

@@ -140,2 +153,25 @@ if (container) {

);
var FocusScopeBranchContext = React.createContext(null);
var FocusScopeBranchProvider = FocusScopeBranchContext.Provider;
function useFocusScopeBranchRegistry() {
const [nodes, setNodes] = React.useState([]);
const registry = React.useMemo(
() => ({
add: /* @__PURE__ */ __name((node) => setNodes((prev) => prev.includes(node) ? prev : [...prev, node]), "add"),
remove: /* @__PURE__ */ __name((node) => setNodes((prev) => prev.filter((current) => current !== node)), "remove")
}),
[]
);
return { nodes, registry };
}
__name(useFocusScopeBranchRegistry, "useFocusScopeBranchRegistry");
function useFocusScopeBranch(node) {
const registry = React.useContext(FocusScopeBranchContext);
React.useEffect(() => {
if (!node || !registry) return;
registry.add(node);
return () => registry.remove(node);
}, [node, registry]);
}
__name(useFocusScopeBranch, "useFocusScopeBranch");
function focusFirst(candidates, { select = false } = {}) {

@@ -236,4 +272,7 @@ const previouslyFocusedElement = document.activeElement;

FocusScope,
FocusScope as Root
FocusScopeBranchProvider,
FocusScope as Root,
useFocusScopeBranch,
useFocusScopeBranchRegistry
};
//# sourceMappingURL=index.mjs.map
{
"version": 3,
"sources": ["../src/focus-scope.tsx"],
"sourcesContent": ["import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { Primitive } from '@radix-ui/react-primitive';\nimport { useCallbackRef } from '@radix-ui/react-use-callback-ref';\n\nconst AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nconst AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus(): void };\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeElement = React.ComponentRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface FocusScopeProps extends PrimitiveDivProps {\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n * @defaultValue false\n */\n loop?: boolean;\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n * @defaultValue false\n */\n trapped?: boolean;\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n */\n onMountAutoFocus?: (event: Event) => void;\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n */\n onUnmountAutoFocus?: (event: Event) => void;\n}\n\nconst FocusScope = /* @__PURE__ */ React.forwardRef<FocusScopeElement, FocusScopeProps>(\n function FocusScope(props, forwardedRef) {\n const {\n loop = false,\n trapped = false,\n onMountAutoFocus: onMountAutoFocusProp,\n onUnmountAutoFocus: onUnmountAutoFocusProp,\n ...scopeProps\n } = props;\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);\n const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, setContainer);\n\n const focusScope = React.useRef({\n paused: false,\n pause() {\n this.paused = true;\n },\n resume() {\n this.paused = false;\n },\n }).current;\n\n // Takes care of trapping focus if focus is moved outside programmatically for example\n React.useEffect(() => {\n if (trapped) {\n function handleFocusIn(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const target = event.target as HTMLElement | null;\n if (container.contains(target)) {\n lastFocusedElementRef.current = target;\n } else {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n function handleFocusOut(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at least two cases:\n //\n // 1. When the user switches app/tabs/windows/the browser itself loses focus.\n // 2. In Google Chrome, when the focused element is removed from the DOM.\n //\n // We let the browser do its thing here because:\n //\n // 1. The browser already keeps a memory of what's focused for when the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element (as per below), it\n // throws the CPU to 100%, so we avoid doing anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element (`relatedTarget !== null`)\n // that is outside the container, we move focus to the last valid focused element inside.\n if (!container.contains(relatedTarget)) {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n // When the focused element gets removed from the DOM, browsers move focus\n // back to the document.body. In this case, we move focus to the container\n // to keep focus trapped correctly.\n function handleMutations(mutations: MutationRecord[]) {\n const focusedElement = document.activeElement as HTMLElement | null;\n if (focusedElement !== document.body) return;\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) focus(container);\n }\n }\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) mutationObserver.observe(container, { childList: true, subtree: true });\n\n return () => {\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n };\n }\n }, [trapped, container, focusScope.paused]);\n\n React.useEffect(() => {\n if (container) {\n focusScopesStack.add(focusScope);\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = container.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n container.dispatchEvent(mountEvent);\n if (!mountEvent.defaultPrevented) {\n focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });\n if (document.activeElement === previouslyFocusedElement) {\n focus(container);\n }\n }\n }\n\n return () => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n\n // We hit a react bug (fixed in v17) with focusing in unmount.\n // We need to delay the focus a little to get around it for now.\n // See: https://github.com/facebook/react/issues/17894\n setTimeout(() => {\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n container.dispatchEvent(unmountEvent);\n if (!unmountEvent.defaultPrevented) {\n focus(previouslyFocusedElement ?? document.body, { select: true });\n }\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n\n focusScopesStack.remove(focusScope);\n }, 0);\n };\n }\n }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);\n\n // Takes care of looping focus (when tabbing whilst at the edges)\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!loop && !trapped) return;\n if (focusScope.paused) return;\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (loop) focus(first, { select: true });\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (loop) focus(last, { select: true });\n }\n }\n }\n },\n [loop, trapped, focusScope.paused],\n );\n\n return (\n <Primitive.div tabIndex={-1} {...scopeProps} ref={composedRefs} onKeyDown={handleKeyDown} />\n );\n },\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nfunction focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = document.activeElement;\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (document.activeElement !== previouslyFocusedElement) return;\n }\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nfunction getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates, container);\n const last = findVisible(candidates.reverse(), container);\n return [first, last] as const;\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nfunction getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';\n if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes;\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nfunction findVisible(elements: HTMLElement[], container: HTMLElement) {\n // `checkVisibility` performs a single native check for `visibility: hidden`\n // and `display: none` across the ancestor chain. This avoids the repeated\n // `getComputedStyle` calls per ancestor that the fallback path requires,\n // which reduces the cost of style resolution triggered by earlier DOM writes\n // (e.g. react-remove-scroll, DismissableLayer setting body styles).\n const canUseCheckVisibility =\n typeof container.checkVisibility === 'function' &&\n container.checkVisibility({ checkVisibilityCSS: true });\n\n for (const element of elements) {\n const hidden = canUseCheckVisibility\n ? !element.checkVisibility({ checkVisibilityCSS: true })\n : isHidden(element, { upTo: container });\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!hidden) {\n return element;\n }\n }\n}\n\nfunction isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n if (getComputedStyle(node).visibility === 'hidden') return true;\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo) return false;\n if (getComputedStyle(node).display === 'none') return true;\n node = node.parentElement as HTMLElement;\n }\n return false;\n}\n\nfunction isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nfunction focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = document.activeElement;\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select)\n element.select();\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope stack\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeAPI = { paused: boolean; pause(): void; resume(): void };\nconst focusScopesStack = createFocusScopesStack();\n\nfunction createFocusScopesStack() {\n /** A stack of focus scopes, with the active one at the top */\n let stack: FocusScopeAPI[] = [];\n\n return {\n add(focusScope: FocusScopeAPI) {\n // pause the currently active focus scope (at the top of the stack)\n const activeFocusScope = stack[0];\n if (focusScope !== activeFocusScope) {\n activeFocusScope?.pause();\n }\n // remove in case it already exists (because we'll re-add it at the top of the stack)\n stack = arrayRemove(stack, focusScope);\n stack.unshift(focusScope);\n },\n\n remove(focusScope: FocusScopeAPI) {\n stack = arrayRemove(stack, focusScope);\n stack[0]?.resume();\n },\n };\n}\n\nfunction arrayRemove<T>(array: T[], item: T) {\n const updatedArray = [...array];\n const index = updatedArray.indexOf(item);\n if (index !== -1) {\n updatedArray.splice(index, 1);\n }\n return updatedArray;\n}\n\nfunction removeLinks(items: HTMLElement[]) {\n return items.filter((item) => item.tagName !== 'A');\n}\n\nexport {\n FocusScope,\n //\n FocusScope as Root,\n};\nexport type { FocusScopeProps };\n"],
"mappings": "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",
"sourcesContent": ["import * as React from 'react';\nimport { useComposedRefs } from '@radix-ui/react-compose-refs';\nimport { Primitive } from '@radix-ui/react-primitive';\nimport { useCallbackRef } from '@radix-ui/react-use-callback-ref';\n\nconst AUTOFOCUS_ON_MOUNT = 'focusScope.autoFocusOnMount';\nconst AUTOFOCUS_ON_UNMOUNT = 'focusScope.autoFocusOnUnmount';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\ntype FocusableTarget = HTMLElement | { focus(): void };\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeElement = React.ComponentRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface FocusScopeProps extends PrimitiveDivProps {\n /**\n * When `true`, tabbing from last item will focus first tabbable\n * and shift+tab from first item will focus last tababble.\n * @defaultValue false\n */\n loop?: boolean;\n\n /**\n * When `true`, focus cannot escape the focus scope via keyboard,\n * pointer, or a programmatic focus.\n * @defaultValue false\n */\n trapped?: boolean;\n\n /**\n * A list of nodes that should be treated as part of the focus scope even\n * though they don't live within the scope's DOM subtree (eg. portalled\n * content of a nested, non-modal layer). When the scope is `trapped`, focus\n * is allowed to move into these branches without being reclaimed.\n *\n * See: https://github.com/radix-ui/primitives/issues/3423\n */\n branches?: HTMLElement[];\n\n /**\n * Event handler called when auto-focusing on mount.\n * Can be prevented.\n */\n onMountAutoFocus?: (event: Event) => void;\n\n /**\n * Event handler called when auto-focusing on unmount.\n * Can be prevented.\n */\n onUnmountAutoFocus?: (event: Event) => void;\n}\n\nconst FocusScope = /* @__PURE__ */ React.forwardRef<FocusScopeElement, FocusScopeProps>(\n function FocusScope(props, forwardedRef) {\n const {\n loop = false,\n trapped = false,\n branches,\n onMountAutoFocus: onMountAutoFocusProp,\n onUnmountAutoFocus: onUnmountAutoFocusProp,\n ...scopeProps\n } = props;\n const [container, setContainer] = React.useState<HTMLElement | null>(null);\n const onMountAutoFocus = useCallbackRef(onMountAutoFocusProp);\n const onUnmountAutoFocus = useCallbackRef(onUnmountAutoFocusProp);\n const lastFocusedElementRef = React.useRef<HTMLElement | null>(null);\n const composedRefs = useComposedRefs(forwardedRef, setContainer);\n\n // Keep the latest branches in a ref so the trap effect below doesn't need to resubscribe its\n // listeners whenever the branch list updates. We sync it in the commit phase (not during render)\n // to stay safe under concurrent rendering, where a render can be discarded or replayed. The trap's\n // focus event handlers only run in response to user interaction (well after commit), so reading\n // the ref from them always sees the committed branch list.\n const branchesRef = React.useRef(branches);\n React.useEffect(() => {\n branchesRef.current = branches;\n });\n const isTargetInScope = React.useCallback(\n (target: HTMLElement | null) => {\n if (!target) return false;\n if (container?.contains(target)) return true;\n return Boolean(branchesRef.current?.some((branch) => branch.contains(target)));\n },\n [container],\n );\n\n const focusScope = React.useRef({\n paused: false,\n pause() {\n this.paused = true;\n },\n resume() {\n this.paused = false;\n },\n }).current;\n\n // Takes care of trapping focus if focus is moved outside programmatically for example\n React.useEffect(() => {\n if (trapped) {\n function handleFocusIn(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const target = event.target as HTMLElement | null;\n if (isTargetInScope(target)) {\n lastFocusedElementRef.current = target;\n } else {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n function handleFocusOut(event: FocusEvent) {\n if (focusScope.paused || !container) return;\n const relatedTarget = event.relatedTarget as HTMLElement | null;\n\n // A `focusout` event with a `null` `relatedTarget` will happen in at\n // least two cases:\n // 1. When the user switches app/tabs/windows/the browser itself loses\n // focus.\n // 2. In Google Chrome, when the focused element is removed from the\n // DOM.\n //\n // We let the browser do its thing here because:\n // 1. The browser already keeps a memory of what's focused for when\n // the page gets refocused.\n // 2. In Google Chrome, if we try to focus the deleted focused element\n // (as per below), it throws the CPU to 100%, so we avoid doing\n // anything for this reason here too.\n if (relatedTarget === null) return;\n\n // If the focus has moved to an actual legitimate element\n // (`relatedTarget !== null`) that is outside the container (and any\n // registered branches), we move focus to the last valid focused\n // element inside.\n if (!isTargetInScope(relatedTarget)) {\n focus(lastFocusedElementRef.current, { select: true });\n }\n }\n\n // When the focused element gets removed from the DOM, browsers move focus\n // back to the document.body. In this case, we move focus to the container\n // to keep focus trapped correctly.\n function handleMutations(mutations: MutationRecord[]) {\n const focusedElement = document.activeElement as HTMLElement | null;\n if (focusedElement !== document.body) return;\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) focus(container);\n }\n }\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n const mutationObserver = new MutationObserver(handleMutations);\n if (container) mutationObserver.observe(container, { childList: true, subtree: true });\n\n return () => {\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n mutationObserver.disconnect();\n };\n }\n }, [trapped, container, focusScope.paused, isTargetInScope]);\n\n React.useEffect(() => {\n if (container) {\n focusScopesStack.add(focusScope);\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = container.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n container.dispatchEvent(mountEvent);\n if (!mountEvent.defaultPrevented) {\n focusFirst(removeLinks(getTabbableCandidates(container)), { select: true });\n if (document.activeElement === previouslyFocusedElement) {\n focus(container);\n }\n }\n }\n\n return () => {\n container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus);\n\n // We hit a react bug (fixed in v17) with focusing in unmount.\n // We need to delay the focus a little to get around it for now.\n // See: https://github.com/facebook/react/issues/17894\n setTimeout(() => {\n const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);\n container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n container.dispatchEvent(unmountEvent);\n if (!unmountEvent.defaultPrevented) {\n focus(previouslyFocusedElement ?? document.body, { select: true });\n }\n // we need to remove the listener after we `dispatchEvent`\n container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus);\n\n focusScopesStack.remove(focusScope);\n }, 0);\n };\n }\n }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]);\n\n // Takes care of looping focus (when tabbing whilst at the edges)\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent) => {\n if (!loop && !trapped) return;\n if (focusScope.paused) return;\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) event.preventDefault();\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n if (loop) focus(first, { select: true });\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n if (loop) focus(last, { select: true });\n }\n }\n }\n },\n [loop, trapped, focusScope.paused],\n );\n\n return (\n <Primitive.div tabIndex={-1} {...scopeProps} ref={composedRefs} onKeyDown={handleKeyDown} />\n );\n },\n);\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope branch registry\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Lets portalled content that is a React descendant of a (modal) layer \u2014 but rendered outside of\n * that layer's DOM subtree \u2014 register itself as a \"branch\" of the layer's focus scope.\n *\n * A modal container (eg. `Dialog`) creates a registry via `useFocusScopeBranchRegistry`, renders a\n * `FocusScopeBranchProvider` around its children, and passes the collected `nodes` to its trapped\n * `FocusScope` (so focus isn't reclaimed from the branch) and, if applicable, to its `RemoveScroll`\n * `shards` (so the branch remains scrollable). Nested layers (eg. a non-modal `Popover`) register\n * their content node with `useFocusScopeBranch`.\n *\n * See: https://github.com/radix-ui/primitives/issues/3423\n */\ninterface FocusScopeBranchRegistry {\n add: (node: HTMLElement) => void;\n remove: (node: HTMLElement) => void;\n}\n\nconst FocusScopeBranchContext = React.createContext<FocusScopeBranchRegistry | null>(null);\n\nconst FocusScopeBranchProvider = FocusScopeBranchContext.Provider;\n\nfunction useFocusScopeBranchRegistry() {\n const [nodes, setNodes] = React.useState<HTMLElement[]>([]);\n const registry = React.useMemo<FocusScopeBranchRegistry>(\n () => ({\n add: (node) => setNodes((prev) => (prev.includes(node) ? prev : [...prev, node])),\n remove: (node) => setNodes((prev) => prev.filter((current) => current !== node)),\n }),\n [],\n );\n return { nodes, registry } as const;\n}\n\n/**\n * Registers `node` as a branch of the nearest ancestor `FocusScopeBranchProvider`. No-ops when\n * there is no ancestor registry (eg. the layer isn't nested inside a modal layer).\n */\nfunction useFocusScopeBranch(node: HTMLElement | null) {\n const registry = React.useContext(FocusScopeBranchContext);\n React.useEffect(() => {\n if (!node || !registry) return;\n registry.add(node);\n return () => registry.remove(node);\n }, [node, registry]);\n}\n\n/* -------------------------------------------------------------------------------------------------\n * Utils\n * -----------------------------------------------------------------------------------------------*/\n\n/**\n * Attempts focusing the first element in a list of candidates.\n * Stops when focus has actually moved.\n */\nfunction focusFirst(candidates: HTMLElement[], { select = false } = {}) {\n const previouslyFocusedElement = document.activeElement;\n for (const candidate of candidates) {\n focus(candidate, { select });\n if (document.activeElement !== previouslyFocusedElement) return;\n }\n}\n\n/**\n * Returns the first and last tabbable elements inside a container.\n */\nfunction getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates, container);\n const last = findVisible(candidates.reverse(), container);\n return [first, last] as const;\n}\n\n/**\n * Returns a list of potential tabbable candidates.\n *\n * NOTE: This is only a close approximation. For example it doesn't take into account cases like when\n * elements are not visible. This cannot be worked out easily by just reading a property, but rather\n * necessitate runtime knowledge (computed styles, etc). We deal with these cases separately.\n *\n * See: https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker\n * Credit: https://github.com/discord/focus-layers/blob/master/src/util/wrapFocus.tsx#L1\n */\nfunction getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: any) => {\n const isHiddenInput = node.tagName === 'INPUT' && node.type === 'hidden';\n if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP;\n // `.tabIndex` is not the same as the `tabindex` attribute. It works on the\n // runtime's understanding of tabbability, so this automatically accounts\n // for any kind of element that could be tabbed to.\n return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n while (walker.nextNode()) nodes.push(walker.currentNode as HTMLElement);\n // we do not take into account the order of nodes with positive `tabIndex` as it\n // hinders accessibility to have tab order different from visual order.\n return nodes;\n}\n\n/**\n * Returns the first visible element in a list.\n * NOTE: Only checks visibility up to the `container`.\n */\nfunction findVisible(elements: HTMLElement[], container: HTMLElement) {\n // `checkVisibility` performs a single native check for `visibility: hidden`\n // and `display: none` across the ancestor chain. This avoids the repeated\n // `getComputedStyle` calls per ancestor that the fallback path requires,\n // which reduces the cost of style resolution triggered by earlier DOM writes\n // (e.g. react-remove-scroll, DismissableLayer setting body styles).\n const canUseCheckVisibility =\n typeof container.checkVisibility === 'function' &&\n container.checkVisibility({ checkVisibilityCSS: true });\n\n for (const element of elements) {\n const hidden = canUseCheckVisibility\n ? !element.checkVisibility({ checkVisibilityCSS: true })\n : isHidden(element, { upTo: container });\n // we stop checking if it's hidden at the `container` level (excluding)\n if (!hidden) {\n return element;\n }\n }\n}\n\nfunction isHidden(node: HTMLElement, { upTo }: { upTo?: HTMLElement }) {\n if (getComputedStyle(node).visibility === 'hidden') return true;\n while (node) {\n // we stop at `upTo` (excluding it)\n if (upTo !== undefined && node === upTo) return false;\n if (getComputedStyle(node).display === 'none') return true;\n node = node.parentElement as HTMLElement;\n }\n return false;\n}\n\nfunction isSelectableInput(element: any): element is FocusableTarget & { select: () => void } {\n return element instanceof HTMLInputElement && 'select' in element;\n}\n\nfunction focus(element?: FocusableTarget | null, { select = false } = {}) {\n // only focus if that element is focusable\n if (element && element.focus) {\n const previouslyFocusedElement = document.activeElement;\n // NOTE: we prevent scrolling on focus, to minimize jarring transitions for users\n element.focus({ preventScroll: true });\n // only select if its not the same element, it supports selection and we need to select\n if (element !== previouslyFocusedElement && isSelectableInput(element) && select)\n element.select();\n }\n}\n\n/* -------------------------------------------------------------------------------------------------\n * FocusScope stack\n * -----------------------------------------------------------------------------------------------*/\n\ntype FocusScopeAPI = { paused: boolean; pause(): void; resume(): void };\nconst focusScopesStack = createFocusScopesStack();\n\nfunction createFocusScopesStack() {\n /** A stack of focus scopes, with the active one at the top */\n let stack: FocusScopeAPI[] = [];\n\n return {\n add(focusScope: FocusScopeAPI) {\n // pause the currently active focus scope (at the top of the stack)\n const activeFocusScope = stack[0];\n if (focusScope !== activeFocusScope) {\n activeFocusScope?.pause();\n }\n // remove in case it already exists (because we'll re-add it at the top of the stack)\n stack = arrayRemove(stack, focusScope);\n stack.unshift(focusScope);\n },\n\n remove(focusScope: FocusScopeAPI) {\n stack = arrayRemove(stack, focusScope);\n stack[0]?.resume();\n },\n };\n}\n\nfunction arrayRemove<T>(array: T[], item: T) {\n const updatedArray = [...array];\n const index = updatedArray.indexOf(item);\n if (index !== -1) {\n updatedArray.splice(index, 1);\n }\n return updatedArray;\n}\n\nfunction removeLinks(items: HTMLElement[]) {\n return items.filter((item) => item.tagName !== 'A');\n}\n\nexport {\n FocusScope,\n FocusScopeBranchProvider,\n useFocusScopeBranchRegistry,\n useFocusScopeBranch,\n //\n FocusScope as Root,\n};\nexport type { FocusScopeProps, FocusScopeBranchRegistry };\n"],
"mappings": "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",
"names": ["FocusScope", "handleFocusIn", "handleFocusOut", "handleMutations", "container"]
}
{
"name": "@radix-ui/react-focus-scope",
"version": "1.1.14",
"version": "1.1.15",
"license": "MIT",

@@ -15,4 +15,4 @@ "source": "./src/index.ts",

"@radix-ui/react-compose-refs": "1.1.4",
"@radix-ui/react-use-callback-ref": "1.1.3",
"@radix-ui/react-primitive": "2.1.8"
"@radix-ui/react-primitive": "2.1.9",
"@radix-ui/react-use-callback-ref": "1.1.3"
},

@@ -25,5 +25,5 @@ "devDependencies": {

"typescript": "^5.9.3",
"@repo/builder": "0.0.0",
"@repo/test-utils": "0.0.0",
"@repo/typescript-config": "0.0.0",
"@repo/builder": "0.0.0"
"@repo/typescript-config": "0.0.0"
},

@@ -30,0 +30,0 @@ "peerDependencies": {