export default function TreeView()

in src/tree-view/tree-view.tsx [29:175]


export default function TreeView(props: TreeViewProps) {
  const {
    data,
    indentGuides = false,
    onToggle,
    overrides = {},
    renderAll,
    getId = defaultGetId,
  } = props;
  const { Root: RootOverride } = overrides;

  const Root = getOverride(RootOverride) || StyledTreeItemList;
  const firstId = data.length > 0 ? getId(data[0]) : 0;
  const [selectedNodeId, setSelectedNodeId] = React.useState(firstId);
  const [focusVisible, setFocusVisible] = React.useState(false);
  const [typeAheadChars, setTypeAheadChars] = React.useState('');
  const timeOutRef = React.useRef(null);
  const treeItemRefs: {
    [key in TreeNodeId]: React.Ref<HTMLLIElement>;
  } = {};

  const focusTreeItem = (id: TreeNodeId | null) => {
    if (!id) return;
    setSelectedNodeId(id);

    const refs = treeItemRefs[id];
    // @ts-expect-error
    const node = refs && refs.current;
    if (node) node.focus();
  };

  const onKeyDown = (e: KeyboardEvent, node: TreeNodeData) => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const elementId = (e.target as any as HTMLLIElement).getAttribute('data-nodeid');
    // this check prevents bubbling
    // @ts-ignore
    if (elementId !== getId(node) && parseInt(elementId) !== getId(node)) {
      return;
    }
    switch (e.key) {
      case 'ArrowRight':
        e.preventDefault();
        if (typeof node.isExpanded === 'boolean' && !node.isExpanded) {
          onToggle && onToggle(node);
        } else {
          focusTreeItem(getFirstChildId(data, selectedNodeId, getId));
        }
        break;
      case 'ArrowLeft':
        e.preventDefault();
        if (typeof node.isExpanded === 'boolean' && node.isExpanded) {
          onToggle && onToggle(node);
        } else {
          focusTreeItem(getParentId(data, selectedNodeId, null, getId));
        }
        break;
      case 'ArrowUp':
        e.preventDefault();
        focusTreeItem(getPrevId(data, selectedNodeId, null, getId));
        break;
      case 'ArrowDown':
        e.preventDefault();
        focusTreeItem(getNextId(data, selectedNodeId, null, getId));
        break;
      case ' ':
      case 'Enter':
        e.preventDefault();
        onToggle && onToggle(node);
        break;
      case 'Home':
        e.preventDefault();
        if (data.length) {
          focusTreeItem(getId(data[0]));
        }
        break;
      case 'End':
        e.preventDefault();
        focusTreeItem(getEndId(data, getId));
        break;
      case '*':
        e.preventDefault();
        getExpandableSiblings(data, selectedNodeId, getId).forEach(
          // @ts-ignore
          (node) => onToggle && onToggle(node)
        );
        break;
      default:
        if (timeOutRef.current !== null) {
          clearTimeout(timeOutRef.current);
        }
        setTypeAheadChars(typeAheadChars + e.key);
        // @ts-ignore
        timeOutRef.current = setTimeout(() => {
          setTypeAheadChars('');
        }, 500);

        focusTreeItem(getCharMatchId(data, selectedNodeId, typeAheadChars + e.key, null, getId));
        break;
    }
  };

  const onFocus = (event: SyntheticEvent) => {
    if (isFocusVisible(event)) {
      setFocusVisible(true);
    }
    if (selectedNodeId === null && data.length) {
      setSelectedNodeId(getId(data[0]));
    }
  };

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const onBlur = (event: SyntheticEvent) => {
    if (focusVisible) {
      setFocusVisible(false);
    }
  };

  return (
    <Root role="tree" {...getOverrideProps(RootOverride)}>
      {data.map((node) => (
        <TreeNode
          indentGuides={indentGuides}
          key={getId(node)}
          node={node}
          getId={getId}
          onToggle={(node) => {
            onToggle && onToggle(node);
            focusTreeItem(getId(node));
          }}
          overrides={overrides}
          renderAll={renderAll}
          selectedNodeId={selectedNodeId}
          onKeyDown={onKeyDown}
          onFocus={onFocus}
          onBlur={onBlur}
          addRef={(id, ref) => {
            treeItemRefs[id] = ref;
          }}
          removeRef={(id: TreeNodeId) => {
            delete treeItemRefs[id];
          }}
          isFocusVisible={focusVisible}
        />
      ))}
    </Root>
  );
}