# TreeView

Import: `import { TreeView } from '@neo4j-ndl/react'`

## Props

### TreeView

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `items` | `TreeItem<T>[]` | ✅ |  | The items to be displayed in the tree view |
| `onItemsChanged` | `(newItems: TreeItem<T>[], itemChangedReason: ItemChangedReason<T>) => void` | ✅ |  | A function that is called when the items in the tree view change |
| `TreeItemComponent` | `TreeItemComponent<T, E>` |  |  | The component to use for the tree items, if not provided the default component will be used |

### TreeView.SortableTreeViewItem

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `ariaLevel` | `number` |  |  |  |
| `ariaPosInSet` | `number` |  |  |  |
| `ariaSetSize` | `number` |  |  |  |
| `childCount` | `number` |  |  | Number of child items |
| `clone` | `boolean` |  |  | Whether this is a clone item |
| `collapsed` | `boolean` |  |  | Whether the item is collapsed |
| `depth` | `number` | ✅ |  | Depth of the item in the tree |
| `id` | `UniqueIdentifier` | ✅ |  |  |
| `indentationWidth` | `number` | ✅ |  | Width of the indentation |
| `indicator` | `boolean` |  |  | Whether this is an indicator item |
| `isLast` | `boolean` | ✅ |  | Whether this is the last item in its parent |
| `item` | `FlattenedTreeItem<T>` | ✅ |  | The tree item data |
| `items` | `FlattenedTreeItem<T>[]` | ✅ |  | All items in the tree |
| `keepGhostInPlace` | `boolean` |  |  | Whether to keep the ghost item in place while dragging |
| `onCollapse` | `(id: UniqueIdentifier) => void` |  |  | Function to call when item is collapsed |
| `onFocus` | `() => void` |  |  | Function to call when the item receives focus |
| `onItemsChanged` | `(newItems: FlattenedTreeItem<T>[], itemChangedReason: ItemChangedReason<T>) => void` | ✅ |  | Function to call when the item has changed |
| `onRemove` | `(id: UniqueIdentifier) => void` |  |  | Function to call when item is removed |
| `parent` | `FlattenedTreeItem<T>` | ✅ |  | Parent item of this item |
| `shouldDisableInteraction` | `boolean` |  |  | Whether interaction should be disabled |
| `shouldDisableSorting` | `boolean` |  |  | Whether sorting should be disabled for this item |
| `sortableProps` | `Omit<Arguments, "id">` |  |  | Props for the sortable functionality |
| `tabIndex` | `number` |  |  |  |
| `trails` | `("none" \| "straight" \| "straight-curved" \| "curved")[]` | ✅ |  | A list of trails to render before the item |
| `TreeItemComponent` | `TreeItemComponent<T, E>` | ✅ |  | The component to use for rendering the tree item |
| `wrapperRef` | `(node: HTMLLIElement) => void` |  |  | Function to set the wrapper ref |

### TreeView.TreeItemWrapper

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `ariaLevel` | `number` |  |  |  |
| `ariaPosInSet` | `number` |  |  |  |
| `ariaSetSize` | `number` |  |  |  |
| `depth` | `number` | ✅ |  | Depth of the item in the tree |
| `dragHandleProps` | `Record<string, unknown>` |  |  | Props for the drag handle |
| `indentationWidth` | `number` | ✅ |  | Width of the indentation |
| `isCollapsed` | `boolean` |  |  | If the item is collapsed |
| `isGhost` | `boolean` |  |  | If the item is should be rendered as a placeholder (for example, while dragging) |
| `isIndicator` | `boolean` |  |  | True if the item should be rendered as a indicator (for example, while dragging) |
| `isLastInParent` | `boolean` | ✅ |  | True if this item is the last in the parent list |
| `isOver` | `boolean` | ✅ |  | True if dragged item is over this Node. |
| `isOverParent` | `boolean` | ✅ |  | True if dragged item is over the parent of this Node. |
| `item` | `FlattenedTreeItem<T>` | ✅ |  | The tree item data |
| `items` | `FlattenedTreeItem<T>[]` | ✅ |  | All items in the tree |
| `onCollapse` | `() => void` |  |  | Function to call when the item is collapsed/expanded |
| `onFocus` | `() => void` |  |  | Function to call when the item receives focus |
| `onItemsChanged` | `(newItems: FlattenedTreeItem<T>[], reason: ItemChangedReason<T>) => void` | ✅ |  | Function to call when the item has changed |
| `parent` | `FlattenedTreeItem<T>` | ✅ |  | Parent item of this item |
| `ref` | `Ref<HTMLDivElement>` |  |  | A ref to apply to the root element. |
| `setActivatorNodeRef` | `(node: HTMLElement) => void` | ✅ |  | Function to set the ref of the element that should activate sorting |
| `setNodeRef` | `(node: HTMLLIElement) => void` | ✅ |  | Function to set the ref of the element that should be sorted (the wrapper element of the item) |
| `shouldDisableInteraction` | `boolean` |  |  | While dragging it makes sense to disable some interaction for all other items. |
| `shouldDisableSorting` | `boolean` |  |  | True if sorting is disabled for this item. |
| `tabIndex` | `number` |  |  |  |
| `trails` | `("none" \| "straight" \| "straight-curved" \| "curved")[]` | ✅ |  | A list of trails to render before the item |

