playbook_ui 16.1.0.pre.alpha.play264213818 → 16.1.0.pre.alpha.play276813969
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_card/docs/_card_light.html.erb +3 -35
- data/app/pb_kits/playbook/pb_dialog/_dialog.scss +8 -6
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +6 -0
- data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +83 -13
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_rails.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_blank_selection_react.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.html.erb +52 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.jsx +72 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_clearable.md +5 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.html.erb +20 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_rails.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_constrain_height_react.md +8 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.html.erb +6 -3
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.jsx +1 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_label.md +3 -1
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.html.erb +9 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.jsx +33 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_with_placeholder.md +3 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +6 -0
- data/app/pb_kits/playbook/pb_dropdown/docs/index.js +4 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +11 -5
- data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +15 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +94 -0
- data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +5 -1
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.html.erb +7 -2
- data/app/pb_kits/playbook/pb_dropdown/dropdown_trigger.rb +4 -0
- data/app/pb_kits/playbook/pb_dropdown/index.js +184 -77
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +3 -0
- data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +18 -1
- data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +6 -0
- data/app/pb_kits/playbook/pb_filter/Filter/SortMenu.tsx +1 -1
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.html.erb +2 -2
- data/app/pb_kits/playbook/pb_filter/docs/_filter_default.jsx +16 -9
- data/app/pb_kits/playbook/pb_filter/filter.rb +2 -2
- data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +1 -0
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.html.erb +5 -5
- data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_truncated_text.jsx +4 -4
- data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +4 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +638 -549
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.html.erb +3 -3
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.jsx +4 -7
- data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_label.md +3 -0
- data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +4 -4
- data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +20 -5
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_meter_settings.jsx +1 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.html.erb +7 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.jsx +24 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/_passphrase_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_passphrase/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.rb +2 -0
- data/app/pb_kits/playbook/pb_passphrase/passphrase.test.jsx +30 -1
- data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.html.erb +5 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.jsx +14 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_required_indicator.md +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +2 -0
- data/app/pb_kits/playbook/pb_phone_number_input/docs/index.js +1 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +3 -0
- data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.test.js +34 -3
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +24 -1
- data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +2 -1
- data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +4 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.html.erb +1 -1
- data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_truncated_text.jsx +1 -1
- data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
- data/dist/chunks/_typeahead-C4YsbA48.js +1 -0
- data/dist/chunks/vendor.js +2 -2
- 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/forms/builder/phone_number_field.rb +9 -0
- data/lib/playbook/truncate.rb +1 -1
- data/lib/playbook/version.rb +1 -1
- metadata +22 -3
- data/dist/chunks/_typeahead-B9a6ZsEP.js +0 -1
|
@@ -26,599 +26,688 @@ import {
|
|
|
26
26
|
getExpandedItems,
|
|
27
27
|
} from "./_helper_functions";
|
|
28
28
|
|
|
29
|
-
interface MultiLevelSelectComponent
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
> {
|
|
29
|
+
interface MultiLevelSelectComponent extends React.ForwardRefExoticComponent<
|
|
30
|
+
MultiLevelSelectProps & React.RefAttributes<HTMLInputElement>
|
|
31
|
+
> {
|
|
33
32
|
Options: typeof MultiLevelSelectOptions;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
type MultiLevelSelectProps = {
|
|
37
|
-
aria?: { [key: string]: string }
|
|
38
|
-
className?: string
|
|
39
|
-
data?: { [key: string]: string }
|
|
40
|
-
disabled?: boolean
|
|
41
|
-
error?: string
|
|
42
|
-
htmlOptions?: {[key: string]: string | number | boolean | (() => void)}
|
|
43
|
-
id?: string
|
|
44
|
-
inputDisplay?: "pills" | "none"
|
|
45
|
-
inputName?: string
|
|
46
|
-
label?: string
|
|
47
|
-
name?: string
|
|
48
|
-
required?: boolean
|
|
49
|
-
returnAllSelected?: boolean
|
|
50
|
-
showCheckedChildren?: boolean
|
|
51
|
-
treeData?: { [key: string]: string
|
|
52
|
-
onChange?: (event: { target: { name?: string; value: any } }) => void
|
|
53
|
-
onSelect?: (prop: { [key: string]: any }) => void
|
|
54
|
-
selectedIds?: string[] | any
|
|
55
|
-
variant?: "multi" | "single"
|
|
56
|
-
wrapped?: boolean
|
|
57
|
-
pillColor?:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
item: [],
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const arrowDownElementId = `arrow_down_${id}`
|
|
121
|
-
const arrowUpElementId = `arrow_up_${id}`
|
|
122
|
-
|
|
123
|
-
const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => {
|
|
124
|
-
if (!Array.isArray(tree)) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
return tree.map((item: { [key: string]: any }) => {
|
|
128
|
-
if (!item.disabled) {
|
|
129
|
-
item.checked = check;
|
|
130
|
-
}
|
|
131
|
-
item.children = modifyRecursive(item.children, check);
|
|
132
|
-
return item;
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// Function to map over data and add parent_id + depth property to each item
|
|
137
|
-
const addCheckedAndParentProperty = (
|
|
138
|
-
treeData: { [key: string]: any }[],
|
|
139
|
-
selectedIds: string[],
|
|
140
|
-
parent_id: string | null = null,
|
|
141
|
-
depth = 0,
|
|
142
|
-
parentDisabled = false
|
|
143
|
-
) => {
|
|
144
|
-
if (!Array.isArray(treeData)) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
return treeData.map((item: { [key: string]: any } | any) => {
|
|
148
|
-
// An item is disabled if it is explicitly set as disabled or if its parent is disabled
|
|
149
|
-
const isDisabled = item.disabled || (parentDisabled && !returnAllSelected);
|
|
150
|
-
|
|
151
|
-
const newItem = {
|
|
152
|
-
...item,
|
|
153
|
-
checked: Boolean(
|
|
154
|
-
selectedIds && selectedIds.length && selectedIds.includes(item.id)
|
|
155
|
-
),
|
|
156
|
-
parent_id,
|
|
157
|
-
depth,
|
|
158
|
-
disabled: isDisabled,
|
|
159
|
-
};
|
|
160
|
-
if (newItem.children && newItem.children.length > 0) {
|
|
161
|
-
const children =
|
|
162
|
-
item.checked && !returnAllSelected
|
|
163
|
-
? modifyRecursive(item.children, true)
|
|
164
|
-
: item.children;
|
|
165
|
-
newItem.children = addCheckedAndParentProperty(
|
|
166
|
-
children,
|
|
167
|
-
selectedIds,
|
|
168
|
-
newItem.id,
|
|
169
|
-
depth + 1,
|
|
170
|
-
isDisabled
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
return newItem;
|
|
174
|
-
});
|
|
175
|
-
};
|
|
36
|
+
aria?: { [key: string]: string };
|
|
37
|
+
className?: string;
|
|
38
|
+
data?: { [key: string]: string };
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
error?: string;
|
|
41
|
+
htmlOptions?: { [key: string]: string | number | boolean | (() => void) };
|
|
42
|
+
id?: string;
|
|
43
|
+
inputDisplay?: "pills" | "none";
|
|
44
|
+
inputName?: string;
|
|
45
|
+
label?: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
required?: boolean;
|
|
48
|
+
returnAllSelected?: boolean;
|
|
49
|
+
showCheckedChildren?: boolean;
|
|
50
|
+
treeData?: { [key: string]: string }[] | any;
|
|
51
|
+
onChange?: (event: { target: { name?: string; value: any } }) => void;
|
|
52
|
+
onSelect?: (prop: { [key: string]: any }) => void;
|
|
53
|
+
selectedIds?: string[] | any;
|
|
54
|
+
variant?: "multi" | "single";
|
|
55
|
+
wrapped?: boolean;
|
|
56
|
+
pillColor?:
|
|
57
|
+
| "primary"
|
|
58
|
+
| "neutral"
|
|
59
|
+
| "success"
|
|
60
|
+
| "warning"
|
|
61
|
+
| "error"
|
|
62
|
+
| "info"
|
|
63
|
+
| "data_1"
|
|
64
|
+
| "data_2"
|
|
65
|
+
| "data_3"
|
|
66
|
+
| "data_4"
|
|
67
|
+
| "data_5"
|
|
68
|
+
| "data_6"
|
|
69
|
+
| "data_7"
|
|
70
|
+
| "data_8"
|
|
71
|
+
| "windows"
|
|
72
|
+
| "siding"
|
|
73
|
+
| "roofing"
|
|
74
|
+
| "doors"
|
|
75
|
+
| "gutters"
|
|
76
|
+
| "solar"
|
|
77
|
+
| "insulation"
|
|
78
|
+
| "accessories";
|
|
79
|
+
} & GlobalProps;
|
|
80
|
+
|
|
81
|
+
const MultiLevelSelect = forwardRef<HTMLInputElement, MultiLevelSelectProps>(
|
|
82
|
+
(props) => {
|
|
83
|
+
const {
|
|
84
|
+
aria = {},
|
|
85
|
+
className,
|
|
86
|
+
data = {},
|
|
87
|
+
disabled = false,
|
|
88
|
+
error,
|
|
89
|
+
htmlOptions = {},
|
|
90
|
+
id,
|
|
91
|
+
inputDisplay = "pills",
|
|
92
|
+
inputName,
|
|
93
|
+
name,
|
|
94
|
+
label,
|
|
95
|
+
required = false,
|
|
96
|
+
returnAllSelected = false,
|
|
97
|
+
showCheckedChildren = true,
|
|
98
|
+
treeData,
|
|
99
|
+
onChange = () => null,
|
|
100
|
+
onSelect = () => null,
|
|
101
|
+
selectedIds,
|
|
102
|
+
variant = "multi",
|
|
103
|
+
wrapped,
|
|
104
|
+
pillColor = "primary",
|
|
105
|
+
} = props;
|
|
106
|
+
|
|
107
|
+
const ariaProps = buildAriaProps(aria);
|
|
108
|
+
const dataProps = buildDataProps(data);
|
|
109
|
+
const htmlProps = buildHtmlProps(htmlOptions);
|
|
110
|
+
const classes = classnames(
|
|
111
|
+
buildCss("pb_multi_level_select"),
|
|
112
|
+
error && "error",
|
|
113
|
+
globalProps(props),
|
|
114
|
+
className
|
|
115
|
+
);
|
|
176
116
|
|
|
177
|
-
|
|
178
|
-
|
|
117
|
+
const dropdownRef = useRef(null);
|
|
118
|
+
|
|
119
|
+
// State for whether dropdown is open or closed
|
|
120
|
+
const [isDropdownClosed, setIsDropdownClosed] = useState(true);
|
|
121
|
+
// State from onChange for textinput, to use for filtering to create typeahead
|
|
122
|
+
const [filterItem, setFilterItem] = useState("");
|
|
123
|
+
// FormattedData with checked and parent_id added
|
|
124
|
+
const [formattedData, setFormattedData] = useState([]);
|
|
125
|
+
// State for the return of returnAllSelected
|
|
126
|
+
const [returnedArray, setReturnedArray] = useState([]);
|
|
127
|
+
// State for default return
|
|
128
|
+
const [defaultReturn, setDefaultReturn] = useState([]);
|
|
129
|
+
// Get expanded items from treeData
|
|
130
|
+
const initialExpandedItems = getExpandedItems(
|
|
179
131
|
treeData,
|
|
180
|
-
|
|
132
|
+
selectedIds,
|
|
133
|
+
showCheckedChildren,
|
|
181
134
|
);
|
|
135
|
+
// Initialize state with expanded items
|
|
136
|
+
const [expanded, setExpanded] = useState(initialExpandedItems);
|
|
137
|
+
|
|
138
|
+
// Single Select specific state
|
|
139
|
+
const [singleSelectedItem, setSingleSelectedItem] = useState({
|
|
140
|
+
id: [],
|
|
141
|
+
value: "",
|
|
142
|
+
item: [],
|
|
143
|
+
});
|
|
182
144
|
|
|
183
|
-
|
|
145
|
+
const arrowDownElementId = `arrow_down_${id}`;
|
|
146
|
+
const arrowUpElementId = `arrow_up_${id}`;
|
|
147
|
+
// Control id for label htmlFor: use suffix to avoid conflict with outer div's id
|
|
148
|
+
const sanitizeForId = (str: string) =>
|
|
149
|
+
str.toLowerCase().replace(/\s+/g, "_").replace(/[^a-z0-9_]/g, "");
|
|
150
|
+
const labelForId = id
|
|
151
|
+
? `${id}_input`
|
|
152
|
+
: (name ? sanitizeForId(name) : null) ||
|
|
153
|
+
(label ? sanitizeForId(label) : null) ||
|
|
154
|
+
"multiselect_input";
|
|
155
|
+
const errorId = error ? `${labelForId}-error` : undefined;
|
|
156
|
+
|
|
157
|
+
const modifyRecursive = (
|
|
158
|
+
tree: { [key: string]: any }[],
|
|
159
|
+
check: boolean,
|
|
160
|
+
) => {
|
|
161
|
+
if (!Array.isArray(tree)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
return tree.map((item: { [key: string]: any }) => {
|
|
165
|
+
if (!item.disabled) {
|
|
166
|
+
item.checked = check;
|
|
167
|
+
}
|
|
168
|
+
item.children = modifyRecursive(item.children, check);
|
|
169
|
+
return item;
|
|
170
|
+
});
|
|
171
|
+
};
|
|
184
172
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
173
|
+
// Function to map over data and add parent_id + depth property to each item
|
|
174
|
+
const addCheckedAndParentProperty = (
|
|
175
|
+
treeData: { [key: string]: any }[],
|
|
176
|
+
selectedIds: string[],
|
|
177
|
+
parent_id: string | null = null,
|
|
178
|
+
depth = 0,
|
|
179
|
+
parentDisabled = false,
|
|
180
|
+
) => {
|
|
181
|
+
if (!Array.isArray(treeData)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
return treeData.map((item: { [key: string]: any } | any) => {
|
|
185
|
+
// An item is disabled if it is explicitly set as disabled or if its parent is disabled
|
|
186
|
+
const isDisabled =
|
|
187
|
+
item.disabled || (parentDisabled && !returnAllSelected);
|
|
188
|
+
|
|
189
|
+
const newItem = {
|
|
190
|
+
...item,
|
|
191
|
+
checked: Boolean(
|
|
192
|
+
selectedIds && selectedIds.length && selectedIds.includes(item.id),
|
|
193
|
+
),
|
|
194
|
+
parent_id,
|
|
195
|
+
depth,
|
|
196
|
+
disabled: isDisabled,
|
|
197
|
+
};
|
|
198
|
+
if (newItem.children && newItem.children.length > 0) {
|
|
199
|
+
const children =
|
|
200
|
+
item.checked && !returnAllSelected
|
|
201
|
+
? modifyRecursive(item.children, true)
|
|
202
|
+
: item.children;
|
|
203
|
+
newItem.children = addCheckedAndParentProperty(
|
|
204
|
+
children,
|
|
205
|
+
selectedIds,
|
|
206
|
+
newItem.id,
|
|
207
|
+
depth + 1,
|
|
208
|
+
isDisabled,
|
|
195
209
|
);
|
|
210
|
+
}
|
|
211
|
+
return newItem;
|
|
212
|
+
});
|
|
213
|
+
};
|
|
196
214
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
const formattedData = addCheckedAndParentProperty(
|
|
217
|
+
treeData,
|
|
218
|
+
variant === "single" ? [selectedIds?.[0]] : selectedIds,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
setFormattedData(formattedData);
|
|
222
|
+
|
|
223
|
+
if (variant === "single") {
|
|
224
|
+
// No selectedIds, reset state
|
|
225
|
+
if (selectedIds?.length === 0 || !selectedIds?.length) {
|
|
226
|
+
setSingleSelectedItem({ id: [], value: "", item: [] });
|
|
227
|
+
} else {
|
|
228
|
+
// If there is a selectedId but no current item, set the selectedItem
|
|
229
|
+
if (selectedIds?.length !== 0 && !singleSelectedItem.value) {
|
|
230
|
+
const selectedItem = filterFormattedDataById(
|
|
231
|
+
formattedData,
|
|
232
|
+
selectedIds[0],
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
if (!selectedItem.length) {
|
|
236
|
+
setSingleSelectedItem({ id: [], value: "", item: [] });
|
|
237
|
+
} else {
|
|
238
|
+
const { id, label } = selectedItem[0];
|
|
239
|
+
setSingleSelectedItem({
|
|
240
|
+
id: [id],
|
|
241
|
+
value: label,
|
|
242
|
+
item: selectedItem,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
202
245
|
}
|
|
203
246
|
}
|
|
204
247
|
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
setDefaultReturn(getDefaultCheckedItems(formattedData));
|
|
215
|
-
}
|
|
216
|
-
}, [formattedData]);
|
|
217
|
-
|
|
218
|
-
useEffect(() => {
|
|
219
|
-
// Function to handle clicks outside the dropdown
|
|
220
|
-
const handleClickOutside = (event: any) => {
|
|
221
|
-
if (
|
|
222
|
-
dropdownRef.current &&
|
|
223
|
-
!dropdownRef.current.contains(event.target) &&
|
|
224
|
-
event.target.id !== arrowDownElementId &&
|
|
225
|
-
event.target.id !== arrowUpElementId
|
|
226
|
-
) {
|
|
227
|
-
setIsDropdownClosed(true)
|
|
248
|
+
}, [treeData, selectedIds]);
|
|
249
|
+
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (returnAllSelected) {
|
|
252
|
+
setReturnedArray(getCheckedItems(formattedData));
|
|
253
|
+
} else if (variant === "single") {
|
|
254
|
+
setDefaultReturn(singleSelectedItem.item);
|
|
255
|
+
} else {
|
|
256
|
+
setDefaultReturn(getDefaultCheckedItems(formattedData));
|
|
228
257
|
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
setSingleSelectedItem({ id: [], value: "", item: [] });
|
|
247
|
-
onSelect([]);
|
|
258
|
+
}, [formattedData]);
|
|
259
|
+
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
// Function to handle clicks outside the dropdown
|
|
262
|
+
const handleClickOutside = (event: any) => {
|
|
263
|
+
// Don't close if clicking on the associated label
|
|
264
|
+
const labelEl = document.querySelector(`label[for="${labelForId}"]`);
|
|
265
|
+
if (labelEl?.contains(event.target)) return;
|
|
266
|
+
|
|
267
|
+
if (
|
|
268
|
+
dropdownRef.current &&
|
|
269
|
+
!dropdownRef.current.contains(event.target) &&
|
|
270
|
+
event.target.id !== arrowDownElementId &&
|
|
271
|
+
event.target.id !== arrowUpElementId
|
|
272
|
+
) {
|
|
273
|
+
setIsDropdownClosed(true);
|
|
274
|
+
}
|
|
248
275
|
};
|
|
276
|
+
// Attach the event listener
|
|
277
|
+
window.addEventListener("click", handleClickOutside);
|
|
278
|
+
// Clean up the event listener on unmount
|
|
249
279
|
return () => {
|
|
250
|
-
|
|
280
|
+
window.removeEventListener("click", handleClickOutside);
|
|
251
281
|
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
}, [labelForId]);
|
|
283
|
+
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (id) {
|
|
286
|
+
// Attach the clear function to the window, scoped by the id
|
|
287
|
+
(window as any)[`clearMultiLevelSelect_${id}`] = () => {
|
|
288
|
+
const resetData = modifyRecursive(formattedData, false);
|
|
289
|
+
setFormattedData(resetData);
|
|
290
|
+
setReturnedArray([]);
|
|
291
|
+
setDefaultReturn([]);
|
|
292
|
+
setSingleSelectedItem({ id: [], value: "", item: [] });
|
|
293
|
+
onSelect([]);
|
|
294
|
+
};
|
|
295
|
+
return () => {
|
|
296
|
+
delete (window as any)[`clearMultiLevelSelect_${id}`];
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}, [formattedData, id, onSelect]);
|
|
300
|
+
|
|
301
|
+
// Iterate over tree, find item and set checked or unchecked
|
|
302
|
+
const modifyValue = (
|
|
303
|
+
id: string,
|
|
304
|
+
tree: { [key: string]: any }[],
|
|
305
|
+
check: boolean,
|
|
306
|
+
) => {
|
|
307
|
+
if (!Array.isArray(tree)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
return tree.map((item: any) => {
|
|
311
|
+
if (item.id != id)
|
|
312
|
+
item.children = modifyValue(id, item.children, check);
|
|
313
|
+
else {
|
|
314
|
+
if (!item.disabled) {
|
|
315
|
+
item.checked = check;
|
|
316
|
+
}
|
|
317
|
+
if (variant === "single") {
|
|
318
|
+
// Single select: no children should be checked
|
|
319
|
+
item.children = modifyRecursive(item.children, !check);
|
|
320
|
+
} else {
|
|
321
|
+
item.children = modifyRecursive(item.children, check);
|
|
322
|
+
}
|
|
275
323
|
}
|
|
324
|
+
|
|
325
|
+
return item;
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Clone tree, check items + children
|
|
330
|
+
const checkItem = (item: { [key: string]: any }) => {
|
|
331
|
+
const tree = cloneDeep(formattedData);
|
|
332
|
+
if (returnAllSelected) {
|
|
333
|
+
return modifyValue(item.id, tree, true);
|
|
334
|
+
} else {
|
|
335
|
+
const checkedTree = modifyValue(item.id, tree, true);
|
|
336
|
+
return recursiveCheckParent(item, checkedTree);
|
|
276
337
|
}
|
|
338
|
+
};
|
|
277
339
|
|
|
278
|
-
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
const checkedTree = modifyValue(item.id, tree, true);
|
|
289
|
-
return recursiveCheckParent(item, checkedTree);
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
// Clone tree, uncheck items + children
|
|
294
|
-
const unCheckItem = (item: { [key: string]: any }) => {
|
|
295
|
-
const tree = cloneDeep(formattedData);
|
|
296
|
-
if (returnAllSelected) {
|
|
297
|
-
return modifyValue(item.id, tree, false);
|
|
298
|
-
} else {
|
|
299
|
-
const uncheckedTree = modifyValue(item.id, tree, false);
|
|
300
|
-
return getAncestorsOfUnchecked(uncheckedTree, item);
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
// setFormattedData with proper properties
|
|
305
|
-
const changeItem = (item: { [key: string]: any }, check: boolean) => {
|
|
306
|
-
const tree = check ? checkItem(item) : unCheckItem(item);
|
|
307
|
-
setFormattedData(tree);
|
|
308
|
-
|
|
309
|
-
return tree;
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// Click event for x on form pill
|
|
313
|
-
const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
|
|
314
|
-
// Prevents the dropdown from closing when clicking on the pill
|
|
315
|
-
event.stopPropagation();
|
|
316
|
-
const updatedTree = changeItem(clickedItem, false);
|
|
317
|
-
// Logic for removing items from returnArray or defaultReturn when pills clicked
|
|
318
|
-
if (returnAllSelected) {
|
|
319
|
-
onSelect(getCheckedItems(updatedTree));
|
|
320
|
-
onChange({ target: { name, value: getCheckedItems(updatedTree) } });
|
|
321
|
-
} else {
|
|
322
|
-
onSelect(getDefaultCheckedItems(updatedTree));
|
|
323
|
-
onChange({ target: { name, value: getDefaultCheckedItems(updatedTree) } });
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
// Handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
|
|
328
|
-
const handleInputWrapperClick = (e: any) => {
|
|
329
|
-
if (
|
|
330
|
-
e.target.id === "multiselect_input" ||
|
|
331
|
-
e.target.classList.contains("pb_form_pill_tag") ||
|
|
332
|
-
disabled
|
|
333
|
-
) {
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
setIsDropdownClosed(!isDropdownClosed);
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
// Main function to handle any click inside dropdown
|
|
340
|
-
const handledropdownItemClick = (e: any, check: boolean) => {
|
|
341
|
-
const clickedItem = e.target.parentNode.id;
|
|
342
|
-
// Setting filterItem to "" will clear textinput and clear typeahead
|
|
343
|
-
setFilterItem("");
|
|
344
|
-
|
|
345
|
-
const filtered = filterFormattedDataById(formattedData, clickedItem);
|
|
346
|
-
const updatedTree = changeItem(filtered[0], check);
|
|
347
|
-
if (returnAllSelected) {
|
|
348
|
-
onSelect(getCheckedItems(updatedTree));
|
|
349
|
-
onChange({ target: { name, value: getCheckedItems(updatedTree) } });
|
|
350
|
-
} else {
|
|
351
|
-
onSelect(getDefaultCheckedItems(updatedTree));
|
|
352
|
-
onChange({ target: { name, value: getDefaultCheckedItems(updatedTree) } });
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
// Single select
|
|
357
|
-
const handleRadioButtonClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
358
|
-
const { id, value: inputText } = e.target;
|
|
359
|
-
// The radio button needs a unique ID, this grabs the ID before the hyphen
|
|
360
|
-
const selectedItemID = id.match(/^[^-]*/)[0];
|
|
361
|
-
|
|
362
|
-
// Check if the item is disabled - if so, don't allow selection (safety check in addition to native disabled attribute)
|
|
363
|
-
const clickedItem = filterFormattedDataById(formattedData, selectedItemID);
|
|
364
|
-
if (clickedItem.length > 0 && clickedItem[0].disabled) {
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Reset tree checked state, triggering useEffect
|
|
369
|
-
const treeWithNoSelections = modifyRecursive(formattedData, false);
|
|
370
|
-
// Update tree with single selection
|
|
371
|
-
const treeWithSelectedItem = modifyValue(
|
|
372
|
-
selectedItemID,
|
|
373
|
-
treeWithNoSelections,
|
|
374
|
-
true
|
|
375
|
-
);
|
|
376
|
-
const selectedItem = filterFormattedDataById(
|
|
377
|
-
treeWithSelectedItem,
|
|
378
|
-
selectedItemID
|
|
379
|
-
);
|
|
340
|
+
// Clone tree, uncheck items + children
|
|
341
|
+
const unCheckItem = (item: { [key: string]: any }) => {
|
|
342
|
+
const tree = cloneDeep(formattedData);
|
|
343
|
+
if (returnAllSelected) {
|
|
344
|
+
return modifyValue(item.id, tree, false);
|
|
345
|
+
} else {
|
|
346
|
+
const uncheckedTree = modifyValue(item.id, tree, false);
|
|
347
|
+
return getAncestorsOfUnchecked(uncheckedTree, item);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
380
350
|
|
|
381
|
-
setFormattedData
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
item: selectedItem,
|
|
386
|
-
});
|
|
387
|
-
// Reset the filter to always display dropdown options on click
|
|
388
|
-
setFilterItem("");
|
|
389
|
-
setIsDropdownClosed(true);
|
|
390
|
-
|
|
391
|
-
onSelect(selectedItem);
|
|
392
|
-
onChange({ target: { name, value: selectedItem } });
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
// Single select: reset the tree state upon typing
|
|
396
|
-
const handleRadioInputChange = (inputText: string) => {
|
|
397
|
-
modifyRecursive(formattedData, false);
|
|
398
|
-
setDefaultReturn([]);
|
|
399
|
-
setSingleSelectedItem({ id: [], value: inputText, item: [] });
|
|
400
|
-
setFilterItem(inputText);
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1;
|
|
404
|
-
|
|
405
|
-
// Handle click on chevron toggles in dropdown
|
|
406
|
-
const handleToggleClick = (id: string, event: React.MouseEvent) => {
|
|
407
|
-
event.stopPropagation();
|
|
408
|
-
const clickedItem = filterFormattedDataById(formattedData, id);
|
|
409
|
-
if (clickedItem) {
|
|
410
|
-
let expandedArray = [...expanded];
|
|
411
|
-
const itemExpanded = isTreeRowExpanded(clickedItem[0]);
|
|
412
|
-
|
|
413
|
-
if (itemExpanded)
|
|
414
|
-
expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
|
|
415
|
-
else expandedArray.push(clickedItem[0].id);
|
|
416
|
-
|
|
417
|
-
setExpanded(expandedArray);
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
const itemsSelectedLength = () => {
|
|
422
|
-
let items;
|
|
423
|
-
if (returnAllSelected && returnedArray && returnedArray.length) {
|
|
424
|
-
items = returnedArray.length;
|
|
425
|
-
} else if (!returnAllSelected && defaultReturn && defaultReturn.length) {
|
|
426
|
-
items = defaultReturn.length;
|
|
427
|
-
}
|
|
428
|
-
return items;
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
// Rendering formattedData to UI based on typeahead
|
|
432
|
-
const renderNestedOptions = (items: { [key: string]: string; }[] | any ) => {
|
|
433
|
-
const hasOptionsChild = React.Children.toArray(props.children).some(
|
|
434
|
-
(child) => React.isValidElement(child) && child.type === MultiLevelSelect.Options
|
|
435
|
-
);
|
|
351
|
+
// setFormattedData with proper properties
|
|
352
|
+
const changeItem = (item: { [key: string]: any }, check: boolean) => {
|
|
353
|
+
const tree = check ? checkItem(item) : unCheckItem(item);
|
|
354
|
+
setFormattedData(tree);
|
|
436
355
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
356
|
+
return tree;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Click event for x on form pill
|
|
360
|
+
const handlePillClose = (
|
|
361
|
+
event: any,
|
|
362
|
+
clickedItem: { [key: string]: any },
|
|
363
|
+
) => {
|
|
364
|
+
// Prevents the dropdown from closing when clicking on the pill
|
|
365
|
+
event.stopPropagation();
|
|
366
|
+
const updatedTree = changeItem(clickedItem, false);
|
|
367
|
+
// Logic for removing items from returnArray or defaultReturn when pills clicked
|
|
368
|
+
if (returnAllSelected) {
|
|
369
|
+
onSelect(getCheckedItems(updatedTree));
|
|
370
|
+
onChange({ target: { name, value: getCheckedItems(updatedTree) } });
|
|
371
|
+
} else {
|
|
372
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
|
373
|
+
onChange({
|
|
374
|
+
target: { name, value: getDefaultCheckedItems(updatedTree) },
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// Handle click on label - focus input and open dropdown
|
|
380
|
+
const handleLabelClick = (e: React.MouseEvent) => {
|
|
381
|
+
e.stopPropagation();
|
|
382
|
+
const input = document.getElementById(labelForId);
|
|
383
|
+
if (input) input.focus();
|
|
384
|
+
setIsDropdownClosed(false);
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
|
|
388
|
+
const handleInputWrapperClick = (e: any) => {
|
|
389
|
+
if (
|
|
390
|
+
e.target.id === labelForId ||
|
|
391
|
+
e.target.classList.contains("pb_form_pill_tag") ||
|
|
392
|
+
disabled
|
|
393
|
+
) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
setIsDropdownClosed(!isDropdownClosed);
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Main function to handle any click inside dropdown
|
|
400
|
+
const handledropdownItemClick = (e: any, check: boolean) => {
|
|
401
|
+
const clickedItem = e.target.parentNode.id;
|
|
402
|
+
// Setting filterItem to "" will clear textinput and clear typeahead
|
|
403
|
+
setFilterItem("");
|
|
404
|
+
|
|
405
|
+
const filtered = filterFormattedDataById(formattedData, clickedItem);
|
|
406
|
+
const updatedTree = changeItem(filtered[0], check);
|
|
407
|
+
if (returnAllSelected) {
|
|
408
|
+
onSelect(getCheckedItems(updatedTree));
|
|
409
|
+
onChange({ target: { name, value: getCheckedItems(updatedTree) } });
|
|
410
|
+
} else {
|
|
411
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
|
412
|
+
onChange({
|
|
413
|
+
target: { name, value: getDefaultCheckedItems(updatedTree) },
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Single select
|
|
419
|
+
const handleRadioButtonClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
420
|
+
const { id, value: inputText } = e.target;
|
|
421
|
+
// The radio button needs a unique ID, this grabs the ID before the hyphen
|
|
422
|
+
const selectedItemID = id.match(/^[^-]*/)[0];
|
|
423
|
+
|
|
424
|
+
// Check if the item is disabled - if so, don't allow selection (safety check in addition to native disabled attribute)
|
|
425
|
+
const clickedItem = filterFormattedDataById(
|
|
426
|
+
formattedData,
|
|
427
|
+
selectedItemID,
|
|
428
|
+
);
|
|
429
|
+
if (clickedItem.length > 0 && clickedItem[0].disabled) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Reset tree checked state, triggering useEffect
|
|
434
|
+
const treeWithNoSelections = modifyRecursive(formattedData, false);
|
|
435
|
+
// Update tree with single selection
|
|
436
|
+
const treeWithSelectedItem = modifyValue(
|
|
437
|
+
selectedItemID,
|
|
438
|
+
treeWithNoSelections,
|
|
439
|
+
true,
|
|
440
|
+
);
|
|
441
|
+
const selectedItem = filterFormattedDataById(
|
|
442
|
+
treeWithSelectedItem,
|
|
443
|
+
selectedItemID,
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
setFormattedData(treeWithSelectedItem);
|
|
447
|
+
setSingleSelectedItem({
|
|
448
|
+
id: [selectedItemID],
|
|
449
|
+
value: inputText,
|
|
450
|
+
item: selectedItem,
|
|
443
451
|
});
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
452
|
+
// Reset the filter to always display dropdown options on click
|
|
453
|
+
setFilterItem("");
|
|
454
|
+
setIsDropdownClosed(true);
|
|
455
|
+
|
|
456
|
+
onSelect(selectedItem);
|
|
457
|
+
onChange({ target: { name, value: selectedItem } });
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Single select: reset the tree state upon typing
|
|
461
|
+
const handleRadioInputChange = (inputText: string) => {
|
|
462
|
+
modifyRecursive(formattedData, false);
|
|
463
|
+
setDefaultReturn([]);
|
|
464
|
+
setSingleSelectedItem({ id: [], value: inputText, item: [] });
|
|
465
|
+
setFilterItem(inputText);
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
const isTreeRowExpanded = (item: any) => expanded.indexOf(item.id) > -1;
|
|
469
|
+
|
|
470
|
+
// Handle click on chevron toggles in dropdown
|
|
471
|
+
const handleToggleClick = (id: string, event: React.MouseEvent) => {
|
|
472
|
+
event.stopPropagation();
|
|
473
|
+
const clickedItem = filterFormattedDataById(formattedData, id);
|
|
474
|
+
if (clickedItem) {
|
|
475
|
+
let expandedArray = [...expanded];
|
|
476
|
+
const itemExpanded = isTreeRowExpanded(clickedItem[0]);
|
|
477
|
+
|
|
478
|
+
if (itemExpanded)
|
|
479
|
+
expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
|
|
480
|
+
else expandedArray.push(clickedItem[0].id);
|
|
481
|
+
|
|
482
|
+
setExpanded(expandedArray);
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const itemsSelectedLength = () => {
|
|
487
|
+
let items;
|
|
488
|
+
if (returnAllSelected && returnedArray && returnedArray.length) {
|
|
489
|
+
items = returnedArray.length;
|
|
490
|
+
} else if (!returnAllSelected && defaultReturn && defaultReturn.length) {
|
|
491
|
+
items = defaultReturn.length;
|
|
492
|
+
}
|
|
493
|
+
return items;
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
// Rendering formattedData to UI based on typeahead
|
|
497
|
+
const renderNestedOptions = (items: { [key: string]: string }[] | any) => {
|
|
498
|
+
const hasOptionsChild = React.Children.toArray(props.children).some(
|
|
499
|
+
(child) =>
|
|
500
|
+
React.isValidElement(child) &&
|
|
501
|
+
child.type === MultiLevelSelect.Options,
|
|
448
502
|
);
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
<
|
|
463
|
-
marginBottom="xs"
|
|
464
|
-
text={label}
|
|
465
|
-
/>
|
|
503
|
+
|
|
504
|
+
if (hasOptionsChild) {
|
|
505
|
+
return React.Children.map(props.children, (child) => {
|
|
506
|
+
if (
|
|
507
|
+
React.isValidElement(child) &&
|
|
508
|
+
child.type === MultiLevelSelect.Options
|
|
509
|
+
) {
|
|
510
|
+
return React.cloneElement(child, { items });
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
});
|
|
514
|
+
} else {
|
|
515
|
+
// If no children, use the default rendering
|
|
516
|
+
return <MultiLevelSelectOptions items={items} />;
|
|
466
517
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}}>
|
|
477
|
-
<div className="wrapper"
|
|
478
|
-
ref={dropdownRef}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
<div
|
|
522
|
+
{...ariaProps}
|
|
523
|
+
{...dataProps}
|
|
524
|
+
{...htmlProps}
|
|
525
|
+
className={classes}
|
|
526
|
+
id={id}
|
|
479
527
|
>
|
|
480
|
-
|
|
481
|
-
|
|
528
|
+
{label && (
|
|
529
|
+
<label htmlFor={labelForId}
|
|
530
|
+
onClick={handleLabelClick}
|
|
531
|
+
>
|
|
532
|
+
<Caption
|
|
533
|
+
className="pb_multi_level_select_kit_label"
|
|
534
|
+
marginBottom="xs"
|
|
535
|
+
text={label}
|
|
536
|
+
/>
|
|
537
|
+
</label>
|
|
538
|
+
)}
|
|
539
|
+
<MultiLevelSelectContext.Provider
|
|
540
|
+
value={{
|
|
541
|
+
variant,
|
|
542
|
+
inputName,
|
|
543
|
+
renderNestedOptions,
|
|
544
|
+
isTreeRowExpanded,
|
|
545
|
+
handleToggleClick,
|
|
546
|
+
handleRadioButtonClick,
|
|
547
|
+
handledropdownItemClick,
|
|
548
|
+
filterItem,
|
|
549
|
+
}}
|
|
482
550
|
>
|
|
483
|
-
<div className="
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
value={selectedItem.id}
|
|
493
|
-
/>
|
|
494
|
-
))
|
|
495
|
-
: null}
|
|
496
|
-
|
|
497
|
-
{variant !== "single" && (
|
|
498
|
-
<>
|
|
499
|
-
{returnAllSelected && returnedArray.length !== 0
|
|
500
|
-
? returnedArray.map((item) => (
|
|
501
|
-
<input
|
|
502
|
-
disabled={disabled}
|
|
503
|
-
key={item.id}
|
|
504
|
-
name={`${name}[]`}
|
|
505
|
-
required={required}
|
|
506
|
-
type="hidden"
|
|
507
|
-
value={item.id}
|
|
508
|
-
/>
|
|
509
|
-
))
|
|
510
|
-
: null}
|
|
511
|
-
|
|
512
|
-
{!returnAllSelected
|
|
513
|
-
? defaultReturn.map((item) => (
|
|
551
|
+
<div className="wrapper"
|
|
552
|
+
ref={dropdownRef}
|
|
553
|
+
>
|
|
554
|
+
<div className="input_wrapper"
|
|
555
|
+
onClick={handleInputWrapperClick}
|
|
556
|
+
>
|
|
557
|
+
<div className="input_inner_container">
|
|
558
|
+
{variant === "single" && defaultReturn.length !== 0
|
|
559
|
+
? defaultReturn.map((selectedItem) => (
|
|
514
560
|
<input
|
|
515
561
|
disabled={disabled}
|
|
516
|
-
key={
|
|
562
|
+
key={selectedItem.id}
|
|
517
563
|
name={`${name}[]`}
|
|
518
564
|
required={required}
|
|
519
565
|
type="hidden"
|
|
520
|
-
value={
|
|
521
|
-
/>
|
|
522
|
-
))
|
|
523
|
-
: null}
|
|
524
|
-
|
|
525
|
-
{returnAllSelected &&
|
|
526
|
-
returnedArray.length !== 0 &&
|
|
527
|
-
inputDisplay === "pills"
|
|
528
|
-
? returnedArray.map((item, index) => (
|
|
529
|
-
<FormPill
|
|
530
|
-
color={pillColor}
|
|
531
|
-
key={index}
|
|
532
|
-
onClick={(event: any) => handlePillClose(event, item)}
|
|
533
|
-
text={item.label}
|
|
534
|
-
wrapped={wrapped}
|
|
535
|
-
/>
|
|
536
|
-
))
|
|
537
|
-
: null}
|
|
538
|
-
|
|
539
|
-
{!returnAllSelected &&
|
|
540
|
-
defaultReturn.length !== 0 &&
|
|
541
|
-
inputDisplay === "pills"
|
|
542
|
-
? defaultReturn.map((item, index) => (
|
|
543
|
-
<FormPill
|
|
544
|
-
color={pillColor}
|
|
545
|
-
key={index}
|
|
546
|
-
onClick={(event: any) => handlePillClose(event, item)}
|
|
547
|
-
text={item.label}
|
|
548
|
-
wrapped={wrapped}
|
|
566
|
+
value={selectedItem.id}
|
|
549
567
|
/>
|
|
550
568
|
))
|
|
551
569
|
: null}
|
|
552
570
|
|
|
553
|
-
{
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
571
|
+
{variant !== "single" && (
|
|
572
|
+
<>
|
|
573
|
+
{returnAllSelected && returnedArray.length !== 0
|
|
574
|
+
? returnedArray.map((item) => (
|
|
575
|
+
<input
|
|
576
|
+
disabled={disabled}
|
|
577
|
+
key={item.id}
|
|
578
|
+
name={`${name}[]`}
|
|
579
|
+
required={required}
|
|
580
|
+
type="hidden"
|
|
581
|
+
value={item.id}
|
|
582
|
+
/>
|
|
583
|
+
))
|
|
584
|
+
: null}
|
|
585
|
+
|
|
586
|
+
{!returnAllSelected
|
|
587
|
+
? defaultReturn.map((item) => (
|
|
588
|
+
<input
|
|
589
|
+
disabled={disabled}
|
|
590
|
+
key={item.id}
|
|
591
|
+
name={`${name}[]`}
|
|
592
|
+
required={required}
|
|
593
|
+
type="hidden"
|
|
594
|
+
value={item.id}
|
|
595
|
+
/>
|
|
596
|
+
))
|
|
597
|
+
: null}
|
|
598
|
+
|
|
599
|
+
{returnAllSelected &&
|
|
600
|
+
returnedArray.length !== 0 &&
|
|
601
|
+
inputDisplay === "pills"
|
|
602
|
+
? returnedArray.map((item, index) => (
|
|
603
|
+
<FormPill
|
|
604
|
+
color={pillColor}
|
|
605
|
+
key={index}
|
|
606
|
+
onClick={(event: any) =>
|
|
607
|
+
handlePillClose(event, item)
|
|
608
|
+
}
|
|
609
|
+
text={item.label}
|
|
610
|
+
wrapped={wrapped}
|
|
611
|
+
/>
|
|
612
|
+
))
|
|
613
|
+
: null}
|
|
614
|
+
|
|
615
|
+
{!returnAllSelected &&
|
|
616
|
+
defaultReturn.length !== 0 &&
|
|
617
|
+
inputDisplay === "pills"
|
|
618
|
+
? defaultReturn.map((item, index) => (
|
|
619
|
+
<FormPill
|
|
620
|
+
color={pillColor}
|
|
621
|
+
key={index}
|
|
622
|
+
onClick={(event: any) =>
|
|
623
|
+
handlePillClose(event, item)
|
|
624
|
+
}
|
|
625
|
+
text={item.label}
|
|
626
|
+
wrapped={wrapped}
|
|
627
|
+
/>
|
|
628
|
+
))
|
|
629
|
+
: null}
|
|
630
|
+
|
|
631
|
+
{returnAllSelected &&
|
|
632
|
+
returnedArray.length !== 0 &&
|
|
633
|
+
inputDisplay === "pills" && <br />}
|
|
634
|
+
|
|
635
|
+
{!returnAllSelected &&
|
|
636
|
+
defaultReturn.length !== 0 &&
|
|
637
|
+
inputDisplay === "pills" && <br />}
|
|
638
|
+
</>
|
|
639
|
+
)}
|
|
640
|
+
|
|
641
|
+
<input
|
|
642
|
+
aria-describedby={errorId}
|
|
643
|
+
aria-invalid={!!error}
|
|
644
|
+
disabled={disabled}
|
|
645
|
+
id={labelForId}
|
|
646
|
+
onChange={(e) => {
|
|
647
|
+
variant === "single"
|
|
648
|
+
? handleRadioInputChange(e.target.value)
|
|
649
|
+
: setFilterItem(e.target.value);
|
|
650
|
+
}}
|
|
651
|
+
onClick={() => setIsDropdownClosed(false)}
|
|
652
|
+
onFocus={() => !disabled && setIsDropdownClosed(false)}
|
|
653
|
+
placeholder={
|
|
654
|
+
inputDisplay === "none" && itemsSelectedLength()
|
|
655
|
+
? `${itemsSelectedLength()} ${
|
|
656
|
+
itemsSelectedLength() === 1 ? "item" : "items"
|
|
657
|
+
} selected`
|
|
658
|
+
: "Start typing..."
|
|
659
|
+
}
|
|
660
|
+
required={required}
|
|
661
|
+
value={singleSelectedItem.value || filterItem}
|
|
662
|
+
/>
|
|
663
|
+
</div>
|
|
664
|
+
|
|
665
|
+
{isDropdownClosed ? (
|
|
666
|
+
<div id={arrowDownElementId}
|
|
667
|
+
key="chevron-down"
|
|
668
|
+
>
|
|
669
|
+
<Icon icon="chevron-down"
|
|
670
|
+
id={arrowDownElementId}
|
|
671
|
+
size="xs"
|
|
672
|
+
/>
|
|
673
|
+
</div>
|
|
674
|
+
) : (
|
|
675
|
+
<div id={arrowUpElementId}
|
|
676
|
+
key="chevron-up"
|
|
677
|
+
>
|
|
678
|
+
<Icon icon="chevron-up"
|
|
679
|
+
id={arrowUpElementId}
|
|
680
|
+
size="xs"
|
|
681
|
+
/>
|
|
682
|
+
</div>
|
|
683
|
+
)}
|
|
592
684
|
</div>
|
|
593
|
-
|
|
594
|
-
<div
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
685
|
+
|
|
686
|
+
<div
|
|
687
|
+
className={`dropdown_menu ${isDropdownClosed ? "close" : "open"}`}
|
|
688
|
+
>
|
|
689
|
+
{renderNestedOptions(
|
|
690
|
+
filterItem
|
|
691
|
+
? findByFilter(formattedData, filterItem)
|
|
692
|
+
: formattedData,
|
|
693
|
+
)}
|
|
601
694
|
</div>
|
|
602
|
-
|
|
603
|
-
</
|
|
604
|
-
|
|
605
|
-
<div className={`dropdown_menu ${isDropdownClosed ? "close" : "open"}`}>
|
|
606
|
-
{renderNestedOptions(
|
|
607
|
-
filterItem ? findByFilter(formattedData, filterItem) : formattedData
|
|
608
|
-
)}
|
|
609
|
-
</div>
|
|
610
|
-
</div>
|
|
611
|
-
</MultiLevelSelectContext.Provider>
|
|
612
|
-
{error &&
|
|
695
|
+
</div>
|
|
696
|
+
</MultiLevelSelectContext.Provider>
|
|
697
|
+
{error && (
|
|
613
698
|
<Body
|
|
699
|
+
aria={{ atomic: "true", live: "polite" }}
|
|
614
700
|
dark={props.dark}
|
|
701
|
+
htmlOptions={{ role: "alert" }}
|
|
702
|
+
id={errorId}
|
|
615
703
|
status="negative"
|
|
616
704
|
text={error}
|
|
617
705
|
/>
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
706
|
+
)}
|
|
707
|
+
</div>
|
|
708
|
+
);
|
|
709
|
+
},
|
|
710
|
+
) as MultiLevelSelectComponent;
|
|
622
711
|
|
|
623
712
|
MultiLevelSelect.displayName = "MultiLevelSelect";
|
|
624
713
|
MultiLevelSelect.Options = MultiLevelSelectOptions;
|