playbook_ui 14.5.0.pre.alpha.javascriptassets3939 → 14.5.0.pre.alpha.play1549upgradereactmodal4130

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/index.js +60 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -9
  4. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +1 -9
  5. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +20 -4
  6. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
  7. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md +1 -0
  8. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +1 -0
  9. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +1 -0
  10. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -1
  11. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -2
  12. data/app/pb_kits/playbook/pb_enhanced_element/{element_observer.js → element_observer.ts} +27 -19
  13. data/app/pb_kits/playbook/pb_enhanced_element/{index.js → index.ts} +22 -15
  14. data/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +4 -2
  15. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +1 -1
  16. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +9 -1
  17. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +19 -0
  18. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +27 -0
  19. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +1 -0
  20. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  21. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  22. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +211 -227
  23. data/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx +5 -0
  24. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +1 -1
  25. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +93 -0
  26. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md +1 -0
  27. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +105 -0
  28. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md +1 -0
  29. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +106 -0
  30. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md +1 -0
  31. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  32. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +2 -0
  33. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx +149 -0
  34. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +169 -65
  35. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +5 -5
  36. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +15 -9
  37. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.html.erb +336 -0
  38. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.jsx +97 -0
  39. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +2 -0
  40. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +1 -0
  41. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +28 -6
  42. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +31 -1
  43. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -1
  44. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +3 -1
  45. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
  46. data/dist/chunks/_typeahead-C9g4qCcE.js +22 -0
  47. data/dist/chunks/_weekday_stacked-DQN7SPcO.js +45 -0
  48. data/dist/chunks/lib-CEpcaI8y.js +29 -0
  49. data/dist/chunks/{pb_form_validation-8H8TD40J.js → pb_form_validation-D9zkwt2b.js} +1 -1
  50. data/dist/chunks/vendor.js +1 -45
  51. data/dist/playbook-doc.js +1 -1
  52. data/dist/playbook-rails-react-bindings.js +1 -1
  53. data/dist/playbook-rails.js +1 -1
  54. data/dist/playbook.css +1 -1
  55. data/lib/playbook/pb_doc_helper.rb +5 -5
  56. data/lib/playbook/version.rb +1 -1
  57. metadata +23 -11
  58. data/dist/chunks/_typeahead-DPGG9h5l.js +0 -65
  59. data/dist/chunks/index-CaXZ6mCT.js +0 -1
  60. data/dist/chunks/index-DfoYI7sS.js +0 -1
  61. data/dist/chunks/lib-ByFv-sq8.js +0 -45
  62. data/dist/mark.js +0 -1
  63. data/dist/playbook-rails-friendly.js +0 -1
@@ -1,14 +1,17 @@
1
- import React, { useState, useEffect, useRef } from "react"
2
- import classnames from "classnames"
3
- import { globalProps, GlobalProps } from "../utilities/globalProps"
4
- import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from "../utilities/props"
5
- import Checkbox from "../pb_checkbox/_checkbox"
6
- import Radio from "../pb_radio/_radio"
7
- import Body from "../pb_body/_body"
8
- import Icon from "../pb_icon/_icon"
9
- import FormPill from "../pb_form_pill/_form_pill"
10
- import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button"
11
- import { cloneDeep } from "lodash"
1
+ import React, { useState, useEffect, useRef } from "react";
2
+ import classnames from "classnames";
3
+ import { globalProps, GlobalProps } from "../utilities/globalProps";
4
+ import {
5
+ buildAriaProps,
6
+ buildCss,
7
+ buildDataProps,
8
+ buildHtmlProps,
9
+ } from "../utilities/props";
10
+ import Icon from "../pb_icon/_icon";
11
+ import FormPill from "../pb_form_pill/_form_pill";
12
+ import { cloneDeep } from "lodash";
13
+ import MultiLevelSelectOptions from "./multi_level_select_options";
14
+ import MultiLevelSelectContext from "./context";
12
15
 
13
16
  import {
14
17
  getAncestorsOfUnchecked,
@@ -18,7 +21,7 @@ import {
18
21
  getDefaultCheckedItems,
19
22
  recursiveCheckParent,
20
23
  getExpandedItems,
21
- } from "./_helper_functions"
24
+ } from "./_helper_functions";
22
25
 