### TreeView.TreeViewTextItem

| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `ariaLevel` | `number` |  |  |  |
| `ariaPosInSet` | `number` |  |  |  |
| `ariaSetSize` | `number` |  |  |  |
| `depth` | `number` | ✅ |  | Depth of the item in the tree |
| `dragHandleProps` | `Record<string, unknown>` |  |  | Props for the drag handle |
| `indentationWidth` | `number` | ✅ |  | Width of the indentation |
| `isCollapsed` | `boolean` |  |  | If the item is collapsed |
| `isGhost` | `boolean` |  |  | If the item is should be rendered as a placeholder (for example, while dragging) |
| `isIndicator` | `boolean` |  |  | True if the item should be rendered as a indicator (for example, while dragging) |
| `isLastInParent` | `boolean` | ✅ |  | True if this item is the last in the parent list |
| `isOver` | `boolean` | ✅ |  | True if dragged item is over this Node. |
| `isOverParent` | `boolean` | ✅ |  | True if dragged item is over the parent of this Node. |
| `item` | `FlattenedTreeItem<T>` | ✅ |  | The tree item data |
| `items` | `FlattenedTreeItem<T>[]` | ✅ |  | All items in the tree |
| `onCollapse` | `() => void` |  |  | Function to call when the item is collapsed/expanded |
| `onFocus` | `() => void` |  |  | Function to call when the item receives focus |
| `onItemsChanged` | `(newItems: FlattenedTreeItem<T>[], reason: ItemChangedReason<T>) => void` | ✅ |  | Function to call when the item has changed |
| `parent` | `FlattenedTreeItem<T>` | ✅ |  | Parent item of this item |
| `ref` | `Ref<HTMLDivElement> & Ref<HTMLElement>` |  |  | A ref to apply to the root element. |
| `setActivatorNodeRef` | `(node: HTMLElement) => void` | ✅ |  | Function to set the ref of the element that should activate sorting |
| `setNodeRef` | `(node: HTMLLIElement) => void` | ✅ |  | Function to set the ref of the element that should be sorted (the wrapper element of the item) |
| `shouldDisableInteraction` | `boolean` |  |  | While dragging it makes sense to disable some interaction for all other items. |
| `shouldDisableSorting` | `boolean` |  |  | True if sorting is disabled for this item. |
| `tabIndex` | `number` |  |  |  |
| `trails` | `("none" \| "straight" \| "straight-curved" \| "curved")[]` | ✅ |  | A list of trails to render before the item |

## Examples

### Treeview Custom

