playbook_ui 14.15.0.pre.rc.2 → 14.15.0.pre.rc.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_overlay/_overlay.scss +13 -0
  3. data/app/pb_kits/playbook/pb_overlay/_overlay.tsx +7 -1
  4. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar.html.erb +11 -0
  5. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar.jsx +37 -0
  6. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar_rails.md +1 -0
  7. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar_react.md +1 -0
  8. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional.html.erb +11 -0
  9. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional.md +1 -0
  10. data/app/pb_kits/playbook/pb_overlay/docs/example.yml +3 -0
  11. data/app/pb_kits/playbook/pb_overlay/docs/index.js +1 -0
  12. data/app/pb_kits/playbook/pb_overlay/index.js +61 -0
  13. data/app/pb_kits/playbook/pb_overlay/overlay.html.erb +5 -3
  14. data/app/pb_kits/playbook/pb_overlay/overlay.rb +16 -1
  15. data/app/pb_kits/playbook/pb_overlay/overlay.test.jsx +12 -0
  16. data/app/pb_kits/playbook/pb_title/_title.scss +0 -35
  17. data/app/pb_kits/playbook/pb_title/_title.tsx +1 -10
  18. data/app/pb_kits/playbook/pb_title/docs/_title_default.html.erb +2 -1
  19. data/app/pb_kits/playbook/pb_title/docs/_title_default.jsx +1 -1
  20. data/app/pb_kits/playbook/pb_title/docs/example.yml +0 -2
  21. data/app/pb_kits/playbook/pb_title/docs/index.js +0 -1
  22. data/app/pb_kits/playbook/pb_title/title.rb +1 -10
  23. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +34 -1
  24. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options.html.erb +45 -0
  25. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options.md +5 -0
  26. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options_pure_rails.html.erb +33 -0
  27. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options_pure_rails.md +3 -0
  28. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  29. data/app/pb_kits/playbook/pb_typeahead/index.ts +61 -8
  30. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +13 -1
  31. data/dist/chunks/{_typeahead-_d2af7Id.js → _typeahead-NXKDTf__.js} +3 -3
  32. data/dist/chunks/{_weekday_stacked-BZe1DgW-.js → _weekday_stacked-DtCYkCXM.js} +2 -2
  33. data/dist/chunks/{lib-Fr78pbpF.js → lib-Dmay5Z6U.js} +1 -1
  34. data/dist/chunks/{pb_form_validation-CN51bfsD.js → pb_form_validation-DdP7BnVX.js} +1 -1
  35. data/dist/chunks/vendor.js +1 -1
  36. data/dist/menu.yml +2 -2
  37. data/dist/playbook-doc.js +1 -1
  38. data/dist/playbook-rails-react-bindings.js +1 -1
  39. data/dist/playbook-rails.js +1 -1
  40. data/dist/playbook.css +1 -1
  41. data/lib/playbook/version.rb +1 -1
  42. metadata +17 -9
  43. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.html.erb +0 -7
  44. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.jsx +0 -54
  45. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.md +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95192d372b4c17ae51023715220171ac3749725807f1d1ca65a05bd5a6a668b7
4
- data.tar.gz: a3242524855afdd47b79766238db473dc0f36abfcc8f186ce894efa70bd061c8
3
+ metadata.gz: a671aef2ddd39a78ec98fcb4073566d8d8c07c887e206ef6e83c6bea4bbeae07
4
+ data.tar.gz: 79a8b005d92dfb2a80c0908349254e76a931f8e366abccbdd079bcf3b57725bd
5
5
  SHA512:
6
- metadata.gz: 8f28b65206e874afdb4f13dbeeca18f4077c4390c41e1366659299cfed0849354403e0f91c0d08503353829ab54f90177dd5c1ecae2c2c2c86649c79770c7ef7
7
- data.tar.gz: f360a678e30f79f34ffe2f43ae42ba6f5e9bb48fdf7dceb64cfdd76abc7323b984466bf61dc2c5a716877d510230e5fc78b24a218504d80f932b92b129638c92
6
+ metadata.gz: 9e1497589d4831e05816145ea26dd5f174b94d035cdfd46ec112f973ad6ffa860821535adced274c664a81dcd356b14876a11f5162a6bb6d1a0a2fda12038799
7
+ data.tar.gz: 985f3701953d47a71657c909d63303cfe60413b0a374bafc96f611fbb477b4262ab4b5011c16c31e124ca05581a66af587c7d6d15b5e99997f2efd2cd43d5ad0
@@ -69,4 +69,17 @@ $overlay_colors: (
69
69
  pointer-events: none;
70
70
  z-index: 1;
71
71
  }
