Skip to content

Examples

Custom Style

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setdata] = useState(() => sortFlatData([
    {
      id: 1,
      parent_id: null,
      name: "Root Category",
    },
    {
      id: 2,
      parent_id: 1,
      name: "Technology",
    },
    {
      id: 5,
      parent_id: 2,
      name: "Hardware",
    },
    {
      id: 10,
      parent_id: 5,
      name: "Computer Components",
    },
    {
      id: 4,
      parent_id: 2,
      name: "Programming",
    },
    {
      id: 8,
      parent_id: 4,
      name: "Python",
    },
    {
      id: 3,
      parent_id: 1,
      name: "Science",
    },
    {
      id: 7,
      parent_id: 3,
      name: "Biology",
    },
    {
      id: 6,
      parent_id: 3,
      name: "Physics",
    },
  ], keys));
  const { renderTree, placeholder } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNodeBox: ({ stat, attrs, isPlaceholder }) => (
      <div {...attrs} key={attrs.key} className="my-node-box">
        {isPlaceholder ? <div className="my-placeholder">DROP HERE</div>
          : <div className="my-node">
            <span className="drag-handler" draggable={stat.draggable}>{dragIcon()}</span>
            {stat.node.name}
          </div>
        }
      </div>
    ),
  })
  return <>
    <h3 style={{ margin: '0 0 0 110px', padding: '20px 0 0px' }}>Draggable Tree</h3>
    <div>
      {renderTree({ className: `my-tree ${placeholder ? 'dragging' : 'no-dragging'}` })}
    </div>
    <style>{`
    .my-tree{
      width: 300px; 
      border: 1px solid #ccc; 
      border-radius: 5px;
      margin: 20px; 
      padding: 20px;
    }
    .my-placeholder{
      height:40px;
      border: 1px dashed blue;
      border-radius: 3px;
      background-color: #f3ffff;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: small;
    }
    /*.no-dragging .my-node-box:hover{
      background-color: #eee;
    }*/
    .my-node-box:not(:last-child){
      margin-bottom: 10px;
    }
    .my-node{
      padding: 5px 10px;
      padding-left: 30px;
      border: 1px solid #e2e2e2;
      border-radius: 3px;
      background-color: #f0f0f0;
      display: flex;
      align-items: center;
      position: relative;
      box-shadow: 1px 1px 3px 0px rgb(0 0 0 / 19%);
    }
    .no-dragging .my-node:hover{
      background-color: #ebfeff;
    }
    .drag-handler{
      position: absolute;
      left: 0;
      top: 0;
      width: 30px;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: grab;
    }
    .drag-handler:hover{
      background-color: #f0f0f0;
    }
    .my-node svg{
      width:16px;
    }
    `}</style>
  </>
}

function dragIcon() {
  return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>drag-horizontal-variant</title><path d="M21 11H3V9H21V11M21 13H3V15H21V13Z" /></svg>
}

Flat Data

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setdata] = useState(() => sortFlatData([
    {
      id: 1,
      parent_id: null,
      name: "Root Category",
    },
    {
      id: 2,
      parent_id: 1,
      name: "Technology",
    },
    {
      id: 5,
      parent_id: 2,
      name: "Hardware",
    },
    {
      id: 10,
      parent_id: 5,
      name: "Computer Components",
    },
    {
      id: 4,
      parent_id: 2,
      name: "Programming",
    },
    {
      id: 8,
      parent_id: 4,
      name: "Python",
    },
    {
      id: 3,
      parent_id: 1,
      name: "Science",
    },
    {
      id: 7,
      parent_id: 3,
      name: "Biology",
    },
    {
      id: 6,
      parent_id: 3,
      name: "Physics",
    },
  ], keys));
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, open, checked }) => <div>
      {node.name}
    </div>,
  })
  return <div>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Tree-shaped Data

Preview

Source