```tsx
import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';

import { TreeView } from '@neo4j-ndl/react';
import {
  ChevronDownIconSolid,
  ChevronUpIconSolid,
  FolderIconOutline,
} from '@neo4j-ndl/react/icons';
import type React from 'react';
import { useState } from 'react';

const TREE_ITEMS: React.ComponentProps<typeof TreeView>['items'] = [
  {
    canHaveSubItems: true,
    data: {
      custom: 'Custom Root',
    },
    id: 'Root',
    isCollapsed: false,
    isSelected: false,
    subItems: [
      {
        canHaveSubItems: false,
        data: {
          custom: 'Custom child 1',
        },
        id: 'Child 1',
        isSelected: false,
      },
      {
        canHaveSubItems: false,
        data: {
          custom: 'Custom child 2',
        },
        id: 'Child 2',
        isSelected: false,
      },
      {
        canHaveSubItems: false,
        data: {
          custom: 'Custom child 3',
        },
        id: 'Child 3',
        isSelected: false,
      },
    ],
  },
];

const Component = () => {
  const [items, setItems] = useState(TREE_ITEMS);

  return (
    <TreeView
      items={items}
      onItemsChanged={(newItems) => {
        setItems(newItems);
      }}
      TreeItemComponent={function CustomTreeItem({
        ref,
        onItemsChanged,
        ...restProps
      }: React.ComponentProps<typeof TreeView.TreeItemWrapper>) {
        return (
          <TreeView.TreeItemWrapper
            {...restProps}
            onItemsChanged={onItemsChanged}
            ref={ref}
          >
            <div
              className="n-items-center n-flex n-flex-row"
              style={{
                background: !restProps.item.canHaveSubItems
                  ? 'linear-gradient(in hsl longer hue 45deg, red 0 0)'
                  : '',
              }}
            >
              {restProps.item.canHaveSubItems && (
                <>
                  <FolderIconOutline className="n-h-5 w-h-5" />
                  <button
                    type="button"
                    onClick={() =>
                      restProps.onCollapse && restProps.onCollapse()
                    }
                  >
                    {restProps.item.isCollapsed ? (
                      <ChevronUpIconSolid className="n-h-5 w-h-5" />
                    ) : (
                      <ChevronDownIconSolid className="n-h-5 w-h-5" />
                    )}
                  </button>
                </>
              )}
              {'custom' in restProps.item.data &&
                typeof restProps.item.data.custom === 'string' &&
                restProps.item.data.custom}
            </div>
          </TreeView.TreeItemWrapper>
        );
      }}
    />
  );
};

export default Component;
```

### Treeview Default