72
+
73
+ &.overlay-hide-scrollbar {
74
+ & [class*="overflow_x_auto"],
75
+ & [class*="overflow_y_auto"],
76
+ & [class*="overflow_auto"] {
77
+ &::-webkit-scrollbar {
78
+ display: none !important;
79
+ }
80
+
81
+ -ms-overflow-style: none !important;
82
+ scrollbar-width: none !important;
83
+ }
84
+ }
72
85
  }
@@ -11,6 +11,7 @@ export type OverlayChildrenProps = {
11
11
  dynamic?: boolean,
12
12
  position: string,
13
13
  size: string,
14
+ scrollBarNone?: boolean,
14
15
  }
15
16
 
16
17
  type OverlayProps = {
@@ -23,6 +24,8 @@ type OverlayProps = {
23
24
  htmlOptions?: { [key: string]: string | number | boolean | (() => void) },
24
25
  id?: string,
25
26
  layout: { [key: string]: string },
27
+ scrollBarNone?: boolean,
28
+
26
29
  }
27
30
 
28
31
  const Overlay = (props: OverlayProps) => {
@@ -36,11 +39,12 @@ const Overlay = (props: OverlayProps) => {
36
39
  htmlOptions = {},
37
40
  id,
38
41
  layout = { "bottom": "full" },
42
+ scrollBarNone = false,
39
43
  } = props
40
44
 
41
45
  const ariaProps = buildAriaProps(aria)
42
46
  const dataProps = buildDataProps(data)
43
- const classes = classnames(buildCss('pb_overlay'), globalProps(props), className)
47
+ const classes = classnames(buildCss('pb_overlay'), { 'overlay-hide-scrollbar': scrollBarNone }, globalProps(props), className )
44
48
  const htmlProps = buildHtmlProps(htmlOptions)
45
49
  const dynamicInlineProps = globalInlineProps(props)
46
50
 
@@ -68,12 +72,14 @@ const Overlay = (props: OverlayProps) => {
68
72
  children,
69
73
  color,
70
74
  position: getPosition(),
75
+ scrollBarNone,
71
76
  size: getSize()
72
77
  }) : OverlayToken({
73
78
  children,
74
79
  color,
75
80
  dynamic: dynamic,
76
81
  position: getPosition(),
82
+ scrollBarNone,
77
83
  size: getSize()
78
84
  })
79
85
  }
@@ -0,0 +1,11 @@
1
+ <%= pb_rails("overlay", props: { layout: { "x": "xl" }, color: "card_light", scroll_bar_none: true }) do %>
2
+ <%= pb_rails("flex", props: { column_gap: "lg", orientation: "row", overflow_x: "auto" }) do %>
3
+ <% 15.times do %>
4
+ <%= pb_rails("flex/flex_item") do %>
5
+ <%= pb_rails("card") do %>
6
+ Card content
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,37 @@
1
+ import React from 'react'
2
+ import {
3
+ Overlay,
4
+ Card,
5
+ Flex,
6
+ FlexItem,
7
+ } from 'playbook-ui'
8
+
9
+ const InlineCardsExample = () => {
10
+ return (
11
+ <Flex
12
+ columnGap="lg"
13
+ orientation="row"
14
+ overflowX="auto"
15
+ >
16
+ {Array.from({ length: 15 }, (_, index) => (
17
+ <FlexItem key={index}>
18
+ <Card>{"Card Content"}</Card>
19
+ </FlexItem>
20
+ ))}
21
+ </Flex>
22
+ )
23
+ }
24
+
25
+ const OverlayHideScrollBar = () => (
26
+ <>
27
+ <Overlay
28
+ color="card_light"
29
+ layout={{"x": "xl"}}
30
+ scrollBarNone
31
+ >
32
+ <InlineCardsExample />
33
+ </Overlay>
34
+ </>
35
+ )
36
+
37
+ export default OverlayHideScrollBar
@@ -0,0 +1 @@
1
+ Pass the `scroll_bar_none` prop to hide the scrollbar from view. This is particularly helpful for small containers where the scrollbar may occupy too much space.
@@ -0,0 +1 @@
1
+ Pass the `scrollBarNone` prop to hide the scrollbar from view. This is particularly helpful for small containers where the scrollbar may occupy too much space.
@@ -0,0 +1,11 @@
1
+ <%= pb_rails("overlay", props: { layout: { "x": "xl" }, color: "card_light", dynamic: true }) do %>
2
+ <%= pb_rails("flex", props: { column_gap: "lg", orientation: "row", overflow_x: "auto" }) do %>
3
+ <% 15.times do %>
4
+ <%= pb_rails("flex/flex_item") do %>
5
+ <%= pb_rails("card") do %>
6
+ Card content
7
+ <% end %>
8
+ <% end %>
9
+ <% end %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1 @@
1
+ Pass the `dynamic` prop to make the overlay render while the scrollbar isn't at either end on the scrollbar.
@@ -4,8 +4,11 @@ examples:
4
4
  - overlay_multi_directional: Multi-directional
