@@ -69,6 +69,7 @@ export interface HeTreeProps<T extends Record<string, any>> extends Partial<type
69
69
onChange : ( data : T [ ] ) => void ,
70
70
openIds ?: Id [ ] ,
71
71
checkedIds ?: Id [ ] ,
72
+ semiCheckedIds ?: Id [ ] ,
72
73
}
73
74
74
75
export function useHeTree < T extends Record < string , any > > (
@@ -82,7 +83,10 @@ export function useHeTree<T extends Record<string, any>>(
82
83
}
83
84
const openIdsStr = useMemo ( ( ) => props . openIds ? [ ...props . openIds ] . sort ( ) . toString ( ) : '' , [ props . openIds ] )
84
85
const openIdSet = useMemo ( ( ) => new Set ( props . openIds ) , [ openIdsStr ] )
85
- const checkedIdsStr = useMemo ( ( ) => props . checkedIds ?. toString ( ) , [ props . checkedIds ] )
86
+ const checkedIdsStr = useMemo ( ( ) => props . checkedIds ? [ ...props . checkedIds ] . sort ( ) . toString ( ) : '' , [ props . checkedIds ] )
87
+ const checkedIdSet = useMemo ( ( ) => new Set ( props . checkedIds ) , [ checkedIdsStr ] )
88
+ const semiCheckedIdStr = useMemo ( ( ) => props . semiCheckedIds ? [ ...props . semiCheckedIds ] . sort ( ) . toString ( ) : '' , [ props . semiCheckedIds ] )
89
+ const semiCheckedIdSet = useMemo ( ( ) => new Set ( props . semiCheckedIds ) , [ semiCheckedIdStr ] )
86
90
// mainCache ==================================
87
91
const mainCache = useMemo (
88
92
( ) => {
@@ -140,7 +144,7 @@ export function useHeTree<T extends Record<string, any>>(
140
144
index,
141
145
level,
142
146
open : props . openIds ? openIdSet . has ( id ) : true ,
143
- checked : false ,
147
+ checked : checkedIdSet . has ( id ) ? true : ( semiCheckedIdSet . has ( id ) ? null : false ) ,
144
148
draggable : false ,
145
149
}
146
150
stats [ id ] = stat
@@ -150,32 +154,6 @@ export function useHeTree<T extends Record<string, any>>(
150
154
siblingStats . push ( stat )
151
155
count ++
152
156
}
153
- // checked
154
- let allCheckedIds : Id [ ] = [ ] ;
155
- let semiCheckedIds : Id [ ] = [ ] ;
156
- if ( props . checkedIds ) {
157
- for ( const id of props . checkedIds ) {
158
- const stat = stats [ id ] ;
159
- if ( stat ) {
160
- stat . checked = true ;
161
- for ( const [ curStat ] of walkTreeDataGenerator ( stat . childStats , 'childStats' ) ) {
162
- curStat . checked = true ;
163
- }
164
- for ( const curStat of walkParentsGenerator ( stat , 'parentStat' , { withSelf : false } ) ) {
165
- let allChecked = true
166
- let hasChecked = false
167
- for ( const child of curStat . childStats ) {
168
- if ( child . checked ) {
169
- hasChecked = true
170
- } else {
171
- allChecked = false
172
- }
173
- }
174
- curStat . checked = allChecked ? true : ( hasChecked ? null : false )
175
- }
176
- }
177
- }
178
- }
179
157
180
158
// after stats ready
181
159
for ( const [ stat ] of walkTreeDataGenerator ( rootStats , 'childStats' ) ) {
@@ -185,12 +163,6 @@ export function useHeTree<T extends Record<string, any>>(
185
163
draggable = stat . parentStat ? stat . parentStat . draggable : true
186
164
}
187
165
stat . draggable = draggable
188
- // checked
189
- if ( stat . checked ) {
190
- allCheckedIds . push ( stat . id )
191
- } else if ( stat . checked === null ) {
192
- semiCheckedIds . push ( stat . id )
193
- }
194
166
}
195
167
const getStat = ( idOrNodeOrStat : T | Stat < T > | Id ) => {
196
168
let id : Id
@@ -232,14 +204,12 @@ export function useHeTree<T extends Record<string, any>>(
232
204
return {
233
205
// root
234
206
rootIds, rootNodes, rootStats,
235
- // open & checked
236
- allCheckedIds, semiCheckedIds,
237
207
// methods
238
208
getStat,
239
209
getDraft,
240
210
nextData,
241
211
}
242
- } , [ props . data , props . dataType , ID , PID , openIdSet , checkedIdsStr , props . rootId ,
212
+ } , [ props . data , props . dataType , ID , PID , openIdSet , checkedIdSet , props . rootId ,
243
213
isFunctionReactive && props . canDrag ,
244
214
]
245
215
) ;
@@ -677,54 +647,6 @@ export function useHeTree<T extends Record<string, any>>(
677
647
renderHeTree,
678
648
}
679
649
}
680
-
681
- // function getMapOfFlatData(params:type) {
682
-
683
- // }
684
-
685
- // function updateChecked<T extends Record<string, any>>(options: {
686
- // data: T[],
687
- // dataType: 'tree' | 'flat',
688
- // checkedIds: Id[],
689
- // idKey: 'id',
690
- // parentIdKey: 'parentId',
691
- // childrenKey: 'children',
692
- // }) {
693
- // const { data, dataType, checkedIds, idKey: ID, parentIdKey: PID, childrenKey: CHILDREN } = options
694
- // if (dataType === 'flat') {
695
- // const byId: Record<Id, T> = {}
696
- // const childrenById: Record<Id, T[]> = {}
697
- // const rootChildren: T[] = [];
698
- // for (const node of data) {
699
- // const id = node[ID];
700
- // byId[id] = node
701
- // childrenById[id] = [];
702
- // const pid = node[PID];
703
- // (childrenById[pid] || rootChildren).push(node);
704
- // }
705
- // //
706
- // const newCheckedIds = new Set<Id>(checkedIds)
707
- // const checked = false
708
- // const changedIds: Id[] = [];
709
- // const allChangedIds = new Set(changedIds)
710
- // data.forEach((node) => {
711
- // if (allChangedIds.has(node[PID])) {
712
- // allChangedIds.add(node[ID])
713
- // }
714
- // })
715
- // }
716
- // let pids = new Set<Id>()
717
- // let allCheckedIds = new Set<Id>(checkedIds)
718
- // for (let id of checkedIds) {
719
- // if (id in checkedIds) {
720
- // allCheckedIds.add(id)
721
- // pids.add(id)
722
- // } else if (allCheckedIds.has(byid[id].pid)) {
723
- // allCheckedIds.add(id)
724
- // pids.add(id)
725
- // }
726
- // }
727
- // }
728
650
// react components ==================================
729
651
// no components
730
652
// utils methods ==================================
@@ -1079,7 +1001,8 @@ export function updateCheckedInFlatData<T extends Record<Id, any>>(
1079
1001
const changedPids = new Set < Id > ( idsToUpdate )
1080
1002
const pidById : Record < Id , Id | null > = { }
1081
1003
const childIdsById = new Map < Id | null , Id [ ] > ( )
1082
- childIdsById . set ( null , [ ] )
1004
+ const rootIds : Id [ ] = [ ] ;
1005
+ childIdsById . set ( null , rootIds )
1083
1006
for ( const [ node , { parents, id, pid } ] of walkFlatDataGenerator ( flatData , options ) ) {
1084
1007
pidById [ id ] = pid
1085
1008
childIdsById . get ( pid ) ! . push ( id )
@@ -1091,22 +1014,42 @@ export function updateCheckedInFlatData<T extends Record<Id, any>>(
1091
1014
changedPids . add ( id )
1092
1015
}
1093
1016
}
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
- }
1017
+ // check from root to each nodes
1018
+ const walk = ( id : Id ) => {
1019
+ const childIds = childIdsById . get ( id )
1020
+ if ( ! childIds || childIds . length === 0 ) {
1021
+ return all . get ( id )
1022
+ }
1023
+ let hasTrue = false
1024
+ let hasFalse = false
1025
+ let hasNull = false
1026
+ for ( const childId of childIds ) {
1027
+ let t = walk ( childId )
1028
+ if ( t === false ) {
1029
+ hasFalse = true
1030
+ } else if ( t === null ) {
1031
+ hasNull = true
1032
+ break
1033
+ } else {
1034
+ hasTrue = true
1106
1035
}
1107
- all . set ( cur , allChecked ? true : ( hasChecked ? null : false ) )
1108
- cur = pidById [ cur ]
1109
1036
}
1037
+ let checked : Checked
1038
+ if ( hasNull ) {
1039
+ checked = null
1040
+ } else if ( hasFalse && hasTrue ) {
1041
+ checked = null
1042
+ } else if ( hasFalse ) {
1043
+ checked = false
1044
+ } else {
1045
+ checked = true
1046
+ }
1047
+ all . set ( id , checked )
1048
+ return checked
1049
+ }
1050
+
1051
+ for ( const id of rootIds ) {
1052
+ walk ( id )
1110
1053
}
1111
1054
const newCheckedIds : Id [ ] = [ ] ;
1112
1055
const semiCheckedIds : Id [ ] = [ ] ;
@@ -1117,11 +1060,10 @@ export function updateCheckedInFlatData<T extends Record<Id, any>>(
1117
1060
semiCheckedIds . push ( k )
1118
1061
}
1119
1062
} )
1120
- const allChecked = [ ...newCheckedIds , ...semiCheckedIds ] ;
1121
- return [ newCheckedIds . sort ( ) , semiCheckedIds . sort ( ) , allChecked . sort ( ) ]
1063
+ return [ newCheckedIds . sort ( ) , semiCheckedIds . sort ( ) ]
1122
1064
}
1123
1065
export function updateCheckedInTreeData < T extends Record < Id , any > > (
1124
- flatData : T [ ] ,
1066
+ treeData : T [ ] ,
1125
1067
checkedIds : Id [ ] ,
1126
1068
idOrIds : Id | Id [ ] ,
1127
1069
checked : boolean ,
@@ -1136,7 +1078,7 @@ export function updateCheckedInTreeData<T extends Record<Id, any>>(
1136
1078
const pidById : Record < Id , Id | null > = { }
1137
1079
const childIdsById = new Map < Id | null , Id [ ] > ( )
1138
1080
childIdsById . set ( null , [ ] )
1139
- for ( const [ node , { parents, parent } ] of walkTreeDataGenerator ( flatData , CHILDREN ) ) {
1081
+ for ( const [ node , { parents, parent } ] of walkTreeDataGenerator ( treeData , CHILDREN ) ) {
1140
1082
const id = node [ ID ] ;
1141
1083
const pid = parent ?. [ ID ] ?? null
1142
1084
pidById [ id ] = pid
@@ -1149,22 +1091,41 @@ export function updateCheckedInTreeData<T extends Record<Id, any>>(
1149
1091
changedPids . add ( id )
1150
1092
}
1151
1093
}
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
- }
1094
+ // check from root to each nodes
1095
+ const walk = ( id : Id ) => {
1096
+ const childIds = childIdsById . get ( id )
1097
+ if ( ! childIds || childIds . length === 0 ) {
1098
+ return all . get ( id )
1099
+ }
1100
+ let hasTrue = false
1101
+ let hasFalse = false
1102
+ let hasNull = false
1103
+ for ( const childId of childIds ) {
1104
+ let t = walk ( childId )
1105
+ if ( t === false ) {
1106
+ hasFalse = true
1107
+ } else if ( t === null ) {
1108
+ hasNull = true
1109
+ break
1110
+ } else {
1111
+ hasTrue = true
1164
1112
}
1165
- all . set ( cur , allChecked ? true : ( hasChecked ? null : false ) )
1166
- cur = pidById [ cur ]
1167
1113
}
1114
+ let checked : Checked
1115
+ if ( hasNull ) {
1116
+ checked = null
1117
+ } else if ( hasFalse && hasTrue ) {
1118
+ checked = null
1119
+ } else if ( hasFalse ) {
1120
+ checked = false
1121
+ } else {
1122
+ checked = true
1123
+ }
1124
+ all . set ( id , checked )
1125
+ return checked
1126
+ }
1127
+ for ( const node of treeData ) {
1128
+ walk ( node [ ID ] )
1168
1129
}
1169
1130
const newCheckedIds : Id [ ] = [ ] ;
1170
1131
const semiCheckedIds : Id [ ] = [ ] ;
@@ -1175,8 +1136,7 @@ export function updateCheckedInTreeData<T extends Record<Id, any>>(
1175
1136
semiCheckedIds . push ( k )
1176
1137
}
1177
1138
} )
1178
- const allChecked = [ ...newCheckedIds , ...semiCheckedIds ] ;
1179
- return [ newCheckedIds . sort ( ) , semiCheckedIds . sort ( ) , allChecked . sort ( ) ]
1139
+ return [ newCheckedIds . sort ( ) , semiCheckedIds . sort ( ) ]
1180
1140
}
1181
1141
// private methods
1182
1142
function calculateDistance ( x1 : number , y1 : number , x2 : number , y2 : number ) {
0 commit comments