"use strict"; "use client"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // packages/react/focus-scope/src/index.ts var src_exports = {}; __export(src_exports, { FocusScope: () => FocusScope, Root: () => Root }); module.exports = __toCommonJS(src_exports); // packages/react/focus-scope/src/FocusScope.tsx var React = __toESM(require("react")); var import_react_compose_refs = require("@radix-ui/react-compose-refs"); var import_react_primitive = require("@radix-ui/react-primitive"); var import_react_use_callback_ref = require("@radix-ui/react-use-callback-ref"); var import_jsx_runtime = require("react/jsx-runtime"); var AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount"; var AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount"; var EVENT_OPTIONS = { bubbles: false, cancelable: true }; var FOCUS_SCOPE_NAME = "FocusScope"; var FocusScope = React.forwardRef((props, forwardedRef) => { const { loop = false, trapped = false, onMountAutoFocus: onMountAutoFocusProp, onUnmountAutoFocus: onUnmountAutoFocusProp, ...scopeProps } = props; const [container, setContainer] = React.useState(null); const onMountAutoFocus = (0, import_react_use_callback_ref.useCallbackRef)(onMountAutoFocusProp); const onUnmountAutoFocus = (0, import_react_use_callback_ref.useCallbackRef)(onUnmountAutoFocusProp); const lastFocusedElementRef = React.useRef(null); const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, (node) => setContainer(node)); const focusScope = React.useRef({ paused: false, pause() { this.paused = true; }, resume() { this.paused = false; } }).current; React.useEffect(() => { if (trapped) { let handleFocusIn2 = function(event) { if (focusScope.paused || !container) return; const target = event.target; if (container.contains(target)) { lastFocusedElementRef.current = target; } else { focus(lastFocusedElementRef.current, { select: true }); } }, handleFocusOut2 = function(event) { if (focusScope.paused || !container) return; const relatedTarget = event.relatedTarget; if (relatedTarget === null) return; if (!container.contains(relatedTarget)) { focus(lastFocusedElementRef.current, { select: true }); } }, handleMutations2 = function(mutations) { const focusedElement = document.activeElement; if (focusedElement !== document.body) return; for (const mutation of mutations) { if (mutation.removedNodes.length > 0) focus(container); } }; var handleFocusIn = handleFocusIn2, handleFocusOut = handleFocusOut2, handleMutations = handleMutations2; document.addEventListener("focusin", handleFocusIn2); document.addEventListener("focusout", handleFocusOut2); const mutationObserver = new MutationObserver(handleMutations2); if (container) mutationObserver.observe(container, { childList: true, subtree: true }); return () => { document.removeEventListener("focusin", handleFocusIn2); document.removeEventListener("focusout", handleFocusOut2); mutationObserver.disconnect(); }; } }, [trapped, container, focusScope.paused]); React.useEffect(() => { if (container) { focusScopesStack.add(focusScope); const previouslyFocusedElement = document.activeElement; const hasFocusedCandidate = container.contains(previouslyFocusedElement); if (!hasFocusedCandidate) { const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS); container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus); container.dispatchEvent(mountEvent); if (!mountEvent.defaultPrevented) { focusFirst(removeLinks(getTabbableCandidates(container)), { select: true }); if (document.activeElement === previouslyFocusedElement) { focus(container); } } } return () => { container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountAutoFocus); setTimeout(() => { const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS); container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus); container.dispatchEvent(unmountEvent); if (!unmountEvent.defaultPrevented) { focus(previouslyFocusedElement ?? document.body, { select: true }); } container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountAutoFocus); focusScopesStack.remove(focusScope); }, 0); }; } }, [container, onMountAutoFocus, onUnmountAutoFocus, focusScope]); const handleKeyDown = React.useCallback( (event) => { if (!loop && !trapped) return; if (focusScope.paused) return; const isTabKey = event.key === "Tab" && !event.altKey && !event.ctrlKey && !event.metaKey; const focusedElement = document.activeElement; if (isTabKey && focusedElement) { const container2 = event.currentTarget; const [first, last] = getTabbableEdges(container2); const hasTabbableElementsInside = first && last; if (!hasTabbableElementsInside) { if (focusedElement === container2) event.preventDefault(); } else { if (!event.shiftKey && focusedElement === last) { event.preventDefault(); if (loop) focus(first, { select: true }); } else if (event.shiftKey && focusedElement === first) { event.preventDefault(); if (loop) focus(last, { select: true }); } } } }, [loop, trapped, focusScope.paused] ); return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.Primitive.div, { tabIndex: -1, ...scopeProps, ref: composedRefs, onKeyDown: handleKeyDown }); }); FocusScope.displayName = FOCUS_SCOPE_NAME; function focusFirst(candidates, { select = false } = {}) { const previouslyFocusedElement = document.activeElement; for (const candidate of candidates) { focus(candidate, { select }); if (document.activeElement !== previouslyFocusedElement) return; } } function getTabbableEdges(container) { const candidates = getTabbableCandidates(container); const first = findVisible(candidates, container); const last = findVisible(candidates.reverse(), container); return [first, last]; } function getTabbableCandidates(container) { const nodes = []; const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { acceptNode: (node) => { const isHiddenInput = node.tagName === "INPUT" && node.type === "hidden"; if (node.disabled || node.hidden || isHiddenInput) return NodeFilter.FILTER_SKIP; return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP; } }); while (walker.nextNode()) nodes.push(walker.currentNode); return nodes; } function findVisible(elements, container) { for (const element of elements) { if (!isHidden(element, { upTo: container })) return element; } } function isHidden(node, { upTo }) { if (getComputedStyle(node).visibility === "hidden") return true; while (node) { if (upTo !== void 0 && node === upTo) return false; if (getComputedStyle(node).display === "none") return true; node = node.parentElement; } return false; } function isSelectableInput(element) { return element instanceof HTMLInputElement && "select" in element; } function focus(element, { select = false } = {}) { if (element && element.focus) { const previouslyFocusedElement = document.activeElement; element.focus({ preventScroll: true }); if (element !== previouslyFocusedElement && isSelectableInput(element) && select) element.select(); } } var focusScopesStack = createFocusScopesStack(); function createFocusScopesStack() { let stack = []; return { add(focusScope) { const activeFocusScope = stack[0]; if (focusScope !== activeFocusScope) { activeFocusScope?.pause(); } stack = arrayRemove(stack, focusScope); stack.unshift(focusScope); }, remove(focusScope) { stack = arrayRemove(stack, focusScope); stack[0]?.resume(); } }; } function arrayRemove(array, item) { const updatedArray = [...array]; const index = updatedArray.indexOf(item); if (index !== -1) { updatedArray.splice(index, 1); } return updatedArray; } function removeLinks(items) { return items.filter((item) => item.tagName !== "A"); } var Root = FocusScope; //# sourceMappingURL=index.js.map