playbook_ui 14.8.0 → 14.9.0.pre.alpha.PBNTR686advancedtablepaginationpoc4747

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/_playbook.scss +2 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +61 -17
  4. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta.html.erb +1 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_beta_sort.html.erb +1 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell_rails.html.erb +53 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_custom_cell_rails.md +5 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination.jsx +50 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination.md +1 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_with_props.jsx +57 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_pagination_with_props.md +5 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_pagination_mock_data.json +5600 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +3 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +3 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +15 -0
  17. data/app/pb_kits/playbook/pb_background/_background.tsx +8 -2
  18. data/app/pb_kits/playbook/pb_button/_button.scss +6 -0
  19. data/app/pb_kits/playbook/pb_button/_button.tsx +1 -3
  20. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +15 -0
  21. data/app/pb_kits/playbook/pb_button/button.rb +1 -1
  22. data/app/pb_kits/playbook/pb_button/docs/_button_default.html.erb +1 -0
  23. data/app/pb_kits/playbook/pb_button/docs/_button_default.jsx +8 -0
  24. data/app/pb_kits/playbook/pb_button/docs/_button_default.md +1 -1
  25. data/app/pb_kits/playbook/pb_checkbox/checkbox.html.erb +2 -2
  26. data/app/pb_kits/playbook/pb_checkbox/checkbox.rb +0 -4
  27. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_indeterminate.html.erb +84 -7
  28. data/app/pb_kits/playbook/pb_collapsible/_collapsible.tsx +3 -1
  29. data/app/pb_kits/playbook/pb_currency/_currency.tsx +7 -3
  30. data/app/pb_kits/playbook/pb_currency/currency.html.erb +2 -2
  31. data/app/pb_kits/playbook/pb_currency/currency.rb +17 -1
  32. data/app/pb_kits/playbook/pb_currency/currency.test.js +40 -3
  33. data/app/pb_kits/playbook/pb_currency/docs/_currency_negative.html.erb +4 -0
  34. data/app/pb_kits/playbook/pb_currency/docs/_currency_negative.jsx +16 -0
  35. data/app/pb_kits/playbook/pb_currency/docs/example.yml +2 -0
  36. data/app/pb_kits/playbook/pb_currency/docs/index.js +1 -0
  37. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +1 -0
  38. data/app/pb_kits/playbook/pb_drawer/_drawer.tsx +159 -45
  39. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +31 -0
  40. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.md +6 -0
  41. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +1 -1
  42. data/app/pb_kits/playbook/pb_drawer/docs/index.js +1 -0
  43. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +1 -0
  44. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.html.erb +0 -1
  45. data/app/pb_kits/playbook/pb_dropdown/dropdown_container.rb +0 -4
  46. data/app/pb_kits/playbook/pb_dropdown/utilities/subComponentHelper.tsx +13 -2
  47. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -2
  48. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_loading.html.erb +1 -1
  49. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +63 -12
  50. data/app/pb_kits/playbook/pb_image/_image.tsx +3 -1
  51. data/app/pb_kits/playbook/pb_layout/_layout.tsx +6 -3
  52. data/app/pb_kits/playbook/pb_overlay/_overlay.tsx +3 -1
  53. data/app/pb_kits/playbook/pb_pagination/_pagination.tsx +2 -2
  54. data/app/pb_kits/playbook/pb_progress_simple/_progress_simple.tsx +5 -3
  55. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.html.erb +3 -2
  56. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.rb +5 -0
  57. data/app/pb_kits/playbook/pb_section_separator/_section_separator.tsx +3 -1
  58. data/app/pb_kits/playbook/pb_selectable_card/docs/_selectable_card_default.html.erb +2 -1
  59. data/app/pb_kits/playbook/pb_table/_table.tsx +109 -25
  60. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns.jsx +87 -0
  61. data/app/pb_kits/playbook/pb_table/docs/_table_sticky_left_columns_react.md +2 -0
  62. data/app/pb_kits/playbook/pb_table/docs/index.js +1 -0
  63. data/app/pb_kits/playbook/pb_table/styles/_all.scss +2 -0
  64. data/app/pb_kits/playbook/pb_table/styles/_scroll.scss +4 -0
  65. data/app/pb_kits/playbook/pb_table/styles/_sticky_columns.scss +18 -0
  66. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.html.erb +47 -0
  67. data/app/pb_kits/playbook/pb_timeline/docs/_timeline_with_children.jsx +59 -0
  68. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +3 -0
  69. data/app/pb_kits/playbook/pb_typeahead/index.ts +29 -3
  70. data/app/pb_kits/playbook/pb_typeahead/typeahead.html.erb +5 -2
  71. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +4 -0
  72. data/app/pb_kits/playbook/tokens/_height.scss +19 -0
  73. data/app/pb_kits/playbook/tokens/exports/_height.module.scss +37 -0
  74. data/app/pb_kits/playbook/utilities/_height.scss +33 -0
  75. data/app/pb_kits/playbook/utilities/_max_width.scss +29 -9
  76. data/app/pb_kits/playbook/utilities/_min_width.scss +6 -2
  77. data/app/pb_kits/playbook/utilities/_width.scss +45 -0
  78. data/app/pb_kits/playbook/utilities/globalPropNames.mjs +1 -1
  79. data/app/pb_kits/playbook/utilities/globalProps.ts +37 -4
  80. data/dist/chunks/_typeahead-B8fkIeXA.js +22 -0
  81. data/dist/chunks/_weekday_stacked-DSEuqOLN.js +45 -0
  82. data/dist/chunks/{lib-BC6ESsxG.js → lib-SyD3buPZ.js} +1 -1
  83. data/dist/chunks/{pb_form_validation-B_Z9rEbg.js → pb_form_validation-Dt8UJgrJ.js} +1 -1
  84. data/dist/chunks/vendor.js +1 -1
  85. data/dist/menu.yml +321 -0
  86. data/dist/playbook-doc.js +1 -1
  87. data/dist/playbook-rails-react-bindings.js +1 -1
  88. data/dist/playbook-rails.js +1 -1
  89. data/dist/playbook.css +1 -1
  90. data/lib/playbook/classnames.rb +4 -0
  91. data/lib/playbook/forms/builder/typeahead_field.rb +13 -0
  92. data/lib/playbook/height.rb +29 -0
  93. data/lib/playbook/kit_base.rb +16 -1
  94. data/lib/playbook/max_height.rb +29 -0
  95. data/lib/playbook/min_height.rb +29 -0
  96. data/lib/playbook/spacing.rb +21 -0
  97. data/lib/playbook/version.rb +2 -2
  98. metadata +31 -9
  99. data/dist/chunks/_typeahead-D0PihN_3.js +0 -22
  100. data/dist/chunks/_weekday_stacked-uMIX8f-A.js +0 -45
