playbook_ui 12.25.0 → 12.26.0.pre.alpha.PLAY603datepickerquickpickinputpresetdropdown794

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +1 -0
  3. data/app/pb_kits/playbook/index.js +1 -0
  4. data/app/pb_kits/playbook/pb_avatar/docs/_avatar_swift.md +82 -1
  5. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +26 -0
  6. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +102 -95
  7. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +30 -2
  8. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +11 -4
  9. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +44 -1
  10. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +38 -4
  11. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.html.erb +8 -0
  12. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_quick_pick.jsx +18 -0
  13. data/app/pb_kits/playbook/pb_date_picker/docs/example.yml +2 -0
  14. data/app/pb_kits/playbook/pb_date_picker/docs/index.js +2 -1
  15. data/app/pb_kits/playbook/pb_date_picker/plugins/quickPick.tsx +168 -0
  16. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +3 -2
  17. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_quick_pick_styles.scss +75 -0
  18. data/app/pb_kits/playbook/pb_detail/_detail.scss +44 -0
  19. data/app/pb_kits/playbook/pb_detail/_detail.tsx +55 -0
  20. data/app/pb_kits/playbook/pb_detail/_detail_mixins.scss +29 -0
  21. data/app/pb_kits/playbook/pb_detail/detail.html.erb +7 -0
  22. data/app/pb_kits/playbook/pb_detail/detail.rb +31 -0
  23. data/app/pb_kits/playbook/pb_detail/detail.test.jsx +46 -0
  24. data/app/pb_kits/playbook/pb_detail/docs/_description.md +1 -0
  25. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.html.erb +34 -0
  26. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.jsx +49 -0
  27. data/app/pb_kits/playbook/pb_detail/docs/_detail_bold.md +1 -0
  28. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.html.erb +24 -0
  29. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.jsx +38 -0
  30. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.md +6 -0
  31. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.html.erb +3 -0
  32. data/app/pb_kits/playbook/pb_detail/docs/_detail_default.jsx +13 -0
  33. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.html.erb +22 -0
  34. data/app/pb_kits/playbook/pb_detail/docs/_detail_styled.jsx +32 -0
  35. data/app/pb_kits/playbook/pb_detail/docs/example.yml +11 -0
  36. data/app/pb_kits/playbook/pb_detail/docs/index.js +4 -0
  37. data/app/pb_kits/playbook/pb_docs/kit_example.html.erb +14 -13
  38. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +3 -2
  39. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +212 -0
  40. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +58 -98
  41. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +340 -86
  42. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_default.md +1 -1
  43. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_return_all_selected.html.erb +1 -0
  44. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.test.jsx +1 -1
  45. data/app/pb_kits/playbook/pb_nav/_item.tsx +1 -1
  46. data/app/pb_kits/playbook/pb_nav/_subtle_mixin.scss +1 -1
  47. data/app/pb_kits/playbook/playbook-doc.js +2 -0
  48. data/dist/menu.yml +1 -0
  49. data/dist/playbook-rails.js +279 -7
  50. data/lib/playbook/version.rb +2 -2
  51. metadata +31 -9
  52. data/app/pb_kits/playbook/pb_multi_level_select/_multi_select_helper.tsx +0 -31
  53. data/app/pb_kits/playbook/pb_multi_level_select/helper_functions.ts +0 -87