5
5
  - overlay_vertical_dynamic_multi_directional: Vertical Dynamic Multi-directional
6
6
  - overlay_toggle: Toggle
7
+ - overlay_hide_scroll_bar: Hide Scroll Bar
7
8
 
8
9
  rails:
9
10
  - overlay_default: Default
10
11
  - overlay_multi_directional: Multi-directional
12
+ - overlay_vertical_dynamic_multi_directional: Vertical Dynamic Multi-directional
11
13
  - overlay_toggle: Toggle
14
+ - overlay_hide_scroll_bar: Hide Scroll Bar
@@ -2,3 +2,4 @@ export { default as OverlayDefault } from './_overlay_default.jsx'
2
2
  export { default as OverlayMultiDirectional } from './_overlay_multi_directional.jsx'
3
3
  export { default as OverlayToggle } from './_overlay_toggle.jsx'
4
4
  export { default as OverlayVerticalDynamicMultiDirectional } from './_overlay_vertical_dynamic_multi_directional.jsx'
5
+ export { default as OverlayHideScrollBar } from './_overlay_hide_scroll_bar.jsx'
@@ -0,0 +1,61 @@
1
+ import PbEnhancedElement from '../pb_enhanced_element'
2
+
3
+ const OVERLAY_SELECTOR = '[data-pb-overlay]'
4
+ const OVERLAY_SCROLL_ELEMENT = '[data-overlay-scroll-element]'
5
+ const PREVIOUS_OVERLAY_CLASSNAME = '[data-previous-overlay-classname]'
6
+ const SUBSEQUENT_OVERLAY_CLASSNAME = '[data-subsequent-overlay-classname]'
7
+
8
+ export default class PbOverlay extends PbEnhancedElement {
9
+ static get selector() {
10
+ return OVERLAY_SELECTOR
11
+ }
12
+
13
+ get target() {
14
+ return this.element.querySelector(OVERLAY_SCROLL_ELEMENT).children[0]
15
+ }
16
+
17
+ connect() {
18
+ this.handleOverlayDynamic()
19
+ }
20
+
21
+ handleOverlayDynamic() {
22
+ const isOverlayDynamic = this.element.dataset?.overlayDynamic
23
+
24
+ if (isOverlayDynamic) {
25
+ const previousOverlayElement = this.element.querySelector(PREVIOUS_OVERLAY_CLASSNAME)
26
+ const previousOverlayClassname = previousOverlayElement?.dataset?.previousOverlayClassname
27
+ const subsequentOverlayElement = this.element.querySelector(SUBSEQUENT_OVERLAY_CLASSNAME)
28
+ const subsequentOverlayClassname = subsequentOverlayElement?.dataset?.subsequentOverlayClassname
29
+
30
+ const handleScrollChange = (target) => {
31
+ const { scrollLeft, scrollWidth, clientWidth } = target
32
+ const isScrollAtStart = scrollLeft === 0
33
+ const isScrollAtEnd = scrollLeft + clientWidth >= scrollWidth - 1
34
+
35
+ if (isScrollAtStart) {
36
+ previousOverlayElement.classList.remove(previousOverlayClassname)
37
+ } else {
38
+ previousOverlayElement.classList.add(previousOverlayClassname)
39
+ }
40
+
41
+ if (isScrollAtEnd) {
42
+ subsequentOverlayElement.classList.remove(subsequentOverlayClassname)
43
+ } else {
44
+ subsequentOverlayElement.classList.add(subsequentOverlayClassname)
45
+ }
46
+ }
47
+
48
+ this.target.addEventListener('scroll', (event) => {
49
+ handleScrollChange(event.target)
50
+ })
51
+
52
+ handleScrollChange(this.target)
53
+ }
54
+ }
55
+
56
+ disconnect() {
57
+ if (this.element.dataset?.overlayDynamic) {
58
+ this.target.removeEventListener('scroll')
59
+ }
60
+ }
61
+ }
@@ -16,12 +16,14 @@ id: object.id,
16
16
  <% end %>
