UNPKG

@mui/x-tree-view

Version:

The community edition of the MUI X Tree View components.

121 lines (114 loc) 4.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TreeViewFocusPlugin = void 0; var _expansion = require("../expansion"); var _selectors = require("./selectors"); var _items = require("../items"); var _tree = require("../../utils/tree"); class TreeViewFocusPlugin { // We can't type `store`, otherwise we get the following TS error: // 'focus' 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; // Whenever the items change, we need to ensure the focused item is still present. // If the focused item was removed, focus the closest neighbor instead of the first item. let previousState = store.state; this.store.subscribe(newState => { // Only run when items actually changed. if (newState.itemMetaLookup === previousState.itemMetaLookup) { previousState = newState; return; } const focusedItemId = _selectors.focusSelectors.focusedItemId(newState); if (focusedItemId == null || _items.itemsSelectors.itemMeta(newState, focusedItemId)) { previousState = newState; return; } const checkItemInNewTree = itemId => itemId == null || !_items.itemsSelectors.itemMeta(newState, itemId) ? null : itemId; const itemToFocusId = checkItemInNewTree((0, _tree.getNextNavigableItem)(previousState, focusedItemId)) ?? checkItemInNewTree((0, _tree.getPreviousNavigableItem)(previousState, focusedItemId)) ?? (0, _tree.getFirstNavigableItem)(newState); if (itemToFocusId == null) { this.setFocusedItemId(null); } else { this.applyItemFocus(null, itemToFocusId); } previousState = newState; }); } setFocusedItemId = itemId => { const focusedItemId = _selectors.focusSelectors.focusedItemId(this.store.state); if (focusedItemId === itemId) { return; } this.store.set('focusedItemId', itemId); }; applyItemFocus = (event, itemId) => { this.store.items.getItemDOMElement(itemId)?.focus(); this.setFocusedItemId(itemId); this.store.parameters.onItemFocus?.(event, itemId); }; buildPublicAPI = () => { return { focusItem: this.focusItem }; }; /** * Focus the item with the given id. * * If the item is the child of a collapsed item, then this method will do nothing. * Make sure to expand the ancestors of the item before calling this method if needed. * @param {React.SyntheticEvent | null} event The DOM event that triggered the change. * @param {TreeViewItemId} itemId The id of the item to focus. */ focusItem = (event, itemId) => { // If we receive an itemId, and it is visible, the focus will be set to it const itemMeta = _items.itemsSelectors.itemMeta(this.store.state, itemId); const isItemVisible = itemMeta && (itemMeta.parentId == null || _expansion.expansionSelectors.isItemExpanded(this.store.state, itemMeta.parentId)); if (isItemVisible) { this.applyItemFocus(event, itemId); } }; /** * Remove the focus from the currently focused item (both from the internal state and the DOM). */ removeFocusedItem = () => { const focusedItemId = _selectors.focusSelectors.focusedItemId(this.store.state); if (focusedItemId == null) { return; } const itemMeta = _items.itemsSelectors.itemMeta(this.store.state, focusedItemId); if (itemMeta) { const itemElement = this.store.items.getItemDOMElement(focusedItemId); if (itemElement) { itemElement.blur(); } } this.setFocusedItemId(null); }; /** * Event handler to fire when the `root` slot of the Tree View is focused. * @param {React.MouseEvent} event The DOM event that triggered the change. */ handleRootFocus = event => { if (event.defaultMuiPrevented) { return; } // if the event bubbled (which is React specific) we don't want to steal focus const defaultFocusableItemId = _selectors.focusSelectors.defaultFocusableItemId(this.store.state); if (event.target === event.currentTarget && defaultFocusableItemId != null) { this.applyItemFocus(event, defaultFocusableItemId); } }; /** * Event handler to fire when the `root` slot of the Tree View is blurred. * @param {React.MouseEvent} event The DOM event that triggered the change. */ handleRootBlur = event => { if (event.defaultMuiPrevented) { return; } this.setFocusedItemId(null); }; } exports.TreeViewFocusPlugin = TreeViewFocusPlugin;