playbook_ui 14.4.0 → 14.5.0.pre.alpha.PBNTR374multilevelselectPOC3946
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/_playbook.scss +2 -0
- data/app/pb_kits/playbook/pb_advanced_table/Components/SortIconButton.tsx +23 -4
- data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +1 -1
- data/app/pb_kits/playbook/pb_button/_button.tsx +6 -2
- data/app/pb_kits/playbook/pb_contact/_contact.tsx +17 -5
- data/app/pb_kits/playbook/pb_contact/contact.html.erb +14 -6
- data/app/pb_kits/playbook/pb_contact/contact.rb +4 -0
- data/app/pb_kits/playbook/pb_contact/contact.test.js +1 -1
- data/app/pb_kits/playbook/pb_dashboard/pbChartsDarkTheme.ts +2 -6
- data/app/pb_kits/playbook/pb_dashboard/pbChartsLightTheme.ts +2 -7
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +4 -3
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +1 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +4 -0
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +6 -3
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_inline.html.erb +1 -1
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_header_styles.scss +6 -2
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +2 -0
- data/app/pb_kits/playbook/pb_dialog/dialog_header.html.erb +1 -1
- data/app/pb_kits/playbook/pb_drawer/_close_icon.tsx +25 -0
- data/app/pb_kits/playbook/pb_drawer/_drawer.scss +465 -0
- data/app/pb_kits/playbook/pb_drawer/_drawer.tsx +195 -0
- data/app/pb_kits/playbook/pb_drawer/_drawer_context.tsx +3 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +117 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +43 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_default.html.erb +1 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_default.jsx +63 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +55 -0
- data/app/pb_kits/playbook/pb_drawer/docs/_drawer_sizes.jsx +113 -0
- data/app/pb_kits/playbook/pb_drawer/docs/example.yml +12 -0
- data/app/pb_kits/playbook/pb_drawer/docs/index.js +5 -0
- data/app/pb_kits/playbook/pb_drawer/drawer.html.erb +12 -0
- data/app/pb_kits/playbook/pb_drawer/drawer.rb +8 -0
- data/app/pb_kits/playbook/pb_drawer/drawer.test.jsx +77 -0
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +10 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_max_height.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_max_width.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_no_background.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_no_sort.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_only.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_placement.html.erb +5 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_single.html.erb +5 -1
- data/app/pb_kits/playbook/pb_map/_map_controls.tsx +7 -1
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +210 -232
- 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_with_children.jsx +107 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_children_with_radios.jsx +108 -0
- data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +3 -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_pagination/docs/_pagination_page_change.jsx +12 -1
- data/app/pb_kits/playbook/pb_pagination/docs/_pagination_page_change_react.md +3 -1
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +10 -2
- data/app/pb_kits/playbook/pb_popover/docs/_popover_list.html.erb +14 -13
- data/app/pb_kits/playbook/pb_popover/docs/_popover_list.jsx +4 -4
- data/app/pb_kits/playbook/pb_radio/_radio.tsx +92 -33
- data/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.html.erb +2 -0
- data/app/pb_kits/playbook/pb_radio/docs/_radio_custom_children.jsx +62 -0
- data/app/pb_kits/playbook/pb_radio/docs/example.yml +1 -0
- data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_rich_text_editor/TipTap/ToolbarDropdown.tsx +12 -5
- data/app/pb_kits/playbook/pb_select/_select.tsx +5 -2
- data/app/pb_kits/playbook/pb_select/select.html.erb +1 -1
- data/app/pb_kits/playbook/pb_select/select.rb +4 -0
- data/app/pb_kits/playbook/pb_text_input/_text_input.scss +0 -1
- data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +17 -13
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +0 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +14 -0
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -0
- data/app/pb_kits/playbook/pb_typeahead/components/Control.tsx +6 -5
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_margin_bottom.html.erb +88 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_margin_bottom.jsx +60 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_typeahead/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +2 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +6 -1
- data/app/pb_kits/playbook/utilities/icons/allicons.tsx +136 -13
- data/app/pb_kits/playbook/utilities/icons/angle-down.svg +3 -0
- data/app/pb_kits/playbook/utilities/icons/envelope.svg +3 -0
- data/dist/chunks/_typeahead-DMIBeCYd.js +22 -0
- data/dist/chunks/_weekday_stacked-CviGlw2m.js +45 -0
- data/dist/chunks/lazysizes-B7xYodB-.js +1 -0
- data/dist/chunks/lib-CEpcaI8y.js +29 -0
- data/dist/chunks/{pb_form_validation-zV9OpdSt.js → pb_form_validation-D9zkwt2b.js} +1 -1
- data/dist/chunks/vendor.js +1 -1
- data/dist/menu.yml +3 -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/pagination_renderer.rb +10 -2
- data/lib/playbook/pb_doc_helper.rb +5 -5
- data/lib/playbook/version.rb +2 -2
- metadata +34 -10
- data/dist/chunks/_typeahead-B2zRxReA.js +0 -22
- data/dist/chunks/_weekday_stacked-BIfZDNDm.js +0 -45
- data/dist/chunks/lazysizes-DHz07jlL.js +0 -1
- data/dist/chunks/lib-D2U4I1U6.js +0 -16
@@ -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,140 +55,152 @@ 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
|
+
});
|
94
|
+
|
95
|
+
const arrowDownElementId = `arrow_down_${id}`
|
96
|
+
const arrowUpElementId = `arrow_up_${id}`
|
90
97
|
|
91
98
|
const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => {
|
92
99
|
if (!Array.isArray(tree)) {
|
93
|
-
return
|
100
|
+
return;
|
94
101
|
}
|
95
102
|
return tree.map((item: { [key: string]: any }) => {
|
96
|
-
item.checked = check
|
97
|
-
item.children = modifyRecursive(item.children, check)
|
98
|
-
return item
|
99
|
-
})
|
100
|
-
}
|
103
|
+
item.checked = check;
|
104
|
+
item.children = modifyRecursive(item.children, check);
|
105
|
+
return item;
|
106
|
+
});
|
107
|
+
};
|
101
108
|
|
102
|
-
|
109
|
+
// Function to map over data and add parent_id + depth property to each item
|
103
110
|
const addCheckedAndParentProperty = (
|
104
111
|
treeData: { [key: string]: any }[],
|
105
112
|
selectedIds: string[],
|
106
|
-
parent_id: string = null,
|
107
|
-
depth = 0
|
113
|
+
parent_id: string | null = null,
|
114
|
+
depth = 0
|
108
115
|
) => {
|
109
116
|
if (!Array.isArray(treeData)) {
|
110
|
-
return
|
117
|
+
return;
|
111
118
|
}
|
112
119
|
return treeData.map((item: { [key: string]: any } | any) => {
|
113
120
|
const newItem = {
|
114
121
|
...item,
|
115
|
-
checked: Boolean(
|
122
|
+
checked: Boolean(
|
123
|
+
selectedIds && selectedIds.length && selectedIds.includes(item.id)
|
124
|
+
),
|
116
125
|
parent_id,
|
117
126
|
depth,
|
118
|
-
}
|
127
|
+
};
|
119
128
|
if (newItem.children && newItem.children.length > 0) {
|
120
129
|
const children =
|
121
130
|
item.checked && !returnAllSelected
|
122
131
|
? modifyRecursive(item.children, true)
|
123
|
-
: item.children
|
132
|
+
: item.children;
|
124
133
|
newItem.children = addCheckedAndParentProperty(
|
125
134
|
children,
|
126
135
|
selectedIds,
|
127
136
|
newItem.id,
|
128
137
|
depth + 1
|
129
|
-
)
|
138
|
+
);
|
130
139
|
}
|
131
|
-
return newItem
|
132
|
-
})
|
133
|
-
}
|
140
|
+
return newItem;
|
141
|
+
});
|
142
|
+
};
|
134
143
|
|
135
144
|
useEffect(() => {
|
136
145
|
const formattedData = addCheckedAndParentProperty(
|
137
146
|
treeData,
|
138
147
|
variant === "single" ? [selectedIds?.[0]] : selectedIds
|
139
|
-
)
|
148
|
+
);
|
140
149
|
|
141
|
-
setFormattedData(formattedData)
|
150
|
+
setFormattedData(formattedData);
|
142
151
|
|
143
152
|
if (variant === "single") {
|
144
153
|
// No selectedIds, reset state
|
145
154
|
if (selectedIds?.length === 0 || !selectedIds?.length) {
|
146
|
-
setSingleSelectedItem({ id: [], value: "", item: []})
|
155
|
+
setSingleSelectedItem({ id: [], value: "", item: [] });
|
147
156
|
} else {
|
148
157
|
// If there is a selectedId but no current item, set the selectedItem
|
149
158
|
if (selectedIds?.length !== 0 && !singleSelectedItem.value) {
|
150
|
-
const selectedItem = filterFormattedDataById(
|
159
|
+
const selectedItem = filterFormattedDataById(
|
160
|
+
formattedData,
|
161
|
+
selectedIds[0]
|
162
|
+
);
|
151
163
|
|
152
164
|
if (!selectedItem.length) {
|
153
|
-
setSingleSelectedItem({ id: [], value: "", item: []})
|
165
|
+
setSingleSelectedItem({ id: [], value: "", item: [] });
|
154
166
|
} else {
|
155
|
-
const { id, value } = selectedItem[0]
|
156
|
-
setSingleSelectedItem({ id: [id], value, item: selectedItem})
|
167
|
+
const { id, value } = selectedItem[0];
|
168
|
+
setSingleSelectedItem({ id: [id], value, item: selectedItem });
|
157
169
|
}
|
158
170
|
}
|
159
171
|
}
|
160
172
|
}
|
161
|
-
}, [treeData, selectedIds])
|
173
|
+
}, [treeData, selectedIds]);
|
162
174
|
|
163
175
|
useEffect(() => {
|
164
176
|
if (returnAllSelected) {
|
165
|
-
setReturnedArray(getCheckedItems(formattedData))
|
177
|
+
setReturnedArray(getCheckedItems(formattedData));
|
166
178
|
} else if (variant === "single") {
|
167
|
-
setDefaultReturn(singleSelectedItem.item)
|
179
|
+
setDefaultReturn(singleSelectedItem.item);
|
168
180
|
} else {
|
169
|
-
setDefaultReturn(getDefaultCheckedItems(formattedData))
|
181
|
+
setDefaultReturn(getDefaultCheckedItems(formattedData));
|
170
182
|
}
|
171
|
-
}, [formattedData])
|
183
|
+
}, [formattedData]);
|
172
184
|
|
173
185
|
useEffect(() => {
|
174
186
|
// Function to handle clicks outside the dropdown
|
175
187
|
const handleClickOutside = (event: any) => {
|
176
|
-
if (
|
188
|
+
if (
|
189
|
+
dropdownRef.current &&
|
190
|
+
!dropdownRef.current.contains(event.target) &&
|
191
|
+
event.target.id !== arrowDownElementId &&
|
192
|
+
event.target.id !== arrowUpElementId
|
193
|
+
) {
|
177
194
|
setIsDropdownClosed(true)
|
178
195
|
}
|
179
|
-
}
|
196
|
+
};
|
180
197
|
// Attach the event listener
|
181
|
-
window.addEventListener("click", handleClickOutside)
|
198
|
+
window.addEventListener("click", handleClickOutside);
|
182
199
|
// Clean up the event listener on unmount
|
183
200
|
return () => {
|
184
|
-
window.removeEventListener("click", handleClickOutside)
|
185
|
-
}
|
186
|
-
}, [])
|
187
|
-
|
188
|
-
|
201
|
+
window.removeEventListener("click", handleClickOutside);
|
202
|
+
};
|
203
|
+
}, []);
|
189
204
|
|
190
205
|
// Iterate over tree, find item and set checked or unchecked
|
191
206
|
const modifyValue = (
|
@@ -194,234 +209,182 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
194
209
|
check: boolean
|
195
210
|
) => {
|
196
211
|
if (!Array.isArray(tree)) {
|
197
|
-
return
|
212
|
+
return;
|
198
213
|
}
|
199
214
|
return tree.map((item: any) => {
|
200
|
-
if (item.id != id) item.children = modifyValue(id, item.children, check)
|
215
|
+
if (item.id != id) item.children = modifyValue(id, item.children, check);
|
201
216
|
else {
|
202
|
-
item.checked = check
|
217
|
+
item.checked = check;
|
203
218
|
|
204
219
|
if (variant === "single") {
|
205
220
|
// Single select: no children should be checked
|
206
|
-
item.children = modifyRecursive(item.children, !check)
|
221
|
+
item.children = modifyRecursive(item.children, !check);
|
207
222
|
} else {
|
208
|
-
item.children = modifyRecursive(item.children, check)
|
223
|
+
item.children = modifyRecursive(item.children, check);
|
209
224
|
}
|
210
225
|
}
|
211
226
|
|
212
|
-
return item
|
213
|
-
})
|
214
|
-
}
|
227
|
+
return item;
|
228
|
+
});
|
229
|
+
};
|
215
230
|
|
216
231
|
// Clone tree, check items + children
|
217
232
|
const checkItem = (item: { [key: string]: any }) => {
|
218
|
-
const tree = cloneDeep(formattedData)
|
233
|
+
const tree = cloneDeep(formattedData);
|
219
234
|
if (returnAllSelected) {
|
220
|
-
return modifyValue(item.id, tree, true)
|
235
|
+
return modifyValue(item.id, tree, true);
|
221
236
|
} else {
|
222
|
-
const checkedTree = modifyValue(item.id, tree, true)
|
223
|
-
return recursiveCheckParent(item, checkedTree)
|
237
|
+
const checkedTree = modifyValue(item.id, tree, true);
|
238
|
+
return recursiveCheckParent(item, checkedTree);
|
224
239
|
}
|
225
|
-
}
|
240
|
+
};
|
226
241
|
|
227
242
|
// Clone tree, uncheck items + children
|
228
243
|
const unCheckItem = (item: { [key: string]: any }) => {
|
229
|
-
const tree = cloneDeep(formattedData)
|
244
|
+
const tree = cloneDeep(formattedData);
|
230
245
|
if (returnAllSelected) {
|
231
|
-
return modifyValue(item.id, tree, false)
|
246
|
+
return modifyValue(item.id, tree, false);
|
232
247
|
} else {
|
233
|
-
const uncheckedTree = modifyValue(item.id, tree, false)
|
234
|
-
return getAncestorsOfUnchecked(uncheckedTree, item)
|
248
|
+
const uncheckedTree = modifyValue(item.id, tree, false);
|
249
|
+
return getAncestorsOfUnchecked(uncheckedTree, item);
|
235
250
|
}
|
236
|
-
}
|
251
|
+
};
|
237
252
|
|
238
253
|
// setFormattedData with proper properties
|
239
254
|
const changeItem = (item: { [key: string]: any }, check: boolean) => {
|
240
|
-
const tree = check ? checkItem(item) : unCheckItem(item)
|
241
|
-
setFormattedData(tree)
|
255
|
+
const tree = check ? checkItem(item) : unCheckItem(item);
|
256
|
+
setFormattedData(tree);
|
242
257
|
|
243
|
-
return tree
|
244
|
-
}
|
245
|
-
|
246
|
-
|
258
|
+
return tree;
|
259
|
+
};
|
247
260
|
|
248
261
|
// Click event for x on form pill
|
249
262
|
const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
|
250
263
|
// Prevents the dropdown from closing when clicking on the pill
|
251
|
-
event.stopPropagation()
|
252
|
-
const updatedTree = changeItem(clickedItem, false)
|
264
|
+
event.stopPropagation();
|
265
|
+
const updatedTree = changeItem(clickedItem, false);
|
253
266
|
// Logic for removing items from returnArray or defaultReturn when pills clicked
|
254
267
|
if (returnAllSelected) {
|
255
|
-
onSelect(getCheckedItems(updatedTree))
|
268
|
+
onSelect(getCheckedItems(updatedTree));
|
256
269
|
} else {
|
257
|
-
onSelect(getDefaultCheckedItems(updatedTree))
|
270
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
258
271
|
}
|
259
|
-
}
|
272
|
+
};
|
260
273
|
|
261
274
|
// Handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
|
262
275
|
const handleInputWrapperClick = (e: any) => {
|
263
|
-
e.stopPropagation()
|
264
276
|
if (
|
265
277
|
e.target.id === "multiselect_input" ||
|
266
278
|
e.target.classList.contains("pb_form_pill_tag")
|
267
279
|
) {
|
268
|
-
return
|
280
|
+
return;
|
269
281
|
}
|
270
|
-
setIsDropdownClosed(!isDropdownClosed)
|
271
|
-
}
|
282
|
+
setIsDropdownClosed(!isDropdownClosed);
|
283
|
+
};
|
272
284
|
|
273
285
|
// Main function to handle any click inside dropdown
|
274
286
|
const handledropdownItemClick = (e: any, check: boolean) => {
|
275
|
-
const clickedItem = e.target.parentNode.id
|
287
|
+
const clickedItem = e.target.parentNode.id;
|
276
288
|
// Setting filterItem to "" will clear textinput and clear typeahead
|
277
|
-
setFilterItem("")
|
289
|
+
setFilterItem("");
|
278
290
|
|
279
|
-
const filtered = filterFormattedDataById(formattedData, clickedItem)
|
280
|
-
const updatedTree = changeItem(filtered[0], check)
|
291
|
+
const filtered = filterFormattedDataById(formattedData, clickedItem);
|
292
|
+
const updatedTree = changeItem(filtered[0], check);
|
281
293
|
if (returnAllSelected) {
|
282
|
-
onSelect(getCheckedItems(updatedTree))
|
294
|
+
onSelect(getCheckedItems(updatedTree));
|
283
295
|
} else {
|
284
|
-
onSelect(getDefaultCheckedItems(updatedTree))
|
296
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
285
297
|
}
|
286
|
-
}
|
298
|
+
};
|
287
299
|
|
288
300
|
// Single select
|
289
|
-
const handleRadioButtonClick = (
|
290
|
-
|
291
|
-
) => {
|
292
|
-
const { id, value: inputText } = e.target
|
301
|
+
const handleRadioButtonClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
302
|
+
const { id, value: inputText } = e.target;
|
293
303
|
// The radio button needs a unique ID, this grabs the ID before the hyphen
|
294
|
-
const selectedItemID = id.match(/^[^-]*/)[0]
|
304
|
+
const selectedItemID = id.match(/^[^-]*/)[0];
|
295
305
|
// Reset tree checked state, triggering useEffect
|
296
|
-
const treeWithNoSelections = modifyRecursive(formattedData, false)
|
306
|
+
const treeWithNoSelections = modifyRecursive(formattedData, false);
|
297
307
|
// Update tree with single selection
|
298
|
-
const treeWithSelectedItem = modifyValue(
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
308
|
+
const treeWithSelectedItem = modifyValue(
|
309
|
+
selectedItemID,
|
310
|
+
treeWithNoSelections,
|
311
|
+
true
|
312
|
+
);
|
313
|
+
const selectedItem = filterFormattedDataById(
|
314
|
+
treeWithSelectedItem,
|
315
|
+
selectedItemID
|
316
|
+
);
|
317
|
+
|
318
|
+
setFormattedData(treeWithSelectedItem);
|
319
|
+
setSingleSelectedItem({
|
320
|
+
id: [selectedItemID],
|
321
|
+
value: inputText,
|
322
|
+
item: selectedItem,
|
323
|
+
});
|
303
324
|
// Reset the filter to always display dropdown options on click
|
304
|
-
setFilterItem("")
|
305
|
-
setIsDropdownClosed(true)
|
325
|
+
setFilterItem("");
|
326
|
+
setIsDropdownClosed(true);
|
306
327
|
|
307
|
-
onSelect(selectedItem)
|
328
|
+
onSelect(selectedItem);
|
308
329
|
};
|
309
330
|
|
310
331
|
// Single select: reset the tree state upon typing
|
311
332
|
const handleRadioInputChange = (inputText: string) => {
|
312
|
-
modifyRecursive(formattedData, false)
|
313
|
-
setDefaultReturn([])
|
314
|
-
setSingleSelectedItem({id: [], value: inputText, item: []})
|
315
|
-
setFilterItem(inputText)
|
333
|
+
modifyRecursive(formattedData, false);
|
334
|
+
setDefaultReturn([]);
|
335
|
+
setSingleSelectedItem({ id: [], value: inputText, item: [] });
|
336
|
+
setFilterItem(inputText);
|
316
337
|
};
|
317
338
|
|
318
|
-
const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1
|
339
|
+
const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1;
|
319
340
|
|
320
341
|
// Handle click on chevron toggles in dropdown
|
321
342
|
const handleToggleClick = (id: string, event: React.MouseEvent) => {
|
322
|
-
event.stopPropagation()
|
323
|
-
const clickedItem = filterFormattedDataById(formattedData, id)
|
343
|
+
event.stopPropagation();
|
344
|
+
const clickedItem = filterFormattedDataById(formattedData, id);
|
324
345
|
if (clickedItem) {
|
325
|
-
let expandedArray = [...expanded]
|
326
|
-
const itemExpanded = isTreeRowExpanded(clickedItem[0])
|
346
|
+
let expandedArray = [...expanded];
|
347
|
+
const itemExpanded = isTreeRowExpanded(clickedItem[0]);
|
327
348
|
|
328
349
|
if (itemExpanded)
|
329
|
-
expandedArray = expandedArray.filter((i) => i != clickedItem[0].id)
|
330
|
-
else expandedArray.push(clickedItem[0].id)
|
350
|
+
expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
|
351
|
+
else expandedArray.push(clickedItem[0].id);
|
331
352
|
|
332
|
-
setExpanded(expandedArray)
|
353
|
+
setExpanded(expandedArray);
|
333
354
|
}
|
334
|
-
}
|
355
|
+
};
|
335
356
|
|
336
357
|
const itemsSelectedLength = () => {
|
337
|
-
let items
|
358
|
+
let items;
|
338
359
|
if (returnAllSelected && returnedArray && returnedArray.length) {
|
339
|
-
items = returnedArray.length
|
360
|
+
items = returnedArray.length;
|
340
361
|
} else if (!returnAllSelected && defaultReturn && defaultReturn.length) {
|
341
|
-
items = defaultReturn.length
|
362
|
+
items = defaultReturn.length;
|
342
363
|
}
|
343
|
-
return items
|
344
|
-
}
|
364
|
+
return items;
|
365
|
+
};
|
345
366
|
|
346
367
|
// Rendering formattedData to UI based on typeahead
|
347
|
-
const renderNestedOptions = (items: { [key: string]:
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
: "toggle_icon"
|
368
|
-
}
|
369
|
-
icon={
|
370
|
-
isTreeRowExpanded(item) ? "chevron-down" : "chevron-right"
|
371
|
-
}
|
372
|
-
onClick={(event: any) =>
|
373
|
-
handleToggleClick(item.id, event)
|
374
|
-
}
|
375
|
-
variant="link"
|
376
|
-
/>
|
377
|
-
</div>
|
378
|
-
}
|
379
|
-
{ variant === "single" ? (
|
380
|
-
item.hideRadio ? (
|
381
|
-
<Body>{item.label}</Body>
|
382
|
-
) :
|
383
|
-
<Radio
|
384
|
-
checked={item.checked}
|
385
|
-
id={`${item.id}-${item.label}`}
|
386
|
-
label={item.label}
|
387
|
-
name={inputName}
|
388
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) => (
|
389
|
-
handleRadioButtonClick(e)
|
390
|
-
)}
|
391
|
-
padding={item.children ? 'none' : 'xs'}
|
392
|
-
type="radio"
|
393
|
-
value={item.label}
|
394
|
-
/>
|
395
|
-
) : (
|
396
|
-
<Checkbox
|
397
|
-
id={item.id}
|
398
|
-
text={item.label}
|
399
|
-
>
|
400
|
-
<input
|
401
|
-
checked={item.checked}
|
402
|
-
name={item.label}
|
403
|
-
onChange={(e) => {
|
404
|
-
handledropdownItemClick(e, !item.checked)
|
405
|
-
}}
|
406
|
-
type="checkbox"
|
407
|
-
value={item.label}
|
408
|
-
/>
|
409
|
-
</Checkbox>
|
410
|
-
)}
|
411
|
-
</div>
|
412
|
-
{isTreeRowExpanded(item) &&
|
413
|
-
item.children &&
|
414
|
-
item.children.length > 0 &&
|
415
|
-
(variant === "single" || !filterItem) && ( // Show children if expanded is true
|
416
|
-
<div>{renderNestedOptions(item.children)}</div>
|
417
|
-
)}
|
418
|
-
</li>
|
419
|
-
</div>
|
420
|
-
)
|
421
|
-
})}
|
422
|
-
</ul>
|
423
|
-
)
|
424
|
-
}
|
368
|
+
const renderNestedOptions = (items: { [key: string]: string; }[] | any ) => {
|
369
|
+
const hasOptionsChild = React.Children.toArray(props.children).some(
|
370
|
+
child => child.type === MultiLevelSelect.Options
|
371
|
+
);
|
372
|
+
|
373
|
+
if (hasOptionsChild) {
|
374
|
+
return React.Children.map(props.children, (child) => {
|
375
|
+
if (child.type === MultiLevelSelect.Options) {
|
376
|
+
return React.cloneElement(child, { items });
|
377
|
+
}
|
378
|
+
return null;
|
379
|
+
});
|
380
|
+
} else {
|
381
|
+
// If nochildren, use the default rendering
|
382
|
+
return (
|
383
|
+
<MultiLevelSelectOptions items={items} />
|
384
|
+
);
|
385
|
+
}
|
386
|
+
};
|
387
|
+
|
425
388
|
|
426
389
|
return (
|
427
390
|
<div
|
@@ -431,12 +394,20 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
431
394
|
className={classes}
|
432
395
|
id={id}
|
433
396
|
>
|
434
|
-
<
|
435
|
-
|
397
|
+
<MultiLevelSelectContext.Provider value={{
|
398
|
+
variant,
|
399
|
+
inputName,
|
400
|
+
renderNestedOptions,
|
401
|
+
isTreeRowExpanded,
|
402
|
+
handleToggleClick,
|
403
|
+
handleRadioButtonClick,
|
404
|
+
handledropdownItemClick,
|
405
|
+
filterItem,
|
406
|
+
}}>
|
407
|
+
<div className="wrapper"
|
436
408
|
ref={dropdownRef}
|
437
409
|
>
|
438
|
-
<div
|
439
|
-
className="input_wrapper"
|
410
|
+
<div className="input_wrapper"
|
440
411
|
onClick={handleInputWrapperClick}
|
441
412
|
>
|
442
413
|
<div className="input_inner_container">
|
@@ -502,15 +473,17 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
502
473
|
|
503
474
|
<input
|
504
475
|
id="multiselect_input"
|
505
|
-
onChange={(e) =>{
|
476
|
+
onChange={(e) => {
|
506
477
|
variant === "single"
|
507
478
|
? handleRadioInputChange(e.target.value)
|
508
|
-
: setFilterItem(e.target.value)
|
479
|
+
: setFilterItem(e.target.value);
|
509
480
|
}}
|
510
481
|
onClick={() => setIsDropdownClosed(false)}
|
511
482
|
placeholder={
|
512
483
|
inputDisplay === "none" && itemsSelectedLength()
|
513
|
-
? `${itemsSelectedLength()} ${
|
484
|
+
? `${itemsSelectedLength()} ${
|
485
|
+
itemsSelectedLength() === 1 ? "item" : "items"
|
486
|
+
} selected`
|
514
487
|
: "Start typing..."
|
515
488
|
}
|
516
489
|
value={singleSelectedItem.value || filterItem}
|
@@ -518,16 +491,20 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
518
491
|
</div>
|
519
492
|
|
520
493
|
{isDropdownClosed ? (
|
521
|
-
<div
|
494
|
+
<div id={arrowDownElementId}
|
495
|
+
key="chevron-down">
|
522
496
|
<Icon
|
523
497
|
icon="chevron-down"
|
498
|
+
id={arrowDownElementId}
|
524
499
|
size="xs"
|
525
500
|
/>
|
526
501
|
</div>
|
527
502
|
) : (
|
528
|
-
<div
|
503
|
+
<div id={arrowUpElementId}
|
504
|
+
key="chevron-up">
|
529
505
|
<Icon
|
530
506
|
icon="chevron-up"
|
507
|
+
id={arrowUpElementId}
|
531
508
|
size="xs"
|
532
509
|
/>
|
533
510
|
</div>
|
@@ -535,15 +512,16 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
535
512
|
</div>
|
536
513
|
|
537
514
|
<div className={`dropdown_menu ${isDropdownClosed ? "close" : "open"}`}>
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
: formattedData
|
542
|
-
)}
|
515
|
+
{renderNestedOptions(
|
516
|
+
filterItem ? findByFilter(formattedData, filterItem) : formattedData
|
517
|
+
)}
|
543
518
|
</div>
|
544
519
|
</div>
|
520
|
+
</MultiLevelSelectContext.Provider>
|
545
521
|
</div>
|
546
|
-
)
|
547
|
-
}
|
522
|
+
);
|
523
|
+
};
|
524
|
+
|
525
|
+
MultiLevelSelect.Options = MultiLevelSelectOptions;
|
548
526
|
|
549
|
-
export default MultiLevelSelect
|
527
|
+
export default MultiLevelSelect;
|