@@ -67,11 +67,7 @@ export interface HeTreeProps<T extends Record<string, any>> extends Partial<type
67
67
onDrop ?: ( e : React . DragEvent < HTMLElement > , parentStat : Stat < T > | null , index : number , isExternal : boolean ) => boolean | void ,
68
68
onDragEnd ?: ( e : React . DragEvent < HTMLElement > , stat : Stat < T > , isOutside : boolean ) => void ,
69
69
onChange : ( data : T [ ] ) => void ,
70
- dragOpen ?: boolean ,
71
- // onDragOpen?: (stat: Stat<T>) => void,
72
70
openIds ?: Id [ ] ,
73
- autoOpenParent ?: boolean ,
74
- onOpenIdsChange ?: ( ids : Id [ ] ) => void ,
75
71
checkedIds ?: Id [ ] ,
76
72
}
77
73
@@ -80,10 +76,12 @@ export function useHeTree<T extends Record<string, any>>(
80
76
) {
81
77
const props = { ...defaultProps , ...props0 }
82
78
const { idKey : ID , parentIdKey : PID , childrenKey : CHILDREN , placeholderId, isFunctionReactive, } = props
79
+ const flatOpt = { idKey : ID , parentIdKey : PID } // shared options for flat data
83
80
if ( ! props . renderNode && ! props . renderNodeBox ) {
84
81
throw new Error ( "Either renderNodeBox or renderNode is required." ) ;
85
82
}
86
- const openIdsStr = useMemo ( ( ) => props . openIds ?. toString ( ) , [ props . openIds ] )
83
+ const openIdsStr = useMemo ( ( ) => props . openIds ? [ ...props . openIds ] . sort ( ) . toString ( ) : '' , [ props . openIds ] )
84
+ const openIdSet = useMemo ( ( ) => new Set ( props . openIds ) , [ openIdsStr ] )
87
85
const checkedIdsStr = useMemo ( ( ) => props . checkedIds ?. toString ( ) , [ props . checkedIds ] )
88
86
// mainCache ==================================
89
87
const mainCache = useMemo (
@@ -106,7 +104,6 @@ export function useHeTree<T extends Record<string, any>>(
106
104
}
107
105
}
108
106
let count = 0
109
- const openIdSet = new Set ( props . openIds )
110
107
for ( const node of simpleWalk ( ) ) {
111
108
const id : Id = node [ ID ] ?? count
112
109
const pid = node [ PID ] as Id
@@ -125,7 +122,7 @@ export function useHeTree<T extends Record<string, any>>(
125
122
siblings = parentStat . children
126
123
siblingStats = parentStat . childStats
127
124
}
128
- const index = siblingIds . indexOf ( id )
125
+ const index = siblingIds . length
129
126
const level = parentStat ?. level + 1 || 1
130
127
const stat = {
131
128
_isStat : true ,
@@ -148,32 +145,11 @@ export function useHeTree<T extends Record<string, any>>(
148
145
}
149
146
stats [ id ] = stat
150
147
nodes [ id ] = node
151
- if ( parentStat ) {
152
- parentStat . childIds . push ( id )
153
- parentStat . children . push ( node )
154
- parentStat . childStats . push ( stat )
155
- } else {
156
- rootIds . push ( id )
157
- rootNodes . push ( node )
158
- rootStats . push ( stat )
159
- }
148
+ siblingIds . push ( id )
149
+ siblings . push ( node )
150
+ siblingStats . push ( stat )
160
151
count ++
161
152
}
162
- // open
163
- let allOpenIds : Id [ ] | null = null ;
164
- if ( props . openIds ) {
165
- allOpenIds = [ ] ;
166
- if ( props . autoOpenParent ) {
167
- for ( const id of props . openIds ) {
168
- const stat = stats [ id ] ;
169
- if ( stat ) {
170
- for ( const curStat of walkParentsGenerator ( stat , 'parentStat' , { withSelf : false } ) ) {
171
- curStat . open = true
172
- }
173
- }
174
- }
175
- }
176
- }
177
153
// checked
178
154
let allCheckedIds : Id [ ] = [ ] ;
179
155
let semiCheckedIds : Id [ ] = [ ] ;
@@ -209,8 +185,6 @@ export function useHeTree<T extends Record<string, any>>(
209
185
draggable = stat . parentStat ? stat . parentStat . draggable : true
210
186
}
211
187
stat . draggable = draggable
212
- // open
213
- stat . open && allOpenIds ?. push ( stat . id )
214
188
// checked
215
189
if ( stat . checked ) {
216
190
allCheckedIds . push ( stat . id )
@@ -259,13 +233,13 @@ export function useHeTree<T extends Record<string, any>>(
259
233
// root
260
234
rootIds, rootNodes, rootStats,
261
235
// open & checked
262
- allOpenIds , allCheckedIds, semiCheckedIds,
236
+ allCheckedIds, semiCheckedIds,
263
237
// methods
264
238
getStat,
265
239
getDraft,
266
240
nextData,
267
241
}
268
- } , [ props . data , props . dataType , ID , PID , openIdsStr , checkedIdsStr , props . autoOpenParent , props . rootId ,
242
+ } , [ props . data , props . dataType , ID , PID , openIdSet , checkedIdsStr , props . rootId ,
269
243
isFunctionReactive && props . canDrag ,
270
244
]
271
245
) ;
@@ -538,37 +512,52 @@ export function useHeTree<T extends Record<string, any>>(
538
512
}
539
513
if ( ! customized && ! isExternal ) {
540
514
// move node
541
- const draft = getDraft ( )
542
- let draggedSiblings : T [ ] , targetSiblings : T [ ]
543
- if ( ! placeholder . parentStat ) {
544
- targetSiblings = draft
545
- }
546
- let draggedNodeDraft : T
547
- for ( const [ node , { siblings } ] of walkTreeDataGenerator ( draft , CHILDREN ) ) {
548
- if ( node [ ID ] === draggedStat . id ) {
549
- draggedSiblings = siblings
550
- draggedNodeDraft = node
551
- }
552
- if ( ! targetSiblings ! && node [ ID ] === placeholder . parentStat ! . id ) {
553
- targetSiblings = node [ CHILDREN ]
515
+ // const draft = getDraft()
516
+ // let draggedSiblings: T[], targetSiblings: T[]
517
+ // if (!placeholder.parentStat) {
518
+ // targetSiblings = draft
519
+ // }
520
+ // let draggedNodeDraft: T
521
+ // for (const [node, { siblings }] of walkTreeDataGenerator(draft, CHILDREN)) {
522
+ // if (node[ID] === draggedStat.id) {
523
+ // draggedSiblings = siblings
524
+ // draggedNodeDraft = node
525
+ // }
526
+ // if (!targetSiblings! && node[ID] === placeholder.parentStat!.id) {
527
+ // targetSiblings = node[CHILDREN]
528
+ // }
529
+ // if (draggedSiblings! && targetSiblings!) {
530
+ // break
531
+ // }
532
+ // }
533
+ // const ds = draggedSiblings!
534
+ // const ts = targetSiblings!
535
+ // const startIndex = ds.findIndex(v => v[ID] === draggedStat.id)
536
+ // let targetIndex = placeholder.index
537
+ // // check index
538
+ // if (ds === ts && placeholder.index > startIndex) {
539
+ // targetIndex -= 1
540
+ // }
541
+ // // remove from start position
542
+ // ds.splice(startIndex, 1)
543
+ // // move to target position
544
+ // ts.splice(targetIndex, 0, draggedNodeDraft!)
545
+ // props.onChange(nextData())
546
+ if ( props . dataType === 'flat' ) {
547
+ const startIndex = props . data . findIndex ( v => v [ ID ] === draggedStat . id )
548
+ let targetIndexInSiblings = placeholder . index
549
+ if ( placeholder . parentStat === draggedStat . parentStat && draggedStat . index < targetIndexInSiblings ) {
550
+ targetIndexInSiblings --
554
551
}
555
- if ( draggedSiblings ! && targetSiblings ! ) {
556
- break
557
- }
558
- }
559
- const ds = draggedSiblings !
560
- const ts = targetSiblings !
561
- const startIndex = ds . findIndex ( v => v [ ID ] === draggedStat . id )
562
- let targetIndex = placeholder . index
563
- // check index
564
- if ( ds === ts && placeholder . index > startIndex ) {
565
- targetIndex -= 1
552
+ const targetParentId = placeholder . parentStat ?. id ?? null
553
+ const newData = [ ...props . data ] ;
554
+ const removed = removeByIdInFlatData ( newData , draggedStat . id , flatOpt )
555
+ const newNode = { ...draggedStat . node , [ PID ] : targetParentId }
556
+ removed [ 0 ] = newNode
557
+ const targetTreeIndex = convertIndexToTreeIndexInFlatData ( newData , targetParentId , targetIndexInSiblings , flatOpt )
558
+ newData . splice ( targetTreeIndex , 0 , ...removed )
559
+ props . onChange ! ( newData )
566
560
}
567
- // remove from start position
568
- ds . splice ( startIndex , 1 )
569
- // move to target position
570
- ts . splice ( targetIndex , 0 , draggedNodeDraft ! )
571
- props . onChange ( nextData ( ) )
572
561
}
573
562
}
574
563
if ( ! customized ) {
@@ -1015,6 +1004,66 @@ export function removeByIdInFlatData<T extends Record<Id, any>>(
1015
1004
return flatData . splice ( startIndex , endIndex - startIndex )
1016
1005
}
1017
1006
1007
+ // 'open' utils methods =============
1008
+ export function openParentsInFlatData < T extends Record < Id , any > > (
1009
+ flatData : T [ ] ,
1010
+ openIds : Id [ ] ,
1011
+ idOrIds : Id | Id [ ] ,
1012
+ options0 ?: Partial < typeof flatDataDefaultOptions >
1013
+ ) {
1014
+ const options = { ...flatDataDefaultOptions , ...options0 }
1015
+ const { idKey : ID , parentIdKey : PID } = options
1016
+ const openIdSet = new Set ( openIds )
1017
+ const idsToOpen = Array . isArray ( idOrIds ) ? idOrIds : [ idOrIds ] ;
1018
+ const idsToOpenSet = new Set ( idsToOpen )
1019
+ if ( idsToOpenSet . size > 0 ) {
1020
+ for ( const [ node , { parents } ] of walkFlatDataGenerator ( flatData , options ) ) {
1021
+ const id = node [ ID ] ;
1022
+ if ( idsToOpenSet . has ( id ) ) {
1023
+ for ( const parent of parents ) {
1024
+ openIdSet . add ( parent [ ID ] )
1025
+ }
1026
+ idsToOpenSet . delete ( id )
1027
+ if ( idsToOpenSet . size === 0 ) {
1028
+ break
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+ return Array . from ( openIdSet ) . sort ( )
1034
+ }
1035
+ const treeDataDefaultOptions = {
1036
+ idKey : 'id' ,
1037
+ childrenKey : 'children' ,
1038
+ }
1039
+ export function openParentsInTreeData < T extends Record < Id , any > > (
1040
+ treeData : T [ ] ,
1041
+ openIds : Id [ ] ,
1042
+ idOrIds : Id | Id [ ] ,
1043
+ options0 ?: Partial < typeof treeDataDefaultOptions >
1044
+ ) {
1045
+ const options = { ...treeDataDefaultOptions , ...options0 }
1046
+ const { idKey : ID , childrenKey : CHILDREN } = options
1047
+ const openIdSet = new Set ( openIds )
1048
+ const idsToOpen = Array . isArray ( idOrIds ) ? idOrIds : [ idOrIds ] ;
1049
+ const idsToOpenSet = new Set ( idsToOpen )
1050
+ if ( idsToOpenSet . size > 0 ) {
1051
+ for ( const [ node , { parents } ] of walkTreeDataGenerator ( treeData , options . childrenKey ) ) {
1052
+ const id = node [ ID ] ;
1053
+ if ( idsToOpenSet . has ( id ) ) {
1054
+ for ( const parent of parents ) {
1055
+ openIdSet . add ( parent [ ID ] )
1056
+ }
1057
+ idsToOpenSet . delete ( id )
1058
+ if ( idsToOpenSet . size === 0 ) {
1059
+ break
1060
+ }
1061
+ }
1062
+ }
1063
+ }
1064
+ return Array . from ( openIdSet ) . sort ( )
1065
+ }
1066
+
1018
1067
// private methods
1019
1068
function calculateDistance ( x1 : number , y1 : number , x2 : number , y2 : number ) {
1020
1069
return Math . sqrt ( Math . pow ( ( x2 - x1 ) , 2 ) + Math . pow ( ( y2 - y1 ) , 2 ) ) ;
0 commit comments