UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

290 lines (289 loc) 9.11 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true, }); Object.defineProperty(exports, 'Tree', { enumerable: true, get: function () { return Tree; }, }); const _interop_require_wildcard = require('@swc/helpers/_/_interop_require_wildcard'); const _react = /*#__PURE__*/ _interop_require_wildcard._(require('react')); const _index = require('../../utils/index.js'); const _TreeContext = require('./TreeContext.js'); const Tree = (props) => { let { data, className, nodeRenderer, getNode, size = 'default', enableVirtualization = false, style, ...rest } = props; let treeRef = _react.useRef(null); let focusedIndex = _react.useRef(0); _react.useEffect(() => { focusedIndex.current = 0; }, [data]); let getFocusableNodes = _react.useCallback(() => { let focusableItems = (0, _index.getFocusableElements)(treeRef.current); return focusableItems.filter( (i) => !focusableItems.some((p) => p.contains(i.parentElement)), ); }, []); let handleKeyDown = (event) => { if (event.altKey) return; let items = getFocusableNodes(); if (!items?.length) return; let activeIndex = items.findIndex((el) => el.contains(treeRef.current?.ownerDocument.activeElement), ); let currentIndex = activeIndex > -1 ? activeIndex : 0; switch (event.key) { case 'ArrowUp': { event.preventDefault(); let newIndex = Math.max(0, currentIndex - 1); items[newIndex].focus(); focusedIndex.current = newIndex; break; } case 'ArrowDown': { event.preventDefault(); let newIndex = Math.min(items.length - 1, currentIndex + 1); items[newIndex].focus(); focusedIndex.current = newIndex; break; } default: break; } }; let [flatNodesList, firstLevelNodesList] = _react.useMemo(() => { let flatList = []; let firstLevelNodes = []; let flattenNodes = (nodes = [], depth = 0, parentNode) => { let nodeIdList = Array(); nodes.forEach((element, index) => { let { subNodes, ...nodeProps } = getNode(element); let flatNode = { nodeProps, depth, parentNode, indexInGroup: index, }; nodeIdList.push(flatNode.nodeProps.nodeId); flatList.push(flatNode); if (0 === depth) firstLevelNodes.push(flatNode); if (flatNode.nodeProps.isExpanded) { let subNodeIds = flattenNodes(subNodes, depth + 1, flatNode); flatNode.subNodeIds = subNodeIds; } }); return nodeIdList; }; flattenNodes(data); return [flatList, firstLevelNodes]; }, [data, getNode]); let itemRenderer = _react.useCallback( (index, virtualItem, virtualizer) => { let node = flatNodesList[index]; return _react.createElement( _TreeContext.TreeContext.Provider, { key: node.nodeProps.nodeId, value: { nodeDepth: node.depth, subNodeIds: node.subNodeIds, groupSize: 0 === node.depth ? firstLevelNodesList.length : node.parentNode?.subNodeIds?.length ?? 0, indexInGroup: node.indexInGroup, parentNodeId: node.parentNode?.nodeProps.nodeId, scrollToParent: node.parentNode ? () => { let parentNodeId = node.parentNode?.nodeProps.nodeId; let parentNodeIndex = flatNodesList.findIndex( (n) => n.nodeProps.nodeId === parentNodeId, ); setScrollToIndex(parentNodeIndex); } : void 0, size, }, }, virtualItem && virtualizer ? (0, _index.cloneElementWithRef)( nodeRenderer(node.nodeProps), (children) => ({ ...children.props, key: virtualItem.key, 'data-iui-index': virtualItem.index, 'data-iui-virtualizer': 'item', ref: virtualizer.measureElement, style: { ...children.props.style, '--_iui-width': '100%', transform: `translateY(${virtualItem.start}px)`, }, }), ) : nodeRenderer(node.nodeProps), ); }, [firstLevelNodesList.length, flatNodesList, nodeRenderer, size], ); let [scrollToIndex, setScrollToIndex] = _react.useState(); let flatNodesListRef = _react.useRef(flatNodesList); _react.useEffect(() => { flatNodesListRef.current = flatNodesList; }, [flatNodesList]); _react.useEffect(() => { setTimeout(() => { if (void 0 !== scrollToIndex) { let nodeId = flatNodesListRef.current[scrollToIndex].nodeProps.nodeId; let nodeElement = treeRef.current?.ownerDocument.querySelector( `#${nodeId}`, ); nodeElement?.focus(); setScrollToIndex(void 0); } }); }, [scrollToIndex]); let handleFocus = (event) => { if (treeRef.current?.contains(event.relatedTarget)) return; let items = getFocusableNodes(); if (items.length > 0) items[focusedIndex.current]?.focus(); }; return _react.createElement( _react.Fragment, null, enableVirtualization ? _react.createElement(VirtualizedTree, { flatNodesList: flatNodesList, itemRenderer: itemRenderer, scrollToIndex: scrollToIndex, onFocus: handleFocus, onKeyDown: handleKeyDown, ref: treeRef, className: className, 'data-iui-size': 'small' === size ? 'small' : void 0, style: style, ...rest, }) : _react.createElement( TreeElement, { onKeyDown: handleKeyDown, onFocus: handleFocus, className: className, 'data-iui-size': 'small' === size ? 'small' : void 0, style: style, ref: treeRef, ...rest, }, flatNodesList.map((_, i) => itemRenderer(i)), ), ); }; if ('development' === process.env.NODE_ENV) Tree.displayName = 'Tree'; const TreeElement = _index.polymorphic.div('iui-tree', { role: 'tree', tabIndex: 0, }); const VirtualizedTree = _react.forwardRef( ({ flatNodesList, itemRenderer, scrollToIndex, ...rest }, ref) => { let parentRef = _react.useRef(null); let virtualizerRootRef = _react.useRef(null); let getItemKey = _react.useCallback( (index) => flatNodesList[index].nodeProps.nodeId, [flatNodesList], ); let onVirtualizerChange = _react.useMemo( () => debounce((virtualizer) => { if (!virtualizer || !virtualizerRootRef.current) return; virtualizerRootRef.current.style.width = ''; let widestNodeWidth = 0; virtualizer.elementsCache.forEach((el) => { if (el.clientWidth > widestNodeWidth) widestNodeWidth = el.clientWidth; }); if (widestNodeWidth) virtualizerRootRef.current.style.width = `${widestNodeWidth}px`; }, 100), [], ); let { virtualizer, css: virtualizerCss } = (0, _index.useVirtualScroll)({ count: flatNodesList.length, getScrollElement: () => parentRef.current, estimateSize: () => 39, getItemKey, onChange: onVirtualizerChange, }); (0, _index.useLayoutEffect)(() => { if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex); }, [virtualizer, scrollToIndex]); return _react.createElement( TreeElement, { ...rest, ref: (0, _index.useMergedRefs)(ref, parentRef), }, _react.createElement( 'div', { style: { display: 'contents', }, }, _react.createElement( _index.ShadowRoot, { css: virtualizerCss, }, _react.createElement( 'div', { 'data-iui-virtualizer': 'root', style: { minBlockSize: virtualizer.getTotalSize(), }, ref: virtualizerRootRef, }, _react.createElement('slot', null), ), ), _react.createElement( _TreeContext.VirtualizedTreeContext.Provider, { value: _react.useMemo( () => ({ virtualizer, onVirtualizerChange, }), [virtualizer, onVirtualizerChange], ), }, virtualizer .getVirtualItems() .map((virtualItem) => itemRenderer(virtualItem.index, virtualItem, virtualizer), ), ), ), ); }, ); function debounce(callback, delay) { let timeoutId; return (...args) => { if (timeoutId) window.clearTimeout(timeoutId); timeoutId = window.setTimeout(() => { callback(...args); }, delay); }; }