17
17
 
18
18
  <% else %>
19
- <div class="<%= previous_overlay_class_name %>"></div>
19
+ <div class="<%= previous_overlay_class_name %>" data-previous-overlay-classname="<%= previous_overlay_class_name %>"></div>
20
20
 
21
- <%= content.presence %>
21
+ <div data-overlay-scroll-element="true">
22
+ <%= content.presence %>
23
+ </div>
22
24
 
23
25
  <% if has_subsequent_overlay %>
24
- <div class="<%= subsequent_overlay_class_name %>"></div>
26
+ <div class="<%= subsequent_overlay_class_name %>" data-subsequent-overlay-classname="<%= subsequent_overlay_class_name %>"></div>
25
27
  <% end %>
26
28
  <% end %>
27
29
  <% end %>
@@ -8,9 +8,13 @@ module Playbook
8
8
  default: "card_light"
9
9
  prop :layout, type: Playbook::Props::HashProp,
10
10
  default: { "bottom": "full" }
11
+ prop :dynamic, type: Playbook::Props::Boolean,
12
+ default: false
13
+ prop :scroll_bar_none, type: Playbook::Props::Boolean,
14
+ default: false
11
15
 
12
16
  def classname
13
- generate_classname("pb_overlay")
17
+ generate_classname("pb_overlay", hide_scroll_bar_class)
14
18
  end
15
19
 
16
20
  def position
@@ -105,6 +109,17 @@ module Playbook
105
109
  "bg_dark": "#0a0527",
106
110
  }
107
111
  end
112
+
113
+ def data_attributes
114
+ data ||= {}
115
+ data.merge!("data-pb-overlay" => true)
116
+ data.merge!("data-overlay-dynamic" => true) if dynamic
117
+ data
118
+ end
119
+
120
+ def hide_scroll_bar_class
121
+ scroll_bar_none ? " overlay-hide-scrollbar" : ""
122
+ end
108
123
  end
109
124
  end
110
125
  end
@@ -64,3 +64,15 @@ test('should render children', () => {
64
64
  const kit = screen.getByTestId(testId)
65
65
  expect(kit).toHaveTextContent(props.children)
66
66
  })
67
+
68
+ test('should add overlay-hide-scrollbar class when scrollBarNone is true', () => {
69
+ const props = {
70
+ children,
71
+ data: { testid: testId },
72
+ scrollBarNone: true
73
+ }
74
+
75
+ render(<Overlay {...props} />)
76
+ const kit = screen.getByTestId(testId)
77
+ expect(kit).toHaveClass('overlay-hide-scrollbar')
78
+ })
@@ -31,41 +31,6 @@
31
31
  @include pb_title_thin;
32
32
  }
33
33
 
