UNPKG

@mui/x-tree-view

Version:

The community edition of the MUI X Tree View components.

153 lines (150 loc) 5.42 kB
'use client'; import { useStore } from '@mui/x-internals/store'; import { useTreeViewContext } from "../../internals/TreeViewProvider/index.js"; import { expansionSelectors } from "../../internals/plugins/expansion/selectors.js"; import { focusSelectors } from "../../internals/plugins/focus/selectors.js"; import { itemsSelectors } from "../../internals/plugins/items/selectors.js"; import { selectionSelectors } from "../../internals/plugins/selection/selectors.js"; import { lazyLoadingSelectors } from "../../internals/plugins/lazyLoading/selectors.js"; import { labelSelectors } from "../../internals/plugins/labelEditing/selectors.js"; export const itemHasChildren = reactChildren => { if (Array.isArray(reactChildren)) { return reactChildren.length > 0 && reactChildren.some(itemHasChildren); } return Boolean(reactChildren); }; export const useTreeItemUtils = ({ itemId, children }) => { const { store, publicAPI } = useTreeViewContext(); const isItemExpandable = useStore(store, expansionSelectors.isItemExpandable, itemId); const isLoading = useStore(store, lazyLoadingSelectors.isItemLoading, itemId); const hasError = useStore(store, lazyLoadingSelectors.itemHasError, itemId); const isExpandable = itemHasChildren(children) || isItemExpandable; const isExpanded = useStore(store, expansionSelectors.isItemExpanded, itemId); const isFocused = useStore(store, focusSelectors.isItemFocused, itemId); const isSelected = useStore(store, selectionSelectors.isItemSelected, itemId); const isDisabled = useStore(store, itemsSelectors.isItemDisabled, itemId); const isEditing = useStore(store, labelSelectors.isItemBeingEdited, itemId); const isEditable = useStore(store, labelSelectors.isItemEditable, itemId); const status = { expandable: isExpandable, expanded: isExpanded, focused: isFocused, selected: isSelected, disabled: isDisabled, editing: isEditing, editable: isEditable, loading: isLoading, error: hasError }; const handleExpansion = event => { if (status.disabled) { return; } if (!status.focused) { store.focus.focusItem(event, itemId); } const multiple = selectionSelectors.isMultiSelectEnabled(store.state) && (event.shiftKey || event.ctrlKey || event.metaKey); // If already expanded and trying to toggle selection don't close if (status.expandable && !(multiple && expansionSelectors.isItemExpanded(store.state, itemId))) { // make sure the children selection is propagated again store.expansion.setItemExpansion({ event, itemId }); } }; const handleSelection = event => { if (!selectionSelectors.canItemBeSelected(store.state, itemId)) { return; } if (!status.focused && !status.editing) { store.focus.focusItem(event, itemId); } const multiple = selectionSelectors.isMultiSelectEnabled(store.state) && (event.shiftKey || event.ctrlKey || event.metaKey); if (multiple) { if (event.shiftKey) { store.selection.expandSelectionRange(event, itemId); } else { store.selection.setItemSelection({ event, itemId, keepExistingSelection: true }); } } else { store.selection.setItemSelection({ event, itemId, shouldBeSelected: true }); } }; const handleCheckboxSelection = event => { const hasShift = event.nativeEvent.shiftKey; const isMultiSelectEnabled = selectionSelectors.isMultiSelectEnabled(store.state); if (isMultiSelectEnabled && hasShift) { store.selection.expandSelectionRange(event, itemId); } else { store.selection.setItemSelection({ event, itemId, keepExistingSelection: isMultiSelectEnabled, shouldBeSelected: event.target.checked }); } }; const toggleItemEditing = () => { // If the store doesn't support label editing, do nothing if (!store.labelEditing) { return; } if (isEditing) { store.labelEditing.setEditedItem(null); } else { store.labelEditing.setEditedItem(itemId); } }; const handleSaveItemLabel = (event, newLabel) => { // If the store doesn't support label editing, do nothing if (!store.labelEditing) { return; } // As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred // The `onBlur` event is triggered, which calls `handleSaveItemLabel` again. // To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel` if (labelSelectors.isItemBeingEdited(store.state, itemId)) { store.labelEditing.updateItemLabel(itemId, newLabel); toggleItemEditing(); store.focus.focusItem(event, itemId); } }; const handleCancelItemLabelEditing = event => { // If the store doesn't support label editing, do nothing if (!store.labelEditing) { return; } if (labelSelectors.isItemBeingEdited(store.state, itemId)) { toggleItemEditing(); store.focus.focusItem(event, itemId); } }; const interactions = { handleExpansion, handleSelection, handleCheckboxSelection, toggleItemEditing, handleSaveItemLabel, handleCancelItemLabelEditing }; return { interactions, status, publicAPI }; };