tsx
import { useHeTree } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const [data, setdata] = useState(() => [
    {
      id: 1,
      name: "Root Category",
      children: [
        {
          id: 2,
          name: "Technology",
          children: [
            {
              id: 5,
              name: "Hardware",
              children: [
                {
                  id: 10,
                  name: "Computer Components",
                  children: [],
                },
              ],
            },
            {
              id: 4,
              name: "Programming",
              children: [
                {
                  id: 8,
                  name: "Python",
                  children: [],
                },
              ],
            },
          ],
        },
        {
          id: 3,
          name: "Science",
          children: [
            {
              id: 7,
              name: "Biology",
              children: [],
            },
            {
              id: 6,
              name: "Physics",
              children: [],
            },
          ],
        },
      ],
    },
  ]);
  const { renderTree } = useHeTree({
    data,
    dataType: 'tree',
    childrenKey: 'children',
    onChange: setdata,
    renderNode: ({ id, node, open, checked }) => <div>
      {node.name}
    </div>,
  })
  return <div>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Trigger Element

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <button draggable={draggable}>Drag</button>
      {node.name}
    </div>,
  })
  return <div>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Placeholder

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNodeBox: ({ stat, attrs, isPlaceholder }) => (
      <div {...attrs} key={attrs.key}>
        {isPlaceholder ? <div className="my-drag-placeholder">drop here</div>
          : <div className="mynode">{stat.node.name}</div>
        }
      </div>
    ),
  })
  return <div>
    {renderTree({ className: 'mytree', style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
    <style>{`
    .mytree [data-node-box]{
      padding: 5px 0;
    }
    .mytree [data-node-box]:hover{
      background-color: #eee;
    }
    .mytree .he-tree-drag-placeholder{
      height: 30px;
      line-height: 30px;
      text-align: center;
      border: 1px dashed red;
    }
    .mynode{
      padding-left:5px;
    }
    `}</style>
  </div>
}

Open

Preview

Source

tsx
import { useHeTree, sortFlatData, openParentsInFlatData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const [openIds, setopenIds] = useState<Id[] | undefined>([]);
  const handleOpen = (id: Id, open: boolean) => {
    if (open) {
      setopenIds([...(openIds || allIds), id]);
    } else {
      setopenIds((openIds || allIds).filter((i) => i !== id));
    }
  }
  const { renderTree, allIds } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    openIds,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <button onClick={() => handleOpen(id, !open)}>{open ? '-' : '+'}</button>
      {node.name} - {id}
    </div>,
  })
  return <div>
    <button onClick={() => setopenIds(allIds)}>Open All</button>
    <button onClick={() => setopenIds([])}>Close All</button>
    <button onClick={() => setopenIds(openParentsInFlatData(data, openIds || allIds, 8, keys))}>Open 'Python'</button>
    <button onClick={() => setopenIds(openParentsInFlatData(data, [], 8, keys))}>Only Open 'Python'</button>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Checked

Preview

Source

tsx
import { useHeTree, sortFlatData, updateCheckedInFlatData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const [checkedIds, setcheckedIds] = useState<Id[]>([]);
  const [semiCheckedIds, setsemiCheckedIds] = useState<Id[]>([]);
  const handleChecked = (id: Id, checked: boolean) => {
    const r = updateCheckedInFlatData(data, checkedIds, id, checked, keys);
    setcheckedIds(r[0]);
    setsemiCheckedIds(r[1]);
  }
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    checkedIds,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <input type="checkbox" checked={checked || false} onChange={() => handleChecked(id, !checked)} />
      {node.name} - {id}
    </div>,
  })
  return <div>
    Checked: {JSON.stringify(checkedIds)} <br />
    Semi-Checked: {JSON.stringify(semiCheckedIds)}
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Draggable & Droppable

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      {node.name} - {id}
    </div>,
    canDrag: ({ id }) => id === 2 ? true : (id === 3 ? false : undefined),
    canDrop: ({ id }) => id === 3 ? true : (id === 2 ? false : undefined),
    canDropToRoot: (index) => false,
  })
  return <div>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Open when drag onto

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const [openIds, setopenIds] = useState<Id[] | undefined>([1, 3]);
  const handleOpen = (id: Id, open: boolean) => {
    if (open) {
      setopenIds([...(openIds || allIds), id]);
    } else {
      setopenIds((openIds || allIds).filter((i) => i !== id));
    }
  }
  const { renderTree, allIds } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    openIds,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <button onClick={() => handleOpen(id, !open)}>{open ? '-' : '+'}</button>
      {node.name} - {id}
    </div>,
    dragOpen: true,
    onDragOpen(stat) {
      handleOpen(stat.id, true)
    },
  })
  return <div>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Update Flat Data

Preview

Source

