Spaces:
Running
Running
| ; | |
| "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); | |
| // src/index.ts | |
| var index_exports = {}; | |
| __export(index_exports, { | |
| Item: () => Item, | |
| Root: () => Root, | |
| RovingFocusGroup: () => RovingFocusGroup, | |
| RovingFocusGroupItem: () => RovingFocusGroupItem, | |
| createRovingFocusGroupScope: () => createRovingFocusGroupScope | |
| }); | |
| module.exports = __toCommonJS(index_exports); | |
| // src/roving-focus-group.tsx | |
| var React = __toESM(require("react")); | |
| var import_primitive = require("@radix-ui/primitive"); | |
| var import_react_collection = require("@radix-ui/react-collection"); | |
| var import_react_compose_refs = require("@radix-ui/react-compose-refs"); | |
| var import_react_context = require("@radix-ui/react-context"); | |
| var import_react_id = require("@radix-ui/react-id"); | |
| var import_react_primitive = require("@radix-ui/react-primitive"); | |
| var import_react_use_callback_ref = require("@radix-ui/react-use-callback-ref"); | |
| var import_react_use_controllable_state = require("@radix-ui/react-use-controllable-state"); | |
| var import_react_direction = require("@radix-ui/react-direction"); | |
| var import_jsx_runtime = require("react/jsx-runtime"); | |
| var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus"; | |
| var EVENT_OPTIONS = { bubbles: false, cancelable: true }; | |
| var GROUP_NAME = "RovingFocusGroup"; | |
| var [Collection, useCollection, createCollectionScope] = (0, import_react_collection.createCollection)(GROUP_NAME); | |
| var [createRovingFocusGroupContext, createRovingFocusGroupScope] = (0, import_react_context.createContextScope)( | |
| GROUP_NAME, | |
| [createCollectionScope] | |
| ); | |
| var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME); | |
| var RovingFocusGroup = React.forwardRef( | |
| (props, forwardedRef) => { | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) }); | |
| } | |
| ); | |
| RovingFocusGroup.displayName = GROUP_NAME; | |
| var RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => { | |
| const { | |
| __scopeRovingFocusGroup, | |
| orientation, | |
| loop = false, | |
| dir, | |
| currentTabStopId: currentTabStopIdProp, | |
| defaultCurrentTabStopId, | |
| onCurrentTabStopIdChange, | |
| onEntryFocus, | |
| preventScrollOnEntryFocus = false, | |
| ...groupProps | |
| } = props; | |
| const ref = React.useRef(null); | |
| const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref); | |
| const direction = (0, import_react_direction.useDirection)(dir); | |
| const [currentTabStopId, setCurrentTabStopId] = (0, import_react_use_controllable_state.useControllableState)({ | |
| prop: currentTabStopIdProp, | |
| defaultProp: defaultCurrentTabStopId ?? null, | |
| onChange: onCurrentTabStopIdChange, | |
| caller: GROUP_NAME | |
| }); | |
| const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false); | |
| const handleEntryFocus = (0, import_react_use_callback_ref.useCallbackRef)(onEntryFocus); | |
| const getItems = useCollection(__scopeRovingFocusGroup); | |
| const isClickFocusRef = React.useRef(false); | |
| const [focusableItemsCount, setFocusableItemsCount] = React.useState(0); | |
| React.useEffect(() => { | |
| const node = ref.current; | |
| if (node) { | |
| node.addEventListener(ENTRY_FOCUS, handleEntryFocus); | |
| return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus); | |
| } | |
| }, [handleEntryFocus]); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| RovingFocusProvider, | |
| { | |
| scope: __scopeRovingFocusGroup, | |
| orientation, | |
| dir: direction, | |
| loop, | |
| currentTabStopId, | |
| onItemFocus: React.useCallback( | |
| (tabStopId) => setCurrentTabStopId(tabStopId), | |
| [setCurrentTabStopId] | |
| ), | |
| onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []), | |
| onFocusableItemAdd: React.useCallback( | |
| () => setFocusableItemsCount((prevCount) => prevCount + 1), | |
| [] | |
| ), | |
| onFocusableItemRemove: React.useCallback( | |
| () => setFocusableItemsCount((prevCount) => prevCount - 1), | |
| [] | |
| ), | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.div, | |
| { | |
| tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0, | |
| "data-orientation": orientation, | |
| ...groupProps, | |
| ref: composedRefs, | |
| style: { outline: "none", ...props.style }, | |
| onMouseDown: (0, import_primitive.composeEventHandlers)(props.onMouseDown, () => { | |
| isClickFocusRef.current = true; | |
| }), | |
| onFocus: (0, import_primitive.composeEventHandlers)(props.onFocus, (event) => { | |
| const isKeyboardFocus = !isClickFocusRef.current; | |
| if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) { | |
| const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS); | |
| event.currentTarget.dispatchEvent(entryFocusEvent); | |
| if (!entryFocusEvent.defaultPrevented) { | |
| const items = getItems().filter((item) => item.focusable); | |
| const activeItem = items.find((item) => item.active); | |
| const currentItem = items.find((item) => item.id === currentTabStopId); | |
| const candidateItems = [activeItem, currentItem, ...items].filter( | |
| Boolean | |
| ); | |
| const candidateNodes = candidateItems.map((item) => item.ref.current); | |
| focusFirst(candidateNodes, preventScrollOnEntryFocus); | |
| } | |
| } | |
| isClickFocusRef.current = false; | |
| }), | |
| onBlur: (0, import_primitive.composeEventHandlers)(props.onBlur, () => setIsTabbingBackOut(false)) | |
| } | |
| ) | |
| } | |
| ); | |
| }); | |
| var ITEM_NAME = "RovingFocusGroupItem"; | |
| var RovingFocusGroupItem = React.forwardRef( | |
| (props, forwardedRef) => { | |
| const { | |
| __scopeRovingFocusGroup, | |
| focusable = true, | |
| active = false, | |
| tabStopId, | |
| children, | |
| ...itemProps | |
| } = props; | |
| const autoId = (0, import_react_id.useId)(); | |
| const id = tabStopId || autoId; | |
| const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup); | |
| const isCurrentTabStop = context.currentTabStopId === id; | |
| const getItems = useCollection(__scopeRovingFocusGroup); | |
| const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context; | |
| React.useEffect(() => { | |
| if (focusable) { | |
| onFocusableItemAdd(); | |
| return () => onFocusableItemRemove(); | |
| } | |
| }, [focusable, onFocusableItemAdd, onFocusableItemRemove]); | |
| return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| Collection.ItemSlot, | |
| { | |
| scope: __scopeRovingFocusGroup, | |
| id, | |
| focusable, | |
| active, | |
| children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( | |
| import_react_primitive.Primitive.span, | |
| { | |
| tabIndex: isCurrentTabStop ? 0 : -1, | |
| "data-orientation": context.orientation, | |
| ...itemProps, | |
| ref: forwardedRef, | |
| onMouseDown: (0, import_primitive.composeEventHandlers)(props.onMouseDown, (event) => { | |
| if (!focusable) event.preventDefault(); | |
| else context.onItemFocus(id); | |
| }), | |
| onFocus: (0, import_primitive.composeEventHandlers)(props.onFocus, () => context.onItemFocus(id)), | |
| onKeyDown: (0, import_primitive.composeEventHandlers)(props.onKeyDown, (event) => { | |
| if (event.key === "Tab" && event.shiftKey) { | |
| context.onItemShiftTab(); | |
| return; | |
| } | |
| if (event.target !== event.currentTarget) return; | |
| const focusIntent = getFocusIntent(event, context.orientation, context.dir); | |
| if (focusIntent !== void 0) { | |
| if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return; | |
| event.preventDefault(); | |
| const items = getItems().filter((item) => item.focusable); | |
| let candidateNodes = items.map((item) => item.ref.current); | |
| if (focusIntent === "last") candidateNodes.reverse(); | |
| else if (focusIntent === "prev" || focusIntent === "next") { | |
| if (focusIntent === "prev") candidateNodes.reverse(); | |
| const currentIndex = candidateNodes.indexOf(event.currentTarget); | |
| candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1); | |
| } | |
| setTimeout(() => focusFirst(candidateNodes)); | |
| } | |
| }), | |
| children: typeof children === "function" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children | |
| } | |
| ) | |
| } | |
| ); | |
| } | |
| ); | |
| RovingFocusGroupItem.displayName = ITEM_NAME; | |
| var MAP_KEY_TO_FOCUS_INTENT = { | |
| ArrowLeft: "prev", | |
| ArrowUp: "prev", | |
| ArrowRight: "next", | |
| ArrowDown: "next", | |
| PageUp: "first", | |
| Home: "first", | |
| PageDown: "last", | |
| End: "last" | |
| }; | |
| function getDirectionAwareKey(key, dir) { | |
| if (dir !== "rtl") return key; | |
| return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key; | |
| } | |
| function getFocusIntent(event, orientation, dir) { | |
| const key = getDirectionAwareKey(event.key, dir); | |
| if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0; | |
| if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0; | |
| return MAP_KEY_TO_FOCUS_INTENT[key]; | |
| } | |
| function focusFirst(candidates, preventScroll = false) { | |
| const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; | |
| for (const candidate of candidates) { | |
| if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; | |
| candidate.focus({ preventScroll }); | |
| if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return; | |
| } | |
| } | |
| function wrapArray(array, startIndex) { | |
| return array.map((_, index) => array[(startIndex + index) % array.length]); | |
| } | |
| var Root = RovingFocusGroup; | |
| var Item = RovingFocusGroupItem; | |
| //# sourceMappingURL=index.js.map | |