@mui/x-tree-view
Version:
The community edition of the MUI X Tree View components.
223 lines (221 loc) • 8.42 kB
JavaScript
'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { useStableCallback } from '@base-ui/utils/useStableCallback';
import { buildItemsLookups, buildItemsState, TREE_VIEW_ROOT_PARENT_ID } from "./useTreeViewItems.utils.js";
import { TreeViewItemDepthContext } from "../../TreeViewItemDepthContext/index.js";
import { itemsSelectors } from "./useTreeViewItems.selectors.js";
import { idSelectors } from "../../corePlugins/useTreeViewId/index.js";
import { generateTreeItemIdAttribute } from "../../corePlugins/useTreeViewId/useTreeViewId.utils.js";
import { jsx as _jsx } from "react/jsx-runtime";
const defaultIsItemSelectionDisabled = item => item.disableSelection === true;
export const useTreeViewItems = ({
instance,
params,
store
}) => {
const itemsConfig = React.useMemo(() => ({
isItemDisabled: params.isItemDisabled,
isItemSelectionDisabled: params.isItemSelectionDisabled,
getItemLabel: params.getItemLabel,
getItemChildren: params.getItemChildren,
getItemId: params.getItemId
}), [params.isItemDisabled, params.isItemSelectionDisabled, params.getItemLabel, params.getItemChildren, params.getItemId]);
const getItem = React.useCallback(itemId => itemsSelectors.itemModel(store.state, itemId), [store]);
const getParentId = React.useCallback(itemId => {
const itemMeta = itemsSelectors.itemMeta(store.state, itemId);
return itemMeta?.parentId || null;
}, [store]);
const setIsItemDisabled = useStableCallback(({
itemId,
shouldBeDisabled
}) => {
if (!store.state.items.itemMetaLookup[itemId]) {
return;
}
const itemMetaLookup = _extends({}, store.state.items.itemMetaLookup);
itemMetaLookup[itemId] = _extends({}, itemMetaLookup[itemId], {
disabled: shouldBeDisabled ?? !itemMetaLookup[itemId].disabled
});
store.set('items', _extends({}, store.state.items, {
itemMetaLookup
}));
});
const getItemTree = React.useCallback(() => {
const getItemFromItemId = itemId => {
const item = itemsSelectors.itemModel(store.state, itemId);
const itemToMutate = _extends({}, item);
const newChildren = itemsSelectors.itemOrderedChildrenIds(store.state, itemId);
if (newChildren.length > 0) {
itemToMutate.children = newChildren.map(getItemFromItemId);
} else {
delete itemToMutate.children;
}
return itemToMutate;
};
return itemsSelectors.itemOrderedChildrenIds(store.state, null).map(getItemFromItemId);
}, [store]);
const getItemOrderedChildrenIds = React.useCallback(itemId => itemsSelectors.itemOrderedChildrenIds(store.state, itemId), [store]);
const getItemDOMElement = itemId => {
const itemMeta = itemsSelectors.itemMeta(store.state, itemId);
if (itemMeta == null) {
return null;
}
const idAttribute = generateTreeItemIdAttribute({
treeId: idSelectors.treeId(store.state),
itemId,
id: itemMeta.idAttribute
});
return document.getElementById(idAttribute);
};
const areItemUpdatesPreventedRef = React.useRef(false);
const preventItemUpdates = React.useCallback(() => {
areItemUpdatesPreventedRef.current = true;
}, []);
const areItemUpdatesPrevented = React.useCallback(() => areItemUpdatesPreventedRef.current, []);
const setItemChildren = ({
items,
parentId,
getChildrenCount
}) => {
const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
const parentDepth = parentId == null ? -1 : itemsSelectors.itemDepth(store.state, parentId);
const {
metaLookup,
modelLookup,
orderedChildrenIds,
childrenIndexes
} = buildItemsLookups({
config: itemsConfig,
items,
parentId,
depth: parentDepth + 1,
isItemExpandable: getChildrenCount ? item => getChildrenCount(item) > 0 : () => false,
otherItemsMetaLookup: itemsSelectors.itemMetaLookup(store.state)
});
const lookups = {
itemModelLookup: _extends({}, store.state.items.itemModelLookup, modelLookup),
itemMetaLookup: _extends({}, store.state.items.itemMetaLookup, metaLookup),
itemOrderedChildrenIdsLookup: _extends({}, store.state.items.itemOrderedChildrenIdsLookup, {
[parentIdWithDefault]: orderedChildrenIds
}),
itemChildrenIndexesLookup: _extends({}, store.state.items.itemChildrenIndexesLookup, {
[parentIdWithDefault]: childrenIndexes
})
};
store.set('items', _extends({}, store.state.items, lookups));
};
const removeChildren = useStableCallback(parentId => {
const newMetaMap = Object.keys(store.state.items.itemMetaLookup).reduce((acc, key) => {
const item = store.state.items.itemMetaLookup[key];
if (item.parentId === parentId) {
return acc;
}
return _extends({}, acc, {
[item.id]: item
});
}, {});
const newItemOrderedChildrenIdsLookup = _extends({}, store.state.items.itemOrderedChildrenIdsLookup);
const newItemChildrenIndexesLookup = _extends({}, store.state.items.itemChildrenIndexesLookup);
const cleanId = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
delete newItemChildrenIndexesLookup[cleanId];
delete newItemOrderedChildrenIdsLookup[cleanId];
store.set('items', _extends({}, store.state.items, {
itemMetaLookup: newMetaMap,
itemOrderedChildrenIdsLookup: newItemOrderedChildrenIdsLookup,
itemChildrenIndexesLookup: newItemChildrenIndexesLookup
}));
});
const addExpandableItems = useStableCallback(items => {
const newItemMetaLookup = _extends({}, store.state.items.itemMetaLookup);
for (const itemId of items) {
newItemMetaLookup[itemId] = _extends({}, newItemMetaLookup[itemId], {
expandable: true
});
}
store.set('items', _extends({}, store.state.items, {
itemMetaLookup: newItemMetaLookup
}));
});
React.useEffect(() => {
if (instance.areItemUpdatesPrevented()) {
return;
}
const newState = buildItemsState({
disabledItemsFocusable: params.disabledItemsFocusable,
items: params.items,
config: itemsConfig
});
store.set('items', _extends({}, store.state.items, newState));
}, [instance, store, params.items, params.disabledItemsFocusable, itemsConfig]);
// Wrap `props.onItemClick` with `useStableCallback` to prevent unneeded context updates.
const handleItemClick = useStableCallback((event, itemId) => {
if (params.onItemClick) {
params.onItemClick(event, itemId);
}
});
return {
getRootProps: () => ({
style: {
'--TreeView-itemChildrenIndentation': typeof params.itemChildrenIndentation === 'number' ? `${params.itemChildrenIndentation}px` : params.itemChildrenIndentation
}
}),
publicAPI: {
getItem,
getItemDOMElement,
getItemTree,
getItemOrderedChildrenIds,
setIsItemDisabled,
getParentId
},
instance: {
getItemDOMElement,
preventItemUpdates,
areItemUpdatesPrevented,
setItemChildren,
removeChildren,
addExpandableItems,
handleItemClick
}
};
};
useTreeViewItems.getInitialState = params => ({
items: buildItemsState({
items: params.items,
disabledItemsFocusable: params.disabledItemsFocusable,
config: {
isItemDisabled: params.isItemDisabled,
isItemSelectionDisabled: params.isItemSelectionDisabled,
getItemId: params.getItemId,
getItemLabel: params.getItemLabel,
getItemChildren: params.getItemChildren
}
})
});
useTreeViewItems.applyDefaultValuesToParams = ({
params
}) => _extends({}, params, {
disabledItemsFocusable: params.disabledItemsFocusable ?? false,
itemChildrenIndentation: params.itemChildrenIndentation ?? '12px',
isItemSelectionDisabled: params.isItemSelectionDisabled ?? defaultIsItemSelectionDisabled
});
useTreeViewItems.wrapRoot = ({
children
}) => {
return /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
value: itemsSelectors.itemDepth,
children: children
});
};
if (process.env.NODE_ENV !== "production") useTreeViewItems.wrapRoot.displayName = "useTreeViewItems.wrapRoot";
useTreeViewItems.params = {
disabledItemsFocusable: true,
items: true,
isItemDisabled: true,
isItemSelectionDisabled: true,
getItemLabel: true,
getItemChildren: true,
getItemId: true,
onItemClick: true,
itemChildrenIndentation: true
};