UNPKG

ivt

Version:

Ivt Components Library

215 lines (212 loc) 9.58 kB
import * as React from 'react'; import { P as Primitive, c as composeEventHandlers } from './index-Dsw02Eup.mjs'; import { createCollection } from '@radix-ui/react-collection'; import { c as createContextScope, u as useComposedRefs } from './index-D4jPaBZ2.mjs'; import { u as useId } from './index-gX0V59dZ.mjs'; import { u as useCallbackRef } from './index-DC-gAYjS.mjs'; import { u as useControllableState } from './index-1toTCGyr.mjs'; import { u as useDirection } from './index-UxWlwQlk.mjs'; import { jsx } from 'react/jsx-runtime'; var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus"; var EVENT_OPTIONS = { bubbles: false, cancelable: true }; var GROUP_NAME = "RovingFocusGroup"; var [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME); var [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(GROUP_NAME, [ createCollectionScope ]); var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME); var RovingFocusGroup = React.forwardRef((props, forwardedRef)=>{ return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ 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 = useComposedRefs(forwardedRef, ref); const direction = useDirection(dir); const [currentTabStopId, setCurrentTabStopId] = useControllableState({ prop: currentTabStopIdProp, defaultProp: defaultCurrentTabStopId ?? null, onChange: onCurrentTabStopIdChange, caller: GROUP_NAME }); const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false); const handleEntryFocus = 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__ */ 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__ */ jsx(Primitive.div, { tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0, "data-orientation": orientation, ...groupProps, ref: composedRefs, style: { outline: "none", ...props.style }, onMouseDown: composeEventHandlers(props.onMouseDown, ()=>{ isClickFocusRef.current = true; }), onFocus: 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: 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 = 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__ */ jsx(Collection.ItemSlot, { scope: __scopeRovingFocusGroup, id, focusable, active, children: /* @__PURE__ */ jsx(Primitive.span, { tabIndex: isCurrentTabStop ? 0 : -1, "data-orientation": context.orientation, ...itemProps, ref: forwardedRef, onMouseDown: composeEventHandlers(props.onMouseDown, (event)=>{ if (!focusable) event.preventDefault(); else context.onItemFocus(id); }), onFocus: composeEventHandlers(props.onFocus, ()=>context.onItemFocus(id)), onKeyDown: 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; export { Item as I, Root as R, createRovingFocusGroupScope as c }; //# sourceMappingURL=index-DIHpzffL.mjs.map