Skip to content

Commit 2fea664

Browse files
committed
feat: add updateCheckedInFlatData
1 parent 0e9cf21 commit 2fea664

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed

lib/HeTree.tsx

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ const flatDataDefaultOptions = {
851851
parentIdKey: 'parent_id',
852852
}
853853
export type WalkFlatDataYield<T> = [T, {
854-
parent: T | null, parents: T[], index: number, treeIndex: number, id: Id, pid: Id, skipChildren: VoidFunction, exitWalk: VoidFunction
854+
parent: T | null, parents: T[], index: number, treeIndex: number, id: Id, pid: Id | null, skipChildren: VoidFunction, exitWalk: VoidFunction
855855
}]
856856
export function* walkFlatDataGenerator<T extends Record<Id, any>>(flatData: T[], options0?: Partial<typeof flatDataDefaultOptions>): Generator<WalkFlatDataYield<T>> {
857857
const options = { ...flatDataDefaultOptions, ...options0 }
@@ -868,7 +868,7 @@ export function* walkFlatDataGenerator<T extends Record<Id, any>>(flatData: T[],
868868
let treeIndex = 0
869869
for (const node of flatData) {
870870
const id: Id = node[ID];
871-
const pid: Id = node[PID];
871+
const pid: Id = node[PID] ?? null;
872872
nodes[id] = node;
873873
const parent = nodes[pid] || null;
874874
childIdsById[id] = [];
@@ -1065,7 +1065,61 @@ export function openParentsInTreeData<T extends Record<Id, any>>(
10651065
}
10661066
return Array.from(openIdSet).sort()
10671067
}
1068-
1068+
// 'checked' utils methods =============
1069+
export function updateCheckedInFlatData<T extends Record<Id, any>>(
1070+
flatData: T[],
1071+
checkedIds: Id[],
1072+
idOrIds: Id | Id[],
1073+
checked: boolean,
1074+
options?: Partial<typeof flatDataDefaultOptions>
1075+
) {
1076+
const checkedIdSet = new Set(checkedIds)
1077+
const idsToUpdate = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
1078+
const all = new Map<Id, Checked>()
1079+
const changedPids = new Set<Id>(idsToUpdate)
1080+
const pidById: Record<Id, Id | null> = {}
1081+
const childIdsById = new Map<Id | null, Id[]>()
1082+
childIdsById.set(null, [])
1083+
for (const [node, { parents, id, pid }] of walkFlatDataGenerator(flatData, options)) {
1084+
pidById[id] = pid
1085+
childIdsById.get(pid)!.push(id)
1086+
childIdsById.set(id, [])
1087+
all.set(id, checkedIdSet.has(id))
1088+
if (changedPids.has(id) || (pid && changedPids.has(pid))) {
1089+
// update self and children
1090+
all.set(id, checked)
1091+
changedPids.add(id)
1092+
}
1093+
}
1094+
// update parents
1095+
for (const id of idsToUpdate) {
1096+
let cur = pidById[id];
1097+
while (cur != null) {
1098+
let allChecked = true
1099+
let hasChecked = false
1100+
for (const childId of childIdsById.get(cur)!) {
1101+
if (all.get(childId) === false) {
1102+
allChecked = false
1103+
} else {
1104+
hasChecked = true
1105+
}
1106+
}
1107+
all.set(cur, allChecked ? true : (hasChecked ? null : false))
1108+
cur = pidById[cur]
1109+
}
1110+
}
1111+
const newCheckedIds: Id[] = [];
1112+
const semiCheckedIds: Id[] = [];
1113+
all.forEach((v, k) => {
1114+
if (v === true) {
1115+
newCheckedIds.push(k)
1116+
} else if (v === null) {
1117+
semiCheckedIds.push(k)
1118+
}
1119+
})
1120+
const allChecked = [...newCheckedIds, ...semiCheckedIds];
1121+
return [newCheckedIds.sort(), semiCheckedIds.sort(), allChecked.sort()]
1122+
}
10691123
// private methods
10701124
function calculateDistance(x1: number, y1: number, x2: number, y2: number) {
10711125
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));

src/test/flatData.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
addToFlatData,
66
removeByIdInFlatData,
77
openParentsInFlatData,
8+
updateCheckedInFlatData,
89
} from "../../lib/HeTree";
910

1011
test("walkFlatDataGenerator: node, treeIndex", () => {
@@ -225,7 +226,7 @@ test("removeByIdInFlatData", () => {
225226
expect(cur.length).toBe(4);
226227
//
227228
cur = [...data];
228-
console.log("if remove id 5", removeByIdInFlatData(cur, 5));
229+
removeByIdInFlatData(cur, 5)
229230
expect(cur.length).toBe(7);
230231
//
231232
cur = createData("key");
@@ -245,6 +246,35 @@ test("openParentsInFlatData", () => {
245246
newOpenids = openParentsInFlatData(cur, [], 11);
246247
expect(newOpenids.toString()).toBe("");
247248
});
249+
test("updateCheckedInFlatData", () => {
250+
let data = createData();
251+
let cur = [...data];
252+
let ids = [3];
253+
let [newIds, semi,all] = updateCheckedInFlatData(cur,ids,[], true);
254+
expect(newIds.toString()).toBe("3");
255+
//
256+
[newIds, semi] = updateCheckedInFlatData(cur,ids,[3], true);
257+
expect(newIds.toString()).toBe("3,6,7");
258+
expect(semi.toString()).toBe("1");
259+
//
260+
[newIds, semi] = updateCheckedInFlatData(cur,[],[8,10], true);
261+
expect(newIds.toString()).toBe("10,2,4,5,8");
262+
expect(semi.toString()).toBe("1");
263+
// uncheck
264+
[newIds, semi, all] = updateCheckedInFlatData(cur,[10,2,4,8,5],[8], false);
265+
expect(newIds.toString()).toBe("10,5");
266+
expect(semi.toString()).toBe("1,2");
267+
expect(all.toString()).toBe("1,10,2,5");
268+
//
269+
[newIds, semi, all] = updateCheckedInFlatData(cur,[1,2,5,10,4,8,3,7,6],[6,7], !false);
270+
expect(newIds.length).toBe(9);
271+
expect(semi.length).toBe(0);
272+
expect(all.length).toBe(9);
273+
//
274+
[newIds, semi, all] = updateCheckedInFlatData(cur,[1,2,5,10,4,8,3,7,6],[6,7], false);
275+
expect(semi.toString()).toBe('1');
276+
277+
});
248278

249279
function createData(id = "id", parent_id = "parent_id") {
250280
// size 9

0 commit comments

Comments
 (0)