@@ -16,6 +16,64 @@ const useStyles = createUseStyles({
1616 }
1717} )
1818
19+ // Generic helper to collect items from tree based on predicate
20+ const collectTreeItems = (
21+ items : TreeViewDataItem [ ] ,
22+ predicate : ( item : TreeViewDataItem ) => boolean ,
23+ leafOnly = false
24+ ) : TreeViewDataItem [ ] => {
25+ const collected : TreeViewDataItem [ ] = [ ] ;
26+
27+ const collect = ( item : TreeViewDataItem ) => {
28+ const isLeaf = ! item . children || item . children . length === 0 ;
29+
30+ if ( predicate ( item ) && ( ! leafOnly || isLeaf ) ) {
31+ collected . push ( item ) ;
32+ }
33+
34+ item . children ?. forEach ( child => collect ( child ) ) ;
35+ } ;
36+
37+ items . forEach ( item => collect ( item ) ) ;
38+ return collected ;
39+ } ;
40+
41+ // Helper function to get all checked items (not just leaf nodes)
42+ const getAllCheckedItems = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] =>
43+ collectTreeItems ( items , item => item . checkProps ?. checked === true , false ) ;
44+
45+ // Get all checked leaf items (returns array of names)
46+ const getAllCheckedLeafItems = ( items : TreeViewDataItem [ ] ) : string [ ] =>
47+ collectTreeItems (
48+ items ,
49+ item => item . checkProps ?. checked === true ,
50+ true
51+ ) . map ( item => String ( item . name ) ) ;
52+
53+ // Helper function to expand all nodes in the tree
54+ const expandAllNodes = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] =>
55+ items . map ( item => ( {
56+ ...item ,
57+ defaultExpanded : true ,
58+ children : item . children ? expandAllNodes ( item . children ) : undefined
59+ } ) ) ;
60+
61+ // Helper function to set pre-selected items
62+ const setPreSelectedItems = ( items : TreeViewDataItem [ ] , selectedIds : string [ ] ) : TreeViewDataItem [ ] =>
63+ items . map ( item => {
64+ const isSelected = selectedIds . includes ( String ( item . id ) ) ;
65+ const hasSelectedChildren = item . children ?. some ( child => selectedIds . includes ( String ( child . id ) ) ) ?? false ;
66+
67+ return {
68+ ...item ,
69+ checkProps : item . checkProps ? {
70+ ...item . checkProps ,
71+ checked : isSelected || hasSelectedChildren
72+ } : undefined ,
73+ children : item . children ? setPreSelectedItems ( item . children , selectedIds ) : undefined
74+ } ;
75+ } ) ;
76+
1977export interface DataViewTreeFilterProps {
2078 /** Unique key for the filter attribute */
2179 filterId : string ;
@@ -58,62 +116,11 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
58116 const isInitialMount = useRef ( true ) ;
59117 const hasCalledInitialOnChange = useRef ( false ) ;
60118
61- // Helper function to expand all nodes in the tree
62- const expandAllNodes = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] => {
63- return items . map ( item => ( {
64- ...item ,
65- defaultExpanded : true ,
66- children : item . children ? expandAllNodes ( item . children ) : undefined
67- } ) ) ;
68- } ;
69-
70- // Helper function to set pre-selected items
71- const setPreSelectedItems = ( items : TreeViewDataItem [ ] , selectedIds : string [ ] ) : TreeViewDataItem [ ] => {
72- return items . map ( item => {
73- const isSelected = selectedIds . includes ( String ( item . id ) ) ;
74- const hasSelectedChildren = item . children ?. some ( child => selectedIds . includes ( String ( child . id ) ) ) ?? false ;
75-
76- return {
77- ...item ,
78- checkProps : item . checkProps ? {
79- ...item . checkProps ,
80- checked : isSelected || hasSelectedChildren
81- } : undefined ,
82- children : item . children ? setPreSelectedItems ( item . children , selectedIds ) : undefined
83- } ;
84- } ) ;
85- } ;
86-
87- // Generic helper to collect items from tree based on predicate
88- const collectTreeItems = (
89- items : TreeViewDataItem [ ] ,
90- predicate : ( item : TreeViewDataItem ) => boolean ,
91- leafOnly = false
92- ) : TreeViewDataItem [ ] => {
93- const collected : TreeViewDataItem [ ] = [ ] ;
94-
95- const collect = ( item : TreeViewDataItem ) => {
96- const isLeaf = ! item . children || item . children . length === 0 ;
97-
98- if ( predicate ( item ) && ( ! leafOnly || isLeaf ) ) {
99- collected . push ( item ) ;
100- }
101-
102- item . children ?. forEach ( child => collect ( child ) ) ;
103- } ;
104-
105- items . forEach ( item => collect ( item ) ) ;
106- return collected ;
107- } ;
108-
109- // Helper function to get all checked items (not just leaf nodes)
110- const getAllCheckedItems = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] => {
111- return collectTreeItems ( items , item => item . checkProps ?. checked === true , false ) ;
112- } ;
113-
114119 // Initialize tree data with defaultExpanded and defaultSelected (only on first mount)
115120 useEffect ( ( ) => {
116- if ( ! items ) return ;
121+ if ( ! items ) {
122+ return ;
123+ }
117124
118125 let initializedData = [ ...items ] ;
119126
@@ -160,21 +167,28 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
160167
161168 // Sync tree checkboxes when value prop changes (when clearAllFilters is called)
162169 useEffect ( ( ) => {
163- if ( value . length === 0 && treeData . length > 0 ) {
164- const currentCheckedItems = getAllCheckedLeafItems ( treeData ) ;
165-
166- // Only update if there are checked items that need to be unchecked
167- if ( currentCheckedItems . length > 0 ) {
168- const uncheckRecursive = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] => {
169- return items . map ( item => ( {
170- ...item ,
171- checkProps : item . checkProps ? { ...item . checkProps , checked : false } : undefined ,
172- children : item . children ? uncheckRecursive ( item . children ) : undefined
173- } ) ) ;
174- } ;
175-
176- setTreeData ( uncheckRecursive ( treeData ) ) ;
177- }
170+ if ( value . length === 0 ) {
171+ setTreeData ( currentTreeData => {
172+ if ( currentTreeData . length === 0 ) {
173+ return currentTreeData ;
174+ }
175+
176+ const currentCheckedItems = getAllCheckedLeafItems ( currentTreeData ) ;
177+
178+ // Only update if there are checked items that need to be unchecked
179+ if ( currentCheckedItems . length > 0 ) {
180+ const uncheckRecursive = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] =>
181+ items . map ( item => ( {
182+ ...item ,
183+ checkProps : item . checkProps ? { ...item . checkProps , checked : false } : undefined ,
184+ children : item . children ? uncheckRecursive ( item . children ) : undefined
185+ } ) ) ;
186+
187+ return uncheckRecursive ( currentTreeData ) ;
188+ }
189+
190+ return currentTreeData ;
191+ } ) ;
178192 }
179193 } , [ value ] ) ;
180194
@@ -202,7 +216,9 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
202216 }
203217 if ( item . children ) {
204218 const found = findItemByName ( item . children , name ) ;
205- if ( found ) return found ;
219+ if ( found ) {
220+ return found ;
221+ }
206222 }
207223 }
208224 return null ;
@@ -216,31 +232,32 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
216232 }
217233 if ( item . children ) {
218234 const found = findParentById ( item . children , childId ) ;
219- if ( found ) return found ;
235+ if ( found ) {
236+ return found ;
237+ }
220238 }
221239 }
222240 return null ;
223241 } ;
224242
225- // Get all checked leaf items (returns array of names)
226- const getAllCheckedLeafItems = ( items : TreeViewDataItem [ ] ) : string [ ] => {
227- return collectTreeItems (
228- items ,
229- item => item . checkProps ?. checked === true ,
230- true
231- ) . map ( item => String ( item . name ) ) ;
232- } ;
233-
234243 // Update parent checkbox states based on children (recursive)
235244 const onCheckParentHandle = ( childId : string ) : void => {
236245 const parent = findParentById ( treeData , childId ) ;
237- if ( ! parent ) return ;
246+ if ( ! parent ) {
247+ return ;
248+ }
238249
239250 if ( parent . checkProps ) {
240251 const allChildrenChecked = areAllChildrenChecked ( parent ) ;
241252 const someChildrenChecked = areSomeChildrenChecked ( parent ) ;
242253
243- parent . checkProps . checked = allChildrenChecked ? true : someChildrenChecked ? null : false ;
254+ if ( allChildrenChecked ) {
255+ parent . checkProps . checked = true ;
256+ } else if ( someChildrenChecked ) {
257+ parent . checkProps . checked = null ;
258+ } else {
259+ parent . checkProps . checked = false ;
260+ }
244261 }
245262
246263 if ( parent . id ) {
@@ -281,7 +298,9 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
281298 // Clear a specific filter by name (when label chip is removed)
282299 const onFilterSelectorClear = ( itemName : string ) => {
283300 const treeViewItem = findItemByName ( treeData , itemName ) ;
284- if ( ! treeViewItem ) return ;
301+ if ( ! treeViewItem ) {
302+ return ;
303+ }
285304
286305 onCheckHandle ( treeViewItem , false ) ;
287306 if ( treeViewItem . id ) {
@@ -291,13 +310,12 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
291310
292311 // Uncheck all items in the tree
293312 const uncheckAllItems = ( ) => {
294- const uncheckRecursive = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] => {
295- return items . map ( item => ( {
313+ const uncheckRecursive = ( items : TreeViewDataItem [ ] ) : TreeViewDataItem [ ] =>
314+ items . map ( item => ( {
296315 ...item ,
297316 checkProps : item . checkProps ? { ...item . checkProps , checked : false } : undefined ,
298317 children : item . children ? uncheckRecursive ( item . children ) : undefined
299318 } ) ) ;
300- } ;
301319
302320 const updatedTreeData = uncheckRecursive ( treeData ) ;
303321 setTreeData ( updatedTreeData ) ;
@@ -329,19 +347,19 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
329347
330348 return (
331349 < ToolbarFilter
332- key = { filterId }
333- data-ouia-component-id = { ouiaId }
334- labels = { value . map ( item => ( { key : item , node : item } ) ) }
335- deleteLabel = { ( _ , label ) => {
336- const labelKey = typeof label === 'string' ? label : label . key ;
337- onChange ?.( undefined , value . filter ( item => item !== labelKey ) ) ;
338- onFilterSelectorClear ( labelKey ) ;
339- } }
340- deleteLabelGroup = { uncheckAllItems }
341- categoryName = { title }
342- showToolbarItem = { showToolbarItem } >
343- { dropdown }
344- </ ToolbarFilter >
350+ key = { filterId }
351+ data-ouia-component-id = { ouiaId }
352+ labels = { value . map ( item => ( { key : item , node : item } ) ) }
353+ deleteLabel = { ( _ , label ) => {
354+ const labelKey = typeof label === 'string' ? label : label . key ;
355+ onChange ?.( undefined , value . filter ( item => item !== labelKey ) ) ;
356+ onFilterSelectorClear ( labelKey ) ;
357+ } }
358+ deleteLabelGroup = { uncheckAllItems }
359+ categoryName = { title }
360+ showToolbarItem = { showToolbarItem } >
361+ { dropdown }
362+ </ ToolbarFilter >
345363 )
346364}
347365
0 commit comments