23
26
  type MultiLevelSelectProps = {
24
27
  aria?: { [key: string]: string }
@@ -30,9 +33,9 @@ type MultiLevelSelectProps = {
30
33
  inputName?: string
31
34
  name?: string
32
35
  returnAllSelected?: boolean
33
- treeData?: { [key: string]: string }[]
36
+ treeData?: { [key: string]: string; }[] | any
34
37
  onSelect?: (prop: { [key: string]: any }) => void
35
- selectedIds?: string[]
38
+ selectedIds?: string[] | any
36
39
  variant?: "multi" | "single"
37
40
  pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories",
38
41
  } & GlobalProps
@@ -52,126 +55,132 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
52
55
  onSelect = () => null,
53
56
  selectedIds,
54
57
  variant = "multi",
58
+ children,
55
59
  pillColor = "primary"
56
60
  } = props
57
61
 
58
- const ariaProps = buildAriaProps(aria)
59
- const dataProps = buildDataProps(data)
60
- const htmlProps = buildHtmlProps(htmlOptions)
62
+ const ariaProps = buildAriaProps(aria);
63
+ const dataProps = buildDataProps(data);
64
+ const htmlProps = buildHtmlProps(htmlOptions);
61
65
  const classes = classnames(
62
66
  buildCss("pb_multi_level_select"),
63
67
  globalProps(props),
64
68
  className
65
- )
69
+ );
66
70
 
67
- const dropdownRef = useRef(null)
71
+ const dropdownRef = useRef(null);
68
72
 
69
73
  // State for whether dropdown is open or closed
70
- const [isDropdownClosed, setIsDropdownClosed] = useState(true)
74
+ const [isDropdownClosed, setIsDropdownClosed] = useState(true);
71
75
  // State from onChange for textinput, to use for filtering to create typeahead
72
- const [filterItem, setFilterItem] = useState("")
76
+ const [filterItem, setFilterItem] = useState("");
73
77
  // FormattedData with checked and parent_id added
74
- const [formattedData, setFormattedData] = useState([])
78
+ const [formattedData, setFormattedData] = useState([]);
75
79
  // State for the return of returnAllSelected
76
- const [returnedArray, setReturnedArray] = useState([])
80
+ const [returnedArray, setReturnedArray] = useState([]);
77
81
  // State for default return
78
- const [defaultReturn, setDefaultReturn] = useState([])
82
+ const [defaultReturn, setDefaultReturn] = useState([]);
79
83
  // Get expanded items from treeData
80
- const initialExpandedItems = getExpandedItems(treeData, selectedIds)
84
+ const initialExpandedItems = getExpandedItems(treeData, selectedIds);
81
85
  // Initialize state with expanded items
82
- const [expanded, setExpanded] = useState(initialExpandedItems)
86
+ const [expanded, setExpanded] = useState(initialExpandedItems);
83
87
 
84
88
  // Single Select specific state
85
89
  const [singleSelectedItem, setSingleSelectedItem] = useState({
86
90
  id: [],
87
91
  value: "",
88
- item: []
89
- })
92
+ item: [],
93
+ });
90
94
 
91
95
  const arrowDownElementId = `arrow_down_${id}`
92
96
  const arrowUpElementId = `arrow_up_${id}`
93
97
 
94
98
  const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => {
95
99
  if (!Array.isArray(tree)) {
96
- return
100
+ return;
97
101
  }
98
102
  return tree.map((item: { [key: string]: any }) => {
99
- item.checked = check
100
- item.children = modifyRecursive(item.children, check)
101
- return item
102
- })
103
- }
103
+ item.checked = check;
104
+ item.children = modifyRecursive(item.children, check);
105
+ return item;
106
+ });
107
+ };
104
108
 