@@ -7,7 +7,7 @@ import {
7
7
  buildDataProps,
8
8
  buildHtmlProps,
9
9
  } from "../utilities/props";
10
- import { globalProps } from "../utilities/globalProps";
10
+ import { globalProps, globalInlineProps } from "../utilities/globalProps";
11
11
 
12
12
  import { DialogContext } from "../pb_dialog/_dialog_context";
13
13
 
@@ -15,20 +15,22 @@ type DrawerProps = {
15
15
  aria?: { [key: string]: string };
16
16
  behavior?: "floating" | "push";
17
17
  border?: "full" | "none" | "right" | "left";
18
- breakpoint?: "none" | "xs" | "sm" | "md" | "lg" | "xl";
18
+ openBreakpoint?: "none" | "xs" | "sm" | "md" | "lg" | "xl";
19
+ closeBreakpoint?: "none" | "xs" | "sm" | "md" | "lg" | "xl";
19
20
  children: React.ReactNode | React.ReactNode[] | string;
20
21
  className?: string;
21
22
  data?: { [key: string]: string };
22
23
  htmlOptions?: { [key: string]: string | number | boolean | (() => void) };
23
24
  id?: string;
24
25
  fullHeight?: boolean;
26
+ menuButtonID?: string;
25
27
  onClose?: () => void;
26
28
  opened: boolean;
27
29
  overlay: boolean;
28
30
  placement?: "left" | "right";
29
31
  size?: "xs" | "sm" | "md" | "lg" | "xl";
30
32
  text?: string;
31
- trigger?: string;
33
+ withinElement?: boolean;
32
34
  };
33
35
 
