@@ -14,6 +14,12 @@ import { TreeViewFinder, TreeViewFinderNodeProps, TreeViewFinderProps } from '..
14
14
import { useSnackMessage } from '../../hooks/useSnackMessage' ;
15
15
import { fetchDirectoryContent , fetchElementsInfos , fetchRootFolders } from '../../services' ;
16
16
import { ElementAttributes } from '../../utils' ;
17
+ import {
18
+ fetchChildrenForExpandedNodes ,
19
+ getExpansionPathsForSelected ,
20
+ initializeFromLastSelected ,
21
+ saveLastSelectedDirectoryFromNode ,
22
+ } from './utils' ;
17
23
18
24
const styles = {
19
25
icon : ( theme : Theme ) => ( {
@@ -173,10 +179,13 @@ export function DirectoryItemSelector({
173
179
itemFilter,
174
180
expanded,
175
181
selected,
182
+ onClose,
176
183
...otherTreeViewFinderProps
177
184
} : Readonly < DirectoryItemSelectorProps > ) {
178
185
const [ data , setData ] = useState < TreeViewFinderNodeProps [ ] > ( [ ] ) ;
179
186
const [ rootDirectories , setRootDirectories ] = useState < ElementAttributes [ ] > ( [ ] ) ;
187
+ const [ isRootsLoaded , setIsRootsLoaded ] = useState ( false ) ;
188
+ const [ autoExpandedNodes , setAutoExpandedNodes ] = useState < UUID [ ] > ( [ ] ) ;
180
189
const nodeMap = useRef < Record < UUID , ElementAttributes > > ( { } ) ;
181
190
const dataRef = useRef < TreeViewFinderNodeProps [ ] > ( [ ] ) ;
182
191
dataRef . current = data ;
@@ -231,6 +240,7 @@ export function DirectoryItemSelector({
231
240
) ;
232
241
233
242
const updateRootDirectories = useCallback ( ( ) => {
243
+ setIsRootsLoaded ( false ) ;
234
244
fetchRootFolders ( types )
235
245
. then ( ( newData ) => {
236
246
const [ nrs , mdr ] = updatedTree ( rootsRef . current , nodeMap . current , null , newData ) ;
@@ -243,6 +253,9 @@ export function DirectoryItemSelector({
243
253
messageTxt : error . message ,
244
254
headerId : 'DirectoryItemSelector' ,
245
255
} ) ;
256
+ } )
257
+ . finally ( ( ) => {
258
+ setIsRootsLoaded ( true ) ;
246
259
} ) ;
247
260
} , [ convertRoots , types , snackError ] ) ;
248
261
@@ -286,39 +299,113 @@ export function DirectoryItemSelector({
286
299
[ types , equipmentTypes , itemFilter , contentFilter , addToDirectory ]
287
300
) ;
288
301
289
- // In this useEffect, we fetch the path (expanded array) of every selected node
290
- useEffect ( ( ) => {
291
- if ( open && expanded && selected ) {
292
- // we check if every selected item is already fetched
293
- const isSelectedItemFetched = selected . every ( ( id ) => nodeMap . current [ id ] ) ;
294
- if ( ! isSelectedItemFetched ) {
295
- expanded . forEach ( ( nodeId ) => {
296
- const node = nodeMap . current [ nodeId ] ;
297
- // we check that the node exist before fetching the children
298
- // And we check if there is already children (Because we are trying to reach a selected element, we know every node has at least one child)
299
- if ( node ?. children && node . children . length === 0 ) {
300
- fetchDirectoryChildren ( nodeId ) ;
301
- }
302
- } ) ;
303
- }
302
+ // Helper function to fetch children for a node if not already loaded
303
+ const fetchNodeChildrenIfNeeded = useCallback (
304
+ ( nodeId : UUID , delay : number = 0 ) => {
305
+ setTimeout ( ( ) => {
306
+ const node = nodeMap . current [ nodeId ] ;
307
+ if ( node && ( ! node . children || node . children . length === 0 ) && node . type === ElementType . DIRECTORY ) {
308
+ fetchDirectoryChildren ( nodeId ) ;
309
+ }
310
+ } , delay ) ;
311
+ } ,
312
+ [ fetchDirectoryChildren ]
313
+ ) ;
314
+
315
+ // Handle expansion from selected items
316
+ const handleSelectedExpansion = useCallback ( async ( ) : Promise < boolean > => {
317
+ if ( ! selected || selected . length === 0 ) {
318
+ return false ;
304
319
}
305
- } , [ open , expanded , fetchDirectoryChildren , selected , data ] ) ;
306
320
321
+ const expandedArray = await getExpansionPathsForSelected ( selected , expanded ) ;
322
+ setAutoExpandedNodes ( expandedArray ) ;
323
+ fetchChildrenForExpandedNodes ( expandedArray , fetchNodeChildrenIfNeeded ) ;
324
+ return true ;
325
+ } , [ selected , expanded , fetchNodeChildrenIfNeeded ] ) ;
326
+
327
+ // Handle expansion from provided expanded prop
328
+ const handleProvidedExpansion = useCallback ( ( ) : boolean => {
329
+ if ( ! expanded || expanded . length === 0 ) {
330
+ return false ;
331
+ }
332
+
333
+ setAutoExpandedNodes ( expanded ) ;
334
+ fetchChildrenForExpandedNodes ( expanded , fetchNodeChildrenIfNeeded ) ;
335
+
336
+ return true ;
337
+ } , [ expanded , fetchNodeChildrenIfNeeded ] ) ;
338
+
339
+ // Handle expansion from last selected directory
340
+ const handleLastSelectedExpansion = useCallback ( async ( ) : Promise < boolean > => {
341
+ const expandPath = await initializeFromLastSelected ( ) ;
342
+
343
+ if ( ! expandPath ) {
344
+ return false ;
345
+ }
346
+
347
+ setAutoExpandedNodes ( expandPath ) ;
348
+ fetchChildrenForExpandedNodes ( expandPath , fetchNodeChildrenIfNeeded ) ;
349
+
350
+ return true ;
351
+ } , [ fetchNodeChildrenIfNeeded ] ) ;
352
+
353
+ // Main expansion orchestrator
354
+ const initializeExpansion = useCallback ( async ( ) => {
355
+ // Priority 1: Handle selected items
356
+ const selectedSuccess = await handleSelectedExpansion ( ) ;
357
+ if ( selectedSuccess ) return ;
358
+
359
+ // Priority 2: Handle provided expanded items
360
+ const expandedSuccess = handleProvidedExpansion ( ) ;
361
+ if ( expandedSuccess ) return ;
362
+
363
+ // Priority 3: Fall back to last selected directory
364
+ await handleLastSelectedExpansion ( ) ;
365
+ } , [ handleSelectedExpansion , handleProvidedExpansion , handleLastSelectedExpansion ] ) ;
366
+
367
+ // Handle root loading and expansion initialization
307
368
useEffect ( ( ) => {
308
- if ( open ) {
369
+ if ( ! open ) {
370
+ setIsRootsLoaded ( false ) ;
371
+ setAutoExpandedNodes ( [ ] ) ;
372
+ return ;
373
+ }
374
+
375
+ // Phase 1: Load root directories if not already loaded
376
+ if ( ! isRootsLoaded ) {
309
377
updateRootDirectories ( ) ;
378
+ return ;
310
379
}
311
- } , [ open , updateRootDirectories ] ) ;
380
+
381
+ // Phase 2: Initialize expansion once roots are loaded
382
+ initializeExpansion ( ) ;
383
+ } , [ open , isRootsLoaded , updateRootDirectories , initializeExpansion ] ) ;
384
+
385
+ const handleClose = useCallback (
386
+ ( nodes : TreeViewFinderNodeProps [ ] ) => {
387
+ if ( nodes && nodes . length > 0 ) {
388
+ const lastSelectedNode = nodes [ 0 ] ;
389
+ saveLastSelectedDirectoryFromNode ( lastSelectedNode ) ;
390
+ }
391
+
392
+ setAutoExpandedNodes ( [ ] ) ;
393
+
394
+ onClose ( nodes ) ;
395
+ } ,
396
+ [ onClose ]
397
+ ) ;
312
398
313
399
return (
314
400
< TreeViewFinder
315
401
onTreeBrowse = { fetchDirectoryChildren as ( NodeId : string ) => void }
316
402
sortMethod = { sortHandlingDirectories }
317
403
multiSelect // defaulted to true
318
404
open = { open }
319
- expanded = { expanded as string [ ] }
405
+ expanded = { autoExpandedNodes }
320
406
onlyLeaves // defaulted to true
321
407
selected = { selected }
408
+ onClose = { handleClose }
322
409
{ ...otherTreeViewFinderProps }
323
410
data = { data }
324
411
/>
0 commit comments