tsx
import {
  useHeTree, sortFlatData,
  addToFlatData, removeByIdInFlatData
} from "he-tree-react";
import type { Id } from "he-tree-react";
import { useRef, useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const add = (pid: Id) => {
    let id = parseInt(Math.random().toString().substring(2, 5));
    let newData = [...data];
    addToFlatData(newData, { id, parent_id: pid as number, name: "New" }, 0, keys)
    setdata(newData);
  }
  const remove = (id: Id) => {
    let newData = [...data];
    removeByIdInFlatData(newData, id as number, keys)
    setdata(newData);
  }
  const initialData = useRef<typeof data>();
  initialData.current = initialData.current || data;
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, draggable }) => <div>
      <button draggable={draggable}>👉</button>
      {node.name} - {id} -
      <button onClick={() => add(id)}>+</button>
      <button onClick={() => remove(id)}>-</button>
    </div>,
  })
  return <div>
    <button onClick={() => setdata(initialData.current!)}>Restore</button>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Update Flat Data with immer

Preview

Source

tsx
import {
  useHeTree, sortFlatData,
  addToFlatData, removeByIdInFlatData
} from "he-tree-react";
import type { Id } from "he-tree-react";
import { useRef } from 'react';
import { useImmer } from "use-immer";

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useImmer(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const add = (pid: Id) => {
    let id = parseInt(Math.random().toString().substring(2, 5));
    setdata(draft => {
      addToFlatData(draft, { id, parent_id: pid as number, name: "New" }, 0, keys)
    });
  }
  const remove = (id: Id) => {
    setdata(draft => {
      removeByIdInFlatData(draft, id as number, keys)
    })
  }
  const edit = (id: Id) => {
    let newName = prompt("Enter new name")
    setdata(draft => {
      if (newName) {
        draft.find(node => node.id === id)!.name = newName
      }
    })
  }
  const initialData = useRef<typeof data>();
  initialData.current = initialData.current || data;
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, draggable }) => <div>
      <button draggable={draggable}>👉</button>
      {node.name} - {id} -
      <button onClick={() => add(id)}>+</button>
      <button onClick={() => remove(id)}>-</button>
      <button onClick={() => edit(id)}>Edit</button>
    </div>,
  })
  return <div>
    <button onClick={() => setdata(initialData.current!)}>Restore</button>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Update Tree Data with immer

Preview

Source

tsx
import { useHeTree, findTreeData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useRef } from 'react';
import { useImmer } from "use-immer";

export default function BasePage() {
  const CHILDREN = 'children'
  const keys = { idKey: 'id', childrenKey: CHILDREN };
  // prettier-ignore
  const [data, setdata] = useImmer(() => [{ id: 1, name: "Root Category", children: [{ id: 2, name: "Technology", children: [{ id: 5, name: "Hardware", children: [{ id: 10, name: "Computer Components", children: [], },], }, { id: 4, name: "Programming", children: [{ id: 8, name: "Python", children: [], },], },], }, { id: 3, name: "Science", children: [{ id: 7, name: "Biology", children: [], }, { id: 6, name: "Physics", children: [], },], },], },]);
  const add = (pid: Id) => {
    let id = parseInt(Math.random().toString().substring(2, 5));
    setdata(draft => {
      findTreeData(draft, (node) => node.id === pid, CHILDREN)![CHILDREN].unshift({ id, name: "New", [CHILDREN]: [], })
    })
  }
  const remove = (id: Id, pid: Id | null) => {
    setdata(draft => {
      const children = findTreeData(draft, (node,) => node.id === pid, CHILDREN)![CHILDREN]
      children.splice(children.findIndex(t => t.id === id), 1)
    })
  }
  const edit = (id: Id) => {
    let newName = prompt("Enter new name")
    setdata(draft => {
      if (newName) {
        findTreeData(draft, (node) => node.id === id, CHILDREN)!.name = newName
      }
    })
  }
  const initialData = useRef<typeof data>();
  initialData.current = initialData.current || data;
  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'tree',
    onChange: setdata,
    renderNode: ({ id, pid, node, draggable }) => <div>
      <button draggable={draggable}>👉</button>
      {node.name} - {id} -
      <button onClick={() => add(id)}>+</button>
      <button onClick={() => remove(id, pid)}>-</button>
      <button onClick={() => edit(id)}>Edit</button>
    </div>,
  })
  return <div>
    <button onClick={() => setdata(initialData.current!)}>Restore</button>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Drag from External

