@tap-payments/os-micro-frontend-shared 0.3.255 → 0.3.256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/build/_chunks/{chunk-5B3BM4HA.js → chunk-24X3BEJR.js} +1 -1
  2. package/build/_chunks/{chunk-YXRP3YVA.js → chunk-2FRVCEJY.js} +1 -1
  3. package/build/_chunks/chunk-2GSUGMI7.js +2 -0
  4. package/build/_chunks/{chunk-6AISJMRA.js → chunk-2J4W2CAI.js} +1 -1
  5. package/build/_chunks/{chunk-SFQF3NEZ.js → chunk-2QA5MXMV.js} +1 -1
  6. package/build/_chunks/{chunk-FXLC7QM6.js → chunk-3GA46JLW.js} +1 -1
  7. package/build/_chunks/{chunk-5CIBM232.js → chunk-4FOFVTQ2.js} +1 -1
  8. package/build/_chunks/{chunk-FEZOSRP2.js → chunk-4Y2COEZC.js} +1 -1
  9. package/build/_chunks/{chunk-7PCMUVPG.js → chunk-5RPLYU74.js} +1 -1
  10. package/build/_chunks/{chunk-BLPTYUL5.js → chunk-5VSOWOX2.js} +1 -1
  11. package/build/_chunks/chunk-6PLO2VOU.js +1 -0
  12. package/build/_chunks/{chunk-3ARGSIW6.js → chunk-6Q5NWGGT.js} +1 -1
  13. package/build/_chunks/{chunk-UDF64CIJ.js → chunk-6SBWV2KY.js} +1 -1
  14. package/build/_chunks/{chunk-467AH657.js → chunk-6WVAYA6W.js} +1 -1
  15. package/build/_chunks/chunk-7GJMLJ2X.js +1 -0
  16. package/build/_chunks/{chunk-LPVUMEPP.js → chunk-7UHNYLD7.js} +1 -1
  17. package/build/_chunks/{chunk-VTM5XAA4.js → chunk-ACIQTR6D.js} +1 -1
  18. package/build/_chunks/{chunk-TP36G4JZ.js → chunk-AO5K32SU.js} +1 -1
  19. package/build/_chunks/{chunk-BVZCPC4B.js → chunk-BDL56M7A.js} +1 -1
  20. package/build/_chunks/{chunk-MZ4CDPE6.js → chunk-C4ZTNHLR.js} +1 -1
  21. package/build/_chunks/{chunk-BNHPA7NL.js → chunk-CAZWVRDN.js} +1 -1
  22. package/build/_chunks/{chunk-USHKRLP3.js → chunk-CK64PBMR.js} +1 -1
  23. package/build/_chunks/{chunk-J7KCH7KM.js → chunk-CWNDTPM5.js} +1 -1
  24. package/build/_chunks/chunk-CZWXKHO7.js +1 -0
  25. package/build/_chunks/{chunk-N4CCG67Z.js → chunk-DFH5QS2C.js} +1 -1
  26. package/build/_chunks/{chunk-6JO37S7T.js → chunk-DN3GVPG7.js} +1 -1
  27. package/build/_chunks/{chunk-G7FUKYC4.js → chunk-DRBZWQC7.js} +1 -1
  28. package/build/_chunks/chunk-DRWSBTEH.js +1 -0
  29. package/build/_chunks/{chunk-6PNJ5QPQ.js → chunk-ECXZJHTU.js} +1 -1
  30. package/build/_chunks/{chunk-NUJYDXCY.js → chunk-ETTNSCF2.js} +1 -1
  31. package/build/_chunks/{chunk-53FTAIGE.js → chunk-F7TSJOX6.js} +1 -1
  32. package/build/_chunks/{chunk-LHPUWQK3.js → chunk-FGZVDVG2.js} +1 -1
  33. package/build/_chunks/{chunk-TXVDWYET.js → chunk-G5OEG7OX.js} +1 -1
  34. package/build/_chunks/{chunk-LSJ4DU5A.js → chunk-HKFOCHKQ.js} +1 -1
  35. package/build/_chunks/{chunk-667MADJD.js → chunk-IO5DNKRE.js} +1 -1
  36. package/build/_chunks/{chunk-ZDRR6O4Q.js → chunk-IQUQQVHU.js} +1 -1
  37. package/build/_chunks/{chunk-URDUQGSO.js → chunk-IULAG2J7.js} +1 -1
  38. package/build/_chunks/{chunk-EOEZUZCE.js → chunk-IUN4DEUF.js} +1 -1
  39. package/build/_chunks/{chunk-U7BM5XPI.js → chunk-IXZBLV5N.js} +1 -1
  40. package/build/_chunks/{chunk-IDIANGVR.js → chunk-IZLK35VJ.js} +2 -2
  41. package/build/_chunks/{chunk-KGG6V7HL.js → chunk-J2QN5EF5.js} +1 -1
  42. package/build/_chunks/{chunk-2FZQAWTT.js → chunk-J3CPRU2X.js} +1 -1
  43. package/build/_chunks/{chunk-YQKWAH6Y.js → chunk-JC7NZ5DQ.js} +1 -1
  44. package/build/_chunks/{chunk-ECCMCTOM.js → chunk-JLL4EOYJ.js} +1 -1
  45. package/build/_chunks/{chunk-M6YVU7V3.js → chunk-JNIXJXK3.js} +1 -1
  46. package/build/_chunks/{chunk-PGMH7MPJ.js → chunk-JO7EW2A3.js} +1 -1
  47. package/build/_chunks/{chunk-DJB73WJX.js → chunk-LBQWWXKH.js} +1 -1
  48. package/build/_chunks/{chunk-CUGWXBZN.js → chunk-LLOK2FPM.js} +1 -1
  49. package/build/_chunks/{chunk-BHQDWVM3.js → chunk-LNEWGSQE.js} +1 -1
  50. package/build/_chunks/chunk-M6JX6YSB.js +1 -0
  51. package/build/_chunks/{chunk-PCY4RMGU.js → chunk-MAPBEJDO.js} +1 -1
  52. package/build/_chunks/{chunk-N63SPB7J.js → chunk-MVWBQNBX.js} +1 -1
  53. package/build/_chunks/{chunk-QLBJFLHC.js → chunk-NVNFBBM3.js} +1 -1
  54. package/build/_chunks/{chunk-SQ6X7XQI.js → chunk-OGARNN6I.js} +1 -1
  55. package/build/_chunks/{chunk-2QYAHYYH.js → chunk-OQA73I3Z.js} +1 -1
  56. package/build/_chunks/{chunk-TSI47ZRD.js → chunk-PEV5GRRD.js} +1 -1
  57. package/build/_chunks/{chunk-NU6TMNMX.js → chunk-PGG4PIRO.js} +1 -1
  58. package/build/_chunks/{chunk-7EBCOZWQ.js → chunk-PIO2RM2Z.js} +1 -1
  59. package/build/_chunks/{chunk-KP2UMMK5.js → chunk-Q7C7A5RK.js} +1 -1
  60. package/build/_chunks/chunk-REMAOTWV.js +2 -0
  61. package/build/_chunks/{chunk-WX3CZG6V.js → chunk-RRBFJGS6.js} +1 -1
  62. package/build/_chunks/{chunk-OGUM23ZV.js → chunk-SKJZPJBN.js} +1 -1
  63. package/build/_chunks/{chunk-6QJCOTT4.js → chunk-SPZGODAQ.js} +1 -1
  64. package/build/_chunks/chunk-UFOU3EJK.js +1 -0
  65. package/build/_chunks/chunk-UOERDOEI.js +1 -0
  66. package/build/_chunks/{chunk-OTAHIU6Z.js → chunk-UZIT72EG.js} +1 -1
  67. package/build/_chunks/chunk-VTFUWYYT.js +1 -0
  68. package/build/_chunks/{chunk-Q2XPHAIX.js → chunk-X26MSUES.js} +1 -1
  69. package/build/_chunks/{chunk-QCVRHMCL.js → chunk-XKMKTJWE.js} +1 -1
  70. package/build/_chunks/{chunk-E7OZE7FK.js → chunk-Y2HHEHH2.js} +2 -2
  71. package/build/_chunks/{chunk-GRNUANVM.js → chunk-YAO5SBRE.js} +1 -1
  72. package/build/_chunks/chunk-YLVW27RP.js +2 -0
  73. package/build/components/ActionsDropdownMenu/index.js +1 -1
  74. package/build/components/Alert/index.js +1 -1
  75. package/build/components/AnimatedSpinnerIcon/index.js +1 -1
  76. package/build/components/AnimatedSpinnerIcon/style.d.ts +1 -1
  77. package/build/components/AppServices/index.js +1 -1
  78. package/build/components/AppServicesBar/index.js +1 -1
  79. package/build/components/AppWindowWrapper/AppWindow/AppWindow.d.ts +2 -1
  80. package/build/components/AppWindowWrapper/AppWindowWrapper.d.ts +1 -1
  81. package/build/components/AppWindowWrapper/index.js +1 -1
  82. package/build/components/AppWindowWrapper/type.d.ts +0 -6
  83. package/build/components/AreaChart/index.js +1 -1
  84. package/build/components/Autocomplete/index.js +1 -1
  85. package/build/components/AvatarStack/index.js +1 -1
  86. package/build/components/Calender/index.js +1 -1
  87. package/build/components/Card/index.js +1 -1
  88. package/build/components/ColorPicker/index.js +1 -1
  89. package/build/components/ColumnViews/index.js +1 -1
  90. package/build/components/ConfirmDialog/index.js +1 -1
  91. package/build/components/CountryFilter/index.js +1 -1
  92. package/build/components/CountryFlag/index.js +1 -1
  93. package/build/components/CurrenciesMenu/index.js +1 -1
  94. package/build/components/CurrencyAmount/index.js +1 -1
  95. package/build/components/CurrencyAmountInput/index.js +1 -1
  96. package/build/components/CurrencySymbol/index.js +1 -1
  97. package/build/components/DateFilter/index.js +1 -1
  98. package/build/components/DeviceIcon/index.js +1 -1
  99. package/build/components/DropdownMenu/index.js +1 -1
  100. package/build/components/EmptyState/index.js +1 -1
  101. package/build/components/ErrorToast/index.js +1 -1
  102. package/build/components/FileDropbox/index.js +1 -1
  103. package/build/components/FilePreview/index.js +1 -1
  104. package/build/components/FileUploader/index.js +1 -1
  105. package/build/components/FilterChip/FilterChip.d.ts +4 -0
  106. package/build/components/FilterChip/constants.d.ts +15 -0
  107. package/build/components/FilterChip/icons.d.ts +4 -0
  108. package/build/components/FilterChip/index.d.ts +4 -0
  109. package/build/components/FilterChip/index.js +1 -0
  110. package/build/components/FilterChip/style.d.ts +8 -0
  111. package/build/components/FilterChip/type.d.ts +43 -0
  112. package/build/components/FilterDropdown/index.js +1 -1
  113. package/build/components/Filters/index.js +1 -1
  114. package/build/components/FlippingCard/index.js +1 -1
  115. package/build/components/FormSelect/index.js +1 -1
  116. package/build/components/GroupedSelect/index.js +1 -1
  117. package/build/components/HeatMap/index.js +1 -1
  118. package/build/components/InputBase/index.js +1 -1
  119. package/build/components/Inputs/index.js +1 -1
  120. package/build/components/JSONViewer/components/JSONSearchBar/styles.d.ts +2 -2
  121. package/build/components/JSONViewer/index.js +1 -1
  122. package/build/components/LeftPeekRightExpandingChip/index.js +1 -1
  123. package/build/components/ListColumnFilter/index.js +1 -1
  124. package/build/components/ListHeader/index.js +1 -1
  125. package/build/components/ListHeader/types.d.ts +0 -2
  126. package/build/components/ListLayout/index.js +1 -1
  127. package/build/components/Loader/index.js +1 -1
  128. package/build/components/Logo/index.js +1 -1
  129. package/build/components/MFWidgetLoader/index.js +1 -1
  130. package/build/components/MaskedText/index.js +1 -1
  131. package/build/components/MultiSelect/index.js +1 -1
  132. package/build/components/NestedDropdown/index.js +1 -1
  133. package/build/components/NestedMultiSelect/index.js +1 -1
  134. package/build/components/PaymentSourceFilter/index.js +1 -1
  135. package/build/components/PhoneFilter/index.js +1 -1
  136. package/build/components/PhoneInput/index.js +1 -1
  137. package/build/components/ProgressRing/index.js +1 -1
  138. package/build/components/RadioGroup/index.js +1 -1
  139. package/build/components/RangeCalender/index.js +1 -1
  140. package/build/components/RatingCount/index.js +1 -1
  141. package/build/components/ReferenceTypeFilter/index.js +1 -1
  142. package/build/components/RightLeftExpandingCenterChip/index.js +1 -1
  143. package/build/components/SearchButton/SearchButton.d.ts +7 -2
  144. package/build/components/SearchButton/index.d.ts +2 -3
  145. package/build/components/SearchButton/index.js +1 -1
  146. package/build/components/SearchButton/{style.d.ts → styles.d.ts} +173 -173
  147. package/build/components/SearchInput/index.js +1 -1
  148. package/build/components/SheetViewChip/index.js +1 -1
  149. package/build/components/Sidebar/index.js +1 -1
  150. package/build/components/StatusBar/index.js +1 -1
  151. package/build/components/StatusButton/index.js +1 -1
  152. package/build/components/StatusFilter/index.js +1 -1
  153. package/build/components/StatusIcons/index.js +1 -1
  154. package/build/components/StatusLabel/index.js +1 -1
  155. package/build/components/TableCells/CustomCells/ActionCell/style.d.ts +1 -1
  156. package/build/components/TableCells/CustomCells/PayoutReportCell/style.d.ts +1 -1
  157. package/build/components/TableCells/CustomCells/StatusCell/style.d.ts +1 -1
  158. package/build/components/TableCells/index.js +1 -1
  159. package/build/components/TableReports/index.js +1 -1
  160. package/build/components/TextInput/index.js +1 -1
  161. package/build/components/Timepicker/index.js +1 -1
  162. package/build/components/ToggleView/index.js +1 -1
  163. package/build/components/Toolbar/index.js +1 -1
  164. package/build/components/ToolbarIcon/index.js +1 -1
  165. package/build/components/TreeDropdown/index.js +1 -1
  166. package/build/components/VerificationIcon/index.js +1 -1
  167. package/build/components/ViewColumnRadioFilter/index.js +1 -1
  168. package/build/components/VirtualTables/components/style.d.ts +1 -1
  169. package/build/components/VirtualTables/index.js +1 -1
  170. package/build/components/Widget/index.js +1 -1
  171. package/build/components/index.d.ts +2 -2
  172. package/build/components/index.js +1 -1
  173. package/build/components/primitives/index.js +1 -1
  174. package/build/constants/assets.d.ts +0 -3
  175. package/build/constants/index.js +1 -1
  176. package/build/datetime/components/index.js +1 -1
  177. package/build/hooks/index.js +1 -1
  178. package/build/index.js +1 -1
  179. package/build/utils/index.js +1 -1
  180. package/package.json +3 -3
  181. package/build/_chunks/chunk-AGUMSHB4.js +0 -1
  182. package/build/_chunks/chunk-C2QLJW5U.js +0 -1
  183. package/build/_chunks/chunk-GUU2BANS.js +0 -1
  184. package/build/_chunks/chunk-IDPTEI5C.js +0 -1
  185. package/build/_chunks/chunk-IRIZPPXM.js +0 -1
  186. package/build/_chunks/chunk-QK77GKMF.js +0 -2
  187. package/build/_chunks/chunk-RNBTIRD7.js +0 -2
  188. package/build/_chunks/chunk-SNMSU243.js +0 -1
  189. package/build/_chunks/chunk-T6FSTHWB.js +0 -2
  190. package/build/_chunks/chunk-UVXP2WAT.js +0 -1
  191. package/build/_chunks/chunk-VKTXI56O.js +0 -1
  192. package/build/_chunks/chunk-ZTZOHMJ3.js +0 -1
  193. package/build/components/ActiveFilterChip/ActiveFilterChip.d.ts +0 -5
  194. package/build/components/ActiveFilterChip/index.d.ts +0 -3
  195. package/build/components/ActiveFilterChip/index.js +0 -1
  196. package/build/components/ActiveFilterChip/styles.d.ts +0 -8
  197. package/build/components/ActiveFilterChip/types.d.ts +0 -7
  198. package/build/components/AppWindowWrapper/AppWindow/nestingContext.d.ts +0 -3
  199. package/build/components/SearchButton/constants.d.ts +0 -37
  200. package/build/components/SearchButton/icons.d.ts +0 -3
  201. package/build/components/SearchButton/type.d.ts +0 -28
  202. package/docs/README.md +0 -40
  203. package/src/components/TreeDropdown/README.md +0 -604
  204. package/src/components/primitives/README.md +0 -507