@@ -0,0 +1,13 @@
1
+ import React from 'react'
2
+ import { Detail } from '../..'
3
+
4
+ const DetailDefault = (props) => (
5
+ <div>
6
+ <Detail
7
+ text="I am a detail kit"
8
+ {...props}
9
+ />
10
+ </div>
11
+ )
12
+
13
+ export default DetailDefault
@@ -0,0 +1,22 @@
1
+ <%= pb_rails("detail") do %>
2
+ <b>This text is using the <%="<b>"%> tag.</b>
3
+ <br />
4
+ <br />
5
+ <strong>This text is using the <%="<strong>"%> tag.</strong>
6
+ <br />
7
+ <br />
8
+ <a>This text is using the <%="<a>"%> tag.</a>
9
+ <br />
10
+ <br />
11
+ <i>This text is using the <%="<i>"%> tag.</i>
12
+ <br />
13
+ <br />
14
+ This <em>word</em> is using an <%="<em>"%> tag.
15
+ <br />
16
+ <br />
17
+ <small>This text is using the <%="<small>"%> tag.</small>
18
+ <br />
19
+ <br />
20
+ <u>This text is using the <%="<u>"%> tag.</u>
21
+ <br />
22
+ <% end %>
@@ -0,0 +1,32 @@
1
+ import React from 'react'
2
+ import { Detail } from '../..'
3
+
4
+ const DetailStyled = (props) => (
5
+ <>
6
+ <Detail {...props}>
7
+ <b>{"This text is using the <b> tag."}</b>
8
+ <br />
9
+ <br />
10
+ <strong>{"This text is using the <strong> tag."}</strong>
11
+ <br />
12
+ <br />
13
+ <a>{"This text is using the <a> tag."}</a>
14
+ <br />
15
+ <br />
16
+ <i>{"This text is using the <i> tag."}</i>
17
+ <br />
18
+ <br />
19
+ {"This "}<em>{"word"}</em>{" is using an <em> tag."}
20
+ <br />
21
+ <br />
22
+ <small>{"This text is using the <small> tag."}</small>
23
+ <br />
24
+ <br />
25
+ <u>{"This text is using the <u> tag."}</u>
26
+ <br />
27
+ <br />
28
+ </Detail>
29
+ </>
30
+ )
31
+
32
+ export default DetailStyled
@@ -0,0 +1,11 @@
1
+ examples:
2
+
3
+ rails:
4
+ - detail_default: Default
5
+ - detail_colors: Colors
6
+ - detail_bold: Bold
7
+
8
+ react:
9
+ - detail_default: Default
10
+ - detail_colors: Colors
11
+ - detail_bold: Bold
@@ -0,0 +1,4 @@
1
+ export { default as DetailDefault } from './_detail_default.jsx'
2
+ export { default as DetailColors } from './_detail_colors.jsx'
3
+ export { default as DetailStyled } from './_detail_styled.jsx'
4
+ export { default as DetailBold } from './_detail_bold.jsx'
@@ -1,20 +1,21 @@
1
1
  <% example_html = ERB.new(example).result %>
2
2
 
3
3
  <%= pb_rails("card", props: { classname: "pb--doc", padding: "none", dark: dark }) do %>
4
- <div class="pb--kit-example">
5
- <%= pb_rails("caption", props: { text: example_title, dark: dark }) %>
6
- <br />
7
- <%= example %>
8
- <br>
9
- </div>
10
-
11
- <% if (action_name == "kit_show_swift") %>
12
- <div class="pb--kit-example-markdown pt_none <%= dark ? "dark" : "" %>">
4
+ <% unless (action_name == "kit_show_swift") %>
5
+ <div class="pb--kit-example">
6
+ <%= pb_rails("caption", props: { text: example_title, dark: dark }) %>
7
+ <br />
8
+ <%= example %>
9
+ <br>
10
+ </div>
11
+ <% else %>
12
+ <div class="pb--kit-example-markdown pt_none markdown <%= dark ? "dark" : "" %>">
13
+ <%= pb_rails("caption", props: { text: example_title, dark: dark, margin_top: "md" }) %>
13
14
  <%= render_markdown(description) %>
14
15
  </div>
15
16
  <% end %>
16
17
  <% if show_code %>
17
- <div class="markdown pb--kit-example-markdown <%= dark ? "dark" : "" %>">
18
+ <div class="markdown pb--kit-example-markdown markdown <%= dark ? "dark" : "" %>">
18
19
  <%= render_markdown(description) %>
19
20
  </div>
20
21
  <div id="code-wrapper">
@@ -32,10 +33,10 @@
32
33
  </div>