105
- // Function to map over data and add parent_id + depth property to each item
109
+ // Function to map over data and add parent_id + depth property to each item
106
110
  const addCheckedAndParentProperty = (
107
111
  treeData: { [key: string]: any }[],
108
112
  selectedIds: string[],
109
- parent_id: string = null,
110
- depth = 0,
113
+ parent_id: string | null = null,
114
+ depth = 0
111
115
  ) => {
112
116
  if (!Array.isArray(treeData)) {
113
- return
117
+ return;
114
118
  }
115
119
  return treeData.map((item: { [key: string]: any } | any) => {
116
120
  const newItem = {
117
121
  ...item,
118
- checked: Boolean(selectedIds && selectedIds.length && selectedIds.includes(item.id)),
122
+ checked: Boolean(
123
+ selectedIds && selectedIds.length && selectedIds.includes(item.id)
124
+ ),
119
125
  parent_id,
120
126
  depth,
121
- }
127
+ };
122
128
  if (newItem.children && newItem.children.length > 0) {
123
129
  const children =
124
130
  item.checked && !returnAllSelected
125
131
  ? modifyRecursive(item.children, true)
126
- : item.children
132
+ : item.children;
127
133
  newItem.children = addCheckedAndParentProperty(
128
134
  children,
129
135
  selectedIds,
130
136
  newItem.id,
131
137
  depth + 1
132
- )
138
+ );
133
139
  }
134
- return newItem
135
- })
136
- }
140
+ return newItem;
141
+ });
142
+ };
137
143
 
138
144
  useEffect(() => {
139
145
  const formattedData = addCheckedAndParentProperty(
140
146
  treeData,
141
147
  variant === "single" ? [selectedIds?.[0]] : selectedIds
142
- )
148
+ );
143
149
 
144
- setFormattedData(formattedData)
150
+ setFormattedData(formattedData);
145
151
 
146
152
  if (variant === "single") {
147
153
  // No selectedIds, reset state
148
154
  if (selectedIds?.length === 0 || !selectedIds?.length) {
149
- setSingleSelectedItem({ id: [], value: "", item: []})
155
+ setSingleSelectedItem({ id: [], value: "", item: [] });
150
156
  } else {
151
157
  // If there is a selectedId but no current item, set the selectedItem
152
158
  if (selectedIds?.length !== 0 && !singleSelectedItem.value) {
153
- const selectedItem = filterFormattedDataById(formattedData, selectedIds[0])
159
+ const selectedItem = filterFormattedDataById(
160
+ formattedData,
161
+ selectedIds[0]
162
+ );
154
163
 
155
164
  if (!selectedItem.length) {
156
- setSingleSelectedItem({ id: [], value: "", item: []})
165
+ setSingleSelectedItem({ id: [], value: "", item: [] });
157
166
  } else {
158
- const { id, value } = selectedItem[0]
159
- setSingleSelectedItem({ id: [id], value, item: selectedItem})
167
+ const { id, value } = selectedItem[0];
168
+ setSingleSelectedItem({ id: [id], value, item: selectedItem });
160
169
  }
161
170
  }
162
171
  }
163
172
  }
164
- }, [treeData, selectedIds])
173
+ }, [treeData, selectedIds]);
165
174
 
166
175
  useEffect(() => {
167
176
  if (returnAllSelected) {
168
- setReturnedArray(getCheckedItems(formattedData))
177
+ setReturnedArray(getCheckedItems(formattedData));
169
178
  } else if (variant === "single") {
170
- setDefaultReturn(singleSelectedItem.item)
179
+ setDefaultReturn(singleSelectedItem.item);
171
180
  } else {
172
- setDefaultReturn(getDefaultCheckedItems(formattedData))
181
+ setDefaultReturn(getDefaultCheckedItems(formattedData));
173
182
  }
174
- }, [formattedData])
183
+ }, [formattedData]);
175
184
 
176
185
  useEffect(() => {
177
186
  // Function to handle clicks outside the dropdown
@@ -184,16 +193,31 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
184
193
  ) {
185
194
  setIsDropdownClosed(true)
186
195
  }
187
- }
196
+ };
188
197
  // Attach the event listener
189
- window.addEventListener("click", handleClickOutside)
198
+ window.addEventListener("click", handleClickOutside);
190
199
  // Clean up the event listener on unmount
