Skip to content

Commit 1afbe26

Browse files
committed
feat: add updateCheckedInTreeData
1 parent 2fea664 commit 1afbe26

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

lib/HeTree.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,64 @@ export function updateCheckedInFlatData<T extends Record<Id, any>>(
11201120
const allChecked = [...newCheckedIds, ...semiCheckedIds];
11211121
return [newCheckedIds.sort(), semiCheckedIds.sort(), allChecked.sort()]
11221122
}
1123+
export function updateCheckedInTreeData<T extends Record<Id, any>>(
1124+
flatData: T[],
1125+
checkedIds: Id[],
1126+
idOrIds: Id | Id[],
1127+
checked: boolean,
1128+
options0?: Partial<typeof treeDataDefaultOptions>
1129+
) {
1130+
const options = { ...treeDataDefaultOptions, ...options0 }
1131+
const { idKey: ID, childrenKey: CHILDREN } = options
1132+
const checkedIdSet = new Set(checkedIds)
1133+
const idsToUpdate = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
1134+
const all = new Map<Id, Checked>()
1135+
const changedPids = new Set<Id>(idsToUpdate)
1136+
const pidById: Record<Id, Id | null> = {}
1137+
const childIdsById = new Map<Id | null, Id[]>()
1138+
childIdsById.set(null, [])
1139+
for (const [node, { parents, parent }] of walkTreeDataGenerator(flatData, CHILDREN)) {
1140+
const id = node[ID];
1141+
const pid = parent?.[ID] ?? null
1142+
pidById[id] = pid
1143+
childIdsById.get(pid)!.push(id)
1144+
childIdsById.set(id, [])
1145+
all.set(id, checkedIdSet.has(id))
1146+
if (changedPids.has(id) || (pid && changedPids.has(pid))) {
1147+
// update self and children
1148+
all.set(id, checked)
1149+
changedPids.add(id)
1150+
}
1151+
}
1152+
// update parents
1153+
for (const id of idsToUpdate) {
1154+
let cur = pidById[id];
1155+
while (cur != null) {
1156+
let allChecked = true
1157+
let hasChecked = false
1158+
for (const childId of childIdsById.get(cur)!) {
1159+
if (all.get(childId) === false) {
1160+
allChecked = false
1161+
} else {
1162+
hasChecked = true
1163+
}
1164+
}
1165+
all.set(cur, allChecked ? true : (hasChecked ? null : false))
1166+
cur = pidById[cur]
1167+
}
1168+
}
1169+
const newCheckedIds: Id[] = [];
1170+
const semiCheckedIds: Id[] = [];
1171+
all.forEach((v, k) => {
1172+
if (v === true) {
1173+
newCheckedIds.push(k)
1174+
} else if (v === null) {
1175+
semiCheckedIds.push(k)
1176+
}
1177+
})
1178+
const allChecked = [...newCheckedIds, ...semiCheckedIds];
1179+
return [newCheckedIds.sort(), semiCheckedIds.sort(), allChecked.sort()]
1180+
}
11231181
// private methods
11241182
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
11251183
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));

src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import example_data from "./examples/example_data.json";
22
import { sortFlatData, useHeTree } from "../lib/index";
33
import { useState } from "react";
44
import { useImmer } from "use-immer";
5+
56
function App() {
67
// const [flatData, setflatData] = useState(() => {
78
// const list: (typeof example_data)[] = [];

src/test/treeData.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
findTreeData,
55
filterTreeData,
66
openParentsInTreeData,
7+
updateCheckedInTreeData,
78
} from "../../lib/HeTree";
89

910
test("walkTreeDataGenerator", () => {
@@ -155,6 +156,34 @@ test("openParentsInTreeData", () => {
155156
newOpenids = openParentsInTreeData(cur, [], "===================");
156157
expect(newOpenids.toString()).toBe("");
157158
});
159+
test("updateCheckedInTreeData", () => {
160+
let data = createData2();
161+
let cur = [...data];
162+
let ids = [3];
163+
let [newIds, semi,all] = updateCheckedInTreeData(cur,ids,[], true);
164+
expect(newIds.toString()).toBe("3");
165+
//
166+
[newIds, semi] = updateCheckedInTreeData(cur,ids,[3], true);
167+
expect(newIds.toString()).toBe("3,6,7");
168+
expect(semi.toString()).toBe("1");
169+
//
170+
[newIds, semi] = updateCheckedInTreeData(cur,[],[8,10], true);
171+
expect(newIds.toString()).toBe("10,2,4,5,8");
172+
expect(semi.toString()).toBe("1");
173+
// uncheck
174+
[newIds, semi, all] = updateCheckedInTreeData(cur,[10,2,4,8,5],[8], false);
175+
expect(newIds.toString()).toBe("10,5");
176+
expect(semi.toString()).toBe("1,2");
177+
expect(all.toString()).toBe("1,10,2,5");
178+
//
179+
[newIds, semi, all] = updateCheckedInTreeData(cur,[1,2,5,10,4,8,3,7,6],[6,7], !false);
180+
expect(newIds.length).toBe(9);
181+
expect(semi.length).toBe(0);
182+
expect(all.length).toBe(9);
183+
//
184+
[newIds, semi, all] = updateCheckedInTreeData(cur,[1,2,5,10,4,8,3,7,6],[6,7], false);
185+
expect(semi.toString()).toBe('1');
186+
});
158187

159188
function createData() {
160189
// return example tree data
@@ -284,3 +313,71 @@ function createData() {
284313
},
285314
];
286315
}
316+
317+
function createData2() {
318+
// same to flatData.test
319+
// size 9
320+
/* structure
321+
1
322+
2
323+
5
324+
10
325+
4
326+
8
327+
3
328+
7
329+
6
330+
*/
331+
return [
332+
{
333+
"id": 1,
334+
"name": "Root Category",
335+
"children": [
336+
{
337+
"id": 2,
338+
"name": "Technology",
339+
"children": [
340+
{
341+
"id": 5,
342+
"name": "Hardware",
343+
"children": [
344+
{
345+
"id": 10,
346+
"name": "Computer Components",
347+
"children": []
348+
}
349+
]
350+
},
351+
{
352+
"id": 4,
353+
"name": "Programming",
354+
"children": [
355+
{
356+
"id": 8,
357+
"name": "Python",
358+
"children": []
359+
}
360+
]
361+
}
362+
]
363+
},
364+
{
365+
"id": 3,
366+
"name": "Science",
367+
"children": [
368+
{
369+
"id": 7,
370+
"name": "Biology",
371+
"children": []
372+
},
373+
{
374+
"id": 6,
375+
"name": "Physics",
376+
"children": []
377+
}
378+
]
379+
}
380+
]
381+
}
382+
]
383+
}

0 commit comments

Comments
 (0)