33
34
  <div class="pb--codeCopy" data-action="toggle" data-togglable="code_example" style="display: none" >
34
35
  <%= pb_rails("section_separator", props: { dark: dark })%>
35
- <a href="#" style="padding: 16px 0px 22px 0px;" id="copy-code-<%= example_key %>" class="pb--copy-code" onclick="copyOnClick(`<%= source %>`, `copy-code-<%= example_key %>`)">
36
- <%= pb_rails("button", props: { id: "copy-button-#{example_key}", text: "Copy Code", size: "sm", icon: "copy" }) %>
36
+ <a href="#" id="copy-code-<%= example_key %>" class="pb--copy-code" onclick="copyOnClick(`<%= source %>`, `copy-code-<%= example_key %>`)">
37
+ <%= pb_rails("button", props: { id: "copy-button-#{example_key}", text: "Copy Code", size: "sm", icon: "copy", padding_x: "xs" }) %>
37
38
  </a>
38
- <pre class="highlight"><%= raw rouge(source, highlighter) %></pre>
39
+ <pre class="highlight"><%= raw render_code(source, highlighter) %></pre>
39
40
  </div>
40
41
  <%= pb_rails("popover", props: {
41
42
  trigger_element_id: "copy-button-#{example_key}",
@@ -10,7 +10,7 @@ type FormPillProps = {
10
10
  className?: string,
11
11
  id?: string,
12
12
  text: string,
13
- name: string,
13
+ name?: string,
14
14
  onClick?: React.MouseEventHandler<HTMLSpanElement>,
15
15
  avatar?: boolean,
16
16
  avatarUrl?: string,
@@ -25,6 +25,7 @@ type FormPillProps = {
25
25
  const FormPill = (props: FormPillProps) => {
26
26
  const {
27
27
  className,
28
+ id,
28
29
  text,
29
30
  name,
30
31
  onClick = () => {},
@@ -41,7 +42,7 @@ const FormPill = (props: FormPillProps) => {
41
42
  textTransform,
42
43
  )
43
44
  return (
44
- <div className={css}>
45
+ <div className={css} id={id}>
45
46
  {name &&
46
47
  <>
47
48
  <Avatar
@@ -0,0 +1,212 @@
1
+ //function for unchecking items in formattedData
2
+ export const unCheckIt = (
3
+ formattedData: { [key: string]: any }[],
4
+ id: string
5
+ ) => {
6
+ formattedData.map((item: { [key: string]: any }) => {
7
+ if (item.id === id && item.checked) {
8
+ item.checked = false;
9
+ }
10
+ if (item.children && item.children.length > 0) {
11
+ unCheckIt(item.children, id);
12
+ }
13
+ return item;
14
+ });
15
+ };
16
+
17
+ //function to retrieve all ancestors of unchecked item and set checked to false
18
+ export const getAncestorsOfUnchecked = (
19
+ formattedData: { [key: string]: any }[],
20
+ item: { [key: string]: any }
21
+ ) => {
22
+ if (item.parent_id) {
23
+ const ancestors = filterFormattedDataById(formattedData, item.parent_id);
24
+ ancestors[0].checked = false;
25
+
26
+ if (ancestors[0].parent_id) {
27
+ getAncestorsOfUnchecked(formattedData, ancestors[0]);
28
+ }
29
+ }
30
+ };
31
+
32
+ //recursively check all child and grandchild items if parent checked
33
+ export const checkedRecursive = (item: { [key: string]: any }) => {
34
+ if (!item.checked) {
35
+ item.checked = true;
36
+ }
37
+ if (item.children && item.children.length > 0) {
38
+ item.children.forEach((childItem: { [key: string]: any }) => {
39
+ checkedRecursive(childItem);
40
+ });
41
+ }
42
+ };
43
+
44
+ //recursively uncheck all child and grandchild items if parent unchecked
45
+ export const unCheckedRecursive = (item: { [key: string]: any }) => {
46
+ if (item.checked) {
47
+ item.checked = false;
48
+ }
49
+ if (item.children && item.children.length > 0) {
50
+ item.children.forEach((childItem: { [key: string]: any }) => {
51
+ unCheckedRecursive(childItem);
52
+ });
53
+ }
54
+ };
55
+
56
+ //function is going over formattedData and returning all objects that match the
57
+ //id of the clicked item from the dropdown
58
+ export const filterFormattedDataById = (
59
+ formattedData: { [key: string]: any }[],
60
+ id: string
61
+ ) => {
62
+ const matched: { [key: string]: any }[] = [];
63
+ const recursiveSearch = (data: { [key: string]: any }[], term: string) => {
64
+ for (const item of data) {
65
+ if (item.id.toLowerCase() === (term.toLowerCase())) {
66
+ matched.push(item);
67
+ }
68
+
69
+ if (item.children && item.children.length > 0) {
70
+ recursiveSearch(item.children, term);
71
+ }
72
+ }
73
+ };
74
+
75
+ recursiveSearch(formattedData, id);
76
+ return matched;
77
+ };
78
+
79
+ export const findByFilter = (
80
+ formattedData: { [key: string]: any }[],
81
+ searchTerm: string
82
+ ) => {
83
+ const matchedItems: { [key: string]: any }[] = [];
84
+ const recursiveSearch = (data: { [key: string]: any }[], term: string) => {
85
+ for (const item of data) {
86
+ if (item.label.toLowerCase().includes(term.toLowerCase())) {
87
+ matchedItems.push(item);
88
+ }
89
+
90
+ if (item.children) {
91
+ recursiveSearch(item.children, term);
92
+ }
93
+ }
94
+ };
95
+
96
+ recursiveSearch(formattedData, searchTerm);
97
+ return matchedItems;
98
+ };
99
+
100
+ //function to get all items with checked = true
101
+ export const getCheckedItems = (
102
+ data: { [key: string]: any }[]
103
+ ): { [key: string]: any }[] => {
104
+ const checkedItems: { [key: string]: any }[] = [];
105
+ if (!Array.isArray(data)) {
106
+ return;
107
+ }
108
+ data.forEach((item: { [key: string]: any }) => {
109
+ if (item.checked) {
110
+ checkedItems.push(item);
111
+ }
112
+ if (item.children && item.children.length > 0) {
113
+ const childCheckedItems = getCheckedItems(item.children);
114
+ checkedItems.push(...childCheckedItems);
115
+ }
116
+ });
117
+ return checkedItems;
118
+ };
119
+
120
+ export const getChildIds = (
121
+ item: { [key: string]: any },
122
+ defaultArray: { [key: string]: any }[]
123
+ ) => {
124
+ let childIds: string[] = [];
125
+ item.children.forEach((child: { [key: string]: any }) => {
126
+ childIds.push(child.id);
127
+ if (child.children && child.children.length > 0) {
128
+ const childChildIds = getChildIds(child, defaultArray);
129
+ childIds.push(...childChildIds);
130
+ }
131
+ });
132
+ return childIds;
133
+ };
134
+
135
+ export const updateReturnItems = (newChecked: { [key: string]: any }[]) => {
136
+ const updatedCheckedItems: { [key: string]: any }[] = [];
137
+ for (const item of newChecked) {
138
+ if (item.children && item.children.length > 0) {
139
+ const allChildrenChecked = item.children.every(
140
+ (child: { [key: string]: any }) => child.checked
141
+ );
142
+ if (allChildrenChecked) {
143
+ updatedCheckedItems.push(item);
144
+ }
145
+ }
146
+ const childItem = updatedCheckedItems.some((x) => x.id === item?.parent_id);
147
+ if (!childItem) {
148
+ updatedCheckedItems.push(item);
149
+ }
150
+ }
151
+ const filteredReturn = updatedCheckedItems.filter((item) => {
152
+ return !updatedCheckedItems.find(
153
+ (otherItem) => otherItem.id === item.parent_id
154
+ );
155
+ });
156
+ return filteredReturn;
157
+ };
158
+
159
+ export const recursiveReturnOnlyParent = (
160
+ items: { [key: string]: any },
161
+ formattedData: { [key: string]: any }[],
162
+ defaultReturn: { [key: string]: any }[],
163
+ setDefaultReturn: any
164
+ ) => {
165
+ const parent = filterFormattedDataById(formattedData, items.parent_id);
166
+ const allChildrenChecked = parent[0].children.every(
167
+ (child: { [key: string]: any }) => child.checked
168
+ );
169
+ if (allChildrenChecked) {
170
+ // Only return the parent and remove its children from defaultReturn
171
+ parent[0].checked = true;
172
+ const filteredDefaultReturn = defaultReturn.filter((item) => {
173
+ // Remove children of the specific parent
174
+ if (
175
+ parent[0].children.find(
176
+ (child: { [key: string]: any }) => child.id === item.id
177
+ )
178
+ ) {
179
+ return false;
180
+ }
181
+ });
182
+ setDefaultReturn([...filteredDefaultReturn, parent[0]]);
183
+ // Check if the parent has a parent and its children are all checked
184
+ const parentHasParent = parent[0].parent_id !== null;
185
+ if (parentHasParent) {
186
+ recursiveReturnOnlyParent(
187
+ parent[0],
188
+ formattedData,
189
+ filteredDefaultReturn,
190
+ setDefaultReturn
191
+ );
192
+ }
193
+ } else {
194
+ const checkedChildren = parent[0].children.filter(
195
+ (child: { [key: string]: any }) => child.checked
196
+ );
197
+ const updatedDefaultReturn = [...defaultReturn, ...checkedChildren];
198
+ setDefaultReturn(updatedDefaultReturn);
199
+ }
200
+ };
201
+
202
+ export const removeChildrenIfParentChecked = (
203
+ items: { [key: string]: any },
204
+ defaultReturn: { [key: string]: any }[],
205
+ setDefaultReturn: any
206
+ ) => {
207
+ const childIds = getChildIds(items, defaultReturn);
208
+ const filteredDefaultArray = defaultReturn.filter(
209
+ (item: { [key: string]: any }) => childIds !== item.id
210
+ );
211
+ setDefaultReturn([...filteredDefaultArray, items]);
212
+ };
@@ -5,123 +5,83 @@
5
5
  @import "../tokens/spacing";
6
6
  @import "../tokens/typography";
7
7
  @import "../tokens/line_height";
8
+ @import "../tokens/positioning";
8
9
 
9
10
  .pb_multi_level_select {
10
11
  font-family: $font-family-base;
11
12
 
12
- .search {
13
- font-family: $font-family-base;
14
- }
13
+ .wrapper {
14
+ position: relative;
15
15
 
16
- .dropdown {
17
- width: 100%;
18
- .search {
19
- border-bottom: none;
16
+ .toggle_icon {
17
+ visibility: hidden;
18
+ .pb_button_content {
19
+ visibility: hidden !important;
20
+ }
20
21
  }
21
22
 
22
- .toggle {
23
- padding: 0px $space_xs + 1;
24
- }
25
- .node.tree {
23
+ .input_wrapper {
24
+ background-color: $white;
25
+ cursor: pointer;
26
+ padding: $space_xs + 4 $space_sm;
27
+ border: 1px solid #e4e8f0;
28
+ border-radius: $border_rad_heavier;
26
29
  display: flex;
27
30
  align-items: center;
28
- }
29
-
30
- .node-label {
31
- line-height: $lh_tight;
32
- color: $text_lt_default;
33
- font-size: $text_default;
34
- font-weight: $regular;
35
- font-family: $font-family-base;
36
- }
37
-
38
- .checkbox-item {
39
- height: $space_sm;
40
- width: $space_sm;
41
- }
42
-
43
- .toggle.expanded:after {
44
- border-style: solid;
45
- border-width: 0.1em 0.1em 0 0;
46
- content: "";
47
- display: inline-block;
48
- height: 0.8em;
49
- position: relative;
50
- vertical-align: top;
51
- width: 0.8em;
52
- top: 7px;
53
- transform: rotate(135deg);
54
- font-size: 0.5em;
55
- }
56
- .toggle.expanded:hover {
57
- background-color: rgba(0, 86, 207, 0.1);
58
- border-radius: $border_radius_rounded;
59
- &::after {
60
- color: $primary;
31
+ justify-content: space-between;
32
+ .input_inner_container {
33
+ max-width: 90%;
61
34
  }
62
- }
63
- .toggle.collapsed:after {
64
- border-style: solid;
65
- border-width: 0.1em 0.1em 0 0;
66
- content: "";
67
- display: inline-block;
68
- height: 0.8em;
69
- right: 0.1em;
70
- position: relative;
71
- vertical-align: top;
72
- width: 0.8em;
73
- top: 7px;
74
- font-size: 0.5em;
75
- transform: rotate(45deg);
76
- top: 9px;
77
- }
78
35
 
79
- .toggle.collapsed:hover {
80
- background-color: rgba(0, 86, 207, 0.1);
81
- border-radius: $border_radius_rounded;
82
- &::after {
83
- color: $primary;
36
+ input {
37
+ border: none;
38
+ font-family: $font_family_base;
39
+ padding: $space_xs;
40
+ &:focus {
41
+ outline: none;
42
+ }
84
43
  }
85
44
  }
45
+ }
46
+ .dropdown_menu {
47
+ position: absolute;
48
+ z-index: $z_1;
49
+ width: 100%;
50
+ background-color: $white;
51
+ padding: $space_sm $space_sm $space_sm 0;
52
+ border-radius: $border_rad_heavier;
53
+ border: 1px solid #e4e8f0;
54
+ max-height: 600px;
55
+ overflow: auto;
86
56
 
87
- .tag {
88
- height: fit-content;
89
- display: inline-flex;
90
- justify-content: center;
91
- align-items: center;
92
- padding: $space_xxs $space_xxs $space_xxs $space_xs;
93
- border-radius: $border_radius_xl + 2;
94
- background-color: rgba($primary, 0.1);
95
- color: $primary;
96
- border: none;
97
- font-size: $font_smaller;
98
- font-weight: $bolder;
57
+ ul {
58
+ padding: 0px !important;
59
+ }
99
60
 
100
- .tag-remove {
101
- color: $primary;
102
- padding-left: $space_xs;
103
- padding-right: $space_xxs;
61
+ .dropdown_item {
62
+ list-style: none;
63
+ padding-left: 20px;
64
+ .dropdown_item_checkbox_row {
65
+ display: flex;
66
+ align-items: center;
67
+ .pb_checkbox_kit {
68
+ align-items: center;
69
+ }
70
+ .pb_button_kit_link_inline_enabled {
71
+ color: $charcoal;
72
+ &:hover {
73
+ color: $primary;
74
+ }
75
+ }
104
76
  }
105
77
  }
106
78
  }
107
- .react-dropdown-tree-select .dropdown .dropdown-trigger {
108
- width: 100%;
109
- @include pb_textarea_light;
110
- }
111
- .react-dropdown-tree-select .dropdown .dropdown-trigger.arrow.bottom:after {
112
- content: none;
113
- }
114
- .react-dropdown-tree-select .dropdown .dropdown-trigger.arrow.top:after {
115
- content: none;
116
- }
117
79
 
118
- .react-dropdown-tree-select .dropdown .dropdown-content {
119
- width: 100%;
120
- border: 1px solid $border_light;
121
- border-radius: $border_rad_heavier;
122
- box-shadow: $shadow_deeper;
80
+ .close {
81
+ display: none;
123
82
  }
124
- .node.leaf.collapsed {
125
- display: block !important;
83
+
84
+ .open {
85
+ display: block;
126
86
  }
127
87
  }