191
200
  return () => {
192
- window.removeEventListener("click", handleClickOutside)
193
- }
194
- }, [])
195
-
201
+ window.removeEventListener("click", handleClickOutside);
202
+ };
203
+ }, []);
196
204
 
205
+ useEffect(() => {
206
+ if (id) {
207
+ // Attach the clear function to the window, scoped by the id
208
+ (window as any)[`clearMultiLevelSelect_${id}`] = () => {
209
+ const resetData = modifyRecursive(formattedData, false);
210
+ setFormattedData(resetData);
211
+ setReturnedArray([]);
212
+ setDefaultReturn([]);
213
+ setSingleSelectedItem({ id: [], value: "", item: [] });
214
+ onSelect([]);
215
+ };
216
+ return () => {
217
+ delete (window as any)[`clearMultiLevelSelect_${id}`];
218
+ };
219
+ }
220
+ }, [formattedData, id, onSelect]);
197
221
 
198
222
  // Iterate over tree, find item and set checked or unchecked
199
223
  const modifyValue = (
@@ -202,69 +226,67 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
202
226
  check: boolean
203
227
  ) => {
204
228
  if (!Array.isArray(tree)) {
205
- return
229
+ return;
206
230
  }
207
231
  return tree.map((item: any) => {
208
- if (item.id != id) item.children = modifyValue(id, item.children, check)
232
+ if (item.id != id) item.children = modifyValue(id, item.children, check);
209
233
  else {
210
- item.checked = check
234
+ item.checked = check;
211
235
 
212
236
  if (variant === "single") {
213
237
  // Single select: no children should be checked
214
- item.children = modifyRecursive(item.children, !check)
238
+ item.children = modifyRecursive(item.children, !check);
215
239
  } else {
216
- item.children = modifyRecursive(item.children, check)
240
+ item.children = modifyRecursive(item.children, check);
217
241
  }
218
242
  }
219
243
 
220
- return item
221
- })
222
- }
244
+ return item;
245
+ });
246
+ };
223
247
 
224
248
  // Clone tree, check items + children
225
249
  const checkItem = (item: { [key: string]: any }) => {
226
- const tree = cloneDeep(formattedData)
250
+ const tree = cloneDeep(formattedData);
227
251
  if (returnAllSelected) {
228
- return modifyValue(item.id, tree, true)
252
+ return modifyValue(item.id, tree, true);
229
253
  } else {
230
- const checkedTree = modifyValue(item.id, tree, true)
231
- return recursiveCheckParent(item, checkedTree)
254
+ const checkedTree = modifyValue(item.id, tree, true);
255
+ return recursiveCheckParent(item, checkedTree);
232
256
  }
233
- }
257
+ };
234
258
 
235
259
  // Clone tree, uncheck items + children
236
260
  const unCheckItem = (item: { [key: string]: any }) => {
237
- const tree = cloneDeep(formattedData)
261
+ const tree = cloneDeep(formattedData);
238
262
  if (returnAllSelected) {
239
- return modifyValue(item.id, tree, false)
263
+ return modifyValue(item.id, tree, false);
240
264
  } else {
241
- const uncheckedTree = modifyValue(item.id, tree, false)
242
- return getAncestorsOfUnchecked(uncheckedTree, item)
265
+ const uncheckedTree = modifyValue(item.id, tree, false);
266
+ return getAncestorsOfUnchecked(uncheckedTree, item);
243
267
  }
244
- }
268
+ };
245
269
 
246
270
  // setFormattedData with proper properties
247
271
  const changeItem = (item: { [key: string]: any }, check: boolean) => {
248
- const tree = check ? checkItem(item) : unCheckItem(item)
249
- setFormattedData(tree)
272
+ const tree = check ? checkItem(item) : unCheckItem(item);
273
+ setFormattedData(tree);
250
274
 
251
- return tree
252
- }
253
-
254
-
275
+ return tree;
276
+ };
255
277
 
256
278
  // Click event for x on form pill
257
279
  const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
258
280
  // Prevents the dropdown from closing when clicking on the pill
259
- event.stopPropagation()
260
- const updatedTree = changeItem(clickedItem, false)
281
+ event.stopPropagation();
282
+ const updatedTree = changeItem(clickedItem, false);
261
283
  // Logic for removing items from returnArray or defaultReturn when pills clicked