34
36
  const Drawer = (props: DrawerProps): React.ReactElement => {
@@ -36,19 +38,21 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
36
38
  aria = {},
37
39
  behavior = "floating",
38
40
  border = "none",
39
- breakpoint = "none",
41
+ openBreakpoint = "none",
42
+ closeBreakpoint = "none",
40
43
  className,
41
44
  data = {},
42
45
  htmlOptions = {},
43
46
  id,
44
47
  size = "md",
45
48
  children,
46
- fullHeight = false,
49
+ fullHeight = true,
50
+ menuButtonID,
47
51
  opened,
48
52
  onClose,
49
53
  overlay = true,
50
54
  placement = "left",
51
- trigger,
55
+ withinElement = false,
52
56
  } = props;
53
57
  const ariaProps = buildAriaProps(aria);
54
58
  const dataProps = buildDataProps(data);
@@ -80,6 +84,7 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
80
84
  drawer_border_full: border === "full",
81
85
  drawer_border_right: border === "right",
82
86
  drawer_border_left: border === "left",
87
+ pb_drawer_within_element: withinElement,
83
88
  }
84
89
  )} ${globalPropsString}`,
85
90
  afterOpen: "pb_drawer_after_open",
@@ -100,10 +105,11 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
100
105
  };
101
106
 
102
107
  const classes = classnames(buildCss("pb_drawer_wrapper"), className);
103
-
108
+ const dynamicInlineProps = globalInlineProps(props)
109
+ const [menuButtonOpened, setMenuButtonOpened] = useState(false);
104
110
  const [triggerOpened, setTriggerOpened] = useState(false);
105
111
 
106
- const breakpointWidths: Record<DrawerProps["breakpoint"], number> = {
112
+ const breakpointWidths: Record<DrawerProps["openBreakpoint"], number> = {
107
113
  none: 0,
108
114
  xs: 575,
109
115
  sm: 768,
@@ -112,20 +118,30 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
112
118
  xl: 1400,
113
119
  };
114
120
 
115
- // State to manage opening the drawer based on breakpoint
116
- const [isBreakpointOpen, setIsBreakpointOpen] = useState(false);
121
+ const breakpointValues = {
122
+ none: 0,
123
+ xs: 575,
124
+ sm: 768,
125
+ md: 992,
126
+ lg: 1200,
127
+ xl: 1400,
128
+ }
129
+
130
+ const [isOpenBreakpointOpen, setIsOpenBreakpointOpen] = useState(false);
131
+ const [isUserClosed, setIsUserClosed] = useState(false);
117
132
 
118
133
  useEffect(() => {
119
- if (breakpoint === "none") return;
134
+ if (openBreakpoint === "none") return;
120
135
 
121
136
  const handleResize = () => {
122
137
  const width = window.innerWidth;
123
- const breakpointWidth = breakpointWidths[breakpoint];
138
+ const openBreakpointWidth = breakpointWidths[openBreakpoint];
124
139
 
125
- if (width <= breakpointWidth) {
126
- setIsBreakpointOpen(true);
140
+ if (width <= openBreakpointWidth) {
141
+ setIsOpenBreakpointOpen(true);
127
142
  } else {
128
- setIsBreakpointOpen(false);
143
+ setIsOpenBreakpointOpen(false);
144
+ setIsUserClosed(false); // Reset when the breakpoint condition changes
129
145
  }
130
146
  };
131
147
 
@@ -137,9 +153,53 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
137
153
  return () => {
138
154
  window.removeEventListener("resize", handleResize);
139
155
  };
140
- }, [breakpoint]);
156
+ }, [openBreakpoint]);
157
+
158
+ useEffect(() => {
159
+ if (closeBreakpoint === "none") return;
141
160
 
142
- const modalIsOpened = trigger ? triggerOpened : opened || isBreakpointOpen;
161
+ const handleResize = () => {
162
+ const width = window.innerWidth;
163
+ if (width >= breakpointValues[closeBreakpoint]) {
164
+ setIsOpenBreakpointOpen(true);
165
+ } else {
166
+ setIsOpenBreakpointOpen(false);
167
+ }
168
+ }
169
+
170
+ window.addEventListener("resize", handleResize);
171
+
172
+ handleResize();
173
+
174
+ return () => {
175
+ window.removeEventListener("resize", handleResize);
176
+ };
177
+
178
+ }, [closeBreakpoint]);
179
+
180
+ //hide menu button if breakpoint opens the drawer
181
+ useEffect(() => {
182
+ if (menuButtonID) {
183
+ const menuButton = document.getElementById(menuButtonID);
184
+ if (menuButton) {
185
+ if (isOpenBreakpointOpen) {
186
+ menuButton.style.display = 'none';
187
+ } else {
188
+ menuButton.style.display = '';
189
+ }
190
+ }
191
+ }
192
+ }, [menuButtonID, isOpenBreakpointOpen]);
193
+
194
+ // Reset isUserClosed when isBreakpointOpen changes
195
+ useEffect(() => {
196
+ if (isOpenBreakpointOpen) {
197
+ setIsUserClosed(false);
198
+ }
199
+ }, [isOpenBreakpointOpen]);
200
+
201
+ const modalIsOpened =
202
+ (isOpenBreakpointOpen && !isUserClosed) || menuButtonOpened || opened;
143
203
 
144
204
  const [animationState, setAnimationState] = useState("");
145
205
 
@@ -150,13 +210,15 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
150
210
  setAnimationState("beforeClose");
151
211
  setTimeout(() => {
152
212
  setAnimationState("");
153
- }, 200); // closeTimeoutMS
213
+ }, 200);
154
214
  }
155
215
  }, [modalIsOpened]);
156
216
 
157
217
  const isModalVisible = modalIsOpened || animationState === "beforeClose";
158
218
 
159
219
  useEffect(() => {
220
+ if (withinElement) return;
221
+
160
222
  const sizeMap: Record<DrawerProps["size"], string> = {
161
223
  xl: "365px",
162
224
  lg: "300px",
@@ -165,7 +227,6 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
165
227
  xs: "64px",
166
228
  };
167
229
  const body = document.querySelector("body");
168
-
169
230
  if (modalIsOpened && behavior === "push" && body) {
170
231
  if (placement === "left") {
171
232
  body.style.cssText = `margin-left: ${sizeMap[size]} !important; margin-right: '' !important;`;
@@ -181,45 +242,98 @@ const Drawer = (props: DrawerProps): React.ReactElement => {
181
242
  body.style.cssText = ""; // Clear the styles when modal is closed or behavior is not 'push'
182
243
  body.classList.remove("PBDrawer__Body--open");
183
244
  }
184
- }, [modalIsOpened, behavior, placement, size]);
245
+ }, [modalIsOpened, behavior, placement, size, withinElement]);
185
246
 
186
247
  const api = {
187
- onClose: trigger
188
- ? function () {
189
- setTriggerOpened(false);
190
- }
191
- : onClose,
248
+ onClose: () => {
249
+ if (menuButtonID) {
250
+ setMenuButtonOpened(false);
251
+ }
252
+ setIsUserClosed(true);
253
+ if (onClose) {
254
+ onClose();
255
+ }
256
+ },
192
257
  };
193
258
 
259
+ useEffect(() => {
260
+ if (menuButtonID) {
261
+ const menuButton = document.getElementById(menuButtonID);
262
+ if (menuButton) {
263
+ const handleMenuButtonClick = () => {
264
+ if (modalIsOpened) {
265
+ // Drawer is open, close it
266
+ setMenuButtonOpened(false);
267
+ setIsUserClosed(true);
268
+ } else {
269
+ // Drawer is closed, open it
270
+ setMenuButtonOpened(true);
271
+ setIsUserClosed(false);
272
+ }
273
+ };
274
+ menuButton.addEventListener("click", handleMenuButtonClick);
275
+ return () => {
276
+ menuButton.removeEventListener("click", handleMenuButtonClick);
277
+ };
278
+ }
279
+ }
280
+ }, [menuButtonID, modalIsOpened]);
281
+
194
282
  return (
195
283
  <DialogContext.Provider value={api}>
196
- <div
197
- {...ariaProps}
198
- {...dataProps}
199
- {...htmlProps}
200
- className={classes}
201
- >
284
+ {withinElement ? (
285
+ isModalVisible && (
286
+ <div
287
+ {...ariaProps}
288
+ {...dataProps}
289
+ {...htmlProps}
290
+ style={dynamicInlineProps}
291
+ className={classnames(drawerClassNames.base, {
292
+ [drawerClassNames.afterOpen]:
293
+ animationState === "afterOpen",
294
+ [drawerClassNames.beforeClose]:
295
+ animationState === "beforeClose",
296
+ })}
297
+ id={id}
298
+ onClick={(e) => e.stopPropagation()}
299
+ >
300
+ {children}
301
+ </div>
302
+ )
303
+ ) : (
304
+ <div
305
+ {...ariaProps}
306
+ {...dataProps}
307
+ {...htmlProps}
308
+ className={classes}
309
+ style={dynamicInlineProps}
310
+ >
202
311
  {isModalVisible && (
203
312
  <div
204
313
  className={classnames(overlayClassNames.base, {
205
- [overlayClassNames.afterOpen]: animationState === "afterOpen",
206
- [overlayClassNames.beforeClose]: animationState === "beforeClose",
207
- })}
314
+ [overlayClassNames.afterOpen]:
315
+ animationState === "afterOpen",
316
+ [overlayClassNames.beforeClose]:
317
+ animationState === "beforeClose",
318
+ })}
208
319
  id={id}
209
- onClick={overlay ? onClose : undefined}
320
+ onClick={overlay ? api.onClose : undefined}
210
321
  >
211
- <div
212
- className={classnames(drawerClassNames.base, {
213
- [drawerClassNames.afterOpen]: animationState === "afterOpen",
214
- [drawerClassNames.beforeClose]: animationState === "beforeClose",
215
- })}
216
- onClick={(e) => e.stopPropagation()}
217
- >
218
- {children}
219
- </div>
322
+ <div
323
+ className={classnames(drawerClassNames.base, {
324
+ [drawerClassNames.afterOpen]:
325
+ animationState === "afterOpen",
326
+ [drawerClassNames.beforeClose]:
327
+ animationState === "beforeClose",
328
+ })}
329
+ onClick={(e) => e.stopPropagation()}
330
+ >
331
+ {children}
332
+ </div>
220
333
  </div>
221
334
  )}
222
- </div>
335
+ </div>
336
+ )}
223
337
  </DialogContext.Provider>
224
338
  );
225
339
  };
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { Button, Drawer, Icon, Title } from "playbook-ui";
3
+
4
+ const DrawerMenu = () => {
5
+
6
+ return (
7
+ <>
8
+ <Button id="menuButton"
9
+ padding="sm"
10
+ >
11
+ <Icon icon="bars"
12
+ size="3x"
13
+ />
14
+ </Button>
15
+ <Drawer
16
+ behavior="push"
17
+ closeBreakpoint="md"
18
+ menuButtonID="menuButton"
19
+ overlay={false}
20
+ placement="left"
21
+ size="lg"
22
+ withinElement
23
+ >
24
+ <Title paddingBottom="md">A really neat menu</Title>
25
+ <Button text="This Button does nothing" />
26
+ </Drawer>
27
+ </>
28
+ );
29
+ };
30
+
31
+ export default DrawerMenu;
@@ -0,0 +1,6 @@
1
+ Our drawer kit can fulfill your responsive menu needs! Using the `closeBreakpoint` prop you can have the menu close on smaller screens like phones/tablets.
2
+
3
+ Set a menu button with the `menuButtonID` props. When the Drawer is open, the menu button will be hidden. But when your Brakpoint closes the drawer, you can toggle the Drawer open/close with your menu butotn.
4
+
5
+ Also use the `withinElement` props to have the Drawer open within a specific element, instead of the default behavior of it taking up the entire screen size.
6
+
@@ -6,7 +6,7 @@ examples:
6
6
 
7
7
  react:
8
8
  - drawer_default: Default
9
+ - drawer_menu: Menu Behavior
9
10
  - drawer_sizes: Sizes
10
11
  - drawer_overlay: Overlay
11
12
  - drawer_borders: Borders
12
- - drawer_breakpoints: Open on Breakpoints
@@ -3,3 +3,4 @@ export { default as DrawerSizes } from './_drawer_sizes.jsx'
3
3
  export { default as DrawerOverlay } from './_drawer_overlay.jsx'
4
4
  export { default as DrawerBorders } from './_drawer_borders.jsx'
5
5
  export { default as DrawerBreakpoints } from './_drawer_breakpoints.jsx'
6
+ export { default as DrawerMenu } from './_drawer_menu.jsx'
@@ -53,6 +53,7 @@
53
53
  }
54
54
 
55
55
  .pb_dropdown_container {
56
+ position: absolute;
56
57
  background-color: $white;
57
58
  overflow: hidden;
58
59
  box-shadow: $shadow_deep;
@@ -3,7 +3,6 @@
3
3
  class: object.classname,
4
4
  data: object.data,
5
5
  id: object.id,
6
- style: object.container_style,
7
6
  **combined_html_options) do %>
8
7
  <%= pb_rails("list", props: {ordered: false, borderless: false}) do %>
9
8
  <% if content.present? %>
@@ -7,10 +7,6 @@ module Playbook
7
7
  generate_classname("pb_dropdown_container", "close", separator: " ")
8
8
  end
9
9
 
10
- def container_style
11
- "position: absolute"
12
- end
13
-
14
10
  def data
15
11
  Hash(prop(:data)).merge(dropdown_container: true)
16
12
  end
@@ -18,9 +18,19 @@ export const separateChildComponents = (children: React.ReactChild[] | React.Rea
18
18
  const otherChildren: React.ReactChild[] = [];
19
19
 
20
20
  React.Children.forEach(children, (child) => {
21
- if (child && (child as ReactElement).type === DropdownTrigger) {
21
+ const element = child as ReactElement;
22
+ const childType = element?.type;
23
+ const childDisplayName = (childType as any)?.displayName || (childType as any)?.name;
24
+
25
+ if (
26
+ childType === DropdownTrigger ||
27
+ childDisplayName === "DropdownTrigger"
28
+ ) {
22
29
  trigger = child;
23
- } else if (child && (child as ReactElement).type === DropdownContainer) {
30
+ } else if (
31
+ childType === DropdownContainer ||
32
+ childDisplayName === "DropdownContainer"
33
+ ) {
24
34
  container = child;
25
35
  } else {
26
36
  otherChildren.push(child);
@@ -30,6 +40,7 @@ export const separateChildComponents = (children: React.ReactChild[] | React.Rea
30
40
  return { trigger, container, otherChildren };
31
41
  };
32
42
 
43
+
33
44
  export const prepareSubcomponents = ({
34
45
  children,
35
46
  hasTriggerSubcomponent,
@@ -23,7 +23,7 @@
23
23
  %>
24
24
 
25
25
  <%= pb_form_with(scope: :example, url: "", method: :get) do |form| %>
26
- <%= form.typeahead :example_user, props: { data: { typeahead_example1: true, user: {} }, placeholder: "Search for a user" } %>
26
+ <%= form.typeahead :example_typeahead, props: { data: { typeahead_example1: true, user: {} }, label: true, placeholder: "Search for a user" } %>
27
27
  <%= form.text_field :example_text_field, props: { label: true } %>
28
28
  <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
29
29
  <%= form.email_field :example_email_field, props: { label: true } %>
@@ -92,7 +92,7 @@
92
92
  const selectedUserData = JSON.parse(selectedUserJSON)
93
93
 
94
94
  // set the input field's value
95
- event.target.querySelector('input[name=example_user]').value = selectedUserData.login
95
+ event.target.querySelector('input[name=example_typeahead]').value = selectedUserData.login
96
96
 
97
97
  // log the selected option's dataset
98
98
  console.log('The selected user data:')
@@ -1,5 +1,5 @@
1
1
  <%= pb_form_with(scope: :example, url: "", method: :get, loading: true) do |form| %>
2
- <%= form.text_field :example_text_field, props: { label: true } %>
2
+ <%= form.text_field :example_text_field_loading, props: { label: true } %>
3
3
 
4
4
  <%= form.actions do |action| %>
5
5
  <%= action.submit %>
@@ -22,23 +22,74 @@
22
22
  %>
23
23
 
24
24
  <%= pb_form_with(scope: :example, method: :get, url: "", validate: true) do |form| %>
25
- <%= form.text_field :example_text_field, props: { label: true, required: true } %>
26
- <%= form.phone_number_field :example_phone_number_field, props: { label: "Example phone field" } %>
27
- <%= form.email_field :example_email_field, props: { label: true, required: true } %>
28
- <%= form.number_field :example_number_field, props: { label: true, required: true } %>
29
- <%= form.search_field :example_project_number, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
30
- <%= form.password_field :example_password_field, props: { label: true, required: true } %>
31
- <%= form.url_field :example_url_field, props: { label: true, required: true } %>
32
- <%= form.text_area :example_text_area, props: { label: true, required: true } %>
33
- <%= form.dropdown_field :example_dropdown, props: { label: true, options: example_dropdown_options, required: true } %>
34
- <%= form.select :example_select, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
35
- <%= form.collection_select :example_collection_select, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
25
+ <%= form.typeahead :example_typeahead_validation, props: { data: { typeahead_example2: true, user: {} }, label: true, placeholder: "Search for a user", required: true, validation: { message: "Please select a user." } } %>
26
+ <%= form.text_field :example_text_field_validation, props: { label: true, required: true } %>
27
+ <%= form.phone_number_field :example_phone_number_field_validation, props: { label: "Example phone field" } %>
28
+ <%= form.email_field :example_email_field_validation, props: { label: true, required: true } %>
29
+ <%= form.number_field :example_number_field_validation, props: { label: true, required: true } %>
30
+ <%= form.search_field :example_project_number_validation, props: { label: true, required: true, validation: { pattern: "[0-9]{2}-[0-9]{5}", message: "Please enter a valid project number (example: 33-12345)." } } %>
31
+ <%= form.password_field :example_password_field_validation, props: { label: true, required: true } %>
32
+ <%= form.url_field :example_url_field_validation, props: { label: true, required: true } %>
33
+ <%= form.text_area :example_text_area_validation, props: { label: true, required: true } %>
34
+ <%= form.dropdown_field :example_dropdown_validation, props: { label: true, options: example_dropdown_options, required: true } %>
35
+ <%= form.select :example_select_validation, [ ["Yes", 1], ["No", 2] ], props: { label: true, blank_selection: "Select One...", required: true } %>
36
+ <%= form.collection_select :example_collection_select_validation, example_collection, :value, :name, props: { label: true, blank_selection: "Select One...", required: true } %>
36
37
  <%= form.check_box :example_checkbox, props: { text: "Example Checkbox", label: true, required: true } %>
37
38
  <%= form.date_picker :example_date_picker_2, props: { label: true, required: true } %>
38
- <%= form.star_rating_field :example_star_rating, props: { variant: "interactive", label: true, required: true } %>
39
+ <%= form.star_rating_field :example_star_rating_validation, props: { variant: "interactive", label: true, required: true } %>
39
40
 
40
41
  <%= form.actions do |action| %>
41
42
  <%= action.submit %>
42
43
  <%= action.button props: { type: "reset", text: "Cancel", variant: "secondary" } %>
43
44
  <% end %>
44
45
  <% end %>
46
+
47
+ <!-- form.typeahead user results example template -->
48
+ <template data-typeahead-example-result-option>
49
+ <%= pb_rails("user", props: {
50
+ name: tag(:slot, name: "name"),
51
+ orientation: "horizontal",
52
+ align: "left",
53
+ avatar_url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII=",
54
+ avatar: true
55
+ }) %>
56
+ </template>
57
+
58
+ <!-- form.typeahead JS example implementation -->
59
+ <%= javascript_tag defer: "defer" do %>
60
+ document.addEventListener("pb-typeahead-kit-search", function(event) {
61
+ if (!event.target.dataset || !event.target.dataset.typeaheadExample2) return
62
+
63
+ fetch(`https://api.github.com/search/users?q=${encodeURIComponent(event.detail.searchingFor)}`)
64
+ .then(response => response.json())
65
+ .then((result) => {
66
+ const resultOptionTemplate = document.querySelector("[data-typeahead-example-result-option]")
67
+
68
+ event.detail.setResults((result.items || []).map((user) => {
69
+ const wrapper = resultOptionTemplate.content.cloneNode(true)
70
+ wrapper.children[0].dataset.user = JSON.stringify(user)
71
+ wrapper.querySelector('slot[name="name"]').replaceWith(user.login)
72
+ wrapper.querySelector('img').dataset.src = user.avatar_url
73
+ return wrapper
74
+ }))
75
+ })
76
+ })
77
+
78
+
79
+ document.addEventListener("pb-typeahead-kit-result-option-selected", function(event) {
80
+ if (!event.target.dataset.typeaheadExample2) return
81
+
82
+ const selectedUserJSON = event.detail.selected.firstElementChild.dataset.user
83
+ const selectedUserData = JSON.parse(selectedUserJSON)
84
+
85
+ // set the input field's value
86
+ event.target.querySelector('input[name=example_typeahead_validation]').value = selectedUserData.login
87
+
88
+ // log the selected option's dataset
89
+ console.log('The selected user data:')
90
+ console.dir(selectedUserData)
91
+
92
+ // do even more with the data later - TBD
93
+ event.target.dataset.user = selectedUserJSON
94
+ })
95
+ <% end %>
@@ -1,6 +1,6 @@
1
1
  import React from 'react'