@@ -1,604 +0,0 @@
1
- # TreeDropdown Component
2
-
3
- A powerful, flexible tree dropdown component for React that supports hierarchical data structures with multi-select, single-select, search functionality, and customizable rendering.
4
-
5
- ## Overview
6
-
7
- The `TreeDropdown` component provides a nested menu structure that can display hierarchical data in a dropdown format. It supports both single and multiple selection modes, includes built-in search capabilities, and allows for extensive customization through props and render functions.
8
-
9
- ## Features
10
-
11
- - 🌳 **Hierarchical Tree Structure**: Display nested data with unlimited depth
12
- - ✅ **Multiple Selection Modes**: Support for both single and multiple selection
13
- - 🔍 **Built-in Search**: Filter tree items with debounced search — simple field matching via `getValues`, or custom matching with subtree pruning via `filterItem`
14
- - 🎨 **Customizable Rendering**: Custom render functions for items, headers, and footers
15
- - 📦 **State Management**: Automatic state management with parent-child relationship handling
16
- - 🎯 **Indeterminate States**: Visual indication for partially selected parent nodes
17
- - 🔄 **Auto-promotion**: Automatically promotes single-child nodes to reduce nesting
18
- - 🎭 **Icon Support**: Built-in support for icons and custom icon components
19
- - 📱 **Responsive**: Works with Material-UI's Popper for positioning
20
- - 📊 **Level-based Queries**: Get selected nodes grouped by tree level with filtering options
21
-
22
- ## Installation
23
-
24
- The component is part of the shared components library. Import it as follows:
25
-
26
- ```typescript
27
- import TreeDropdown from '@components/TreeDropdown'
28
- import type { TreeDropdownProps, TreeDropdownRef, TreeNode, TreeDefinition, SelectedState } from '@components/TreeDropdown'
29
-
30
- // Optional: Import GetNodesByLevelOptions if you need to type variables
31
- // import type { GetNodesByLevelOptions } from '@components/TreeDropdown/utils'
32
- ```
33
-
34
- ## Basic Usage
35
-
36
- ```tsx
37
- import { useState, useRef } from 'react'
38
- import { Button } from '@mui/material'
39
- import TreeDropdown from '@components/TreeDropdown'
40
- import type { TreeDropdownRef, TreeDefinition } from '@components/TreeDropdown'
41
-
42
- const MyComponent = () => {
43
- const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
44
- const treeRef = useRef<TreeDropdownRef>(null)
45
-
46
- const treeDefinition: TreeDefinition = {
47
- children: [
48
- {
49
- id: '1',
50
- label: 'Category 1',
51
- children: [
52
- {
53
- id: '1-1',
54
- label: 'Subcategory 1',
55
- children: [
56
- { id: '1-1-1', label: 'Item 1' },
57
- { id: '1-1-2', label: 'Item 2' },
58
- ],
59
- },
60
- ],
61
- },
62
- ],
63
- }
64
-
65
- return (
66
- <>
67
- <Button onClick={(e) => setAnchorEl(e.currentTarget)}>Open Tree</Button>
68
- <TreeDropdown
69
- ref={treeRef}
70
- anchorEl={anchorEl}
71
- treeDefinition={treeDefinition}
72
- multiple={true}
73
- onChange={(tree) => console.log('Selected:', tree)}
74
- />
75
- </>
76
- )
77
- }
78
- ```
79
-
80
- ## Props
81
-
82
- ### TreeDropdownProps
83
-
84
- | Prop | Type | Default | Description |
85
- | ------------------------ | ---------------------------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------- |
86
- | `anchorEl` | `HTMLElement \| null` | **Required** | The element that the dropdown will anchor to |
87
- | `treeDefinition` | `TreeDefinition` | **Required** | The tree structure definition |
88
- | `onChange` | `(tree: TreeNode[]) => void` | `undefined` | Callback fired when selection changes |
89
- | `multiple` | `boolean` | `true` | Enable multiple selection mode |
90
- | `placement` | `PopperProps['placement']` | `'bottom-start'` | Placement of the dropdown relative to anchor |
91
- | `sx` | `SxProps<Theme>` | `undefined` | Material-UI sx prop for styling |
92
- | `IconComponent` | `React.ComponentType<{ fileId: string; width?: number \| string; height?: number \| string }>` | `undefined` | Custom component for rendering icons |
93
- | `exposeOnlySelectedTree` | `boolean` | `false` | When `true`, `onChange` callback receives only selected branches instead of the entire tree |
94
-
95
- ## Types
96
-
97
- ### TreeNode
98
-
99
- The basic node structure in the tree:
100
-
101
- ```typescript
102
- type TreeNode = {
103
- id: string // Unique identifier (required)
104
- label?: React.ReactNode // Display label
105
- hintText?: React.ReactNode // Additional hint text below label
106
- icon?: TreeNodeIcon // Icon definition
107
- iconComponent?: React.ReactNode // Custom icon component
108
- itemProps?: MenuItemProps // Props passed to underlying MenuItem
109
- renderItem?: (params: {
110
- nodeProps: TreeNode
111
- checked?: boolean
112
- indeterminate?: boolean
113
- onSelect: (e: React.ChangeEvent<HTMLInputElement>) => void
114
- }) => React.ReactNode // Custom render function
115
- } & TreeDefinition // Note: status is part of TreeDefinition, not directly on TreeNode
116
- ```
117
-
118
- ### TreeDefinition
119
-
120
- Configuration for the tree structure:
121
-
122
- ```typescript
123
- type TreeDefinition = {
124
- header?: React.ReactNode // Header content for this level
125
- footer?: React.ReactNode // Footer content for this level
126
- levelKey?: string // Unique key for this level (appears as data attribute)
127
- searchProps?: {
128
- getValues?: (item: TreeNode) => string[] // Simple filtering: strings to match against (case-insensitive `includes`)
129
- filterItem?: (item: TreeNode, searchText: string) => TreeNode | null // Advanced: keep/drop AND reshape a node
130
- onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
131
- noResultsText?: React.ReactNode
132
- inputProps?: InputProps
133
- }
134
- children?: TreeNode[] // Child nodes
135
- disablePromotion?: boolean // Disable auto-promotion of single children
136
- status?: SelectedState // Initial selection state for this level
137
- }
138
- ```
139
-
140
- ### TreeDropdownRef
141
-
142
- Methods exposed via the ref:
143
-
144
- ```typescript
145
- type TreeDropdownRef = {
146
- internalMapper: TreeMapper // Internal state mapper
147
- getTree: () => TreeNode[] // Get the entire tree with current states
148
- getSelectedBranches: () => TreeNode[] // Get only selected branches
149
- getSelectedNodesByLevel: (options?: GetNodesByLevelOptions) => Record<number, TreeNode[]> // Get selected nodes grouped by level
150
- }
151
- ```
152
-
153
- ### GetNodesByLevelOptions
154
-
155
- Options for the `getSelectedNodesByLevel` method:
156
-
157
- ```typescript
158
- type GetNodesByLevelOptions = {
159
- filter?: (node: TreeNode) => boolean // Optional filter function to include/exclude nodes
160
- includeChildren?: boolean // When true, includes children in the result; when false, excludes them (default: false)
161
- }
162
- ```
163
-
164
- ### SelectedState
165
-
166
- Enumeration of selection states:
167
-
168
- ```typescript
169
- enum SelectedState {
170
- CHECKED = 'CHECKED',
171
- UNCHECKED = 'UNCHECKED',
172
- INDETERMINATE = 'INDETERMINATE',
173
- }
174
- ```
175
-
176
- ## Usage Examples
177
-
178
- ### Single Selection Mode
179
-
180
- ```tsx
181
- <TreeDropdown
182
- anchorEl={anchorEl}
183
- treeDefinition={treeDefinition}
184
- multiple={false}
185
- onChange={(tree) => {
186
- // Only one item can be selected at a time
187
- console.log('Selected:', tree)
188
- }}
189
- />
190
- ```
191
-
192
- ### With Search
193
-
194
- A level renders a search input whenever its `searchProps` is set. Matching is driven by one of two mutually exclusive hooks:
195
-
196
- #### `getValues` — simple field matching
197
-
198
- Return the strings a node should be matched against. The component does the matching for you (case-insensitive `includes`) and keeps or drops each item unchanged.
199
-
200
- ```tsx
201
- const treeDefinition: TreeDefinition = {
202
- searchProps: {
203
- getValues: (item) => [typeof item.label === 'string' ? item.label : '', typeof item.hintText === 'string' ? item.hintText : ''],
204
- noResultsText: 'No items found',
205
- inputProps: {
206
- placeholder: 'Search items...',
207
- },
208
- },
209
- children: [
210
- /* ... */
211
- ],
212
- }
213
- ```
214
-
215
- #### `filterItem` — custom matching + subtree pruning
216
-
217
- Use `filterItem` when you need custom match logic, or when a match on a parent should show only the matching branches underneath it. You receive the node and the (already lowercased) search text, and return:
218
-
219
- - the node (optionally with a **modified `children`** array) to keep it, or
220
- - `null` to drop it.
221
-
222
- `filterItem` takes precedence over `getValues` when both are provided.
223
-
224
- ```tsx
225
- // Keep a node if its label matches; otherwise prune its subtree down to
226
- // only the branches that lead to a matching leaf.
227
- const pruneToMatch = (node: TreeNode, text: string): TreeNode | null => {
228
- if (!node.children?.length) {
229
- return node.id.toLowerCase().includes(text) ? node : null
230
- }
231
- const children = node.children.map((c) => pruneToMatch(c, text)).filter(Boolean) as TreeNode[]
232
- return children.length ? { ...node, children } : null
233
- }
234
-
235
- const treeDefinition: TreeDefinition = {
236
- searchProps: {
237
- filterItem: (item, searchText) => {
238
- const label = typeof item.label === 'string' ? item.label.toLowerCase() : ''
239
- if (label.includes(searchText)) return item // parent match → keep whole subtree
240
- return pruneToMatch(item, searchText) // otherwise → keep only matching branches
241
- },
242
- noResultsText: 'No items found',
243
- },
244
- children: [
245
- /* ... */
246
- ],
247
- }
248
- ```
249
-
250
- > **Tip:** When pruning, consider stripping `searchProps` from levels that no longer exceed your "show search" threshold, so a search box isn't rendered over a single remaining result.
251
-
252
- ### With Headers and Footers
253
-
254
- ```tsx
255
- const treeDefinition: TreeDefinition = {
256
- header: <div>Category Header</div>,
257
- footer: <div>Category Footer</div>,
258
- children: [
259
- {
260
- id: '1',
261
- label: 'Category 1',
262
- header: <div>Subcategory Header</div>,
263
- footer: <div>Subcategory Footer</div>,
264
- children: [
265
- /* ... */
266
- ],
267
- },
268
- ],
269
- }
270
- ```
271
-
272
- ### With Icons
273
-
274
- ```tsx
275
- // Using icon property
276
- const treeDefinition: TreeDefinition = {
277
- children: [
278
- {
279
- id: '1',
280
- label: 'Item with Icon',
281
- icon: {
282
- fileId: 'icon-id',
283
- width: 20,
284
- height: 20,
285
- },
286
- },
287
- ],
288
- }
289
-
290
- // With custom IconComponent
291
- <TreeDropdown
292
- anchorEl={anchorEl}
293
- treeDefinition={treeDefinition}
294
- IconComponent={({ fileId, width, height }) => (
295
- <img src={`/icons/${fileId}.svg`} width={width} height={height} />
296
- )}
297
- />
298
- ```
299
-
300
- ### Custom Item Rendering
301
-
302
- ```tsx
303
- const treeDefinition: TreeDefinition = {
304
- children: [
305
- {
306
- id: '1',
307
- label: 'Custom Item',
308
- renderItem: ({ nodeProps, checked, indeterminate, onSelect }) => (
309
- <div style={{ padding: '8px' }}>
310
- <input type="checkbox" checked={checked} onChange={onSelect} />
311
- <span>{nodeProps.label}</span>
312
- {indeterminate && <span> (partial)</span>}
313
- </div>
314
- ),
315
- },
316
- ],
317
- }
318
- ```
319
-
320
- ### Using Ref Methods
321
-
322
- ```tsx
323
- const treeRef = useRef<TreeDropdownRef>(null)
324
-
325
- // Get entire tree with current states
326
- const handleGetTree = () => {
327
- const tree = treeRef.current?.getTree()
328
- console.log('Full tree:', tree)
329
- }
330
-
331
- // Get only selected branches
332
- const handleGetSelected = () => {
333
- const selected = treeRef.current?.getSelectedBranches()
334
- console.log('Selected branches:', selected)
335
- }
336
-
337
- // Get selected nodes grouped by level
338
- const handleGetByLevel = () => {
339
- const nodesByLevel = treeRef.current?.getSelectedNodesByLevel({
340
- filter: (node) => node.status === SelectedState.CHECKED, // Only fully checked nodes
341
- includeChildren: false, // Exclude children from the result
342
- })
343
- console.log('Nodes by level:', nodesByLevel)
344
- // Result: { 0: [TreeNode, ...], 1: [TreeNode, ...], ... }
345
- }
346
-
347
- ;<TreeDropdown ref={treeRef} anchorEl={anchorEl} treeDefinition={treeDefinition} />
348
- ```
349
-
350
- ### Get Selected Nodes by Level
351
-
352
- The `getSelectedNodesByLevel` method allows you to retrieve selected nodes grouped by their depth level in the tree:
353
-
354
- ```tsx
355
- const treeRef = useRef<TreeDropdownRef>(null)
356
-
357
- // Get all selected nodes grouped by level
358
- const handleGetByLevel = () => {
359
- const nodesByLevel = treeRef.current?.getSelectedNodesByLevel()
360
- // Result: { 0: [TreeNode, ...], 1: [TreeNode, ...], 2: [TreeNode, ...] }
361
- console.log('Level 0 nodes:', nodesByLevel?.[0])
362
- console.log('Level 1 nodes:', nodesByLevel?.[1])
363
- }
364
-
365
- // With filtering - only get fully checked nodes
366
- const handleGetCheckedByLevel = () => {
367
- const nodesByLevel = treeRef.current?.getSelectedNodesByLevel({
368
- filter: (node) => node.status === SelectedState.CHECKED,
369
- })
370
- console.log('Fully checked nodes by level:', nodesByLevel)
371
- }
372
-
373
- // Include children in the result
374
- const handleGetWithChildren = () => {
375
- const nodesByLevel = treeRef.current?.getSelectedNodesByLevel({
376
- includeChildren: true, // Include children in each node
377
- })
378
- console.log('Nodes with children:', nodesByLevel)
379
- }
380
-
381
- // Exclude children (default behavior)
382
- const handleGetWithoutChildren = () => {
383
- const nodesByLevel = treeRef.current?.getSelectedNodesByLevel({
384
- includeChildren: false, // Exclude children (default)
385
- })
386
- // Each node in the result will not have its children property
387
- console.log('Nodes without children:', nodesByLevel)
388
- }
389
- ```
390
-
391
- **Use Cases:**
392
-
393
- - Display selected items grouped by category level
394
- - Validate selections at specific tree levels
395
- - Process selections level by level
396
- - Filter and transform selected data by depth
397
-
398
- ### With Level Keys
399
-
400
- Level keys are useful for styling or testing specific levels:
401
-
402
- ```tsx
403
- const treeDefinition: TreeDefinition = {
404
- levelKey: 'category',
405
- children: [
406
- {
407
- id: '1',
408
- label: 'Category 1',
409
- levelKey: 'subcategory',
410
- children: [
411
- {
412
- id: '1-1',
413
- label: 'Item',
414
- levelKey: 'item',
415
- },
416
- ],
417
- },
418
- ],
419
- }
420
-
421
- // Results in data attributes:
422
- // <div data-level-key="category">...</div>
423
- // <div data-level-key="subcategory">...</div>
424
- // <div data-level-key="item">...</div>
425
- ```
426
-
427
- ### Pre-selected Items
428
-
429
- You can pre-select items by setting the `status` property in the `TreeDefinition`:
430
-
431
- ```tsx
432
- import { SelectedState } from '@components/TreeDropdown'
433
-
434
- const treeDefinition: TreeDefinition = {
435
- status: SelectedState.CHECKED, // Pre-select at the root level
436
- children: [
437
- {
438
- id: '1',
439
- label: 'Pre-selected Item',
440
- status: SelectedState.CHECKED, // Pre-select this node
441
- children: [
442
- {
443
- id: '1-1',
444
- label: 'Child Item',
445
- status: SelectedState.CHECKED, // Pre-select this child
446
- },
447
- ],
448
- },
449
- ],
450
- }
451
- ```
452
-
453
- **Note**: The `status` property is part of `TreeDefinition`, so it can be set at any level of the tree hierarchy.
454
-
455
- ### Expose Only Selected Tree
456
-
457
- When `exposeOnlySelectedTree` is `true`, the `onChange` callback receives only the selected branches instead of the entire tree. This is useful when you only need to work with selected items:
458
-
459
- ```tsx
460
- // With exposeOnlySelectedTree = false (default)
461
- // onChange receives the entire tree with all nodes and their states
462
- <TreeDropdown
463
- anchorEl={anchorEl}
464
- treeDefinition={treeDefinition}
465
- onChange={(tree) => {
466
- // tree contains ALL nodes, with their selection states
467
- console.log('Full tree:', tree)
468
- }}
469
- />
470
-
471
- // With exposeOnlySelectedTree = true
472
- // onChange receives only the selected branches
473
- <TreeDropdown
474
- anchorEl={anchorEl}
475
- treeDefinition={treeDefinition}
476
- exposeOnlySelectedTree={true}
477
- onChange={(selectedBranches) => {
478
- // selectedBranches contains ONLY selected nodes and their selected children
479
- // Unselected branches are filtered out
480
- console.log('Selected branches only:', selectedBranches)
481
- }}
482
- />
483
- ```
484
-
485
- **Note**: The `getSelectedBranches()` method on the ref always returns only selected branches, regardless of this prop setting.
486
-
487
- ## Advanced Features
488
-
489
- ### Auto-Promotion
490
-
491
- The component automatically promotes single-child nodes to reduce unnecessary nesting. If a node has exactly one child and that child has children, the intermediate level is skipped.
492
-
493
- To disable this behavior:
494
-
495
- ```tsx
496
- const treeDefinition: TreeDefinition = {
497
- children: [
498
- {
499
- id: '1',
500
- label: 'Parent',
501
- disablePromotion: true, // Disable promotion for this node
502
- children: [
503
- {
504
- id: '1-1',
505
- label: 'Only Child',
506
- children: [
507
- /* ... */
508
- ],
509
- },
510
- ],
511
- },
512
- ],
513
- }
514
- ```
515
-
516
- ### Styling
517
-
518
- Customize the appearance using the `sx` prop:
519
-
520
- ```tsx
521
- <TreeDropdown
522
- anchorEl={anchorEl}
523
- treeDefinition={treeDefinition}
524
- sx={{
525
- '& .MuiPopper-dropdown': {
526
- width: '300px',
527
- maxHeight: '400px',
528
- },
529
- }}
530
- />
531
- ```
532
-
533
- ### Positioning
534
-
535
- Control dropdown placement:
536
-
537
- ```tsx
538
- <TreeDropdown
539
- anchorEl={anchorEl}
540
- treeDefinition={treeDefinition}
541
- placement="bottom-end" // Options: 'bottom-start', 'bottom-end', 'top-start', etc.
542
- />
543
- ```
544
-
545
- ## Selection Behavior
546
-
547
- ### Multiple Selection Mode
548
-
549
- - Clicking a checkbox toggles its state
550
- - Selecting a parent automatically selects all children
551
- - Deselecting a parent automatically deselects all children
552
- - Parent nodes show indeterminate state when some (but not all) children are selected
553
- - The `onChange` callback behavior depends on `exposeOnlySelectedTree`:
554
- - When `false` (default): receives the entire tree with updated states
555
- - When `true`: receives only the selected branches (unselected nodes are filtered out)
556
-
557
- ### Single Selection Mode
558
-
559
- - Only one item can be selected at a time
560
- - Selecting a new item deselects the previously selected item
561
- - Selection controls only appear on leaf nodes (nodes without children)
562
- - Parent nodes without children can also be selected
563
-
564
- ## State Management
565
-
566
- The component maintains an internal state mapper (`TreeMapper`) that tracks:
567
-
568
- - Selection status of each node (`CHECKED`, `UNCHECKED`, `INDETERMINATE`)
569
- - Parent-child relationships
570
-
571
- You can access this internal state via the ref:
572
-
573
- ```tsx
574
- const internalMapper = treeRef.current?.internalMapper
575
- ```
576
-
577
- ## Best Practices
578
-
579
- 1. **Unique IDs**: Ensure all node IDs are unique across the entire tree
580
- 2. **Search Performance**: Use `getValues` efficiently - it's called for every item during filtering
581
- 3. **Large Trees**: For very large trees, consider implementing virtualization (not currently built-in)
582
- 4. **Accessibility**: The component uses Material-UI's MenuItem which provides basic accessibility, but consider adding ARIA labels for complex use cases
583
- 5. **State Initialization**: Use `status` prop to initialize selection state rather than managing it externally
584
- 6. **Debouncing**: Search is automatically debounced (300ms default), but you can customize this in the search hook if needed
585
-
586
- ## Component Architecture
587
-
588
- The component is built with the following structure:
589
-
590
- - **TreeDropdown.tsx**: Main component that manages state and provides the ref API
591
- - **TreeNodeList**: Renders a list of tree nodes at a given level
592
- - **TreeNodeItem**: Individual tree node component with hover-based submenu
593
- - **TreeDropdownProvider**: Context provider for sharing state across the tree
594
- - **utils.ts**: Utility functions for tree manipulation, normalization, and selection logic
595
- - **hooks/useSearch.ts**: Search functionality with debouncing
596
- - **hooks/useTreeDropdown.ts**: Hook to access tree dropdown context
597
-
598
- ## Notes
599
-
600
- - The component uses Material-UI's Popper for positioning nested menus
601
- - Nested menus appear on hover by default
602
- - Scrolling disables hover behavior temporarily to prevent menu flickering
603
- - The component automatically handles parent-child selection relationships
604
- - Search is case-insensitive by default