34
- &[class*=_display] {
35
- font-size: clamp(
36
- 24px,
37
- calc(24px + (128 - 24) * ((100vw - 320px) / (1920 - 320))),
38
- 128px
39
- );
40
- }
41
-
42
- &[class*=_dynamic] {
43
- &[class*=_xs] {
44
- font-size: min(2vw, 5rem);
45
- }
46
-
47
- &[class*=_sm] {
48
- font-size: min(2.5vw, 6rem);
49
- }
50
-
51
- &[class*=_md] {
52
- font-size: min(4vw, 10rem);
53
- }
54
-
55
- &[class*=_lg] {
56
- font-size: min(5vw, 12rem);
57
- }
58
-
59
- &[class*=_xl] {
60
- font-size: min(6vw, 14rem);
61
- }
62
-
63
- &[class*=_xxl] {
64
- font-size: min(8vw, 16rem);
65
- }
66
- }
67
-
68
-
69
34
  @each $size, $size_value in $breakpoints_grid {
70
35
  @for $title_size_value from 1 through 4 {
71
36
  $min_size: map-get($size_value, "min");
@@ -3,7 +3,7 @@ import classnames from 'classnames'
3
3
  import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
4
4
  import { deprecatedProps, GlobalProps, globalProps } from '../utilities/globalProps'
5
5
 
6
- type SizeType = 1 | 2 | 3 | 4 | "1" | "2" | "3" | "4" | "display"
6
+ type SizeType = 1 | 2 | 3 | 4 | "1" | "2" | "3" | "4"
7
7
  type SizeResponsiveType = {[key: string]: SizeType}
8
8
 
9
9
  type TitleProps = {
@@ -16,7 +16,6 @@ type TitleProps = {
16
16
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
17
17
  id?: string,
18
18
  size?: SizeType | SizeResponsiveType,
19
- displaySize?: null | "xs" | "sm" | "md" | "lg" | "xl" | "xxl",
20
19
  tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "div" | "span",
21
20
  text?: string,
22
21
  variant?: null | "link",
@@ -33,7 +32,6 @@ const Title = (props: TitleProps): React.ReactElement => {
33
32
  htmlOptions = {},
34
33
  id,
35
34
  size = 3,
36
- displaySize = null,
37
35
  bold = true,
38
36
  tag = 'h3',
39
37
  text,
@@ -58,16 +56,9 @@ const Title = (props: TitleProps): React.ReactElement => {
58
56
  return css.trim()
59
57
  }
60
58
 
61
- const buildDisplaySize = () => {
62
- if (displaySize) {
63
- return `pb_title_kit_dynamic_${displaySize}`
64
- }
65
- }
66
-
67
59
  const classes = classnames(
68
60
  buildCss('pb_title_kit', isSizeNumberOrString ? `size_${size}` : "", variant, color, getBold),
69
61
  globalProps(props),
70
- buildDisplaySize(),
71
62
  buildResponsiveSizeCss(),
72
63
  className
73
64
  )
@@ -1,9 +1,10 @@
1
1
  <%= pb_rails("title", props: {
2
- margin_bottom: "md"
3
2
  }) do %>
4
3
  Default Title
5
4
  <% end %>
6
5
 
6
+ <br/>
7
+
7
8
  <%= pb_rails("title", props: { text: "Title 1", tag: "h1", size: 1 }) %>
8
9
  <%= pb_rails("title", props: { text: "Title 2", tag: "h2", size: 2 }) %>
9
10
  <%= pb_rails("title", props: { text: "Title 3", tag: "h3", size: 3 }) %>
@@ -6,10 +6,10 @@ const TitleDefault = (props) => {
6
6
  return (
7
7
  <div>
8
8
  <Title
9
- marginBottom='md'
10
9
  text="Default Title"
11
10
  {...props}
12
11
  />
12
+ <br />
13
13
  <Title
14
14
  size={1}
15
15
  tag="h1"
@@ -5,7 +5,6 @@ examples:
5
5
  - title_colors: Colors
6
6
  - title_responsive: Responsive
7
7
  - title_truncate: Truncate
8
- - title_display_size: Display Size
9
8
 
10
9
  react:
11
10
  - title_default: Default UI
@@ -13,4 +12,3 @@ examples:
13
12
  - title_colors: Colors
14
13
  - title_responsive: Responsive
15
14
  - title_truncate: Truncate
16
- - title_display_size: Display Size
@@ -3,4 +3,3 @@ export { default as TitleLightWeight } from './_title_light_weight.jsx'
3
3
  export { default as TitleColors } from './_title_colors.jsx'
4
4
  export { default as TitleResponsive } from './_title_responsive.jsx'
5
5
  export { default as TitleTruncate } from './_title_truncate.jsx'
6
- export { default as TitleDisplaySize } from './_title_display_size.jsx'
@@ -16,15 +16,12 @@ module Playbook
16
16
  default: nil,
17
17
  deprecated: true
18
18
  prop :bold, type: Playbook::Props::Boolean, default: true
19
- prop :display_size, type: Playbook::Props::Enum,
20
- values: [nil, "xs", "sm", "md", "lg", "xl", "xxl"],
21
- default: nil
22
19
 
23
20
  def classname
24
21
  if is_size_responsive
25
22
  generate_classname("pb_title_kit", variant, color, is_bold) + generate_responsive_size_classname
26
23
  else
27
- generate_classname("pb_title_kit", size, variant, color, is_bold) + generate_display_size
24
+ generate_classname("pb_title_kit", size, variant, color, is_bold)
28
25
  end
29
26
  end
30
27
 
@@ -32,12 +29,6 @@ module Playbook
32
29
  bold ? nil : "thin"
33
30
  end
34
31
 
35
- def generate_display_size
36
- return "" if display_size.nil?
37
-
38
- " pb_title_kit_dynamic_#{display_size}"
39
- end
40
-
41
32
  def is_size_responsive
42
33
  try(:size).is_a?(::Hash)
43
34
  end
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { useState, useEffect} from 'react'
2
2
  import Select from 'react-select'
3
3
  import AsyncSelect from 'react-select/async'
4
4
  import CreateableSelect from 'react-select/creatable'
@@ -45,8 +45,12 @@ type TypeaheadProps = {
45
45
  getOptionLabel?: string | (() => string),
46
46
  getOptionValue?: string | (() => string),
47
47
  name?: string,
48
+ options?: Array<{ label: string; value?: string }>,
48
49
  marginBottom?: "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl",
49
50
  pillColor?: "primary" | "neutral" | "success" | "warning" | "error" | "info" | "data_1" | "data_2" | "data_3" | "data_4" | "data_5" | "data_6" | "data_7" | "data_8" | "windows" | "siding" | "roofing" | "doors" | "gutters" | "solar" | "insulation" | "accessories",
51
+ optionsByContext?: Record<string, Array<{ label: string; value?: string }>>
52
+ searchContextSelector?: string,
53
+ clearOnContextChange?: boolean,
50
54
  } & GlobalProps
51
55
 
52
56
  export type SelectValueType = {
@@ -81,6 +85,9 @@ const Typeahead = ({
81
85
  loadOptions = noop,
82
86
  marginBottom = "sm",
83
87
  pillColor,
88
+ optionsByContext = {},
89
+ searchContextSelector,
90
+ clearOnContextChange = false,
84
91
  ...props
85
92
  }: TypeaheadProps) => {
86
93
  const selectProps = {
@@ -115,6 +122,32 @@ const Typeahead = ({
115
122
  ...props,
116
123
  }
117
124
 
125
+ const [contextValue, setContextValue] = useState("")
126
+
127
+ useEffect(() => {
128
+ if (searchContextSelector) {
129
+ const searchContextElement = document.getElementById(searchContextSelector)
130
+
131
+ setContextValue((searchContextElement as HTMLInputElement)?.value)
132
+ const handleContextChange = (e: Event) => {
133
+ const target = e.target as HTMLInputElement;
134
+ setContextValue(target.value);
135
+ if (clearOnContextChange) document.dispatchEvent(new CustomEvent(`pb-typeahead-kit-${selectProps.id}:clear`))
136
+ }
137
+
138
+ if (searchContextElement) searchContextElement.addEventListener('change', handleContextChange)
139
+
140
+ return () => {
141
+ if (searchContextElement) searchContextElement.removeEventListener('change', handleContextChange)
142
+ }
143
+ }
144
+ }, [searchContextSelector])
145
+
146
+ const contextArray = optionsByContext[contextValue]
147
+ if (Array.isArray(contextArray) && contextArray.length > 0) {
148
+ selectProps.options = contextArray
149
+ }
150
+
118
151
  const Tag = (
119
152
  createable
120
153
  ? (async ? AsyncCreateableSelect : CreateableSelect)
@@ -0,0 +1,45 @@
1
+ <%= pb_rails("select", props: {
2
+ id:"color_context_2",
3
+ label: "Choose a Color",
4
+ name: "color_name",
5
+ options: [
6
+ { value: "red", value_text: "Red" },
7
+ { value: "blue", value_text: "Blue" },
8
+ { value: "green", value_text: "Green" }
9
+ ],
10
+ }) %>
11
+
12
+ <%= pb_rails("typeahead", props: {
13
+ label: "Pick a Shade",
14
+ is_multi: false,
15
+ search_context_selector: "color_context_2",
16
+ options_by_context: {
17
+ "red": [
18
+ { label: "Scarlet", value: "scarlet" },
19
+ { label: "Mahogany", value: "mahogany" },
20
+ { label: "Crimson", value: "crimson" }
21
+ ],
22
+ "blue": [
23
+ { label: "Sky Blue", value: "sky" },
24
+ { label: "Cerulean", value: "cerulean" },
25
+ { label: "Navy", value: "navy" }
26
+ ],
27
+ "green": [
28
+ { label: "Emerald", value: "emerald" },
29
+ { label: "Mint", value: "mint" },
30
+ { label: "Olive", value: "olive" }
31
+ ]
32
+ },
33
+ id: "typeahead-dynamic-options",
34
+ }) %>
35
+
36
+
37
+ <%= javascript_tag defer: "defer" do %>
38
+ document.addEventListener("pb-typeahead-kit-typeahead-dynamic-options-result-option-select", function(event) {
39
+ console.log('Single Option selected')
40
+ console.dir(event.detail)
41
+ })
42
+ document.addEventListener("pb-typeahead-kit-typeahead-dynamic-options-result-clear", function() {
43
+ console.log('All options cleared')
44
+ })
45
+ <% end %>
@@ -0,0 +1,5 @@
1
+ You can also set up a typeahead to render options dynamically based on input from a select. To achieve this:
2
+ - The typeahead must have a unique `id`
3
+ - Use the `search_context_selector` prop on the typeahead. The value here must match the id of the select so the Typeahead knows where to read the current "context" from.
4
+ - Use `options_by_context` to pass in a hash whose keys match the possible values of your “context” select. Each key maps to an array of { label, value } objects. The typeahead automatically displays only the subset of options matching the current context.
5
+ - Additionally, the optional `clear_on_context_change` prop controls whether the typeahead clears or not when a change happens in the linked select. This prop is set to true by default so that whenever a selection is made in the select, the Typeahead automatically clears its current input/selection.
@@ -0,0 +1,33 @@
1
+ <%= pb_rails("select", props: {
2
+ id:"color_context",
3
+ label: "Choose a Color",
4
+ name: "color_name_2",
5
+ options: [
6
+ { value: "red", value_text: "Red" },
7
+ { value: "blue", value_text: "Blue" },
8
+ { value: "green", value_text: "Green" }
9
+ ],
10
+ }) %>
11
+
12
+ <%= pb_rails("typeahead", props: {
13
+ label: "Pick a Shade",
14
+ search_context_selector: "color_context",
15
+ options_by_context: {
16
+ "red": [
17
+ { label: "Scarlet", value: "scarlet" },
18
+ { label: "Mahogany", value: "mahogany" },
19
+ { label: "Crimson", value: "crimson" }
20
+ ],
21
+ "blue": [
22
+ { label: "Sky Blue", value: "sky" },
23
+ { label: "Cerulean", value: "cerulean" },
24
+ { label: "Navy", value: "navy" }
25
+ ],
26
+ "green": [
27
+ { label: "Emerald", value: "emerald" },
28
+ { label: "Mint", value: "mint" },
29
+ { label: "Olive", value: "olive" }
30
+ ]
31
+ },
32
+ search_term_minimum_length: 0,
33
+ }) %>
@@ -0,0 +1,3 @@
1
+ The dynamic rendering of options for the typeahead can also be achieved with a pure Rails implementation (not react rendered). For this implementation, use all the props as above with the following additions:
2
+
3
+ - `search_term_minimum_length`: this sets the minimum input in the typeahead needed to display the dropdown. This is set to 3 by default. Set it to 0 for the dropdown to always display when the typeahead is interacted with.
@@ -12,6 +12,8 @@ examples:
12
12
  - typeahead_margin_bottom: Margin Bottom
13
13
  - typeahead_with_pills_color: With Pills (Custom Color)
14
14
  - typeahead_truncated_text: Truncated Text
15
+ - typeahead_dynamic_options: Dynamic Options
16
+ - typeahead_dynamic_options_pure_rails: Dynamic Options (Pure Rails)
15
17
 
16
18
  react:
17
19
  - typeahead_default: Default