2
2
  import classnames from 'classnames'
3
- import { GlobalProps, globalProps } from '../utilities/globalProps'
3
+ import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
4
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
5
 
6
6
  type ImageType = {
@@ -41,6 +41,7 @@ const Image = (props: ImageType): React.ReactElement => {
41
41
  globalProps(props),
42
42
  className
43
43
  )
44
+ const dynamicInlineProps = globalInlineProps(props)
44
45
  const dataProps = buildDataProps(data)
45
46
  const htmlProps = buildHtmlProps(htmlOptions)
46
47
 
@@ -56,6 +57,7 @@ const Image = (props: ImageType): React.ReactElement => {
56
57
  id={id}
57
58
  onError={onError}
58
59
  src={url}
60
+ style={dynamicInlineProps}
59
61
  />
60
62
  )
61
63
  }
@@ -2,7 +2,7 @@ import React from 'react'
2
2
  import classnames from 'classnames'
3
3
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
4
4
 
5
- import { globalProps } from '../utilities/globalProps'
5
+ import { GlobalProps, globalProps, globalInlineProps } from '../utilities/globalProps'
6
6
 
7
7
  type LayoutPropTypes = {
8
8
  aria?: {[key: string]: string},
@@ -19,7 +19,7 @@ type LayoutPropTypes = {
19
19
  variant?: "light" | "dark" | "gradient",
20
20
  transparent?: boolean,
21
21
  layout?: "sidebar" | "collection" | "kanban" | "content" | "masonry",
22
- }
22
+ } & GlobalProps
23
23
 
