playbook_ui 14.5.0 → 14.6.0.pre.alpha.PBNTR5554217
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.
- checksums.yaml +4 -4
- data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +25 -7
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.jsx +72 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell.md +5 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_advanced_table/index.js +60 -0
- data/app/pb_kits/playbook/pb_advanced_table/table_header.html.erb +1 -9
- data/app/pb_kits/playbook/pb_advanced_table/table_subrow_header.html.erb +1 -9
- data/app/pb_kits/playbook/pb_card/_card.tsx +5 -1
- data/app/pb_kits/playbook/pb_currency/_currency.tsx +16 -6
- data/app/pb_kits/playbook/pb_currency/currency.rb +38 -11
- data/app/pb_kits/playbook/pb_currency/currency.test.js +35 -0
- data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.html.erb +7 -0
- data/app/pb_kits/playbook/pb_currency/docs/_currency_comma_separator.jsx +18 -0
- data/app/pb_kits/playbook/pb_currency/docs/example.yml +3 -1
- data/app/pb_kits/playbook/pb_currency/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -1
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.html.erb +30 -7
- data/app/pb_kits/playbook/pb_dialog/docs/_dialog_loading.md +0 -2
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +84 -3
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +28 -5
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.jsx +45 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_clear_selection.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_separators_hidden.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.html.erb +10 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.jsx +34 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_subtle_variant.md +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +3 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +10 -1
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownOption.tsx +1 -1
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +2 -2
- data/app/pb_kits/playbook/pb_filter/Filter/FilterDouble.tsx +2 -0
- data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -0
- data/app/pb_kits/playbook/pb_filter/Filter/FiltersPopover.tsx +4 -1
- data/app/pb_kits/playbook/pb_filter/Filter/ResultsCount.tsx +4 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +1 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.html.erb +41 -0
- data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props.jsx +71 -0
- data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_rails.md +1 -0
- data/app/pb_kits/playbook/pb_filter/docs/_filter_popover_props_react.md +1 -0
- data/app/pb_kits/playbook/pb_filter/docs/example.yml +3 -0
- data/app/pb_kits/playbook/pb_filter/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_filter/filter.html.erb +2 -2
- data/app/pb_kits/playbook/pb_filter/filter.rb +2 -0
- data/app/pb_kits/playbook/pb_flex/_flex.tsx +3 -1
- data/app/pb_kits/playbook/pb_flex/_flex_item.tsx +8 -2
- data/app/pb_kits/playbook/pb_flex/flex_item.html.erb +3 -6
- data/app/pb_kits/playbook/pb_flex/flex_item.rb +7 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +39 -0
- data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.md +1 -0
- data/app/pb_kits/playbook/pb_form/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_form/form.rb +2 -0
- data/app/pb_kits/playbook/pb_form/formHelper.js +27 -0
- data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +9 -1
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +19 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +27 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.md +1 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +211 -227
- data/app/pb_kits/playbook/pb_multi_level_select/context/index.tsx +5 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.jsx +1 -1
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.html.erb +93 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_reset.md +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.jsx +105 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children.md +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +106 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.md +1 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +2 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select_options.tsx +149 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.scss +169 -65
- data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.test.js +5 -5
- data/app/pb_kits/playbook/pb_multiple_users_stacked/_multiple_users_stacked.tsx +15 -9
- data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.html.erb +336 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/_multiple_users_stacked_size.jsx +97 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.html.erb +28 -6
- data/app/pb_kits/playbook/pb_multiple_users_stacked/multiple_users_stacked.rb +31 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +86 -18
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +15 -6
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_preferred_countries.md +1 -1
- data/app/pb_kits/playbook/pb_phone_number_input/intlTelInput.scss +849 -931
- data/app/pb_kits/playbook/pb_phone_number_input/types.d.ts +4 -1
- data/app/pb_kits/playbook/pb_popover/_popover.tsx +6 -2
- data/app/pb_kits/playbook/pb_popover/docs/_popover_default.html.erb +1 -1
- data/app/pb_kits/playbook/pb_popover/popover.rb +3 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +4 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +3 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +3 -1
- data/app/pb_kits/playbook/utilities/globalPropNames.mjs +3 -0
- data/app/pb_kits/playbook/utilities/globalProps.ts +39 -2
- data/dist/chunks/_typeahead-BhHnXJjy.js +22 -0
- data/dist/chunks/_weekday_stacked-C2icYweq.js +45 -0
- data/dist/chunks/{lib-CEpcaI8y.js → lib-D-mTv-kp.js} +1 -1
- data/dist/chunks/{pb_form_validation-D9zkwt2b.js → pb_form_validation-BkWGwJsl.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/playbook-doc.js +1 -1
- data/dist/playbook-rails-react-bindings.js +1 -1
- data/dist/playbook-rails.js +1 -1
- data/dist/playbook.css +1 -1
- data/lib/playbook/kit_base.rb +21 -1
- data/lib/playbook/pb_doc_helper.rb +5 -5
- data/lib/playbook/pb_forms_helper.rb +3 -1
- data/lib/playbook/version.rb +2 -2
- metadata +40 -9
- data/dist/chunks/_typeahead-BYw0HEgO.js +0 -22
- 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 {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
import
|
11
|
-
import
|
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
|
-
|
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(
|
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(
|
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
|
-
|
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(
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
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]:
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
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
|
-
<
|
442
|
-
|
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()} ${
|
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
|
-
|
550
|
-
|
551
|
-
|
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;
|