```tsx
import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';

import { TreeView } from '@neo4j-ndl/react';
import { PencilIconOutline } from '@neo4j-ndl/react/icons';
import type React from 'react';
import { useState } from 'react';

const TREE_ITEMS: React.ComponentProps<typeof TreeView>['items'] = [
  {
    canHaveSubItems: true,
    data: {
      actions: [
        {
          buttonProps: {
            description: 'Edit',
            onClick: () => {
              alert('Pencil was clicked');
            },
          },
          icon: <PencilIconOutline />,
        },
      ],
      onTextClick: () => {
        alert('Text was clicked');
      },
      text: 'Folder 1',
    },
    id: 'Folder 1',
    isCollapsed: false,
    isSelected: false,
    isSortable: false,
    subItems: [
      {
        canHaveSubItems: false,
        data: {
          actions: [
            {
              buttonProps: {
                description: 'Edit',
                onClick: () => {
                  alert('Pencil was clicked');
                },
              },
              icon: <PencilIconOutline />,
            },
          ],
          onTextClick: () => {
            alert('Text was clicked');
          },
          text: 'Child 1',
        },
        id: 'Child 1',
        isSelected: false,
        isSortable: true,
      },
      {
        canHaveSubItems: false,
        data: {
          actions: [
            {
              buttonProps: {
                description: 'Edit',
                onClick: () => {
                  alert('Pencil was clicked');
                },
              },
              icon: <PencilIconOutline />,
            },
          ],
          onTextClick: () => {
            alert('Text was clicked');
          },
          text: 'Child 2',
        },
        id: 'Child 2',
        isSelected: false,
        isSortable: true,
      },
      {
        canHaveSubItems: false,
        data: {
          actions: [
            {
              buttonProps: {
                description: 'Edit',
                onClick: () => {
                  alert('Pencil was clicked');
                },
              },
              icon: <PencilIconOutline />,
            },
          ],
          onTextClick: () => {
            alert('Text was clicked');
          },
          text: 'Child 3',
        },
        id: 'Child 3',
        isSelected: false,
        isSortable: true,
      },
      {
        canHaveSubItems: true,
        data: {
          actions: [
            {
              buttonProps: {
                description: 'Edit',
                onClick: () => {
                  alert('Pencil was clicked');
                },
              },
              icon: <PencilIconOutline />,
            },
          ],
          onTextClick: () => {
            alert('Text was clicked');
          },
          text: 'Folder 2',
        },
        id: 'Folder 2',
        isCollapsed: true,
        isSelected: false,
        isSortable: true,
        subItems: [
          {
            canHaveSubItems: false,
            data: {
              actions: [
                {
                  buttonProps: {
                    description: 'Edit',
                    onClick: () => {
                      alert('Pencil was clicked');
                    },
                  },
                  icon: <PencilIconOutline />,
                },
              ],
              onTextClick: () => {
                alert('Text was clicked');
              },
              text: 'Child 4',
            },

            id: 'Child 4',
            isSelected: false,
          },
          {
            canHaveSubItems: false,
            data: {
              actions: [
                {
                  buttonProps: {
                    description: 'Edit',
                    onClick: () => {
                      alert('Pencil was clicked');
                    },
                  },
                  icon: <PencilIconOutline />,
                },
              ],
              onTextClick: () => {
                alert('Text was clicked');
              },
              text: 'Child 5',
            },
            id: 'Child 5',
            isSelected: false,
          },
          {
            canHaveSubItems: false,
            data: {
              actions: [
                {
                  buttonProps: {
                    description: 'Edit',
                    onClick: () => {
                      alert('Pencil was clicked');
                    },
                  },
                  icon: <PencilIconOutline />,
                },
              ],
              onTextClick: () => {
                alert('Text was clicked');
              },
              text: 'Child 6',
            },
            id: 'Child 6',
            isSelected: false,
          },
        ],
      },
    ],
  },
  {
    canHaveSubItems: false,
    data: {
      actions: [
        {
          buttonProps: {
            description: 'Edit',
            onClick: () => {
              alert('Pencil was clicked');
            },
          },
          icon: <PencilIconOutline />,
        },
      ],
      onTextClick: () => {
        alert('Text was clicked');
      },
      text: 'Child 7',
    },
    id: 'Child 7',
    isSelected: false,
    isSortable: true,
  },
];

const Component = () => {
  const [items, setItems] =
    useState<React.ComponentProps<typeof TreeView>['items']>(TREE_ITEMS);

  return (
    <TreeView
      items={items}
      onItemsChanged={(newItems, itemChangedReason) => {
        // eslint-disable-next-line no-restricted-syntax
        console.log('change with itemChangedReason:', itemChangedReason);
        setItems(newItems);
      }}
      TreeItemComponent={undefined}
    />
  );
};

export default Component;
```

### Treeview Skeleton Loading

```tsx
import '@neo4j-ndl/base/lib/neo4j-ds-styles.css';

import { TreeView } from '@neo4j-ndl/react';
import type React from 'react';
import { useState } from 'react';

const TREE_ITEMS: React.ComponentProps<typeof TreeView>['items'] = [
  {
    canHaveSubItems: true,
    data: {
      actions: [],
      text: 'Folder 1',
    },
    id: 'Folder 1',
    isCollapsed: false,
    isSelected: false,
    subItems: [
      {
        canHaveSubItems: false,
        data: {
          actions: [],
          text: 'Item 1',
        },
        id: 'Item 1',
        isSelected: false,
      },
      {
        canHaveSubItems: true,
        data: {
          actions: [],
          text: 'Folder 2',
        },
        id: 'Folder 2',
        isCollapsed: false,
        isSelected: false,
        isSkeletonLoading: true,
        subItems: [
          {
            canHaveSubItems: false,
            data: {
              actions: [],
              text: 'Item 2',
            },
            id: 'Item 2',
            isSelected: false,
          },
          {
            canHaveSubItems: false,
            data: {
              actions: [],
              text: 'Item 3',
            },
            id: 'Item 3',
            isSelected: false,
            isSkeletonLoading: true,
          },
        ],
      },
    ],
  },
];

const Component = () => {
  const [items, setItems] =
    useState<React.ComponentProps<typeof TreeView>['items']>(TREE_ITEMS);

  return (
    <TreeView
      items={items}
      onItemsChanged={(newItems, itemChangedReason) => {
        console.info('change with itemChangedReason:', itemChangedReason);
        setItems(newItems);
      }}
      TreeItemComponent={undefined}
    />
  );
};

export default Component;
```
