playbook_ui 14.5.0 → 14.6.0.pre.alpha.PBNTR5554217

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +25 -7
  3. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx +72 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md +5 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/index.js +60 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -9
  9. data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +1 -9
  10. data/app/pb_kits/playbook/pb_card/_card.tsx +5 -1
  11. data/app/pb_kits/playbook/pb_currency/_currency.tsx +16 -6
  12. data/app/pb_kits/playbook/pb_currency/currency.rb +38 -11
  13. data/app/pb_kits/playbook/pb_currency/currency.test.js +35 -0
  14. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb +7 -0
  15. data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx +18 -0
  16. data/app/pb_kits/playbook/pb_currency/docs/example.yml +3 -1
  17. data/app/pb_kits/playbook/pb_currency/docs/index.js +1 -0
  18. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -1
  19. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb +30 -7
  20. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md +0 -2
  21. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +84 -3
  22. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +28 -5
  23. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
  24. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md +1 -0
  25. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.html.erb +9 -0
  26. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.jsx +33 -0
  27. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.html.erb +10 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.jsx +34 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.md +1 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +3 -0
  32. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +10 -1
  33. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -1
  34. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -2
  35. data/app/pb_kits/playbook/pb_filter/Filter/FilterDouble.tsx +2 -0
  36. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -0
  37. data/app/pb_kits/playbook/pb_filter/Filter/FiltersPopover.tsx +4 -1
  38. data/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +4 -2
  39. data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +1 -1
  40. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.html.erb +41 -0
  41. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.jsx +71 -0
  42. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_rails.md +1 -0
  43. data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_react.md +1 -0
  44. data/app/pb_kits/playbook/pb_filter/docs/example.yml +3 -0
  45. data/app/pb_kits/playbook/pb_filter/docs/index.js +1 -0
  46. data/app/pb_kits/playbook/pb_filter/filter.html.erb +2 -2
  47. data/app/pb_kits/playbook/pb_filter/filter.rb +2 -0
  48. data/app/pb_kits/playbook/pb_flex/_flex.tsx +3 -1
  49. data/app/pb_kits/playbook/pb_flex/_flex_item.tsx +8 -2
  50. data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +3 -6
  51. data/app/pb_kits/playbook/pb_flex/flex_item.rb +7 -2
  52. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +39 -0
  53. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.md +1 -0
  54. data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
  55. data/app/pb_kits/playbook/pb_form/form.rb +2 -0
  56. data/app/pb_kits/playbook/pb_form/formHelper.js +27 -0
  57. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +9 -1
  58. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +19 -0
  59. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +27 -0
  60. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +1 -0
  61. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  62. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  63. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +211 -227
  64. data/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx +5 -0
  65. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +1 -1
  66. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +93 -0
  67. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md +1 -0
  68. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +105 -0
  69. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md +1 -0
  70. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +106 -0
  71. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md +1 -0
  72. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
  73. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +2 -0
  74. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx +149 -0
  75. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +169 -65
  76. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +5 -5
  77. data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +15 -9
  78. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.html.erb +336 -0
  79. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.jsx +97 -0
  80. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +2 -0
  81. data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +1 -0
  82. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +28 -6
  83. data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +31 -1
  84. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +86 -18
  85. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +15 -6
  86. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.md +1 -1
  87. data/app/pb_kits/playbook/pb_phone_number_input/intlTelInput.scss +849 -931
  88. data/app/pb_kits/playbook/pb_phone_number_input/types.d.ts +4 -1
  89. data/app/pb_kits/playbook/pb_popover/_popover.tsx +6 -2
  90. data/app/pb_kits/playbook/pb_popover/docs/_popover_default.html.erb +1 -1
  91. data/app/pb_kits/playbook/pb_popover/popover.rb +3 -1
  92. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -1
  93. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +3 -1
  94. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
  95. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +3 -0
  96. data/app/pb_kits/playbook/utilities/globalProps.ts +39 -2
  97. data/dist/chunks/_typeahead-BhHnXJjy.js +22 -0
  98. data/dist/chunks/_weekday_stacked-C2icYweq.js +45 -0
  99. data/dist/chunks/{lib-CEpcaI8y.js → lib-D-mTv-kp.js} +1 -1
  100. data/dist/chunks/{pb_form_validation-D9zkwt2b.js → pb_form_validation-BkWGwJsl.js} +1 -1
  101. data/dist/chunks/vendor.js +1 -1
  102. data/dist/playbook-doc.js +1 -1
  103. data/dist/playbook-rails-react-bindings.js +1 -1
  104. data/dist/playbook-rails.js +1 -1
  105. data/dist/playbook.css +1 -1
  106. data/lib/playbook/kit_base.rb +21 -1
  107. data/lib/playbook/pb_doc_helper.rb +5 -5
  108. data/lib/playbook/pb_forms_helper.rb +3 -1
  109. data/lib/playbook/version.rb +2 -2
  110. metadata +40 -9
  111. data/dist/chunks/_typeahead-BYw0HEgO.js +0 -22
  112. data/dist/chunks/_weekday_stacked-DumiyWjh.js +0 -45
@@ -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;