playbook_ui 12.25.0.pre.alpha.railsmultilevelimprovements785 → 12.25.0

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.
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,