@fluentui/react-northstar
Version:
A themable React component library.
122 lines (116 loc) • 6.35 kB
JavaScript
exports.__esModule = true;
exports.useVirtualTree = useVirtualTree;
var React = _interopRequireWildcard(require("react"));
var _useTree = require("./useTree");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
// export our own interface that is similar to react-window VariableSizeList,
// to avoid dependency to react-window
function useVirtualTree(props) {
var baseTree = (0, _useTree.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
;