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;
}