24
24
  type LayoutSideProps = {
25
25
  children: React.ReactNode[] | React.ReactNode,
@@ -159,6 +159,8 @@ const Layout = (props: LayoutPropTypes) => {
159
159
  const filteredProps = {...props}
160
160
  delete filteredProps?.position
161
161
 
162
+ const dynamicInlineProps = globalInlineProps(props)
163
+
162
164
  return (
163
165
  <div
164
166
  {...ariaProps}
@@ -171,7 +173,8 @@ const Layout = (props: LayoutPropTypes) => {
171
173
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
172
174
  //@ts-ignore
173
175
  globalProps(filteredProps)
174
- )}
176
+ )}
177
+ style={dynamicInlineProps}
175
178
  >
176
179
  {subComponentTags('Side')}
177
180
  {nonSideChildren}
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
  import classnames from 'classnames'
3
3
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
4
- import { globalProps } from '../utilities/globalProps'
4
+ import { globalProps, globalInlineProps } from '../utilities/globalProps'
5
5
  import OverlayPercentage from './subcomponents/_overlay_percentage'
6
6
  import OverlayToken from './subcomponents/_overlay_token'
7
7
 
@@ -39,6 +39,7 @@ const Overlay = (props: OverlayProps) => {
39
39
  const dataProps = buildDataProps(data)
40
40
  const classes = classnames(buildCss('pb_overlay'), globalProps(props), className)
41
41
  const htmlProps = buildHtmlProps(htmlOptions)
42
+ const dynamicInlineProps = globalInlineProps(props)
42
43
 
43
44
  const getPosition = () => {
44
45
  return Object.keys(layout)[0]
@@ -57,6 +58,7 @@ const Overlay = (props: OverlayProps) => {
57
58
  {...htmlProps}
58
59
  className={classes}
59
60
  id={id}
61
+ style={dynamicInlineProps}
60
62
  >
61
63
  {isSizePercentage ?
62
64
  OverlayPercentage({
@@ -1,6 +1,6 @@
1
1
  import React, { useState } from "react";
2
2
  import classnames from 'classnames'
3
- import { globalProps } from '../utilities/globalProps'
3
+ import { GlobalProps, globalProps } from '../utilities/globalProps'
4
4
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
5
5
  import Icon from '../pb_icon/_icon';
6
6
 
@@ -14,7 +14,7 @@ type PaginationProps = {
14
14
  onChange?: (pageNumber: number) => void;
15
15
  range?: number;
16
16
  total?: number;
17
- };
17
+ } & GlobalProps;
18
18
 
19
19
  const Pagination = ( props: PaginationProps) => {
20
20
  const {