262
284
  if (returnAllSelected) {
263
- onSelect(getCheckedItems(updatedTree))
285
+ onSelect(getCheckedItems(updatedTree));
264
286
  } else {
265
- onSelect(getDefaultCheckedItems(updatedTree))
287
+ onSelect(getDefaultCheckedItems(updatedTree));
266
288
  }
267
- }
289
+ };
268
290
 
269
291
  // Handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
270
292
  const handleInputWrapperClick = (e: any) => {
@@ -272,163 +294,114 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
272
294
  e.target.id === "multiselect_input" ||
273
295
  e.target.classList.contains("pb_form_pill_tag")
274
296
  ) {
275
- return
297
+ return;
276
298
  }
277
- setIsDropdownClosed(!isDropdownClosed)
278
- }
299
+ setIsDropdownClosed(!isDropdownClosed);
300
+ };
279
301
 
280
302
  // Main function to handle any click inside dropdown
281
303
  const handledropdownItemClick = (e: any, check: boolean) => {
282
- const clickedItem = e.target.parentNode.id
304
+ const clickedItem = e.target.parentNode.id;
283
305
  // Setting filterItem to "" will clear textinput and clear typeahead
284
- setFilterItem("")
306
+ setFilterItem("");
285
307
 
286
- const filtered = filterFormattedDataById(formattedData, clickedItem)
287
- const updatedTree = changeItem(filtered[0], check)
308
+ const filtered = filterFormattedDataById(formattedData, clickedItem);
309
+ const updatedTree = changeItem(filtered[0], check);
288
310
  if (returnAllSelected) {
289
- onSelect(getCheckedItems(updatedTree))
311
+ onSelect(getCheckedItems(updatedTree));
290
312
  } else {
291
- onSelect(getDefaultCheckedItems(updatedTree))
313
+ onSelect(getDefaultCheckedItems(updatedTree));
292
314
  }
293
- }
315
+ };
294
316
 
295
317
  // Single select
296
- const handleRadioButtonClick = (
297
- e: React.ChangeEvent<HTMLInputElement>,
298
- ) => {
299
- const { id, value: inputText } = e.target
318
+ const handleRadioButtonClick = (e: React.ChangeEvent<HTMLInputElement>) => {
319
+ const { id, value: inputText } = e.target;
300
320
  // The radio button needs a unique ID, this grabs the ID before the hyphen
301
- const selectedItemID = id.match(/^[^-]*/)[0]
321
+ const selectedItemID = id.match(/^[^-]*/)[0];
302
322
  // Reset tree checked state, triggering useEffect
303
- const treeWithNoSelections = modifyRecursive(formattedData, false)
323
+ const treeWithNoSelections = modifyRecursive(formattedData, false);
304
324
  // Update tree with single selection
305
- const treeWithSelectedItem = modifyValue(selectedItemID, treeWithNoSelections, true)
306
- const selectedItem = filterFormattedDataById(treeWithSelectedItem, selectedItemID)
307
-
308
- setFormattedData(treeWithSelectedItem)
309
- setSingleSelectedItem({id: [selectedItemID], value: inputText, item: selectedItem})
325
+ const treeWithSelectedItem = modifyValue(
326
+ selectedItemID,
327
+ treeWithNoSelections,
328
+ true
329
+ );
330
+ const selectedItem = filterFormattedDataById(
331
+ treeWithSelectedItem,
332
+ selectedItemID
333
+ );
334
+
335
+ setFormattedData(treeWithSelectedItem);
336
+ setSingleSelectedItem({
337
+ id: [selectedItemID],
338
+ value: inputText,
339
+ item: selectedItem,
340
+ });
310
341
  // Reset the filter to always display dropdown options on click
311
- setFilterItem("")
312
- setIsDropdownClosed(true)
342
+ setFilterItem("");
343
+ setIsDropdownClosed(true);
313
344
 
314
- onSelect(selectedItem)
345
+ onSelect(selectedItem);
315
346
  };
316
347
 
317
348
  // Single select: reset the tree state upon typing
318
349
  const handleRadioInputChange = (inputText: string) => {
319
- modifyRecursive(formattedData, false)
320
- setDefaultReturn([])
321
- setSingleSelectedItem({id: [], value: inputText, item: []})
322
- setFilterItem(inputText)
350
+ modifyRecursive(formattedData, false);
351
+ setDefaultReturn([]);
352
+ setSingleSelectedItem({ id: [], value: inputText, item: [] });
353
+ setFilterItem(inputText);
323
354
  };
324
355
 
325
- const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1
356
+ const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1;
326
357
 
327
358
  // Handle click on chevron toggles in dropdown
328
359
  const handleToggleClick = (id: string, event: React.MouseEvent) => {
329
- event.stopPropagation()
330
- const clickedItem = filterFormattedDataById(formattedData, id)
360
+ event.stopPropagation();
361
+ const clickedItem = filterFormattedDataById(formattedData, id);
331
362
  if (clickedItem) {
332
- let expandedArray = [...expanded]
333
- const itemExpanded = isTreeRowExpanded(clickedItem[0])
363
+ let expandedArray = [...expanded];
364
+ const itemExpanded = isTreeRowExpanded(clickedItem[0]);
334
365
 
335
366
  if (itemExpanded)
336
- expandedArray = expandedArray.filter((i) => i != clickedItem[0].id)
337
- else expandedArray.push(clickedItem[0].id)
367
+ expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
368
+ else expandedArray.push(clickedItem[0].id);
338
369
 
339
- setExpanded(expandedArray)
370
+ setExpanded(expandedArray);
340
371
  }
341
- }
372
+ };
342
373
 
