UNPKG

@mui/x-tree-view

Version:

The community edition of the MUI X Tree View components.

269 lines (266 loc) 10.6 kB
'use client'; import _extends from "@babel/runtime/helpers/esm/extends"; import * as React from 'react'; import { useStore } from '@mui/x-internals/store'; import extractEventHandlers from '@mui/utils/extractEventHandlers'; import { useMergedRefs } from '@base-ui/utils/useMergedRefs'; import { useTreeViewContext } from "../internals/TreeViewProvider/index.js"; import { useTreeItemUtils } from "../hooks/useTreeItemUtils/index.js"; import { TreeViewItemDepthContext } from "../internals/TreeViewItemDepthContext/index.js"; import { isTargetInDescendants } from "../internals/utils/tree.js"; import { generateTreeItemIdAttribute } from "../internals/corePlugins/useTreeViewId/useTreeViewId.utils.js"; import { focusSelectors } from "../internals/plugins/useTreeViewFocus/index.js"; import { itemsSelectors } from "../internals/plugins/useTreeViewItems/index.js"; import { idSelectors } from "../internals/corePlugins/useTreeViewId/index.js"; import { expansionSelectors } from "../internals/plugins/useTreeViewExpansion/index.js"; import { selectionSelectors } from "../internals/plugins/useTreeViewSelection/index.js"; const depthSelector = (state, itemId, depthContext) => { if (typeof depthContext === 'function') { return depthContext(state, itemId); } return depthContext; }; export const useTreeItem = parameters => { const { runItemPlugins, instance, publicAPI, store } = useTreeViewContext(); const depthContext = React.useContext(TreeViewItemDepthContext); const depth = useStore(store, depthSelector, parameters.itemId, depthContext); const { id, itemId, label, children, rootRef } = parameters; const { rootRef: pluginRootRef, contentRef, propsEnhancers } = runItemPlugins(parameters); const { interactions, status } = useTreeItemUtils({ itemId, children }); const rootRefObject = React.useRef(null); const contentRefObject = React.useRef(null); const handleRootRef = useMergedRefs(rootRef, pluginRootRef, rootRefObject); const handleContentRef = useMergedRefs(contentRef, contentRefObject); const checkboxRef = React.useRef(null); const treeId = useStore(store, idSelectors.treeId); const isCheckboxSelectionEnabled = useStore(store, selectionSelectors.isCheckboxSelectionEnabled); const idAttribute = generateTreeItemIdAttribute({ itemId, treeId, id }); const shouldBeAccessibleWithTab = useStore(store, focusSelectors.isItemTheDefaultFocusableItem, itemId); const sharedPropsEnhancerParams = { rootRefObject, contentRefObject, interactions }; const createRootHandleFocus = otherHandlers => event => { otherHandlers.onFocus?.(event); if (event.defaultMuiPrevented) { return; } if (!status.focused && itemsSelectors.canItemBeFocused(store.state, itemId) && event.currentTarget === event.target) { instance.focusItem(event, itemId); } }; const createRootHandleBlur = otherHandlers => event => { otherHandlers.onBlur?.(event); if (event.defaultMuiPrevented) { return; } const rootElement = instance.getItemDOMElement(itemId); // Don't blur the root when switching to editing mode // the input that triggers the root blur can be either the relatedTarget (when entering editing state) or the target (when exiting editing state) // when we enter the editing state, we focus the input -> we don't want to remove the focused item from the state if (status.editing || // we can exit the editing state by clicking outside the input (within the Tree Item) or by pressing Enter or Escape -> we don't want to remove the focused item from the state in these cases // we can also exit the editing state by clicking on the root itself -> want to remove the focused item from the state in this case event.relatedTarget && isTargetInDescendants(event.relatedTarget, rootElement) && (event.target && event.target?.dataset?.element === 'labelInput' && isTargetInDescendants(event.target, rootElement) || event.relatedTarget?.dataset?.element === 'labelInput')) { return; } instance.removeFocusedItem(); }; const createRootHandleKeyDown = otherHandlers => event => { otherHandlers.onKeyDown?.(event); if (event.defaultMuiPrevented || event.target?.dataset?.element === 'labelInput') { return; } instance.handleItemKeyDown(event, itemId); }; const createLabelHandleDoubleClick = otherHandlers => event => { otherHandlers.onDoubleClick?.(event); if (event.defaultMuiPrevented) { return; } interactions.toggleItemEditing(); }; const createContentHandleClick = otherHandlers => event => { otherHandlers.onClick?.(event); instance.handleItemClick(event, itemId); if (event.defaultMuiPrevented || checkboxRef.current?.contains(event.target)) { return; } if (expansionSelectors.triggerSlot(store.state) === 'content') { interactions.handleExpansion(event); } if (!isCheckboxSelectionEnabled) { interactions.handleSelection(event); } }; const createContentHandleMouseDown = otherHandlers => event => { otherHandlers.onMouseDown?.(event); if (event.defaultMuiPrevented) { return; } // Prevent text selection if (event.shiftKey || event.ctrlKey || event.metaKey || status.disabled) { event.preventDefault(); } }; const createIconContainerHandleClick = otherHandlers => event => { otherHandlers.onClick?.(event); if (event.defaultMuiPrevented) { return; } if (expansionSelectors.triggerSlot(store.state) === 'iconContainer') { interactions.handleExpansion(event); } }; const getContextProviderProps = () => ({ itemId, id }); const getRootProps = (externalProps = {}) => { const externalEventHandlers = _extends({}, extractEventHandlers(parameters), extractEventHandlers(externalProps)); const props = _extends({}, externalEventHandlers, { ref: handleRootRef, role: 'treeitem', tabIndex: shouldBeAccessibleWithTab ? 0 : -1, id: idAttribute, 'aria-expanded': status.expandable ? status.expanded : undefined, 'aria-disabled': status.disabled || undefined }, externalProps, { style: _extends({}, externalProps.style ?? {}, { '--TreeView-itemDepth': depth }), onFocus: createRootHandleFocus(externalEventHandlers), onBlur: createRootHandleBlur(externalEventHandlers), onKeyDown: createRootHandleKeyDown(externalEventHandlers) }); const enhancedRootProps = propsEnhancers.root?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, props, enhancedRootProps); }; const getContentProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); const props = _extends({}, externalEventHandlers, externalProps, { ref: handleContentRef, onClick: createContentHandleClick(externalEventHandlers), onMouseDown: createContentHandleMouseDown(externalEventHandlers), status }); ['expanded', 'selected', 'focused', 'disabled', 'editing', 'editable'].forEach(key => { if (status[key]) { props[`data-${key}`] = ''; } }); const enhancedContentProps = propsEnhancers.content?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, props, enhancedContentProps); }; const getCheckboxProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); const props = _extends({}, externalEventHandlers, { ref: checkboxRef, 'aria-hidden': true }, externalProps); const enhancedCheckboxProps = propsEnhancers.checkbox?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, props, enhancedCheckboxProps); }; const getLabelProps = (externalProps = {}) => { const externalEventHandlers = _extends({}, extractEventHandlers(externalProps)); const props = _extends({}, externalEventHandlers, { children: label }, externalProps, { onDoubleClick: createLabelHandleDoubleClick(externalEventHandlers) }); const enhancedLabelProps = propsEnhancers.label?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, enhancedLabelProps, props); }; const getLabelInputProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); const enhancedLabelInputProps = propsEnhancers.labelInput?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, externalProps, enhancedLabelInputProps); }; const getIconContainerProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); return _extends({}, externalEventHandlers, externalProps, { onClick: createIconContainerHandleClick(externalEventHandlers) }); }; const getErrorContainerProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); return _extends({}, externalEventHandlers, externalProps); }; const getLoadingContainerProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); return _extends({ size: '12px', thickness: 6 }, externalEventHandlers, externalProps); }; const getGroupTransitionProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); const response = _extends({}, externalEventHandlers, { unmountOnExit: true, component: 'ul', role: 'group', in: status.expanded, children }, externalProps); return response; }; const getDragAndDropOverlayProps = (externalProps = {}) => { const externalEventHandlers = extractEventHandlers(externalProps); const enhancedDragAndDropOverlayProps = propsEnhancers.dragAndDropOverlay?.(_extends({}, sharedPropsEnhancerParams, { externalEventHandlers })) ?? {}; return _extends({}, externalProps, enhancedDragAndDropOverlayProps); }; return { getContextProviderProps, getRootProps, getContentProps, getGroupTransitionProps, getIconContainerProps, getCheckboxProps, getLabelProps, getLabelInputProps, getDragAndDropOverlayProps, getErrorContainerProps, getLoadingContainerProps, rootRef: handleRootRef, status, publicAPI }; };