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.

265 lines (259 loc) 7.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createGridCellMap = createGridCellMap; exports.findNonDisabledListIndex = findNonDisabledListIndex; exports.getGridCellIndexOfCorner = getGridCellIndexOfCorner; exports.getGridCellIndices = getGridCellIndices; exports.getGridNavigatedIndex = getGridNavigatedIndex; exports.getMaxListIndex = getMaxListIndex; exports.getMinListIndex = getMinListIndex; exports.isDifferentGridRow = isDifferentGridRow; exports.isIndexOutOfListBounds = isIndexOutOfListBounds; exports.isListIndexDisabled = isListIndexDisabled; var _utils = require("@floating-ui/utils"); var _event = require("./event"); var _constants = require("./constants"); function isDifferentGridRow(index, cols, prevRow) { return Math.floor(index / cols) !== prevRow; } function isIndexOutOfListBounds(listRef, index) { return index < 0 || index >= listRef.current.length; } function getMinListIndex(listRef, disabledIndices) { return findNonDisabledListIndex(listRef, { disabledIndices }); } function getMaxListIndex(listRef, disabledIndices) { return findNonDisabledListIndex(listRef, { decrement: true, startingIndex: listRef.current.length, disabledIndices }); } function findNonDisabledListIndex(listRef, { startingIndex = -1, decrement = false, disabledIndices, amount = 1 } = {}) { let index = startingIndex; do { index += decrement ? -amount : amount; } while (index >= 0 && index <= listRef.current.length - 1 && isListIndexDisabled(listRef, index, disabledIndices)); return index; } function getGridNavigatedIndex(listRef, { event, orientation, loop, rtl, cols, disabledIndices, minIndex, maxIndex, prevIndex, stopEvent: stop = false }) { let nextIndex = prevIndex; if (event.key === _constants.ARROW_UP) { if (stop) { (0, _event.stopEvent)(event); } if (prevIndex === -1) { nextIndex = maxIndex; } else { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: nextIndex, amount: cols, decrement: true, disabledIndices }); if (loop && (prevIndex - cols < minIndex || nextIndex < 0)) { const col = prevIndex % cols; const maxCol = maxIndex % cols; const offset = maxIndex - (maxCol - col); if (maxCol === col) { nextIndex = maxIndex; } else { nextIndex = maxCol > col ? offset : offset - cols; } } } if (isIndexOutOfListBounds(listRef, nextIndex)) { nextIndex = prevIndex; } } if (event.key === _constants.ARROW_DOWN) { if (stop) { (0, _event.stopEvent)(event); } if (prevIndex === -1) { nextIndex = minIndex; } else { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex, amount: cols, disabledIndices }); if (loop && prevIndex + cols > maxIndex) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex % cols - cols, amount: cols, disabledIndices }); } } if (isIndexOutOfListBounds(listRef, nextIndex)) { nextIndex = prevIndex; } } // Remains on the same row/column. if (orientation === 'both') { const prevRow = (0, _utils.floor)(prevIndex / cols); if (event.key === (rtl ? _constants.ARROW_LEFT : _constants.ARROW_RIGHT)) { if (stop) { (0, _event.stopEvent)(event); } if (prevIndex % cols !== cols - 1) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex, disabledIndices }); if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex - prevIndex % cols - 1, disabledIndices }); } } else if (loop) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex - prevIndex % cols - 1, disabledIndices }); } if (isDifferentGridRow(nextIndex, cols, prevRow)) { nextIndex = prevIndex; } } if (event.key === (rtl ? _constants.ARROW_RIGHT : _constants.ARROW_LEFT)) { if (stop) { (0, _event.stopEvent)(event); } if (prevIndex % cols !== 0) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex, decrement: true, disabledIndices }); if (loop && isDifferentGridRow(nextIndex, cols, prevRow)) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex + (cols - prevIndex % cols), decrement: true, disabledIndices }); } } else if (loop) { nextIndex = findNonDisabledListIndex(listRef, { startingIndex: prevIndex + (cols - prevIndex % cols), decrement: true, disabledIndices }); } if (isDifferentGridRow(nextIndex, cols, prevRow)) { nextIndex = prevIndex; } } const lastRow = (0, _utils.floor)(maxIndex / cols) === prevRow; if (isIndexOutOfListBounds(listRef, nextIndex)) { if (loop && lastRow) { nextIndex = event.key === (rtl ? _constants.ARROW_RIGHT : _constants.ARROW_LEFT) ? maxIndex : findNonDisabledListIndex(listRef, { startingIndex: prevIndex - prevIndex % cols - 1, disabledIndices }); } else { nextIndex = prevIndex; } } } return nextIndex; } /** For each cell index, gets the item index that occupies that cell */ function createGridCellMap(sizes, cols, dense) { const cellMap = []; let startIndex = 0; sizes.forEach(({ width, height }, index) => { if (width > cols) { if (process.env.NODE_ENV !== 'production') { throw new Error(`[Floating UI]: Invalid grid - item width at index ${index} is greater than grid columns`); } } let itemPlaced = false; if (dense) { startIndex = 0; } while (!itemPlaced) { const targetCells = []; for (let i = 0; i < width; i += 1) { for (let j = 0; j < height; j += 1) { targetCells.push(startIndex + i + j * cols); } } if (startIndex % cols + width <= cols && targetCells.every(cell => cellMap[cell] == null)) { targetCells.forEach(cell => { cellMap[cell] = index; }); itemPlaced = true; } else { startIndex += 1; } } }); // convert into a non-sparse array return [...cellMap]; } /** Gets cell index of an item's corner or -1 when index is -1. */ function getGridCellIndexOfCorner(index, sizes, cellMap, cols, corner) { if (index === -1) { return -1; } const firstCellIndex = cellMap.indexOf(index); const sizeItem = sizes[index]; switch (corner) { case 'tl': return firstCellIndex; case 'tr': if (!sizeItem) { return firstCellIndex; } return firstCellIndex + sizeItem.width - 1; case 'bl': if (!sizeItem) { return firstCellIndex; } return firstCellIndex + (sizeItem.height - 1) * cols; case 'br': return cellMap.lastIndexOf(index); default: return -1; } } /** Gets all cell indices that correspond to the specified indices */ function getGridCellIndices(indices, cellMap) { return cellMap.flatMap((index, cellIndex) => indices.includes(index) ? [cellIndex] : []); } function isListIndexDisabled(listRef, index, disabledIndices) { if (typeof disabledIndices === 'function') { return disabledIndices(index); } if (disabledIndices) { return disabledIndices.includes(index); } const element = listRef.current[index]; return element == null || element.hasAttribute('disabled') || element.getAttribute('aria-disabled') === 'true'; }