UNPKG

@fluentui/react-northstar

Version:
122 lines (116 loc) 6.35 kB
"use strict"; 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