playbook_ui 12.26.0 → 12.26.1.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown831
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_date_picker/_date_picker.scss +26 -0
- data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +102 -95
- data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +18 -2
- data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +15 -4
- data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +84 -1
- data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +38 -4
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_rails.html.erb +12 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_rails.md +3 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.html.erb +12 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.jsx +18 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_range_limit.md +1 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_react.jsx +17 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick_react.md +1 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +4 -0
- data/app/pb_kits/playbook/pb_date_picker/docs/index.js +3 -1
- data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +168 -0
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +3 -2
- data/app/pb_kits/playbook/pb_date_picker/sass_partials/_quick_pick_styles.scss +75 -0
- data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +47 -125
- data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +125 -166
- data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -1
- data/app/pb_kits/playbook/pb_nav/_subtle_mixin.scss +1 -1
- data/dist/playbook-rails.js +279 -7
- data/lib/playbook/version.rb +2 -2
- metadata +16 -7
@@ -6,18 +6,15 @@ import Icon from "../pb_icon/_icon";
|
|
6
6
|
import Checkbox from "../pb_checkbox/_checkbox";
|
7
7
|
import FormPill from "../pb_form_pill/_form_pill";
|
8
8
|
import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button";
|
9
|
+
import { cloneDeep } from "lodash";
|
10
|
+
|
9
11
|
import {
|
10
|
-
unCheckIt,
|
11
12
|
getAncestorsOfUnchecked,
|
12
|
-
unCheckedRecursive,
|
13
|
-
checkedRecursive,
|
14
13
|
filterFormattedDataById,
|
15
14
|
findByFilter,
|
16
15
|
getCheckedItems,
|
17
|
-
|
18
|
-
|
19
|
-
removeChildrenIfParentChecked,
|
20
|
-
getChildIds,
|
16
|
+
getDefaultCheckedItems,
|
17
|
+
recursiveCheckParent,
|
21
18
|
} from "./_helper_functions";
|
22
19
|
|
23
20
|
type MultiLevelSelectProps = {
|
@@ -51,6 +48,8 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
51
48
|
|
52
49
|
const dropdownRef = useRef(null);
|
53
50
|
|
51
|
+
//state for expanded property
|
52
|
+
const [expanded, setExpanded] = useState([]);
|
54
53
|
//state for whether dropdown is open or closed
|
55
54
|
const [isClosed, setIsClosed] = useState(true);
|
56
55
|
//state from onchange for textinput, to use for filtering to create typeahead
|
@@ -58,45 +57,29 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
58
57
|
//this is essentially the return that the user will get when they use the kit
|
59
58
|
const [returnedArray, setReturnedArray] = useState([]);
|
60
59
|
//formattedData with checked and parent_id added
|
61
|
-
const [formattedData, setFormattedData] = useState(
|
62
|
-
//toggle chevron in dropdown
|
63
|
-
//@ts-ignore
|
64
|
-
const [isToggled, setIsToggled] = useState<{ [id: number]: boolean }>({});
|
60
|
+
const [formattedData, setFormattedData] = useState([]);
|
65
61
|
//state for return for default
|
66
62
|
const [defaultReturn, setDefaultReturn] = useState([]);
|
67
63
|
|
68
64
|
useEffect(() => {
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
);
|
65
|
+
setFormattedData(addCheckedAndParentProperty(treeData));
|
66
|
+
}, [treeData]);
|
67
|
+
|
68
|
+
useEffect(() => {
|
69
|
+
if (returnAllSelected) {
|
70
|
+
setReturnedArray(getCheckedItems(formattedData));
|
71
|
+
} else {
|
72
|
+
setDefaultReturn(getDefaultCheckedItems(formattedData));
|
75
73
|
}
|
76
|
-
|
77
|
-
? onSelect(returnedArray)
|
78
|
-
: onSelect(
|
79
|
-
defaultReturn.filter(
|
80
|
-
(item, index, self) =>
|
81
|
-
index === self.findIndex((obj) => obj.id === item.id)
|
82
|
-
)
|
83
|
-
);
|
84
|
-
}, [returnedArray, defaultReturn]);
|
74
|
+
}, [formattedData]);
|
85
75
|
|
86
76
|
useEffect(() => {
|
87
|
-
//Create new formattedData array for use
|
88
|
-
setFormattedData(addCheckedAndParentProperty(treeData));
|
89
77
|
// Function to handle clicks outside the dropdown
|
90
78
|
const handleClickOutside = (event: any) => {
|
91
79
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
92
80
|
setIsClosed(true);
|
93
81
|
}
|
94
82
|
};
|
95
|
-
//if any items already checked in first render, set return accordingly
|
96
|
-
const initialChecked = getCheckedItems(treeData)
|
97
|
-
initialChecked && returnAllSelected && setReturnedArray(initialChecked)
|
98
|
-
initialChecked && !returnAllSelected && setDefaultReturn(initialChecked)
|
99
|
-
|
100
83
|
// Attach the event listener
|
101
84
|
window.addEventListener("click", handleClickOutside);
|
102
85
|
// Clean up the event listener on unmount
|
@@ -105,11 +88,72 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
105
88
|
};
|
106
89
|
}, []);
|
107
90
|
|
91
|
+
const modifyRecursive = (tree: { [key: string]: any }[], check: boolean) => {
|
92
|
+
if (!Array.isArray(tree)) {
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
return tree.map((item: { [key: string]: any }) => {
|
96
|
+
item.checked = check;
|
97
|
+
item.children = modifyRecursive(item.children, check);
|
98
|
+
return item;
|
99
|
+
});
|
100
|
+
};
|
101
|
+
|
102
|
+
//iterate over tree, find item and set checked or unchecked
|
103
|
+
const modifyValue = (
|
104
|
+
id: string,
|
105
|
+
tree: { [key: string]: any }[],
|
106
|
+
check: boolean
|
107
|
+
) => {
|
108
|
+
if (!Array.isArray(tree)) {
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
return tree.map((item: any) => {
|
112
|
+
if (item.id != id) item.children = modifyValue(id, item.children, check);
|
113
|
+
else {
|
114
|
+
item.checked = check;
|
115
|
+
item.children = modifyRecursive(item.children, check);
|
116
|
+
}
|
117
|
+
|
118
|
+
return item;
|
119
|
+
});
|
120
|
+
};
|
121
|
+
|
122
|
+
//clone tree, check items + children
|
123
|
+
const checkItem = (item: { [key: string]: any }) => {
|
124
|
+
const tree = cloneDeep(formattedData);
|
125
|
+
if (returnAllSelected) {
|
126
|
+
return modifyValue(item.id, tree, true);
|
127
|
+
} else {
|
128
|
+
const checkedTree = modifyValue(item.id, tree, true);
|
129
|
+
return recursiveCheckParent(item, checkedTree);
|
130
|
+
}
|
131
|
+
};
|
132
|
+
|
133
|
+
//clone tree, uncheck items + children
|
134
|
+
const unCheckItem = (item: { [key: string]: any }) => {
|
135
|
+
const tree = cloneDeep(formattedData);
|
136
|
+
if (returnAllSelected) {
|
137
|
+
return modifyValue(item.id, tree, false);
|
138
|
+
} else {
|
139
|
+
const uncheckedTree = modifyValue(item.id, tree, false);
|
140
|
+
return getAncestorsOfUnchecked(uncheckedTree, item);
|
141
|
+
}
|
142
|
+
};
|
143
|
+
|
144
|
+
//setformattedData with proper properties
|
145
|
+
const changeItem = (item: { [key: string]: any }, check: boolean) => {
|
146
|
+
const tree = check ? checkItem(item) : unCheckItem(item);
|
147
|
+
setFormattedData(tree);
|
148
|
+
|
149
|
+
return tree;
|
150
|
+
};
|
151
|
+
|
108
152
|
//function to map over data and add parent_id + depth property to each item
|
109
153
|
const addCheckedAndParentProperty = (
|
110
154
|
treeData: { [key: string]: any }[],
|
111
155
|
parent_id: string = null,
|
112
|
-
depth: number = 0
|
156
|
+
depth: number = 0
|
113
157
|
) => {
|
114
158
|
if (!Array.isArray(treeData)) {
|
115
159
|
return;
|
@@ -121,10 +165,14 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
121
165
|
depth,
|
122
166
|
};
|
123
167
|
if (newItem.children && newItem.children.length > 0) {
|
168
|
+
const children =
|
169
|
+
item.checked && !returnAllSelected
|
170
|
+
? modifyRecursive(item.children, true)
|
171
|
+
: item.children;
|
124
172
|
newItem.children = addCheckedAndParentProperty(
|
125
|
-
|
173
|
+
children,
|
126
174
|
newItem.id,
|
127
|
-
depth + 1
|
175
|
+
depth + 1
|
128
176
|
);
|
129
177
|
}
|
130
178
|
return newItem;
|
@@ -135,37 +183,13 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
135
183
|
const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
|
136
184
|
// prevents the dropdown from closing when clicking on the pill
|
137
185
|
event.stopPropagation();
|
186
|
+
const updatedTree = changeItem(clickedItem, false);
|
138
187
|
//logic for removing items from returnArray or defaultReturn when pills clicked
|
139
188
|
if (returnAllSelected) {
|
140
|
-
|
141
|
-
if (clickedItem.children && clickedItem.children.length > 0) {
|
142
|
-
const childrenOfChecked = getChildIds(clickedItem, returnedArray);
|
143
|
-
const updatedFiltered = returnedArray
|
144
|
-
.filter((item) => item !== clickedItem)
|
145
|
-
.filter((item) => !childrenOfChecked.includes(item.id));
|
146
|
-
setReturnedArray(updatedFiltered);
|
147
|
-
} else {
|
148
|
-
const updatedFiltered = returnedArray.filter(
|
149
|
-
(item) => item !== clickedItem
|
150
|
-
);
|
151
|
-
setReturnedArray(updatedFiltered);
|
152
|
-
}
|
153
|
-
}
|
189
|
+
onSelect(getCheckedItems(updatedTree));
|
154
190
|
} else {
|
155
|
-
|
156
|
-
getAncestorsOfUnchecked(formattedData, clickedItem);
|
157
|
-
const newChecked = getCheckedItems(formattedData);
|
158
|
-
const filteredReturn = updateReturnItems(newChecked).filter(
|
159
|
-
(item) => item.id !== clickedItem.id
|
160
|
-
);
|
161
|
-
setDefaultReturn(filteredReturn);
|
162
|
-
}
|
163
|
-
}
|
164
|
-
if (clickedItem.children && clickedItem.children.length > 0) {
|
165
|
-
unCheckedRecursive(clickedItem);
|
191
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
166
192
|
}
|
167
|
-
//logic to uncheck clickedItem in formattedData
|
168
|
-
unCheckIt(formattedData, clickedItem.id);
|
169
193
|
};
|
170
194
|
|
171
195
|
//handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
|
@@ -181,95 +205,36 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
181
205
|
};
|
182
206
|
|
183
207
|
//Main function to handle any click inside dropdown
|
184
|
-
const handledropdownItemClick = (e: any) => {
|
208
|
+
const handledropdownItemClick = (e: any, check: boolean) => {
|
185
209
|
const clickedItem = e.target.parentNode.id;
|
186
210
|
//setting filterItem to "" will clear textinput and clear typeahead
|
187
211
|
setFilterItem("");
|
188
212
|
|
189
213
|
const filtered = filterFormattedDataById(formattedData, clickedItem);
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
checkedRecursive(item);
|
195
|
-
});
|
196
|
-
} else if (!filtered[0].checked) {
|
197
|
-
filtered[0].children.forEach((item: { [key: string]: any }) => {
|
198
|
-
unCheckedRecursive(item);
|
199
|
-
});
|
200
|
-
}
|
201
|
-
}
|
202
|
-
|
203
|
-
const checkedItems = getCheckedItems(formattedData);
|
204
|
-
|
205
|
-
//checking and unchecking items for returnAllSelected variant
|
206
|
-
if (returnedArray.includes(filtered[0])) {
|
207
|
-
if (!filtered[0].checked) {
|
208
|
-
if (filtered[0].children && filtered[0].children.length > 0) {
|
209
|
-
const childrenOfChecked = getChildIds(filtered[0], returnedArray);
|
210
|
-
const updatedFiltered = returnedArray
|
211
|
-
.filter((item) => item !== filtered[0])
|
212
|
-
.filter((item) => !childrenOfChecked.includes(item.id));
|
213
|
-
|
214
|
-
setReturnedArray(updatedFiltered);
|
215
|
-
} else {
|
216
|
-
const updatedFiltered = returnedArray.filter(
|
217
|
-
(item) => item !== filtered[0]
|
218
|
-
);
|
219
|
-
setReturnedArray(updatedFiltered);
|
220
|
-
}
|
221
|
-
}
|
214
|
+
const updatedTree = changeItem(filtered[0], check);
|
215
|
+
console.log(updatedTree);
|
216
|
+
if (returnAllSelected) {
|
217
|
+
onSelect(getCheckedItems(updatedTree));
|
222
218
|
} else {
|
223
|
-
|
224
|
-
}
|
225
|
-
|
226
|
-
//when item is unchecked for default variant
|
227
|
-
if (!filtered[0].checked && !returnAllSelected) {
|
228
|
-
//uncheck parent and grandparent if any child unchecked
|
229
|
-
getAncestorsOfUnchecked(formattedData, filtered[0]);
|
230
|
-
|
231
|
-
const newChecked = getCheckedItems(formattedData);
|
232
|
-
//get all checked items, and filter to check if all children checked, if yes return only parent
|
233
|
-
const filteredReturn = updateReturnItems(newChecked);
|
234
|
-
setDefaultReturn(filteredReturn);
|
235
|
-
}
|
236
|
-
|
237
|
-
//when item is checked for default variant
|
238
|
-
if (!returnAllSelected && filtered[0].checked) {
|
239
|
-
//if checked item has children
|
240
|
-
if (filtered[0].children && filtered[0].children.length > 0) {
|
241
|
-
removeChildrenIfParentChecked(
|
242
|
-
filtered[0],
|
243
|
-
defaultReturn,
|
244
|
-
setDefaultReturn
|
245
|
-
);
|
246
|
-
}
|
247
|
-
|
248
|
-
//if clicked item has parent_id, find parent and check if all children checked or not
|
249
|
-
if (filtered[0].parent_id !== null) {
|
250
|
-
recursiveReturnOnlyParent(
|
251
|
-
filtered[0],
|
252
|
-
formattedData,
|
253
|
-
defaultReturn,
|
254
|
-
setDefaultReturn
|
255
|
-
);
|
256
|
-
} else {
|
257
|
-
setDefaultReturn([filtered[0]]);
|
258
|
-
}
|
219
|
+
onSelect(getDefaultCheckedItems(updatedTree));
|
259
220
|
}
|
260
221
|
};
|
261
222
|
|
223
|
+
const isExpanded = (item: any) => expanded.indexOf(item.id) > -1;
|
224
|
+
|
262
225
|
//handle click on chevron toggles in dropdown
|
263
226
|
const handleToggleClick = (id: string, event: React.MouseEvent) => {
|
264
227
|
event.stopPropagation();
|
265
|
-
setIsToggled((prevState: { [id: string]: boolean }) => ({
|
266
|
-
...prevState,
|
267
|
-
[id]: !prevState[id],
|
268
|
-
}));
|
269
228
|
const clickedItem = filterFormattedDataById(formattedData, id);
|
270
|
-
|
271
229
|
if (clickedItem) {
|
272
|
-
|
230
|
+
let expandedArray = [...expanded];
|
231
|
+
const itemExpanded = isExpanded(clickedItem[0]);
|
232
|
+
|
233
|
+
if (itemExpanded)
|
234
|
+
expandedArray = expandedArray.filter((i) => i != clickedItem[0].id);
|
235
|
+
else expandedArray.push(clickedItem[0].id);
|
236
|
+
|
237
|
+
setExpanded(expandedArray);
|
273
238
|
}
|
274
239
|
};
|
275
240
|
|
@@ -281,23 +246,23 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
281
246
|
items.map((item: { [key: string]: any }) => {
|
282
247
|
return (
|
283
248
|
<>
|
284
|
-
<li
|
285
|
-
key={item.id}
|
286
|
-
className="dropdown_item"
|
287
|
-
data-name={item.id}
|
288
|
-
>
|
249
|
+
<li key={item.id} className="dropdown_item" data-name={item.id}>
|
289
250
|
<div className="dropdown_item_checkbox_row">
|
290
251
|
<div
|
291
|
-
key={
|
292
|
-
item.expanded ? "chevron-down" : "chevron-right"
|
293
|
-
}
|
252
|
+
key={isExpanded(item) ? "chevron-down" : "chevron-right"}
|
294
253
|
>
|
295
254
|
<CircleIconButton
|
296
255
|
icon={
|
297
|
-
item
|
256
|
+
isExpanded(item) ? "chevron-down" : "chevron-right"
|
257
|
+
}
|
258
|
+
className={
|
259
|
+
item.children && item.children.length > 0
|
260
|
+
? ""
|
261
|
+
: "toggle_icon"
|
262
|
+
}
|
263
|
+
onClick={(event: any) =>
|
264
|
+
handleToggleClick(item.id, event)
|
298
265
|
}
|
299
|
-
className={item.children && item.children.length > 0 ? "" : "toggle_icon"}
|
300
|
-
onClick={(event) => handleToggleClick(item.id, event)}
|
301
266
|
variant="link"
|
302
267
|
/>
|
303
268
|
</div>
|
@@ -308,13 +273,12 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
308
273
|
name={item.label}
|
309
274
|
value={item.label}
|
310
275
|
onChange={(e) => {
|
311
|
-
|
312
|
-
handledropdownItemClick(e);
|
276
|
+
handledropdownItemClick(e, !item.checked);
|
313
277
|
}}
|
314
278
|
/>
|
315
279
|
</Checkbox>
|
316
280
|
</div>
|
317
|
-
{item
|
281
|
+
{isExpanded(item) &&
|
318
282
|
item.children &&
|
319
283
|
item.children.length > 0 &&
|
320
284
|
!filterItem && ( // Show children if expanded is true
|
@@ -339,25 +303,20 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
|
|
339
303
|
key={index}
|
340
304
|
text={item.label}
|
341
305
|
size="small"
|
342
|
-
onClick={(event) => handlePillClose(event, item)}
|
306
|
+
onClick={(event: any) => handlePillClose(event, item)}
|
343
307
|
/>
|
344
308
|
))
|
345
309
|
: null}
|
346
310
|
{!returnAllSelected &&
|
347
311
|
defaultReturn.length !== 0 &&
|
348
|
-
defaultReturn
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
text={item.label}
|
357
|
-
size="small"
|
358
|
-
onClick={(event) => handlePillClose(event, item)}
|
359
|
-
/>
|
360
|
-
))}
|
312
|
+
defaultReturn.map((item, index) => (
|
313
|
+
<FormPill
|
314
|
+
key={index}
|
315
|
+
text={item.label}
|
316
|
+
size="small"
|
317
|
+
onClick={(event: any) => handlePillClose(event, item)}
|
318
|
+
/>
|
319
|
+
))}
|
361
320
|
{returnedArray.length !== 0 && returnAllSelected && <br />}
|
362
321
|
{defaultReturn.length !== 0 && !returnAllSelected && <br />}
|
363
322
|
<input
|