343
374
  const itemsSelectedLength = () => {
344
- let items
375
+ let items;
345
376
  if (returnAllSelected && returnedArray && returnedArray.length) {
346
- items = returnedArray.length
377
+ items = returnedArray.length;
347
378
  } else if (!returnAllSelected && defaultReturn && defaultReturn.length) {
348
- items = defaultReturn.length
379
+ items = defaultReturn.length;
349
380
  }
350
- return items
351
- }
381
+ return items;
382
+ };
352
383
 
353
384
  // Rendering formattedData to UI based on typeahead
354
- const renderNestedOptions = (items: { [key: string]: any }[]) => {
355
- return (
356
- <ul>
357
- {Array.isArray(items) &&
358
- items.map((item: { [key: string]: any }) => {
359
- return (
360
- <div key={item.id}>
361
- <li
362
- className={"dropdown_item"}
363
- data-name={item.id}
364
- >
365
- <div className="dropdown_item_checkbox_row">
366
- { !item.parent_id && !item.children ? null :
367
- <div
368
- key={isTreeRowExpanded(item) ? "chevron-down" : "chevron-right"}
369
- >
370
- <CircleIconButton
371
- className={
372
- item.children && item.children.length > 0
373
- ? ""
374
- : "toggle_icon"
375
- }
376
- icon={
377
- isTreeRowExpanded(item) ? "chevron-down" : "chevron-right"
378
- }
379
- onClick={(event: any) =>
380
- handleToggleClick(item.id, event)
381
- }
382
- variant="link"
383
- />
384
- </div>
385
- }
386
- { variant === "single" ? (
387
- item.hideRadio ? (
388
- <Body>{item.label}</Body>
389
- ) :
390
- <Radio
391
- checked={item.checked}
392
- id={`${item.id}-${item.label}`}
393
- label={item.label}
394
- name={inputName}
395
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => (
396
- handleRadioButtonClick(e)
397
- )}
398
- padding={item.children ? 'none' : 'xs'}
399
- type="radio"
400
- value={item.label}
401
- />
402
- ) : (
403
- <Checkbox
404
- id={item.id}
405
- text={item.label}
406
- >
407
- <input
408
- checked={item.checked}
409
- name={item.label}
410
- onChange={(e) => {
411
- handledropdownItemClick(e, !item.checked)
412
- }}
413
- type="checkbox"
414
- value={item.label}
415
- />
416
- </Checkbox>
417
- )}
418
- </div>
419
- {isTreeRowExpanded(item) &&
420
- item.children &&
421
- item.children.length > 0 &&
422
- (variant === "single" || !filterItem) && ( // Show children if expanded is true
423
- <div>{renderNestedOptions(item.children)}</div>
424
- )}
425
- </li>
426
- </div>
427
- )
428
- })}
429
- </ul>
430
- )
431
- }
385
+ const renderNestedOptions = (items: { [key: string]: string; }[] | any ) => {
386
+ const hasOptionsChild = React.Children.toArray(props.children).some(
387
+ (child: any) => child.type === MultiLevelSelect.Options
388
+ );
389
+
390
+ if (hasOptionsChild) {
391
+ return React.Children.map(props.children, (child) => {
392
+ if (child.type === MultiLevelSelect.Options) {
393
+ return React.cloneElement(child, { items });
394
+ }
395
+ return null;
396
+ });
397
+ } else {
398
+ // If no children, use the default rendering
399
+ return (
400
+ <MultiLevelSelectOptions items={items} />
401
+ );
402
+ }
403
+ };
404
+
432
405
 
