playbook_ui 12.25.0.pre.alpha.railsmultilevelimprovements785 → 12.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +0 -1
  3. data/app/pb_kits/playbook/index.js +0 -1
  4. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_swift.md +1 -82
  5. data/app/pb_kits/playbook/pb_docs/kit_example.html.erb +13 -14
  6. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +2 -3
  7. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +98 -58
  8. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +86 -356
  9. data/app/pb_kits/playbook/pb_multi_level_select/_multi_select_helper.tsx +31 -0
  10. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +1 -1
  11. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +1 -1
  12. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +0 -1
  13. data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +87 -0
  14. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +0 -3
  15. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +1 -1
  16. data/app/pb_kits/playbook/playbook-doc.js +0 -2
  17. data/dist/menu.yml +0 -1
  18. data/dist/playbook-rails.js +7 -7
  19. data/lib/playbook/forms/builder.rb +0 -1
  20. data/lib/playbook/version.rb +2 -2
  21. metadata +9 -29
  22. data/app/pb_kits/playbook/pb_detail/_detail.scss +0 -44
  23. data/app/pb_kits/playbook/pb_detail/_detail.tsx +0 -55
  24. data/app/pb_kits/playbook/pb_detail/_detail_mixins.scss +0 -29
  25. data/app/pb_kits/playbook/pb_detail/detail.html.erb +0 -7
  26. data/app/pb_kits/playbook/pb_detail/detail.rb +0 -31
  27. data/app/pb_kits/playbook/pb_detail/detail.test.jsx +0 -46
  28. data/app/pb_kits/playbook/pb_detail/docs/_description.md +0 -1
  29. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.html.erb +0 -34
  30. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.jsx +0 -49
  31. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.md +0 -1
  32. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.html.erb +0 -24
  33. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.jsx +0 -38
  34. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.md +0 -6
  35. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.html.erb +0 -3
  36. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.jsx +0 -13
  37. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.html.erb +0 -22
  38. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.jsx +0 -32
  39. data/app/pb_kits/playbook/pb_detail/docs/example.yml +0 -11
  40. data/app/pb_kits/playbook/pb_detail/docs/index.js +0 -4
  41. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +0 -212
  42. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_with_form.html.erb +0 -72
  43. data/lib/playbook/forms/builder/multi_level_select_field.rb +0 -12
@@ -1,35 +1,19 @@
1
- import React, { useState, useEffect, useRef } from "react";
1
+ import React, { useState, useEffect, useMemo } from "react";
2
2
  import classnames from "classnames";
3
3
  import { buildAriaProps, buildCss, buildDataProps } from "../utilities/props";
4
- import { globalProps, GlobalProps } from "../utilities/globalProps";
5
- import Icon from "../pb_icon/_icon";
6
- import Checkbox from "../pb_checkbox/_checkbox";
7
- import FormPill from "../pb_form_pill/_form_pill";
8
- import CircleIconButton from "../pb_circle_icon_button/_circle_icon_button";
9
- import {
10
- unCheckIt,
11
- getAncestorsOfUnchecked,
12
- unCheckedRecursive,
13
- checkedRecursive,
14
- filterFormattedDataById,
15
- findByFilter,
16
- getCheckedItems,
17
- updateReturnItems,
18
- recursiveReturnOnlyParent,
19
- removeChildrenIfParentChecked,
20
- getChildIds,
21
- } from "./_helper_functions";
4
+ import { globalProps } from "../utilities/globalProps";
5
+ import { findItemById, checkIt, unCheckIt, getParentAndAncestorsIds } from "./helper_functions";
6
+ import MultiSelectHelper from "./_multi_select_helper";
22
7
 
23
8
  type MultiLevelSelectProps = {
24
9
  aria?: { [key: string]: string };
25
10
  className?: string;
26
11
  data?: { [key: string]: string };
27
12
  id?: string;
28
- name?: string;
29
13
  returnAllSelected?: boolean;
30
14
  treeData?: { [key: string]: string }[];
31
15
  onSelect?: (prop: { [key: string]: any }) => void;
32
- } & GlobalProps;
16
+ };
33
17
 
