UNPKG

@mui/x-tree-view

Version:

The community edition of the MUI X Tree View components.

113 lines (111 loc) 3.65 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { useStableCallback } from '@base-ui/utils/useStableCallback'; import { useStoreEffect } from '@mui/x-internals/store'; import { focusSelectors } from "./useTreeViewFocus.selectors.js"; import { expansionSelectors } from "../useTreeViewExpansion/useTreeViewExpansion.selectors.js"; import { itemsSelectors } from "../useTreeViewItems/useTreeViewItems.selectors.js"; export const useTreeViewFocus = ({ instance, params, store }) => { const setFocusedItemId = useStableCallback(itemId => { const focusedItemId = focusSelectors.focusedItemId(store.state); if (focusedItemId === itemId) { return; } store.set('focus', _extends({}, store.state.focus, { focusedItemId: itemId })); }); const isItemVisible = itemId => { const itemMeta = itemsSelectors.itemMeta(store.state, itemId); return itemMeta && (itemMeta.parentId == null || expansionSelectors.isItemExpanded(store.state, itemMeta.parentId)); }; const innerFocusItem = (event, itemId) => { const itemElement = instance.getItemDOMElement(itemId); if (itemElement) { itemElement.focus(); } setFocusedItemId(itemId); if (params.onItemFocus) { params.onItemFocus(event, itemId); } }; const focusItem = useStableCallback((event, itemId) => { // If we receive an itemId, and it is visible, the focus will be set to it if (isItemVisible(itemId)) { innerFocusItem(event, itemId); } }); const removeFocusedItem = useStableCallback(() => { const focusedItemId = focusSelectors.focusedItemId(store.state); if (focusedItemId == null) { return; } const itemMeta = itemsSelectors.itemMeta(store.state, focusedItemId); if (itemMeta) { const itemElement = instance.getItemDOMElement(focusedItemId); if (itemElement) { itemElement.blur(); } } setFocusedItemId(null); }); // Whenever the items change, we need to ensure the focused item is still present. useStoreEffect(store, itemsSelectors.itemMetaLookup, () => { const focusedItemId = focusSelectors.focusedItemId(store.state); if (focusedItemId == null) { return; } const hasItemBeenRemoved = !itemsSelectors.itemMeta(store.state, focusedItemId); if (!hasItemBeenRemoved) { return; } const defaultFocusableItemId = focusSelectors.defaultFocusableItemId(store.state); if (defaultFocusableItemId == null) { setFocusedItemId(null); return; } innerFocusItem(null, defaultFocusableItemId); }); const createRootHandleFocus = otherHandlers => event => { otherHandlers.onFocus?.(event); if (event.defaultMuiPrevented) { return; } // if the event bubbled (which is React specific) we don't want to steal focus const defaultFocusableItemId = focusSelectors.defaultFocusableItemId(store.state); if (event.target === event.currentTarget && defaultFocusableItemId != null) { innerFocusItem(event, defaultFocusableItemId); } }; const createRootHandleBlur = otherHandlers => event => { otherHandlers.onBlur?.(event); if (event.defaultMuiPrevented) { return; } setFocusedItemId(null); }; return { getRootProps: otherHandlers => ({ onFocus: createRootHandleFocus(otherHandlers), onBlur: createRootHandleBlur(otherHandlers) }), publicAPI: { focusItem }, instance: { focusItem, removeFocusedItem } }; }; useTreeViewFocus.getInitialState = () => ({ focus: { focusedItemId: null } }); useTreeViewFocus.params = { onItemFocus: true };