@fluentui/react-northstar
Version:
A themable React component library.
116 lines (111 loc) • 5.1 kB
JavaScript
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