Preview

Source

tsx
import { useHeTree, sortFlatData, addToFlatData } from "he-tree-react";
import { useImmer } from "use-immer";

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  // prettier-ignore
  const [data, setdata] = useImmer(() => sortFlatData([{ id: 1, parent_id: null, name: "Root Category", }, { id: 2, parent_id: 1, name: "Technology", }, { id: 5, parent_id: 2, name: "Hardware", }, { id: 10, parent_id: 5, name: "Computer Components", }, { id: 4, parent_id: 2, name: "Programming", }, { id: 8, parent_id: 4, name: "Python", }, { id: 3, parent_id: 1, name: "Science", }, { id: 7, parent_id: 3, name: "Biology", }, { id: 6, parent_id: 3, name: "Physics", },], keys));
  const { renderTree, allIds } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      {node.name} - {id}
    </div>,
    onExternalDragOver: (e) => true,
    onExternalDrop: (e, parentStat, index) => {
      setdata(draft => {
        const newNode = { id: 100 + data.length, parent_id: parentStat?.id ?? null, name: "New Node" }
        addToFlatData(draft, newNode, index, keys)
      })
    },
  })
  return <div>
    <button draggable={true}>Drag me in to the tree</button>
    {renderTree({ style: { width: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

Big Data

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'pid' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData(createData(), keys));
  const [openIds, setopenIds] = useState<Id[] | undefined>([]);
  const handleOpen = (id: Id, open: boolean) => {
    if (open) {
      setopenIds([...(openIds || allIds), id]);
    } else {
      setopenIds((openIds || allIds).filter((i) => i !== id));
    }
  }
  const { renderTree, allIds } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    openIds,
    virtual: true,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <button onClick={() => handleOpen(id, !open)}>{open ? '-' : '+'}</button>
      {id}
    </div>,
  })
  return <div>
    {renderTree({ style: { width: '300px', height: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

// generate 10000 nodes
function createData() {
  const genId = () => result.length
  const result: { id: number, pid: number | null }[] = [];
  for (let i = 0; i < 1000; i++) {
    let id1 = genId()
    result.push({ id: id1, pid: null })
    for (let j = 0; j < 4; j++) {
      result.push({ id: genId(), pid: id1 })
    }
    let id2 = genId()
    result.push({ id: id2, pid: null })
    for (let j = 0; j < 4; j++) {
      result.push({ id: genId(), pid: id2 })
    }
  }
  return result;
}

Scroll to Node

Preview

Source

tsx
import { useHeTree, sortFlatData } from "he-tree-react";
import type { Id } from "he-tree-react";
import { useState } from 'react';

export default function BasePage() {
  const keys = { idKey: 'id', parentIdKey: 'pid' };
  // prettier-ignore
  const [data, setdata] = useState(() => sortFlatData(createData(), keys));
  const [openIds, setopenIds] = useState<Id[] | undefined>([]);
  const handleOpen = (id: Id, open: boolean) => {
    if (open) {
      setopenIds([...(openIds || allIds), id]);
    } else {
      setopenIds((openIds || allIds).filter((i) => i !== id));
    }
  }
  const { renderTree, allIds, scrollToNode } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setdata,
    openIds,
    virtual: true,
    renderNode: ({ id, node, open, checked, draggable }) => <div>
      <button onClick={() => handleOpen(id, !open)}>{open ? '-' : '+'}</button>
      {id}
    </div>,
  })
  return <div>
    <button onClick={() => scrollToNode(910)}>Scroll to 910</button>
    {renderTree({ style: { width: '300px', height: '300px', border: '1px solid #555', padding: '20px' } })}
  </div>
}

// generate 10000 nodes
function createData() {
  const genId = () => result.length
  const result: { id: number, pid: number | null }[] = [];
  for (let i = 0; i < 1000; i++) {
    let id1 = genId()
    result.push({ id: id1, pid: null })
    for (let j = 0; j < 4; j++) {
      result.push({ id: genId(), pid: id1 })
    }
    let id2 = genId()
    result.push({ id: id2, pid: null })
    for (let j = 0; j < 4; j++) {
      result.push({ id: genId(), pid: id2 })
    }
  }
  return result;
}

Last updated: