UNPKG

@fluentui/react-northstar

Version:
116 lines (111 loc) 5.1 kB
import * as React from 'react'; import { useTree } from './useTree'; // export our own interface that is similar to react-window VariableSizeList, // to avoid dependency to react-window export function useVirtualTree(props) { var baseTree = useTree(props); var baseRegisterItemRef = baseTree.registerItemRef, baseExpandSiblings = baseTree.expandSiblings, getItemById = baseTree.getItemById, getItemRef = baseTree.getItemRef, visibleItemIds = baseTree.visibleItemIds; var listRef = React.useRef(); var focusIdRef = React.useRef(); var focusItemById = React.useCallback(function (id) { var _getItemById; var itemRef = getItemRef(id); // item is not mounted yet if (itemRef == null) { // set focusIdRef so item can be focused on mount; then scroll to item focusIdRef.current = id; var focusIndex = visibleItemIds.indexOf(focusIdRef.current); if (focusIndex >= 0) { var _listRef$current; (_listRef$current = listRef.current) == null ? void 0 : _listRef$current.scrollToItem(focusIndex, 'center'); } return; } // item is mounted, set focus if ((_getItemById = getItemById(id)) != null && _getItemById.hasSubtree) { itemRef.focus(); } else { var _itemRef$firstElement; // when tree item is leaf, need to focus on the inner treeTitle (_itemRef$firstElement = itemRef.firstElementChild) == null ? void 0 : _itemRef$firstElement.focus(); } }, [getItemById, getItemRef, visibleItemIds]); var registerItemRef = React.useCallback(function (id, node) { baseRegisterItemRef(id, node); if (node && focusIdRef.current === id) { var _getItemById2; // focus on this tree item if ((_getItemById2 = getItemById(id)) != null && _getItemById2.hasSubtree) { node.focus(); } else { var _node$firstElementChi; // when node is leaf, need to focus on the inner treeTitle (_node$firstElementChi = node.firstElementChild) == null ? void 0 : _node$firstElementChi.focus(); } focusIdRef.current = null; } }, [baseRegisterItemRef, getItemById]); var expandSiblings = React.useCallback(function (e, id) { baseExpandSiblings(e, id); focusIdRef.current = id; }, [baseExpandSiblings]); React.useLayoutEffect(function () { /** * Reason for scroll in useLayoutEffect: * Without useLayoutEffect, scrolling works for focus parent and focus first child, but it is problematic for expanding sibings. * When focus parent/child, the number of items (itemCount) in the virtual list does not change. But when sibling expand, itemCount could change. * When siblings are expanded: * without useLayoutEffect, react window uses the itemCount before siblings are expanded, causing it to compute wrong scroll offset. * with useLayoutEffect, the scrolling happens after the new itemCount passed into list as props. Therefore the computed scroll offset is correct. */ if (focusIdRef.current != null && getItemRef(focusIdRef.current) == null) { var focusIndex = visibleItemIds.indexOf(focusIdRef.current); if (focusIndex >= 0) { var _listRef$current2; (_listRef$current2 = listRef.current) == null ? void 0 : _listRef$current2.scrollToItem(focusIndex, 'center'); } } }, [getItemRef, visibleItemIds]); var searchByFirstChar = React.useCallback(function (startIndex, endIndex, char) { var itemToString = props.itemToString || function (item) { return item.content || ''; }; for (var i = startIndex; i < endIndex; ++i) { var _itemToString, _itemToString$trim, _itemToString$trim$ch; var itemFirstChar = (_itemToString = itemToString(getItemById(visibleItemIds[i]).item)) == null ? void 0 : (_itemToString$trim = _itemToString.trim()) == null ? void 0 : (_itemToString$trim$ch = _itemToString$trim.charAt(0)) == null ? void 0 : _itemToString$trim$ch.toLowerCase(); if (itemFirstChar === char.toLowerCase()) { return i; } } return -1; }, [getItemById, props.itemToString, visibleItemIds]); var getToFocusIDByFirstCharacter = React.useCallback(function (e, idToStartSearch) { // Get start index for search var starIndex = visibleItemIds.indexOf(idToStartSearch) + 1; if (starIndex === visibleItemIds.length) { starIndex = 0; } // Check following nodes in tree var toFocusIndex = searchByFirstChar(starIndex, visibleItemIds.length, e.key); // If not found in following nodes, check from beginning if (toFocusIndex === -1) { toFocusIndex = searchByFirstChar(0, starIndex - 1, e.key); } if (toFocusIndex === -1) { return idToStartSearch; } return visibleItemIds[toFocusIndex]; }, [searchByFirstChar, visibleItemIds]); return Object.assign({}, baseTree, { registerItemRef: registerItemRef, focusItemById: focusItemById, expandSiblings: expandSiblings, getToFocusIDByFirstCharacter: getToFocusIDByFirstCharacter, listRef: listRef }); } //# sourceMappingURL=useVirtualTree.js.map