@ariakit/core
Version:
Ariakit core
279 lines (261 loc) • 10.8 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true});
var _GDZQUFNPcjs = require('./GDZQUFNP.cjs');
var _MNBBM5CRcjs = require('./MNBBM5CR.cjs');
var _ASR6PAFNcjs = require('./ASR6PAFN.cjs');
var _MLCPLY2Pcjs = require('./MLCPLY2P.cjs');
var _7EQBAZ46cjs = require('./7EQBAZ46.cjs');
// src/composite/composite-store.ts
var NULL_ITEM = { id: null };
function findFirstEnabledItem(items, excludeId) {
return items.find((item) => {
if (excludeId) {
return !item.disabled && item.id !== excludeId;
}
return !item.disabled;
});
}
function getEnabledItems(items, excludeId) {
return items.filter((item) => {
if (excludeId) {
return !item.disabled && item.id !== excludeId;
}
return !item.disabled;
});
}
function getItemsInRow(items, rowId) {
return items.filter((item) => item.rowId === rowId);
}
function flipItems(items, activeId, shouldInsertNullItem = false) {
const index = items.findIndex((item) => item.id === activeId);
return [
...items.slice(index + 1),
...shouldInsertNullItem ? [NULL_ITEM] : [],
...items.slice(0, index)
];
}
function groupItemsByRows(items) {
const rows = [];
for (const item of items) {
const row = rows.find((currentRow) => {
var _a;
return ((_a = currentRow[0]) == null ? void 0 : _a.rowId) === item.rowId;
});
if (row) {
row.push(item);
} else {
rows.push([item]);
}
}
return rows;
}
function getMaxRowLength(array) {
let maxLength = 0;
for (const { length } of array) {
if (length > maxLength) {
maxLength = length;
}
}
return maxLength;
}
function createEmptyItem(rowId) {
return {
id: "__EMPTY_ITEM__",
disabled: true,
rowId
};
}
function normalizeRows(rows, activeId, focusShift) {
const maxLength = getMaxRowLength(rows);
for (const row of rows) {
for (let i = 0; i < maxLength; i += 1) {
const item = row[i];
if (!item || focusShift && item.disabled) {
const isFirst = i === 0;
const previousItem = isFirst && focusShift ? findFirstEnabledItem(row) : row[i - 1];
row[i] = previousItem && activeId !== previousItem.id && focusShift ? previousItem : createEmptyItem(previousItem == null ? void 0 : previousItem.rowId);
}
}
}
return rows;
}
function verticalizeItems(items) {
const rows = groupItemsByRows(items);
const maxLength = getMaxRowLength(rows);
const verticalized = [];
for (let i = 0; i < maxLength; i += 1) {
for (const row of rows) {
const item = row[i];
if (item) {
verticalized.push(_7EQBAZ46cjs.__spreadProps.call(void 0, _7EQBAZ46cjs.__spreadValues.call(void 0, {}, item), {
// If there's no rowId, it means that it's not a grid composite, but
// a single row instead. So, instead of verticalizing it, that is,
// assigning a different rowId based on the column index, we keep it
// undefined so they will be part of the same row. This is useful
// when using up/down on one-dimensional composites.
rowId: item.rowId ? `${i}` : void 0
}));
}
}
}
return verticalized;
}
function createCompositeStore(props = {}) {
var _a;
const syncState = (_a = props.store) == null ? void 0 : _a.getState();
const collection = _MNBBM5CRcjs.createCollectionStore.call(void 0, props);
const activeId = _MLCPLY2Pcjs.defaultValue.call(void 0,
props.activeId,
syncState == null ? void 0 : syncState.activeId,
props.defaultActiveId
);
const initialState = _7EQBAZ46cjs.__spreadProps.call(void 0, _7EQBAZ46cjs.__spreadValues.call(void 0, {}, collection.getState()), {
id: _MLCPLY2Pcjs.defaultValue.call(void 0,
props.id,
syncState == null ? void 0 : syncState.id,
`id-${Math.random().toString(36).slice(2, 8)}`
),
activeId,
baseElement: _MLCPLY2Pcjs.defaultValue.call(void 0, syncState == null ? void 0 : syncState.baseElement, null),
includesBaseElement: _MLCPLY2Pcjs.defaultValue.call(void 0,
props.includesBaseElement,
syncState == null ? void 0 : syncState.includesBaseElement,
activeId === null
),
moves: _MLCPLY2Pcjs.defaultValue.call(void 0, syncState == null ? void 0 : syncState.moves, 0),
orientation: _MLCPLY2Pcjs.defaultValue.call(void 0,
props.orientation,
syncState == null ? void 0 : syncState.orientation,
"both"
),
rtl: _MLCPLY2Pcjs.defaultValue.call(void 0, props.rtl, syncState == null ? void 0 : syncState.rtl, false),
virtualFocus: _MLCPLY2Pcjs.defaultValue.call(void 0,
props.virtualFocus,
syncState == null ? void 0 : syncState.virtualFocus,
false
),
focusLoop: _MLCPLY2Pcjs.defaultValue.call(void 0, props.focusLoop, syncState == null ? void 0 : syncState.focusLoop, false),
focusWrap: _MLCPLY2Pcjs.defaultValue.call(void 0, props.focusWrap, syncState == null ? void 0 : syncState.focusWrap, false),
focusShift: _MLCPLY2Pcjs.defaultValue.call(void 0, props.focusShift, syncState == null ? void 0 : syncState.focusShift, false)
});
const composite = _ASR6PAFNcjs.createStore.call(void 0, initialState, collection, props.store);
_ASR6PAFNcjs.setup.call(void 0,
composite,
() => _ASR6PAFNcjs.sync.call(void 0, composite, ["renderedItems", "activeId"], (state) => {
composite.setState("activeId", (activeId2) => {
var _a2;
if (activeId2 !== void 0) return activeId2;
return (_a2 = findFirstEnabledItem(state.renderedItems)) == null ? void 0 : _a2.id;
});
})
);
const getNextId = (direction = "next", options = {}) => {
var _a2, _b;
const defaultState = composite.getState();
const {
skip = 0,
activeId: activeId2 = defaultState.activeId,
focusShift = defaultState.focusShift,
focusLoop = defaultState.focusLoop,
focusWrap = defaultState.focusWrap,
includesBaseElement = defaultState.includesBaseElement,
renderedItems = defaultState.renderedItems,
rtl = defaultState.rtl
} = options;
const isVerticalDirection = direction === "up" || direction === "down";
const isNextDirection = direction === "next" || direction === "down";
const canReverse = isNextDirection ? rtl && !isVerticalDirection : !rtl || isVerticalDirection;
const canShift = focusShift && !skip;
let items = !isVerticalDirection ? renderedItems : _GDZQUFNPcjs.flatten2DArray.call(void 0,
normalizeRows(groupItemsByRows(renderedItems), activeId2, canShift)
);
items = canReverse ? _GDZQUFNPcjs.reverseArray.call(void 0, items) : items;
items = isVerticalDirection ? verticalizeItems(items) : items;
if (activeId2 == null) {
return (_a2 = findFirstEnabledItem(items)) == null ? void 0 : _a2.id;
}
const activeItem = items.find((item) => item.id === activeId2);
if (!activeItem) {
return (_b = findFirstEnabledItem(items)) == null ? void 0 : _b.id;
}
const isGrid = items.some((item) => item.rowId);
const activeIndex = items.indexOf(activeItem);
const nextItems = items.slice(activeIndex + 1);
const nextItemsInRow = getItemsInRow(nextItems, activeItem.rowId);
if (skip) {
const nextEnabledItemsInRow = getEnabledItems(nextItemsInRow, activeId2);
const nextItem2 = nextEnabledItemsInRow.slice(skip)[0] || // If we can't find an item, just return the last one.
nextEnabledItemsInRow[nextEnabledItemsInRow.length - 1];
return nextItem2 == null ? void 0 : nextItem2.id;
}
const canLoop = focusLoop && (isVerticalDirection ? focusLoop !== "horizontal" : focusLoop !== "vertical");
const canWrap = isGrid && focusWrap && (isVerticalDirection ? focusWrap !== "horizontal" : focusWrap !== "vertical");
const hasNullItem = isNextDirection ? (!isGrid || isVerticalDirection) && canLoop && includesBaseElement : isVerticalDirection ? includesBaseElement : false;
if (canLoop) {
const loopItems = canWrap && !hasNullItem ? items : getItemsInRow(items, activeItem.rowId);
const sortedItems = flipItems(loopItems, activeId2, hasNullItem);
const nextItem2 = findFirstEnabledItem(sortedItems, activeId2);
return nextItem2 == null ? void 0 : nextItem2.id;
}
if (canWrap) {
const nextItem2 = findFirstEnabledItem(
// We can use nextItems, which contains all the next items, including
// items from other rows, to wrap between rows. However, if there is a
// null item (the composite container), we'll only use the next items in
// the row. So moving next from the last item will focus on the
// composite container. On grid composites, horizontal navigation never
// focuses on the composite container, only vertical.
hasNullItem ? nextItemsInRow : nextItems,
activeId2
);
const nextId = hasNullItem ? (nextItem2 == null ? void 0 : nextItem2.id) || null : nextItem2 == null ? void 0 : nextItem2.id;
return nextId;
}
const nextItem = findFirstEnabledItem(nextItemsInRow, activeId2);
if (!nextItem && hasNullItem) {
return null;
}
return nextItem == null ? void 0 : nextItem.id;
};
return _7EQBAZ46cjs.__spreadProps.call(void 0, _7EQBAZ46cjs.__spreadValues.call(void 0, _7EQBAZ46cjs.__spreadValues.call(void 0, {}, collection), composite), {
setBaseElement: (element) => composite.setState("baseElement", element),
setActiveId: (id) => composite.setState("activeId", id),
move: (id) => {
if (id === void 0) return;
composite.setState("activeId", id);
composite.setState("moves", (moves) => moves + 1);
},
first: () => {
var _a2;
return (_a2 = findFirstEnabledItem(composite.getState().renderedItems)) == null ? void 0 : _a2.id;
},
last: () => {
var _a2;
return (_a2 = findFirstEnabledItem(_GDZQUFNPcjs.reverseArray.call(void 0, composite.getState().renderedItems))) == null ? void 0 : _a2.id;
},
next: (options) => {
if (options !== void 0 && typeof options === "number") {
options = { skip: options };
}
return getNextId("next", options);
},
previous: (options) => {
if (options !== void 0 && typeof options === "number") {
options = { skip: options };
}
return getNextId("previous", options);
},
down: (options) => {
if (options !== void 0 && typeof options === "number") {
options = { skip: options };
}
return getNextId("down", options);
},
up: (options) => {
if (options !== void 0 && typeof options === "number") {
options = { skip: options };
}
return getNextId("up", options);
}
});
}
exports.createCompositeStore = createCompositeStore;