433
406
  return (
434
407
  <div
@@ -438,12 +411,20 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
438
411
  className={classes}
439
412
  id={id}
440
413
  >
441
- <div
442
- className="wrapper"
414
+ <MultiLevelSelectContext.Provider value={{
415
+ variant,
416
+ inputName,
417
+ renderNestedOptions,
418
+ isTreeRowExpanded,
419
+ handleToggleClick,
420
+ handleRadioButtonClick,
421
+ handledropdownItemClick,
422
+ filterItem,
423
+ }}>
424
+ <div className="wrapper"
443
425
  ref={dropdownRef}
444
426
  >
445
- <div
446
- className="input_wrapper"
427
+ <div className="input_wrapper"
447
428
  onClick={handleInputWrapperClick}
448
429
  >
449
430
  <div className="input_inner_container">
@@ -509,15 +490,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
509
490
 
510
491
  <input
511
492
  id="multiselect_input"
512
- onChange={(e) =>{
493
+ onChange={(e) => {
513
494
  variant === "single"
514
495
  ? handleRadioInputChange(e.target.value)
515
- : setFilterItem(e.target.value)
496
+ : setFilterItem(e.target.value);
516
497
  }}
517
498
  onClick={() => setIsDropdownClosed(false)}
518
499
  placeholder={
519
500
  inputDisplay === "none" && itemsSelectedLength()
520
- ? `${itemsSelectedLength()} ${itemsSelectedLength() === 1 ? "item" : "items"} selected`
501
+ ? `${itemsSelectedLength()} ${
502
+ itemsSelectedLength() === 1 ? "item" : "items"
503
+ } selected`
521
504
  : "Start typing..."
522
505
  }
523
506
  value={singleSelectedItem.value || filterItem}
@@ -546,15 +529,16 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
546
529
  </div>
547
530
 
548
531
  <div className={`dropdown_menu ${isDropdownClosed ? "close" : "open"}`}>
549
- {renderNestedOptions(
550
- filterItem
551
- ? findByFilter(formattedData, filterItem)
552
- : formattedData
553
- )}
532
+ {renderNestedOptions(
533
+ filterItem ? findByFilter(formattedData, filterItem) : formattedData
534
+ )}
554
535
  </div>
555
536
  </div>
537
+ </MultiLevelSelectContext.Provider>
556
538
  </div>
557
- )
558
- }
539
+ );
540
+ };
541
+
542
+ MultiLevelSelect.Options = MultiLevelSelectOptions;
559
543
 
560
- export default MultiLevelSelect
544
+ export default MultiLevelSelect;
@@ -0,0 +1,5 @@
1
+ import { createContext } from "react";
2
+
3
+ const MultiLevelSelectContext = createContext<any>({});
4
+
5
+ export default MultiLevelSelectContext;
@@ -87,4 +87,4 @@ const MultiLevelSelectDefault = (props) => {
87
87
  )
88
88
  };
89
89
 
90
- export default MultiLevelSelectDefault;
90
+ export default MultiLevelSelectDefault;