@mui/x-tree-view
Version:
The community edition of the MUI X Tree View components.
121 lines (114 loc) • 4.61 kB
JavaScript
"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;