UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

155 lines (152 loc) 7.67 kB
"use strict"; 'use client'; Object.defineProperty(exports, "__esModule", { value: true }); exports.useCompositeRoot = useCompositeRoot; var React = _interopRequireWildcard(require("react")); var _mergeReactProps = require("../../utils/mergeReactProps"); var _useEventCallback = require("../../utils/useEventCallback"); var _useForkRef = require("../../utils/useForkRef"); var _composite = require("../composite"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } // Advanced options of Composite, to be implemented later if needed. const disabledIndices = undefined; /** * @ignore - internal hook. */ function useCompositeRoot(params) { const { itemSizes, cols = 1, loop = true, dense = false, orientation = 'both', direction, highlightedIndex: externalHighlightedIndex, onHighlightedIndexChange: externalSetHighlightedIndex, rootRef: externalRef, enableHomeAndEndKeys = false, stopEventPropagation = false } = params; const [internalHighlightedIndex, internalSetHighlightedIndex] = React.useState(0); const isGrid = cols > 1; const highlightedIndex = externalHighlightedIndex ?? internalHighlightedIndex; const onHighlightedIndexChange = (0, _useEventCallback.useEventCallback)(externalSetHighlightedIndex ?? internalSetHighlightedIndex); const textDirectionRef = React.useRef(direction ?? null); const rootRef = React.useRef(null); const mergedRef = (0, _useForkRef.useForkRef)(rootRef, externalRef); const elementsRef = React.useRef([]); const getRootProps = React.useCallback((externalProps = {}) => (0, _mergeReactProps.mergeReactProps)(externalProps, { 'aria-orientation': orientation === 'both' ? undefined : orientation, ref: mergedRef, onKeyDown(event) { const RELEVANT_KEYS = enableHomeAndEndKeys ? _composite.ALL_KEYS : _composite.ARROW_KEYS; if (!RELEVANT_KEYS.includes(event.key)) { return; } const element = rootRef.current; if (!element) { return; } if (textDirectionRef?.current == null) { textDirectionRef.current = (0, _composite.getTextDirection)(element); } const isRtl = textDirectionRef.current === 'rtl'; let nextIndex = highlightedIndex; const minIndex = (0, _composite.getMinIndex)(elementsRef, disabledIndices); const maxIndex = (0, _composite.getMaxIndex)(elementsRef, disabledIndices); if (isGrid) { const sizes = itemSizes || Array.from({ length: elementsRef.current.length }, () => ({ width: 1, height: 1 })); // To calculate movements on the grid, we use hypothetical cell indices // as if every item was 1x1, then convert back to real indices. const cellMap = (0, _composite.buildCellMap)(sizes, cols, dense); const minGridIndex = cellMap.findIndex(index => index != null && !(0, _composite.isDisabled)(elementsRef.current, index, disabledIndices)); // last enabled index const maxGridIndex = cellMap.reduce((foundIndex, index, cellIndex) => index != null && !(0, _composite.isDisabled)(elementsRef.current, index, disabledIndices) ? cellIndex : foundIndex, -1); nextIndex = cellMap[(0, _composite.getGridNavigatedIndex)({ current: cellMap.map(itemIndex => itemIndex ? elementsRef.current[itemIndex] : null) }, { event, orientation, loop, cols, // treat undefined (empty grid spaces) as disabled indices so we // don't end up in them disabledIndices: (0, _composite.getCellIndices)([...(disabledIndices || elementsRef.current.map((_, index) => (0, _composite.isDisabled)(elementsRef.current, index) ? index : undefined)), undefined], cellMap), minIndex: minGridIndex, maxIndex: maxGridIndex, prevIndex: (0, _composite.getCellIndexOfCorner)(highlightedIndex > maxIndex ? minIndex : highlightedIndex, sizes, cellMap, cols, // use a corner matching the edge closest to the direction we're // moving in so we don't end up in the same item. Prefer // top/left over bottom/right. // eslint-disable-next-line no-nested-ternary event.key === _composite.ARROW_DOWN ? 'bl' : event.key === _composite.ARROW_RIGHT ? 'tr' : 'tl'), rtl: isRtl })]; // navigated cell will never be nullish } const horizontalEndKey = isRtl ? _composite.ARROW_LEFT : _composite.ARROW_RIGHT; const toEndKeys = { horizontal: [horizontalEndKey], vertical: [_composite.ARROW_DOWN], both: [horizontalEndKey, _composite.ARROW_DOWN] }[orientation]; const horizontalStartKey = isRtl ? _composite.ARROW_RIGHT : _composite.ARROW_LEFT; const toStartKeys = { horizontal: [horizontalStartKey], vertical: [_composite.ARROW_UP], both: [horizontalStartKey, _composite.ARROW_UP] }[orientation]; const preventedKeys = isGrid ? RELEVANT_KEYS : { horizontal: enableHomeAndEndKeys ? _composite.HORIZONTAL_KEYS_WITH_EXTRA_KEYS : _composite.HORIZONTAL_KEYS, vertical: enableHomeAndEndKeys ? _composite.VERTICAL_KEYS_WITH_EXTRA_KEYS : _composite.VERTICAL_KEYS, both: RELEVANT_KEYS }[orientation]; if (enableHomeAndEndKeys) { if (event.key === _composite.HOME) { nextIndex = minIndex; } else if (event.key === _composite.END) { nextIndex = maxIndex; } } if (nextIndex === highlightedIndex && [...toEndKeys, ...toStartKeys].includes(event.key)) { if (loop && nextIndex === maxIndex && toEndKeys.includes(event.key)) { nextIndex = minIndex; } else if (loop && nextIndex === minIndex && toStartKeys.includes(event.key)) { nextIndex = maxIndex; } else { nextIndex = (0, _composite.findNonDisabledIndex)(elementsRef, { startingIndex: nextIndex, decrement: toStartKeys.includes(event.key), disabledIndices }); } } if (nextIndex !== highlightedIndex && !(0, _composite.isIndexOutOfBounds)(elementsRef, nextIndex)) { if (stopEventPropagation) { event.stopPropagation(); } if (preventedKeys.includes(event.key)) { event.preventDefault(); } onHighlightedIndexChange(nextIndex); // Wait for FocusManager `returnFocus` to execute. queueMicrotask(() => { elementsRef.current[nextIndex]?.focus(); }); } } }), [highlightedIndex, stopEventPropagation, cols, dense, elementsRef, isGrid, itemSizes, loop, mergedRef, onHighlightedIndexChange, orientation, enableHomeAndEndKeys]); return React.useMemo(() => ({ getRootProps, highlightedIndex, onHighlightedIndexChange, elementsRef }), [getRootProps, highlightedIndex, onHighlightedIndexChange, elementsRef]); }