34
18
  const MultiLevelSelect = (props: MultiLevelSelectProps) => {
35
19
  const {
@@ -37,7 +21,6 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
37
21
  className,
38
22
  data = {},
39
23
  id,
40
- name,
41
24
  returnAllSelected = false,
42
25
  treeData,
43
26
  onSelect = () => {},
@@ -51,357 +34,104 @@ const MultiLevelSelect = (props: MultiLevelSelectProps) => {
51
34
  className
52
35
  );
53
36
 
54
- const dropdownRef = useRef(null);
55
-
56
- //state for whether dropdown is open or closed
57
- const [isClosed, setIsClosed] = useState(true);
58
- //state from onchange for textinput, to use for filtering to create typeahead
59
- const [filterItem, setFilterItem] = useState("");
60
- //this is essentially the return that the user will get when they use the kit
61
- const [returnedArray, setReturnedArray] = useState([]);
62
- //formattedData with checked and parent_id added
63
37
  const [formattedData, setFormattedData] = useState(treeData);
64
- //toggle chevron in dropdown
65
- //@ts-ignore
66
- const [isToggled, setIsToggled] = useState<{ [id: number]: boolean }>({});
67
- //state for return for default
68
- const [defaultReturn, setDefaultReturn] = useState([]);
69
-
70
- useEffect(() => {
71
- let el = document.getElementById(`pb_data_wrapper_${id}`);
72
- if (el) {
73
- el.setAttribute(
74
- "data-tree",
75
- JSON.stringify(returnAllSelected ? returnedArray : defaultReturn)
76
- );
77
- }
78
-
79
- const updateHiddenInputValue = (value: any) => {
80
- console.log("value", value)
81
- const hiddenInput = document.querySelector(
82
- "input#" + id
83
- ) as HTMLInputElement
84
- console.log("hiddenInput", hiddenInput)
85
- if (hiddenInput) {
86
- hiddenInput.value = JSON.stringify(value)
87
- }
88
- }
89
-
90
- updateHiddenInputValue(returnAllSelected ? returnedArray : defaultReturn);
91
- returnAllSelected
92
- ? onSelect(returnedArray)
93
- : onSelect(
94
- defaultReturn.filter(
95
- (item, index, self) =>
96
- index === self.findIndex((obj) => obj.id === item.id)
97
- )
98
- );
99
- }, [returnedArray, defaultReturn]);
100
-
101
- useEffect(() => {
102
- //Create new formattedData array for use
103
- setFormattedData(addCheckedAndParentProperty(treeData));
104
- // Function to handle clicks outside the dropdown
105
- const handleClickOutside = (event: any) => {
106
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
107
- setIsClosed(true);
108
- }
109
- };
110
- //if any items already checked in first render, set return accordingly
111
- const initialChecked = getCheckedItems(treeData)
112
- initialChecked && returnAllSelected && setReturnedArray(initialChecked)
113
- initialChecked && !returnAllSelected && setDefaultReturn(initialChecked)
114
-
115
- // Attach the event listener
116
- window.addEventListener("click", handleClickOutside);
117
- // Clean up the event listener on unmount
118
- return () => {
119
- window.removeEventListener("click", handleClickOutside);
120
- };
121
- }, []);
122
-
123
- //function to map over data and add parent_id + depth property to each item
124
- const addCheckedAndParentProperty = (
125
- treeData: { [key: string]: any }[],
126
- parent_id: string = null,
127
- depth: number = 0,
128
- ) => {
129
- if (!Array.isArray(treeData)) {
130
- return;
131
- }
132
- return treeData.map((item: { [key: string]: any } | any) => {
133
- const newItem = {
134
- ...item,
135
- parent_id,
136
- depth,
137
- };
138
- if (newItem.children && newItem.children.length > 0) {
139
- newItem.children = addCheckedAndParentProperty(
140
- newItem.children,
141
- newItem.id,
142
- depth + 1,
143
- );
144
- }
145
- return newItem;
146
- });
147
- };
148
-
149
- //click event for x on form pill
150
- const handlePillClose = (event: any, clickedItem: { [key: string]: any }) => {
151
- // prevents the dropdown from closing when clicking on the pill
152
- event.stopPropagation();
153
- //logic for removing items from returnArray or defaultReturn when pills clicked
154
- if (returnAllSelected) {
155
- if (returnedArray.includes(clickedItem)) {
156
- if (clickedItem.children && clickedItem.children.length > 0) {
157
- const childrenOfChecked = getChildIds(clickedItem, returnedArray);
158
- const updatedFiltered = returnedArray
159
- .filter((item) => item !== clickedItem)
160
- .filter((item) => !childrenOfChecked.includes(item.id));
161
- setReturnedArray(updatedFiltered);
38
+ const [selectedItems, setSelectedItems] = useState([]);
39
+ const [checkedData, setCheckedData] = useState([]);
40
+
41
+ const onChange = (currentNode: { [key: string]: any }) => {
42
+ const updatedData = formattedData.map((item: any) => {
43
+ if (item.id === currentNode._id) {
44
+ if (currentNode.checked) {
45
+ checkIt(item, selectedItems, setSelectedItems, false);
162
46
  } else {
163
- const updatedFiltered = returnedArray.filter(
164
- (item) => item !== clickedItem
165
- );
166
- setReturnedArray(updatedFiltered);
47
+ unCheckIt(item, selectedItems, setSelectedItems, false);
167
48
  }
168
- }
169
- } else {
170
- if (defaultReturn.includes(clickedItem)) {
171
- getAncestorsOfUnchecked(formattedData, clickedItem);
172
- const newChecked = getCheckedItems(formattedData);
173
- const filteredReturn = updateReturnItems(newChecked).filter(
174
- (item) => item.id !== clickedItem.id
175
- );
176
- setDefaultReturn(filteredReturn);
177
- }
178
- }
179
- if (clickedItem.children && clickedItem.children.length > 0) {
180
- unCheckedRecursive(clickedItem);
181
- }
182
- //logic to uncheck clickedItem in formattedData
183
- unCheckIt(formattedData, clickedItem.id);
184
- };
185
-
186
- //handle click on input wrapper(entire div with pills, typeahead, etc) so it doesn't close when input or form pill is clicked
187
- const handleInputWrapperClick = (e: any) => {
188
- e.stopPropagation();
189
- if (
190
- e.target.id === "multiselect_input" ||
191
- e.target.classList.contains("pb_form_pill_tag")
192
- ) {
193
- return;
194
- }
195
- setIsClosed(!isClosed);
196
- };
197
-
198
- //Main function to handle any click inside dropdown
199
- const handledropdownItemClick = (e: any) => {
200
- const clickedItem = e.target.parentNode.id;
201
- //setting filterItem to "" will clear textinput and clear typeahead
202
- setFilterItem("");
203
-
204
- const filtered = filterFormattedDataById(formattedData, clickedItem);
205
- //check and uncheck all children of checked/unchecked parent item
206
- if (filtered[0].children && filtered[0].children.length > 0) {
207
- if (filtered[0].checked) {
208
- filtered[0].children.forEach((item: { [key: string]: any }) => {
209
- checkedRecursive(item);
210
- });
211
- } else if (!filtered[0].checked) {
212
- filtered[0].children.forEach((item: { [key: string]: any }) => {
213
- unCheckedRecursive(item);
214
- });
215
- }
216
- }
217
-
218
- const checkedItems = getCheckedItems(formattedData);
219
-
220
- //checking and unchecking items for returnAllSelected variant
221
- if (returnedArray.includes(filtered[0])) {
222
- if (!filtered[0].checked) {
223
- if (filtered[0].children && filtered[0].children.length > 0) {
224
- const childrenOfChecked = getChildIds(filtered[0], returnedArray);
225
- const updatedFiltered = returnedArray
226
- .filter((item) => item !== filtered[0])
227
- .filter((item) => !childrenOfChecked.includes(item.id));
228
-
229
- setReturnedArray(updatedFiltered);
230
- } else {
231
- const updatedFiltered = returnedArray.filter(
232
- (item) => item !== filtered[0]
233
- );
234
- setReturnedArray(updatedFiltered);
49
+ } else if (item.children) {
50
+ const foundItem = findItemById(item.children, currentNode._id);
51
+ if (foundItem) {
52
+ if (currentNode.checked) {
53
+ checkIt(foundItem, selectedItems, setSelectedItems, false);
54
+ if (currentNode._parent) {
55
+ const parents = getParentAndAncestorsIds(currentNode._parent, formattedData)
56
+ parents.forEach((item:string) => {
57
+ const ancestor = findItemById(formattedData,item)
58
+ ancestor.expanded = true
59
+ });
60
+ }
61
+ } else {
62
+ unCheckIt(foundItem, selectedItems, setSelectedItems, false);
63
+ if (currentNode._parent) {
64
+ const parents = getParentAndAncestorsIds(currentNode._parent, formattedData)
65
+ parents.forEach((item:string) => {
66
+ const ancestor = findItemById(formattedData,item)
67
+ ancestor.expanded = true
68
+ });
69
+ }
70
+ }
235
71
  }
236
72
  }
237
- } else {
238
- setReturnedArray(checkedItems);
239
- }
240
73
 
241
- //when item is unchecked for default variant
242
- if (!filtered[0].checked && !returnAllSelected) {
243
- //uncheck parent and grandparent if any child unchecked
244
- getAncestorsOfUnchecked(formattedData, filtered[0]);
245
-
246
- const newChecked = getCheckedItems(formattedData);
247
- //get all checked items, and filter to check if all children checked, if yes return only parent
248
- const filteredReturn = updateReturnItems(newChecked);
249
- setDefaultReturn(filteredReturn);
250
- }
251
-
252
- //when item is checked for default variant
253
- if (!returnAllSelected && filtered[0].checked) {
254
- //if checked item has children
255
- if (filtered[0].children && filtered[0].children.length > 0) {
256
- removeChildrenIfParentChecked(
257
- filtered[0],
258
- defaultReturn,
259
- setDefaultReturn
260
- );
261
- }
74
+ return item;
75
+ });
262
76
 
263
- //if clicked item has parent_id, find parent and check if all children checked or not
264
- if (filtered[0].parent_id !== null) {
265
- recursiveReturnOnlyParent(
266
- filtered[0],
267
- formattedData,
268
- defaultReturn,
269
- setDefaultReturn
270
- );
271
- } else {
272
- setDefaultReturn([filtered[0]]);
273
- }
274
- }
77
+ setFormattedData(updatedData);
275
78
  };
276
79
 
277
- //handle click on chevron toggles in dropdown
278
- const handleToggleClick = (id: string, event: React.MouseEvent) => {
279
- event.stopPropagation();
280
- setIsToggled((prevState: { [id: string]: boolean }) => ({
281
- ...prevState,
282
- [id]: !prevState[id],
283
- }));
284
- const clickedItem = filterFormattedDataById(formattedData, id);
80
+ useEffect(() => {
81
+ if (returnAllSelected) {
82
+ const selected = selectedItems.filter(
83
+ (item: { [key: string]: any }) => item.checked
84
+ );
85
+ //filter to remove duplicate items
86
+ const uniqueSelected = selected.filter(
87
+ (obj, index, self) => index === self.findIndex((t) => t.id === obj.id)
88
+ );
89
+ setCheckedData(uniqueSelected);
90
+ }
91
+ }, [selectedItems]);
285
92
 
286
- if (clickedItem) {
287
- clickedItem[0].expanded = !clickedItem[0].expanded;
93
+ useEffect(() => {
94
+ let el = document.getElementById(`pb_data_wrapper_${id}`);
95
+ if (el) {
96
+ el.setAttribute("data-tree", JSON.stringify(checkedData));
288
97
  }
289
- };
98
+ if (returnAllSelected) {
99
+ onSelect(checkedData);
100
+ }
101
+ }, [checkedData]);
290
102
 
291
- //rendering formattedData to UI based on typeahead
292
- const renderNestedOptions = (items: { [key: string]: any }[]) => {
103
+ const DropDownSelectComponent = useMemo(() => {
293
104
  return (
294
- <ul>
295
- {Array.isArray(items) &&
296
- items.map((item: { [key: string]: any }) => {
297
- return (
298
- <>
299
- <li
300
- key={item.id}
301
- className="dropdown_item"
302
- data-name={item.id}
303
- >
304
- <div className="dropdown_item_checkbox_row">
305
- <div
306
- key={
307
- item.expanded ? "chevron-down" : "chevron-right"
308
- }
309
- >
310
- <CircleIconButton
311
- icon={
312
- item.expanded ? "chevron-down" : "chevron-right"
313
- }
314
- className={item.children && item.children.length > 0 ? "" : "toggle_icon"}
315
- onClick={(event) => handleToggleClick(item.id, event)}
316
- variant="link"
317
- />
318
- </div>
319
- <Checkbox text={item.label} id={item.id}>
320
- <input
321
- checked={item.checked}
322
- type="checkbox"
323
- name={item.label}
324
- value={item.label}
325
- onChange={(e) => {
326
- item.checked = !item.checked;
327
- handledropdownItemClick(e);
328
- }}
329
- />
330
- </Checkbox>
331
- </div>
332
- {item.expanded &&
333
- item.children &&
334
- item.children.length > 0 &&
335
- !filterItem && ( // Show children if expanded is true
336
- <div>{renderNestedOptions(item.children)}</div>
337
- )}
338
- </li>
339
- </>
340
- );
341
- })}
342
- </ul>
105
+ <MultiSelectHelper
106
+ treeData={formattedData}
107
+ onChange={(
108
+ // @ts-ignore
109
+ selectedNodes: { [key: string]: any }[],
110
+ currentNode: { [key: string]: any }[]
111
+ ) => {
112
+ setCheckedData(currentNode);
113
+ onSelect(currentNode);
114
+
115
+ }}
116
+ id={id}
117
+ {...props}
118
+ />
343
119
  );
344
- };
120
+ }, [formattedData])
345
121
 
346
122
  return (
347
123
  <div {...ariaProps} {...dataProps} className={classes} id={id}>
348
- <div ref={dropdownRef} className="wrapper">
349
- <div className="input_wrapper" onClick={handleInputWrapperClick}>
350
- <div className="input_inner_container">
351
- <input type="hidden" id={id} name={name} value={JSON.stringify(returnAllSelected ? returnedArray : defaultReturn)} />
352
- {returnedArray.length !== 0 && returnAllSelected
353
- ? returnedArray.map((item, index) => (
354
- <FormPill
355
- key={index}
356
- text={item.label}
357
- size="small"
358
- onClick={(event) => handlePillClose(event, item)}
359
- />
360
- ))
361
- : null}
362
- {!returnAllSelected &&
363
- defaultReturn.length !== 0 &&
364
- defaultReturn
365
- .filter(
366
- (item, index, self) =>
367
- index === self.findIndex((obj) => obj.id === item.id)
368
- )
369
- .map((item, index) => (
370
- <FormPill
371
- key={index}
372
- text={item.label}
373
- size="small"
374
- onClick={(event) => handlePillClose(event, item)}
375
- />
376
- ))}
377
- {returnedArray.length !== 0 && returnAllSelected && <br />}
378
- {defaultReturn.length !== 0 && !returnAllSelected && <br />}
379
- <input
380
- id="multiselect_input"
381
- onChange={(e) => {
382
- setFilterItem(e.target.value);
383
- }}
384
- placeholder="Start typing..."
385
- value={filterItem}
386
- onClick={() => setIsClosed(false)}
387
- />
388
- </div>
389
- {isClosed ? (
390
- <div key="chevron-down">
391
- <Icon icon="chevron-down" />
392
- </div>
393
- ) : (
394
- <div key="chevron-up">
395
- <Icon icon="chevron-up" />
396
- </div>
397
- )}
398
- </div>
399
- <div className={`dropdown_menu ${isClosed ? "close" : "open"}`}>
400
- {renderNestedOptions(
401
- filterItem ? findByFilter(formattedData, filterItem) : formattedData
402
- )}
403
- </div>
404
- </div>
124
+ {returnAllSelected ? (
125
+ <MultiSelectHelper
126
+ treeData={formattedData}
127
+ treeMode={returnAllSelected}
128
+ id={id}
129
+ onChange={onChange}
130
+ {...props}
131
+ />
132
+ ) : (
133
+ <>{DropDownSelectComponent}</>
134
+ )}
405
135
  </div>
406
136
  );
407
137
  };
@@ -0,0 +1,31 @@
1
+ import React from "react"
2
+ import DropdownTreeSelect from "react-dropdown-tree-select"
3
+ import "react-dropdown-tree-select/dist/styles.css"
4
+
5
+ type HelperProps = {
6
+ id?: string
7
+ treeData?: { [key: string]: string }[]
8
+ treeMode?: boolean
9
+ onChange?: any
10
+
11
+ }
12
+
13
+ const MultiSelectHelper = (props: HelperProps) => {
14
+ const { id, treeData, onChange, treeMode } = props
15
+
16
+
17
+ return (
18
+ <DropdownTreeSelect
19
+ data={treeData}
20
+ id={id}
21
+ keepOpenOnSelect
22
+ keepTreeOnSearch
23
+ keepChildrenOnSearch
24
+ onChange={onChange}
25
+ texts={{ placeholder: "Select..." }}
26
+ mode={treeMode ? 'hierarchical' : 'multiSelect'}
27
+ />
28
+ )
29
+ }
30
+
31
+ export default MultiSelectHelper
@@ -2,4 +2,4 @@ The MultiLevelSelect kit renders a multi leveled select dropdown based on data f
2
2
 
3
3
  For the React version of the kit, the `onSelect` prop returns an array of all checked items, irrespective of whether it is a parent, child or grandchild. Open the console on this example and check and uncheck checkboxes to see this is action!
4
4
 
5
- For the Rails version, the array of checked items is attached to the DOM in a data attribute titled `data-tree` on the wrapping div around the MultiLevelSelect.
5
+ For the Rails version, the array of checked items is attached to the DOM in a data attribute titled `data-tree` on the wrapping div around the MultiLevelSelect.
@@ -3,7 +3,6 @@
3
3
  value: "Power Home Remodeling",
4
4
  id: "powerhome1",
5
5
  expanded: true,
6
-
7
6
  children: [
8
7
  {
9
8
  label: "People",
@@ -64,6 +63,7 @@
64
63
  ],
65
64
  }] %>
66
65
 
66
+
67
67
  <%= pb_rails("multi_level_select", props: {
68
68
  id: "parent-persistence-multi-level-select",
69
69
  tree_data:treeData,
@@ -2,7 +2,6 @@ examples:
2
2
  rails:
3
3
  - multi_level_select_default: Default
4
4
  - multi_level_select_return_all_selected: Return All Selected
5
- - multi_level_select_with_form: With Form
6
5
 
7
6
  react:
8
7
  - multi_level_select_default: Default
@@ -0,0 +1,87 @@
1
+ export const findItemById = (
2
+ items: { [key: string]: any }[],
3
+ id: string
4
+ ): any => {
5
+ for (const item of items) {
6
+ if (item.id === id) {
7
+ return item;
8
+ }
9
+ if (item.children) {
10
+ const found = findItemById(item.children, id);
11
+ if (found) {
12
+ return found;
13
+ }
14
+ }
15
+ }
16
+ return null;
17
+ };
18
+
19
+ export const checkIt = (
20
+ foundItem: { [key: string]: any },
21
+ selectedItems: any[],
22
+ setSelectedItems: Function,
23
+ expand: boolean
24
+ ) => {
25
+ if (!foundItem) {
26
+ return;
27
+ }
28
+
29
+ foundItem.checked = true;
30
+ foundItem.expanded = expand;
31
+ selectedItems.push(foundItem);
32
+
33
+ if (foundItem.children) {
34
+ foundItem.children.map((x: any) => {
35
+ checkIt(x, selectedItems, setSelectedItems, expand);
36
+ });
37
+ }
38
+
39
+ setSelectedItems([...selectedItems]);
40
+ };
41
+
42
+ export const unCheckIt = (
43
+ foundItem: { [key: string]: any },
44
+ selectedItems: any,
45
+ setSelectedItems: any,
46
+ expand: boolean
47
+ ) => {
48
+ if (!foundItem) {
49
+ return;
50
+ }
51
+
52
+ foundItem.checked = false;
53
+ foundItem.expanded = false;
54
+ const newSelectedItems = selectedItems.filter(
55
+ (item: any) => item.id !== foundItem.id
56
+ );
57
+ if (foundItem.children) {
58
+ foundItem.children.map((x: any) => {
59
+ unCheckIt(x, selectedItems, setSelectedItems, expand);
60
+ });
61
+ }
62
+ setSelectedItems([...newSelectedItems]);
63
+ };
64
+
65
+
66
+ export const getParentAndAncestorsIds = (itemId:string, items:{ [key: string]: string; }[], ancestors:string[] = []):any => {
67
+ for (let i = 0; i < items.length; i++) {
68
+ const item:any = items[i];
69
+ if (item.id === itemId) {
70
+ // item found in current level of items array
71
+ return [...ancestors, item.id];
72
+ }
73
+ if (item.children && item.children.length > 0) {
74
+ // recursively search through children
75
+ const foundAncestors = getParentAndAncestorsIds(
76
+ itemId,
77
+ item.children,
78
+ [...ancestors, item.id]
79
+ );
80
+ if (foundAncestors) {
81
+ return foundAncestors;
82
+ }
83
+ }
84
+ }
85
+ // item not found in this level of items array or its children
86
+ return null;
87
+ }
@@ -3,8 +3,6 @@
3
3
  module Playbook
4
4
  module PbMultiLevelSelect
5
5
  class MultiLevelSelect < Playbook::KitBase
6
- prop :id
7
- prop :name
8
6
  prop :tree_data, type: Playbook::Props::Array,
9
7
  default: []
10
8
  prop :return_all_selected, type: Playbook::Props::Boolean,
@@ -17,7 +15,6 @@ module Playbook
17
15
  def multi_level_select_options
18
16
  {
19
17
  id: id,
20
- name: name,
21
18
  treeData: tree_data,
22
19
  returnAllSelected: return_all_selected,
23
20
  }
@@ -16,7 +16,7 @@ const treeData = {
16
16
  {
17
17
  label: 'No one can get me',
18
18
  value: 'anonymous',
19
- id:'default3',
19
+ id:'default2',
20
20
  },
21
21
  ],
22
22
  },
@@ -30,7 +30,6 @@ import * as DateStacked from 'pb_date_stacked/docs'
30
30
  import * as DateTime from 'pb_date_time/docs'
31
31
  import * as DateTimeStacked from 'pb_date_time_stacked/docs'
32
32
  import * as DateYearStacked from 'pb_date_year_stacked/docs'
33
- import * as Detail from 'pb_detail/docs'
34
33
  import * as Dialog from 'pb_dialog/docs'
35
34
  import * as DistributionBarDocs from 'pb_distribution_bar/docs'
36
35
  import * as FileUpload from 'pb_file_upload/docs'
@@ -131,7 +130,6 @@ WebpackerReact.setup({
131
130
  ...DateTime,
132
131
  ...DateTimeStacked,
133
132
  ...DateYearStacked,
134
- ...Detail,
135
133
  ...Dialog,
136
134
  ...DistributionBarDocs,
137
135
  ...FileUpload,