UNPKG

@mui/x-tree-view

Version:

The community edition of the MUI X Tree View components.

223 lines (212 loc) 8.46 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { idSelectors } from "../id/index.js"; import { itemsSelectors } from "./selectors.js"; import { buildItemsLookups, TREE_VIEW_ROOT_PARENT_ID } from "./utils.js"; export class TreeViewItemsPlugin { // We can't type `store`, otherwise we get the following TS error: // 'items' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. constructor(store) { this.store = store; } /** * Determines if the items state should be rebuilt based on the new and previous parameters. */ static shouldRebuildItemsState = (newParameters, previousParameters) => { return ['items', 'isItemDisabled', 'isItemSelectionDisabled', 'getItemId', 'getItemLabel', 'getItemChildren'].some(key => { const typedKey = key; return newParameters[typedKey] !== previousParameters[typedKey]; }); }; /** * Builds the state properties derived from the `items` prop. */ static buildItemsStateIfNeeded = parameters => { const itemMetaLookup = {}; const itemModelLookup = {}; const itemOrderedChildrenIdsLookup = {}; const itemChildrenIndexesLookup = {}; function processSiblings(items, parentId, depth) { const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID; const { metaLookup, modelLookup, orderedChildrenIds, childrenIndexes, itemsChildren } = buildItemsLookups({ storeParameters: parameters, items, parentId, depth, isItemExpandable: (item, children) => !!children && children.length > 0, otherItemsMetaLookup: itemMetaLookup }); Object.assign(itemMetaLookup, metaLookup); Object.assign(itemModelLookup, modelLookup); itemOrderedChildrenIdsLookup[parentIdWithDefault] = orderedChildrenIds; itemChildrenIndexesLookup[parentIdWithDefault] = childrenIndexes; for (const item of itemsChildren) { processSiblings(item.children || [], item.id, depth + 1); } } processSiblings(parameters.items, null, 0); return { itemMetaLookup, itemModelLookup, itemOrderedChildrenIdsLookup, itemChildrenIndexesLookup }; }; /** * Get the item with the given id. * When used in the Simple Tree View, it returns an object with the `id` and `label` properties. * @param {TreeViewItemId} itemId The id of the item to retrieve. * @returns {R} The item with the given id. */ getItem = itemId => itemsSelectors.itemModel(this.store.state, itemId); /** * Get all the items in the same format as provided by `props.items`. * @returns {R[]} The items in the tree. */ getItemTree = () => { const getItemFromItemId = itemId => { const item = itemsSelectors.itemModel(this.store.state, itemId); const itemToMutate = _extends({}, item); const newChildren = itemsSelectors.itemOrderedChildrenIds(this.store.state, itemId); if (newChildren.length > 0) { itemToMutate.children = newChildren.map(getItemFromItemId); } else { delete itemToMutate.children; } return itemToMutate; }; return itemsSelectors.itemOrderedChildrenIds(this.store.state, null).map(getItemFromItemId); }; /** * Get the ids of a given item's children. * Those ids are returned in the order they should be rendered. * To get the root items, pass `null` as the `itemId`. * @param {TreeViewItemId | null} itemId The id of the item to get the children of. * @returns {TreeViewItemId[]} The ids of the item's children. */ getItemOrderedChildrenIds = itemId => itemsSelectors.itemOrderedChildrenIds(this.store.state, itemId); /** * Get the id of the parent item. * @param {TreeViewItemId} itemId The id of the item to whose parentId we want to retrieve. * @returns {TreeViewItemId | null} The id of the parent item. */ getParentId = itemId => { const itemMeta = itemsSelectors.itemMeta(this.store.state, itemId); return itemMeta?.parentId || null; }; /** * Toggle the disabled state of the item with the given id. * @param {object} parameters The params of the method. * @param {TreeViewItemId } parameters.itemId The id of the item to get the children of. * @param {boolean } parameters.shouldBeDisabled true if the item should be disabled. */ setIsItemDisabled = ({ itemId, shouldBeDisabled }) => { if (!this.store.state.itemMetaLookup[itemId]) { return; } const itemMetaLookup = _extends({}, this.store.state.itemMetaLookup); itemMetaLookup[itemId] = _extends({}, itemMetaLookup[itemId], { disabled: shouldBeDisabled ?? !itemMetaLookup[itemId].disabled }); this.store.set('itemMetaLookup', itemMetaLookup); }; buildPublicAPI = () => { return { getItem: this.getItem, getItemDOMElement: this.getItemDOMElement, getItemOrderedChildrenIds: this.getItemOrderedChildrenIds, getItemTree: this.getItemTree, getParentId: this.getParentId, setIsItemDisabled: this.setIsItemDisabled }; }; /** * Get the DOM element of the item with the given id. * @param {TreeViewItemId} itemId The id of the item to get the DOM element of. * @returns {HTMLElement | null} The DOM element of the item with the given id. */ getItemDOMElement = itemId => { const itemMeta = itemsSelectors.itemMeta(this.store.state, itemId); if (itemMeta == null) { return null; } const idAttribute = idSelectors.treeItemIdAttribute(this.store.state, itemId, itemMeta.idAttribute); return document.getElementById(idAttribute); }; /** * Add an array of items to the tree. * @param {SetItemChildrenParameters<R>} args The items to add to the tree and information about their ancestors. */ setItemChildren = ({ items, parentId, getChildrenCount }) => { const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID; const parentDepth = parentId == null ? -1 : itemsSelectors.itemDepth(this.store.state, parentId); const { metaLookup, modelLookup, orderedChildrenIds, childrenIndexes } = buildItemsLookups({ storeParameters: this.store.parameters, items, parentId, depth: parentDepth + 1, isItemExpandable: getChildrenCount ? item => getChildrenCount(item) !== 0 : () => false, otherItemsMetaLookup: itemsSelectors.itemMetaLookup(this.store.state) }); this.store.update({ itemModelLookup: _extends({}, this.store.state.itemModelLookup, modelLookup), itemMetaLookup: _extends({}, this.store.state.itemMetaLookup, metaLookup), itemOrderedChildrenIdsLookup: _extends({}, this.store.state.itemOrderedChildrenIdsLookup, { [parentIdWithDefault]: orderedChildrenIds }), itemChildrenIndexesLookup: _extends({}, this.store.state.itemChildrenIndexesLookup, { [parentIdWithDefault]: childrenIndexes }) }); }; /** * Remove the children of an item. * @param {TreeViewItemId | null} parentId The id of the item to remove the children of. */ removeChildren = parentId => { const itemMetaLookup = this.store.state.itemMetaLookup; const newMetaMap = Object.keys(itemMetaLookup).reduce((acc, key) => { const item = itemMetaLookup[key]; if (item.parentId === parentId) { return acc; } return _extends({}, acc, { [item.id]: item }); }, {}); const newItemOrderedChildrenIdsLookup = _extends({}, this.store.state.itemOrderedChildrenIdsLookup); const newItemChildrenIndexesLookup = _extends({}, this.store.state.itemChildrenIndexesLookup); const cleanId = parentId ?? TREE_VIEW_ROOT_PARENT_ID; delete newItemChildrenIndexesLookup[cleanId]; delete newItemOrderedChildrenIdsLookup[cleanId]; this.store.update({ itemMetaLookup: newMetaMap, itemOrderedChildrenIdsLookup: newItemOrderedChildrenIdsLookup, itemChildrenIndexesLookup: newItemChildrenIndexesLookup }); }; /** * Callback fired when the `content` slot of a given Tree Item is clicked. * @param {React.MouseEvent} event The DOM event that triggered the change. * @param {TreeViewItemId} itemId The id of the item being clicked. */ handleItemClick = (event, itemId) => { this.store.parameters.onItemClick?.(event, itemId); }; }