@@ -19,7 +19,7 @@ import {
19
19
styled ,
20
20
Typography ,
21
21
} from '@mui/material' ;
22
- import { TreeItem , TreeView , TreeViewClasses } from '@mui/x-tree-view' ;
22
+ import { TreeItem , SimpleTreeView , SimpleTreeViewClasses } from '@mui/x-tree-view' ;
23
23
import {
24
24
Check as CheckIcon ,
25
25
ChevronRight as ChevronRightIcon ,
@@ -64,6 +64,14 @@ const defaultStyles = {
64
64
export const generateTreeViewFinderClass = ( className : string ) => `GsiTreeViewFinder-${ className } ` ;
65
65
const composeClasses = makeComposeClasses ( generateTreeViewFinderClass ) ;
66
66
67
+ function CustomExpandIcon ( { className } : Readonly < { className ?: string } > ) {
68
+ return < ChevronRightIcon className = { className } /> ;
69
+ }
70
+
71
+ function CustomCollapseIcon ( { className } : Readonly < { className ?: string } > ) {
72
+ return < ExpandMoreIcon className = { className } /> ;
73
+ }
74
+
67
75
export interface TreeViewFinderNodeProps {
68
76
id : UUID ;
69
77
name : string ;
@@ -85,7 +93,7 @@ export interface TreeViewFinderProps {
85
93
selected ?: string [ ] ;
86
94
expanded ?: string [ ] ;
87
95
multiSelect ?: boolean ;
88
- classes ?: Partial < TreeViewClasses > ;
96
+ classes ?: Partial < SimpleTreeViewClasses > ;
89
97
className ?: string ;
90
98
91
99
// dialog props
@@ -99,7 +107,7 @@ export interface TreeViewFinderProps {
99
107
// data management props
100
108
onlyLeaves ?: boolean ;
101
109
data ?: TreeViewFinderNodeProps [ ] ;
102
- onTreeBrowse ?: ( nodeId : string ) => void ;
110
+ onTreeBrowse ?: ( itemId : string ) => void ;
103
111
sortMethod ?: ( a : TreeViewFinderNodeProps , b : TreeViewFinderNodeProps ) => number ;
104
112
}
105
113
@@ -175,7 +183,7 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
175
183
const isValidationDisabled = ( ) => {
176
184
return (
177
185
selected ?. length === 0 ||
178
- ( selected ?. length === selectedProp ?. length && selected ?. every ( ( nodeId ) => selectedProp ?. includes ( nodeId ) ) )
186
+ ( selected ?. length === selectedProp ?. length && selected ?. every ( ( itemId ) => selectedProp ?. includes ( itemId ) ) )
179
187
) ;
180
188
} ;
181
189
@@ -191,22 +199,22 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
191
199
} , [ ] ) ;
192
200
193
201
const findParents = (
194
- nodeId : string ,
202
+ itemId : string ,
195
203
nodes : TreeViewFinderNodeProps [ ] ,
196
204
parentPath : TreeViewFinderNodeProps [ ] = [ ]
197
205
) : TreeViewFinderNodeProps [ ] | null => {
198
206
let result : TreeViewFinderNodeProps [ ] | null = null ;
199
207
200
208
nodes . some ( ( node ) => {
201
209
// If the current node matches the selected node, set result and break
202
- if ( node . id === nodeId ) {
210
+ if ( node . id === itemId ) {
203
211
result = parentPath ;
204
212
return true ;
205
213
}
206
214
207
215
// If the current node has children, recursively search them
208
216
if ( node . children ) {
209
- const childResult = findParents ( nodeId , node . children , [ ...parentPath , node ] ) ;
217
+ const childResult = findParents ( itemId , node . children , [ ...parentPath , node ] ) ;
210
218
if ( childResult ) {
211
219
result = childResult ;
212
220
return true ;
@@ -232,34 +240,34 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
232
240
return [ ] ;
233
241
}
234
242
return selected
235
- . map ( ( nodeId ) => {
236
- const selectedNode = mapPrintedNodes [ nodeId ] ;
243
+ . map ( ( itemId ) => {
244
+ const selectedNode = mapPrintedNodes [ itemId ] ;
237
245
if ( ! selectedNode ) {
238
246
return null ;
239
247
}
240
248
241
- const parents = findParents ( nodeId , data ?? [ ] ) ;
249
+ const parents = findParents ( itemId , data ?? [ ] ) ;
242
250
243
251
return {
244
252
...selectedNode ,
245
253
parents : parents ?? [ ] ,
246
254
} ;
247
255
} )
248
- . filter ( ( node ) => node !== null ) ;
256
+ . filter ( ( node ) => node !== null ) as TreeViewFinderNodeProps [ ] ;
249
257
} ;
250
258
251
- const handleNodeToggle = ( _e : React . SyntheticEvent , nodeIds : string [ ] ) => {
259
+ const handleNodeToggle = ( _e : React . SyntheticEvent , itemIds : string [ ] ) => {
252
260
// onTreeBrowse proc only on last node clicked and only when expanded
253
- nodeIds . every ( ( nodeId ) => {
254
- if ( ! expanded ?. includes ( nodeId ) ) {
261
+ itemIds . every ( ( itemId ) => {
262
+ if ( ! expanded ?. includes ( itemId ) ) {
255
263
// proc onTreeBrowse here
256
- onTreeBrowse ?.( nodeId ) ;
264
+ onTreeBrowse ?.( itemId ) ;
257
265
return false ; // break loop to call onTreeBrowse only once
258
266
}
259
267
return true ;
260
268
} ) ;
261
269
262
- setExpanded ( nodeIds ) ;
270
+ setExpanded ( itemIds ) ;
263
271
// will proc onNodeSelect then ...
264
272
} ;
265
273
@@ -288,7 +296,7 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
288
296
// if we have selected elements by default, we scroll to it
289
297
if ( selectedProp . length > 0 && autoScrollAllowed ) {
290
298
// we check if all expanded nodes by default all already expanded first
291
- const isNodeExpanded = expandedProp ?. every ( ( nodeId ) => expanded ?. includes ( nodeId ) ) ;
299
+ const isNodeExpanded = expandedProp ?. every ( ( itemId ) => expanded ?. includes ( itemId ) ) ;
292
300
293
301
// we got the last element that we suppose to scroll to
294
302
const lastScrollRef = scrollRef . current [ scrollRef . current . length - 1 ] ;
@@ -304,11 +312,11 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
304
312
} , [ expanded , selectedProp , expandedProp , data , autoScrollAllowed ] ) ;
305
313
306
314
/* User Interaction management */
307
- const handleNodeSelect = ( _e : React . SyntheticEvent , values : string | string [ ] ) => {
315
+ const handleNodeSelect = ( _e : React . SyntheticEvent , values : string | string [ ] | null ) => {
308
316
// Default management
309
317
if ( multiSelect && Array . isArray ( values ) ) {
310
- setSelected ( values . filter ( ( nodeId ) => isSelectable ( mapPrintedNodes [ nodeId ] ) ) ) ;
311
- } else if ( ! Array . isArray ( values ) ) {
318
+ setSelected ( values . filter ( ( itemId ) => isSelectable ( mapPrintedNodes [ itemId ] ) ) ) ;
319
+ } else if ( typeof values === 'string' ) {
312
320
// Toggle selection to allow unselection
313
321
if ( selected ?. includes ( values ) ) {
314
322
setSelected ( [ ] ) ;
@@ -348,7 +356,7 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
348
356
return null ;
349
357
}
350
358
351
- if ( isSelectable ( node ) && selected ?. find ( ( nodeId ) => nodeId === node . id ) ) {
359
+ if ( isSelectable ( node ) && selected ?. find ( ( itemId ) => itemId === node . id ) ) {
352
360
return < CheckIcon className = { composeClasses ( classes , cssLabelIcon ) } /> ;
353
361
}
354
362
if ( node . icon ) {
@@ -365,36 +373,39 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
365
373
</ div >
366
374
) ;
367
375
} ;
376
+
368
377
const showChevron = ( node : TreeViewFinderNodeProps ) => {
369
- // by defaut show Chevron if childrenCount is null or undefined otherwise only if > 0
370
- return ! ! ( node . childrenCount == null || ( node . childrenCount && node . childrenCount > 0 ) ) ;
378
+ return ! ! ( node . childrenCount && node . childrenCount > 0 ) ;
371
379
} ;
372
380
373
381
const renderTree = ( node : TreeViewFinderNodeProps ) => {
374
382
if ( ! node ) {
375
383
return null ;
376
384
}
377
385
let childrenNodes = null ;
378
-
379
- if ( Array . isArray ( node . children ) ) {
380
- if ( node . children . length ) {
381
- const sortedChildren = node . children . sort ( sortMethod ) ;
382
- childrenNodes = sortedChildren . map ( ( child ) => renderTree ( child ) ) ;
383
- } else {
384
- childrenNodes = [ false ] ; // Pass non empty Array here to simulate a child then this node isn't considered as a leaf.
385
- }
386
+ const showExpandIcon = showChevron ( node ) ;
387
+ if ( Array . isArray ( node . children ) && node . children . length > 0 ) {
388
+ childrenNodes = node . children . toSorted ( sortMethod ) . map ( renderTree ) ;
389
+ } else if ( showExpandIcon ) {
390
+ childrenNodes = [ < span key = "placeholder" style = { { display : 'none' } } /> ] ; // simulate placeholder so expand icon is shown
386
391
}
387
392
return (
388
393
< TreeItem
389
394
key = { node . id }
390
- nodeId = { node . id }
395
+ itemId = { node . id }
391
396
label = { renderTreeItemLabel ( node ) }
392
- expandIcon = {
393
- showChevron ( node ) ? < ChevronRightIcon className = { composeClasses ( classes , cssIcon ) } /> : null
394
- }
395
- collapseIcon = {
396
- showChevron ( node ) ? < ExpandMoreIcon className = { composeClasses ( classes , cssIcon ) } /> : null
397
- }
397
+ slots = { {
398
+ expandIcon : CustomExpandIcon ,
399
+ collapseIcon : CustomCollapseIcon ,
400
+ } }
401
+ slotProps = { {
402
+ expandIcon : {
403
+ className : composeClasses ( classes , cssIcon ) ,
404
+ } ,
405
+ collapseIcon : {
406
+ className : composeClasses ( classes , cssIcon ) ,
407
+ } ,
408
+ } }
398
409
ref = { ( element ) => {
399
410
if ( selectedProp ?. includes ( node . id ) ) {
400
411
scrollRef . current . push ( element ) ;
@@ -445,17 +456,15 @@ function TreeViewFinderComponant(props: Readonly<TreeViewFinderProps>) {
445
456
{ contentText ?? intl . formatMessage ( { id : 'treeview_finder/contentText' } , { multiSelect } ) }
446
457
</ DialogContentText >
447
458
448
- < TreeView
449
- // Controlled props
450
- expanded = { expanded }
451
- // events
452
- onNodeToggle = { handleNodeToggle }
453
- onNodeSelect = { handleNodeSelect }
459
+ < SimpleTreeView
460
+ expandedItems = { expanded }
461
+ onExpandedItemsChange = { handleNodeToggle }
462
+ onSelectedItemsChange = { handleNodeSelect }
454
463
// Uncontrolled props
455
464
{ ...getTreeViewSelectionProps ( ) }
456
465
>
457
466
{ data && Array . isArray ( data ) ? data . sort ( sortMethod ) . map ( ( child ) => renderTree ( child ) ) : null }
458
- </ TreeView >
467
+ </ SimpleTreeView >
459
468
</ DialogContent >
460
469
< DialogActions >
461
470
< CancelButton
0 commit comments