Skip to main content

Tree

SpeedSearchTree

ItemLayout

While you can render any custom content in Items of a tree, ItemLayout is a useful helper component implementing the most common cases. Render different parts of an item, such as icon, text, etc. inside a ItemLayout and it handles the layout, and the spacing between them. Some common parts that require a special style are implemented as components accessible on ItemLayout.

Result
Loading...
Live Editor
<SpeedSearchTree selectionMode="multiple">
  <Item textValue="jui">
    <ItemLayout>
      <PlatformIcon icon="nodes/folder" />
      <HighlightedTextValue />
      <ItemLayout.Hint>~/workspace/jui</ItemLayout.Hint>
    </ItemLayout>
  </Item>
</SpeedSearchTree>

Context Menu

In order to have context menu for tree nodes, just wrap the tree in a ContextMenuContainer. When the context menu is triggered on an item, the selection is also updated right before the context menu opens, so you can render the context menu based on the selected tree node(s).

Result
Loading...
Live Editor
function TreeContextMenuExample() {
  const treeItems = [
    {
      type: "file",
      name: "ToolWindowState.test.ts",
      items: [
        {
          type: "spec",
          name: "tool window state",
          items: [
            {
              type: "case",
              name: "hide or show return the same state if the key doesn't exist in the state",
            },
            {
              type: "case",
              name: "hide returns the same state if the window is already not visible",
            },
            {
              type: "case",
              name: "hiding a tool window only toggles the visibility of that window",
            },
          ],
        },
      ],
    },
  ];
  const getAllKeys = (items) =>
    items.flatMap((item) => [item.name, ...getAllKeys(item.items || [])]);
  const allKeys = getAllKeys(treeItems);
  const [selectedKeys, setSelectedKeys] = useState(new Set());

  return (
    <ContextMenuContainer
      renderMenu={() => {
        const selectedKey = Array.from(selectedKeys)[0];
        if (!selectedKey) {
          return (
            <Menu>
              <Item>Nothing here</Item>
            </Menu>
          );
        }
        return (
          <Menu>
            <Item textValue={`Run '${selectedKey}'`}>
              <MenuItemLayout
                icon={<PlatformIcon icon="debugger/threadRunning.svg" />}
                content={`Run '${selectedKey}'`}
                shortcut="⌃⇧R"
              />
            </Item>
            <Item textValue={`Debug '${selectedKey}'`}>
              <MenuItemLayout
                icon={<PlatformIcon icon="actions/startDebugger.svg" />}
                content={`Debug '${selectedKey}'`}
                shortcut="⌃⇧D"
              />
            </Item>
            <Divider />
            <Item textValue="Jump to source">
              <MenuItemLayout
                icon={<PlatformIcon icon="actions/editSource.svg" />}
                content="Jump to source"
                shortcut="⌘↓"
              />
            </Item>
          </Menu>
        );
      }}
    >
      <SpeedSearchTree
        items={treeItems}
        fillAvailableSpace
        selectionMode="single"
        defaultExpandedKeys={allKeys}
        keepSearchActiveOnBlur
        selectedKeys={selectedKeys}
        onSelectionChange={setSelectedKeys}
      >
        {(item) => (
          <Item key={item.name} textValue={item.name} childItems={item.items}>
            <ItemLayout>
              <PlatformIcon icon="runConfigurations/testPassed.svg" />
              <HighlightedTextValue />
            </ItemLayout>
          </Item>
        )}
      </SpeedSearchTree>
    </ContextMenuContainer>
  );
}

Advanced use

Custom list component with useList and useSpeedSearchList