playbook_ui 14.14.0.pre.rc.5 → 14.15.0.pre.alpha.PBNTR900phonenumberinputhidden6593

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +127 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableActionBar.tsx +55 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/TablePagination.tsx +33 -0
  5. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +275 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/Context/AdvancedTableContext.tsx +143 -3
  7. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +66 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +195 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/SubKits/TableBody.tsx +45 -99
  10. data/app/pb_kits/playbook/pb_advanced_table/Utilities/CellRendererUtils.tsx +73 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowUtils.ts +52 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/Utilities/TableContainerStyles.ts +80 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +126 -7
  14. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +153 -298
  15. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +8 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_headers_custom_cell.jsx +75 -0
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_infinite_scroll.jsx +50 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props.html.erb +1 -1
  19. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_rails.md +3 -1
  20. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_react.md +1 -1
  21. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header.html.erb +33 -0
  22. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_table_props_sticky_header_rails.md +3 -0
  23. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_infinite_scroll.json +152002 -0
  24. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  25. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  26. data/app/pb_kits/playbook/pb_card/_card.tsx +2 -1
  27. data/app/pb_kits/playbook/pb_checkbox/_checkbox.tsx +17 -8
  28. data/app/pb_kits/playbook/pb_checkbox/checkbox.test.js +16 -0
  29. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_react_hook.jsx +69 -0
  30. data/app/pb_kits/playbook/pb_checkbox/docs/_checkbox_react_hook.md +1 -0
  31. data/app/pb_kits/playbook/pb_checkbox/docs/example.yml +2 -1
  32. data/app/pb_kits/playbook/pb_checkbox/docs/index.js +1 -0
  33. data/app/pb_kits/playbook/pb_currency/_currency.tsx +46 -31
  34. data/app/pb_kits/playbook/pb_currency/currency.html.erb +15 -8
  35. data/app/pb_kits/playbook/pb_currency/currency.rb +17 -2
  36. data/app/pb_kits/playbook/pb_currency/docs/_currency_null_display.html.erb +22 -0
  37. data/app/pb_kits/playbook/pb_currency/docs/_currency_null_display.jsx +34 -0
  38. data/app/pb_kits/playbook/pb_currency/docs/_currency_null_display_rails.md +1 -0
  39. data/app/pb_kits/playbook/pb_currency/docs/_currency_null_display_react.md +1 -0
  40. data/app/pb_kits/playbook/pb_currency/docs/example.yml +2 -0
  41. data/app/pb_kits/playbook/pb_currency/docs/index.js +2 -1
  42. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +4 -1
  43. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +2 -0
  44. data/app/pb_kits/playbook/pb_date_picker/index.ts +38 -0
  45. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +5 -6
  46. data/app/pb_kits/playbook/pb_dialog/dialogHelper.js +2 -0
  47. data/app/pb_kits/playbook/pb_dialog/index.js +75 -0
  48. data/app/pb_kits/playbook/pb_drawer/_drawer.scss +43 -3
  49. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_behavior.html.erb +8 -0
  50. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.html.erb +33 -0
  51. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_borders.jsx +3 -3
  52. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.html.erb +0 -0
  53. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_breakpoints.jsx +20 -37
  54. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_default.html.erb +20 -1
  55. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.html.erb +24 -0
  56. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_menu.jsx +6 -6
  57. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.html.erb +21 -0
  58. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.jsx +1 -0
  59. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_overlay.md +1 -0
  60. data/app/pb_kits/playbook/pb_drawer/docs/_drawer_sizes.html.erb +49 -0
  61. data/app/pb_kits/playbook/pb_drawer/docs/example.yml +6 -0
  62. data/app/pb_kits/playbook/pb_drawer/drawer.html.erb +20 -12
  63. data/app/pb_kits/playbook/pb_drawer/drawer.rb +49 -1
  64. data/app/pb_kits/playbook/pb_drawer/index.js +257 -0
  65. data/app/pb_kits/playbook/pb_filter/Filter/CurrentFilters.tsx +5 -4
  66. data/app/pb_kits/playbook/pb_filter/Filter/FilterSingle.tsx +2 -2
  67. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +1 -0
  68. data/app/pb_kits/playbook/pb_form/docs/_form_form_with_validate.html.erb +3 -2
  69. data/app/pb_kits/playbook/pb_form_pill/_form_pill.scss +9 -2
  70. data/app/pb_kits/playbook/pb_form_pill/_form_pill.tsx +4 -0
  71. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.html.erb +40 -0
  72. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.jsx +50 -0
  73. data/app/pb_kits/playbook/pb_form_pill/docs/_form_pill_wrapped.md +3 -0
  74. data/app/pb_kits/playbook/pb_form_pill/docs/example.yml +2 -0
  75. data/app/pb_kits/playbook/pb_form_pill/docs/index.js +1 -0
  76. data/app/pb_kits/playbook/pb_form_pill/form_pill.rb +7 -1
  77. data/app/pb_kits/playbook/pb_icon_button/_icon_button.tsx +71 -0
  78. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_default.jsx +17 -0
  79. data/app/pb_kits/playbook/pb_icon_button/docs/_icon_button_sizes.jsx +61 -0
  80. data/app/pb_kits/playbook/pb_icon_button/docs/example.yml +4 -0
  81. data/app/pb_kits/playbook/pb_icon_button/docs/index.js +2 -0
  82. data/app/pb_kits/playbook/pb_icon_button/icon_button.test.jsx +39 -0
  83. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +7 -0
  84. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +13 -3
  85. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.html.erb +72 -0
  86. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_disabled.jsx +91 -0
  87. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +2 -1
  88. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  89. data/app/pb_kits/playbook/pb_multi_level_select/multi_level_select.rb +6 -0
  90. data/app/pb_kits/playbook/pb_overlay/_overlay.scss +13 -0
  91. data/app/pb_kits/playbook/pb_overlay/_overlay.tsx +11 -1
  92. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar.html.erb +11 -0
  93. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar.jsx +37 -0
  94. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar_rails.md +1 -0
  95. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_hide_scroll_bar_react.md +1 -0
  96. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional.html.erb +11 -0
  97. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional.jsx +37 -0
  98. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional.md +1 -0
  99. data/app/pb_kits/playbook/pb_overlay/docs/_overlay_vertical_dynamic_multi_directional_react.md +1 -0
  100. data/app/pb_kits/playbook/pb_overlay/docs/example.yml +4 -0
  101. data/app/pb_kits/playbook/pb_overlay/docs/index.js +2 -0
  102. data/app/pb_kits/playbook/pb_overlay/index.js +61 -0
  103. data/app/pb_kits/playbook/pb_overlay/overlay.html.erb +5 -3
  104. data/app/pb_kits/playbook/pb_overlay/overlay.rb +16 -1
  105. data/app/pb_kits/playbook/pb_overlay/overlay.test.jsx +12 -0
  106. data/app/pb_kits/playbook/pb_overlay/subcomponents/_overlay_token.tsx +48 -10
  107. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.tsx +4 -0
  108. data/app/pb_kits/playbook/pb_phone_number_input/docs/_phone_number_input_default_rails.md +2 -0
  109. data/app/pb_kits/playbook/pb_phone_number_input/docs/example.yml +1 -1
  110. data/app/pb_kits/playbook/pb_popover/_popover.tsx +1 -1
  111. data/app/pb_kits/playbook/pb_progress_pills/progress_pills.html.erb +1 -6
  112. data/app/pb_kits/playbook/pb_progress_simple/progress_simple.html.erb +1 -5
  113. data/app/pb_kits/playbook/pb_progress_step/progress_step.html.erb +1 -5
  114. data/app/pb_kits/playbook/pb_progress_step/progress_step_item.html.erb +3 -7
  115. data/app/pb_kits/playbook/pb_radio/_radio.tsx +85 -74
  116. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.jsx +60 -0
  117. data/app/pb_kits/playbook/pb_radio/docs/_radio_react_hook.md +1 -0
  118. data/app/pb_kits/playbook/pb_radio/docs/example.yml +2 -1
  119. data/app/pb_kits/playbook/pb_radio/docs/index.js +1 -0
  120. data/app/pb_kits/playbook/pb_radio/radio.html.erb +6 -11
  121. data/app/pb_kits/playbook/pb_radio/radio.test.js +16 -0
  122. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.jsx +58 -0
  123. data/app/pb_kits/playbook/pb_select/docs/_select_react_hook.md +1 -0
  124. data/app/pb_kits/playbook/pb_select/docs/example.yml +1 -0
  125. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  126. data/app/pb_kits/playbook/pb_select/index.js +38 -0
  127. data/app/pb_kits/playbook/pb_select/select.html.erb +3 -5
  128. data/app/pb_kits/playbook/pb_select/select.rb +8 -0
  129. data/app/pb_kits/playbook/pb_selectable_card/selectable_card.html.erb +1 -5
  130. data/app/pb_kits/playbook/pb_selectable_card_icon/selectable_card_icon.html.erb +1 -4
  131. data/app/pb_kits/playbook/pb_selectable_icon/selectable_icon.html.erb +1 -5
  132. data/app/pb_kits/playbook/pb_timeline/_timeline.scss +2 -2
  133. data/app/pb_kits/playbook/pb_title/_title.scss +32 -0
  134. data/app/pb_kits/playbook/pb_title/_title.tsx +10 -1
  135. data/app/pb_kits/playbook/pb_title/docs/_title_default.html.erb +1 -2
  136. data/app/pb_kits/playbook/pb_title/docs/_title_default.jsx +1 -1
  137. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.html.erb +7 -0
  138. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.jsx +54 -0
  139. data/app/pb_kits/playbook/pb_title/docs/_title_display_size.md +1 -0
  140. data/app/pb_kits/playbook/pb_title/docs/example.yml +2 -0
  141. data/app/pb_kits/playbook/pb_title/docs/index.js +1 -0
  142. data/app/pb_kits/playbook/pb_title/title.rb +10 -1
  143. data/app/pb_kits/playbook/pb_tooltip/_tooltip.scss +0 -3
  144. data/app/pb_kits/playbook/pb_tooltip/_tooltip.tsx +25 -0
  145. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_delay_rails.html.erb +39 -0
  146. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_delay_rails.md +3 -0
  147. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_interaction.html.erb +26 -0
  148. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.jsx +69 -0
  149. data/app/pb_kits/playbook/pb_tooltip/docs/_tooltip_sizing.md +3 -0
  150. data/app/pb_kits/playbook/pb_tooltip/docs/example.yml +3 -1
  151. data/app/pb_kits/playbook/pb_tooltip/docs/index.js +1 -0
  152. data/app/pb_kits/playbook/pb_tooltip/floating_ui.js +282 -0
  153. data/app/pb_kits/playbook/pb_tooltip/index.js +2 -2
  154. data/app/pb_kits/playbook/pb_tooltip/tooltip.rb +10 -2
  155. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +36 -2
  156. data/app/pb_kits/playbook/pb_typeahead/components/MultiValue.tsx +5 -1
  157. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options.html.erb +45 -0
  158. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options.md +5 -0
  159. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options_pure_rails.html.erb +33 -0
  160. data/app/pb_kits/playbook/pb_typeahead/docs/_typeahead_dynamic_options_pure_rails.md +3 -0
  161. data/app/pb_kits/playbook/pb_typeahead/docs/example.yml +2 -0
  162. data/app/pb_kits/playbook/pb_typeahead/index.ts +61 -8
  163. data/app/pb_kits/playbook/pb_typeahead/typeahead.rb +17 -2
  164. data/app/pb_kits/playbook/utilities/object.test.js +99 -0
  165. data/app/pb_kits/playbook/utilities/object.ts +29 -1
  166. data/dist/chunks/_typeahead-Cu4WtFsC.js +36 -0
  167. data/dist/chunks/_weekday_stacked-iCkVKLxW.js +45 -0
  168. data/dist/chunks/{lib-D3us1bGD.js → lib-5OzNgeeu.js} +2 -2
  169. data/dist/chunks/{pb_form_validation-BpihMSOQ.js → pb_form_validation-DGhKbZtO.js} +1 -1
  170. data/dist/chunks/vendor.js +1 -1
  171. data/dist/menu.yml +3 -3
  172. data/dist/playbook-doc.js +1 -1
  173. data/dist/playbook-rails-react-bindings.js +1 -1
  174. data/dist/playbook-rails.js +1 -1
  175. data/dist/playbook.css +1 -1
  176. data/lib/playbook/forms/builder/time_zone_select_field.rb +19 -0
  177. data/lib/playbook/forms/builder.rb +1 -0
  178. data/lib/playbook/version.rb +2 -2
  179. metadata +77 -7
  180. data/dist/chunks/_typeahead-CAIQfP7X.js +0 -36
  181. data/dist/chunks/_weekday_stacked-DstwbGUv.js +0 -45
  182. /data/app/pb_kits/playbook/pb_phone_number_input/docs/{_phone_number_input_default.html.erb → _phone_number_input_default_rails.html.erb} +0 -0
@@ -2,15 +2,17 @@ examples:
2
2
 
3
3
  rails:
4
4
  - tooltip_default: Default
5
+ - tooltip_interaction: Content Interaction
5
6
  - tooltip_selectors: Using Common Selectors
6
7
  - tooltip_with_icon_circle: Icon Circle Tooltip
8
+ - tooltip_delay_rails: Delay
7
9
  - tooltip_show_tooltip: Show Tooltip
8
10
 
9
11
  react:
10
12
  - tooltip_default_react: Default
11
13
  - tooltip_interaction: Content Interaction
12
14
  - tooltip_margin: Margin
15
+ - tooltip_sizing: Tooltip Sizing
13
16
  - tooltip_icon: Tooltip with Icon
14
17
  - tooltip_delay: Delay
15
18
  - tooltip_show_tooltip_react: Show Tooltip
16
-
@@ -1,6 +1,7 @@
1
1
  export { default as TooltipDefaultReact } from './_tooltip_default_react'
2
2
  export { default as TooltipInteraction } from './_tooltip_interaction'
3
3
  export { default as TooltipMargin } from './_tooltip_margin'
4
+ export { default as TooltipSizing } from './_tooltip_sizing'
4
5
  export { default as TooltipIcon } from './_tooltip_icon'
5
6
  export { default as TooltipDelay } from './_tooltip_delay'
6
7
  export { default as TooltipShowTooltipReact } from './_tooltip_show_tooltip_react'
@@ -0,0 +1,282 @@
1
+ import PbEnhancedElement from '../pb_enhanced_element'
2
+ import { computePosition, offset, flip, shift, arrow, autoUpdate } from '@floating-ui/dom'
3
+
4
+ const TOOLTIP_OFFSET = 20
5
+ const TOOLTIP_TIMEOUT = 250
6
+ const SAFE_ZONE_MARGIN = 1
7
+
8
+ export default class PbTooltipFloatingUi extends PbEnhancedElement {
9
+ static get selector() {
10
+ return '[data-pb-tooltip-kit="true"][data-pb-tooltip-delay-open], [data-pb-tooltip-kit="true"][data-pb-tooltip-delay-close], [data-pb-tooltip-kit="true"][data-pb-tooltip-interaction="true"]'
11
+ }
12
+
13
+ connect() {
14
+ if (this.tooltipInteraction) {
15
+ document.addEventListener('mousemove', (e) => {
16
+ this.lastMouseX = e.clientX
17
+ this.lastMouseY = e.clientY
18
+ })
19
+ }
20
+
21
+ this.triggerElements.forEach((trigger) => {
22
+ const method = this.triggerMethod
23
+ const interactionEnabled = this.tooltipInteraction
24
+
25
+ if (method === 'click') {
26
+ trigger.addEventListener('click', () => {
27
+ this.showTooltip(trigger)
28
+ })
29
+ } else {
30
+ trigger.addEventListener('mouseenter', () => {
31
+ clearSafeZoneListener(this)
32
+ clearTimeout(this.mouseleaveTimeout)
33
+ this.currentTrigger = trigger
34
+ const delayOpen = this.delayOpen ? parseInt(this.delayOpen) : TOOLTIP_TIMEOUT
35
+ this.mouseenterTimeout = setTimeout(() => {
36
+ this.showTooltip(trigger)
37
+ if (interactionEnabled) {
38
+ this.checkCloseTooltip(trigger)
39
+ }
40
+ }, delayOpen)
41
+ })
42
+
43
+ trigger.addEventListener('mouseleave', () => {
44
+ clearTimeout(this.mouseenterTimeout)
45
+ if (this.delayClose) {
46
+ const delayClose = parseInt(this.delayClose)
47
+ this.mouseleaveTimeout = setTimeout(() => {
48
+ if (interactionEnabled) {
49
+ this.attachSafeZoneListener()
50
+ } else {
51
+ this.hideTooltip()
52
+ }
53
+ }, delayClose)
54
+ } else {
55
+ if (interactionEnabled) {
56
+ this.attachSafeZoneListener()
57
+ } else {
58
+ this.hideTooltip()
59
+ }
60
+ }
61
+ })
62
+
63
+ if (interactionEnabled) {
64
+ this.tooltip.addEventListener('mouseenter', () => {
65
+ clearSafeZoneListener(this)
66
+ })
67
+
68
+ this.tooltip.addEventListener('mouseleave', () => {
69
+ this.attachSafeZoneListener()
70
+ })
71
+ }
72
+ }
73
+ })
74
+ }
75
+
76
+ attachSafeZoneListener() {
77
+ clearSafeZoneListener(this)
78
+ this.safeZoneHandler = (e) => {
79
+ if (!this.currentTrigger) return
80
+ const triggerRect = this.currentTrigger.getBoundingClientRect()
81
+ const tooltipRect = this.tooltip.getBoundingClientRect()
82
+ const safeRect = getSafeZone(triggerRect, tooltipRect, this.position, SAFE_ZONE_MARGIN)
83
+ if (!isPointInsideRect(e.clientX, e.clientY, safeRect)) {
84
+ this.hideTooltip()
85
+ clearSafeZoneListener(this)
86
+ }
87
+ }
88
+ document.addEventListener('mousemove', this.safeZoneHandler)
89
+ }
90
+
91
+ checkCloseTooltip(trigger) {
92
+ document.querySelector('body').addEventListener('click', ({ target }) => {
93
+ const isTooltip = target.closest(`#${this.tooltipId}`) === this.tooltip
94
+ const isTrigger = target.closest(this.triggerElementSelector) === trigger
95
+ if (isTrigger || isTooltip) {
96
+ this.checkCloseTooltip(trigger)
97
+ } else {
98
+ this.hideTooltip()
99
+ }
100
+ }, { once: true })
101
+ }
102
+
103
+ showTooltip(trigger) {
104
+ if (this.shouldShowTooltip === 'false') return
105
+
106
+ clearSafeZoneListener(this)
107
+
108
+ this.tooltip.style.opacity = '1'
109
+ this.tooltip.style.visibility = 'visible'
110
+ this.tooltip.style.pointerEvents = 'auto'
111
+
112
+ if (this.cleanup) {
113
+ this.cleanup()
114
+ }
115
+
116
+ const arrowElement = document.querySelector(`#${this.tooltipId}-arrow`)
117
+
118
+ this.cleanup = autoUpdate(trigger, this.tooltip, () => {
119
+ computePosition(trigger, this.tooltip, {
120
+ placement: this.position,
121
+ strategy: 'fixed',
122
+ middleware: [
123
+ offset({ mainAxis: TOOLTIP_OFFSET, crossAxis: 0 }),
124
+ flip(),
125
+ shift(),
126
+ arrow({ element: arrowElement })
127
+ ],
128
+ }).then(({ x, y, placement, middlewareData }) => {
129
+ Object.assign(this.tooltip.style, {
130
+ left: `${x}px`,
131
+ top: `${y}px`,
132
+ position: 'fixed'
133
+ })
134
+ this.tooltip.setAttribute('data-popper-placement', placement)
135
+ if (arrowElement && middlewareData.arrow) {
136
+ const { x: arrowX, y: arrowY } = middlewareData.arrow
137
+ Object.assign(arrowElement.style, {
138
+ left: arrowX != null ? `${arrowX}px` : '',
139
+ top: arrowY != null ? `${arrowY}px` : '',
140
+ position: 'absolute'
141
+ })
142
+ }
143
+ })
144
+ })
145
+
146
+ this.tooltip.classList.add('show')
147
+
148
+ if (this.triggerMethod === 'click') {
149
+ clearTimeout(this.autoHideTimeout)
150
+ this.autoHideTimeout = setTimeout(() => {
151
+ this.hideTooltip()
152
+ }, 1000)
153
+ }
154
+ }
155
+
156
+ hideTooltip() {
157
+ if (!this.tooltip) return
158
+
159
+ this.tooltip.classList.add('fade_out')
160
+ setTimeout(() => {
161
+ if (this.cleanup) {
162
+ this.cleanup()
163
+ this.cleanup = null
164
+ }
165
+ this.tooltip.classList.remove('show')
166
+ this.tooltip.classList.remove('fade_out')
167
+ this.tooltip.style.opacity = '0'
168
+ this.tooltip.style.visibility = 'hidden'
169
+ this.tooltip.style.pointerEvents = 'none'
170
+ this.tooltip.style.position = ''
171
+ this.tooltip.style.top = ''
172
+ this.tooltip.style.left = ''
173
+ this.tooltip.style.transform = ''
174
+ }, TOOLTIP_TIMEOUT)
175
+ }
176
+
177
+ get triggerElements() {
178
+ let triggerEl
179
+ if (this.triggerElementId) {
180
+ triggerEl = document.querySelector(`#${this.triggerElementId}`)
181
+ } else if (this.triggerElementSelector) {
182
+ const selectorIsId = this.triggerElementSelector.indexOf('#') > -1
183
+ triggerEl = selectorIsId
184
+ ? document.querySelector(this.triggerElementSelector)
185
+ : document.querySelectorAll(this.triggerElementSelector)
186
+ } else {
187
+ triggerEl = this.element
188
+ }
189
+ if (!triggerEl) {
190
+ console.error('Tooltip Kit: No valid trigger element found!')
191
+ return []
192
+ }
193
+ if (triggerEl.length === undefined) {
194
+ triggerEl = [triggerEl]
195
+ }
196
+ return triggerEl
197
+ }
198
+
199
+ get tooltip() {
200
+ return (this._tooltip = this._tooltip || this.element.querySelector(`#${this.tooltipId}`))
201
+ }
202
+
203
+ get position() {
204
+ return this.element.dataset.pbTooltipPosition
205
+ }
206
+
207
+ get triggerElementId() {
208
+ return this.element.dataset.pbTooltipTriggerElementId
209
+ }
210
+
211
+ get tooltipId() {
212
+ return this.element.dataset.pbTooltipTooltipId
213
+ }
214
+
215
+ get triggerElementSelector() {
216
+ return this.element.dataset.pbTooltipTriggerElementSelector
217
+ }
218
+
219
+ get shouldShowTooltip() {
220
+ return this.element.dataset.pbTooltipShowTooltip
221
+ }
222
+
223
+ get triggerMethod() {
224
+ return this.element.dataset.pbTooltipTriggerMethod || 'hover'
225
+ }
226
+
227
+ get tooltipInteraction() {
228
+ return this.element.dataset.pbTooltipInteraction === 'true'
229
+ }
230
+
231
+ get delayOpen() {
232
+ return this.element.dataset.pbTooltipDelayOpen
233
+ }
234
+
235
+ get delayClose() {
236
+ return this.element.dataset.pbTooltipDelayClose
237
+ }
238
+ }
239
+
240
+ function clearSafeZoneListener(context) {
241
+ if (context.safeZoneHandler) {
242
+ document.removeEventListener('mousemove', context.safeZoneHandler)
243
+ context.safeZoneHandler = null
244
+ }
245
+ }
246
+
247
+ function getSafeZone(triggerRect, tooltipRect, placement, margin) {
248
+ let safeRect = {}
249
+ if (placement.startsWith('top')) {
250
+ safeRect.left = triggerRect.left - margin
251
+ safeRect.right = triggerRect.right + margin
252
+ safeRect.top = tooltipRect.bottom - margin
253
+ safeRect.bottom = triggerRect.top + margin
254
+ } else if (placement.startsWith('bottom')) {
255
+ safeRect.left = triggerRect.left - margin
256
+ safeRect.right = triggerRect.right + margin
257
+ safeRect.top = triggerRect.bottom - margin
258
+ safeRect.bottom = tooltipRect.top + margin
259
+ } else if (placement.startsWith('left')) {
260
+ safeRect.top = triggerRect.top - margin
261
+ safeRect.bottom = triggerRect.bottom + margin
262
+ safeRect.left = tooltipRect.right - margin
263
+ safeRect.right = triggerRect.left + margin
264
+ } else if (placement.startsWith('right')) {
265
+ safeRect.top = triggerRect.top - margin
266
+ safeRect.bottom = triggerRect.bottom + margin
267
+ safeRect.left = triggerRect.right - margin
268
+ safeRect.right = tooltipRect.left + margin
269
+ } else {
270
+ safeRect = {
271
+ left: triggerRect.left - margin,
272
+ right: triggerRect.right + margin,
273
+ top: triggerRect.top - margin,
274
+ bottom: triggerRect.bottom + margin,
275
+ }
276
+ }
277
+ return safeRect
278
+ }
279
+
280
+ function isPointInsideRect(x, y, rect) {
281
+ return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
282
+ }
@@ -11,7 +11,7 @@ const TOOLTIP_TIMEOUT = 250
11
11
 
12
12
  export default class PbTooltip extends PbEnhancedElement {
13
13
  static get selector() {
14
- return '[data-pb-tooltip-kit]'
14
+ return '[data-pb-tooltip-kit="true"][data-pb-tooltip-interaction="false"]:not([data-pb-tooltip-delay-open]):not([data-pb-tooltip-delay-close])'
15
15
  }
16
16
 
17
17
  connect() {
@@ -152,4 +152,4 @@ export default class PbTooltip extends PbEnhancedElement {
152
152
  get triggerMethod() {
153
153
  return this.element.dataset.pbTooltipTriggerMethod || 'hover'
154
154
  }
155
- }
155
+ }
@@ -7,6 +7,10 @@ module Playbook
7
7
  prop :trigger_element_selector
8
8
  prop :trigger_element_id, deprecated: true
9
9
  prop :tooltip_id
10
+ prop :interaction, type: Playbook::Props::Boolean,
11
+ default: false
12
+ prop :delay_open
13
+ prop :delay_close
10
14
  prop :dark, type: Playbook::Props::Boolean,
11
15
  default: false
12
16
  prop :trigger_method, type: Playbook::Props::Enum,
@@ -18,15 +22,19 @@ module Playbook
18
22
  end
19
23
 
20
24
  def data
21
- Hash(values[:data]).merge(
25
+ data = Hash(values[:data]).merge(
22
26
  pb_tooltip_kit: true,
23
27
  pb_tooltip_position: position,
24
28
  pb_tooltip_trigger_element_selector: trigger_element_selector,
25
29
  pb_tooltip_trigger_element_id: trigger_element_id,
26
30
  pb_tooltip_tooltip_id: tooltip_id,
27
31
  pb_tooltip_show_tooltip: true,
28
- pb_tooltip_trigger_method: trigger_method
32
+ pb_tooltip_trigger_method: trigger_method,
33
+ pb_tooltip_interaction: interaction
29
34
  )
35
+ data = data.merge(pb_tooltip_delay_open: delay_open) if delay_open
36
+ data = data.merge(pb_tooltip_delay_close: delay_close) if delay_close
37
+ data
30
38
  end
31
39
 
32
40
  private
@@ -1,9 +1,10 @@
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'
5
5
  import AsyncCreateableSelect from 'react-select/async-creatable'
6
- import { get, isString, uniqueId } from 'lodash'
6
+ import { get, isString, uniqueId } from '../utilities/object'
7
+
7
8
  import { globalProps, GlobalProps } from '../utilities/globalProps'
8
9
  import classnames from 'classnames'
9
10
 
@@ -45,8 +46,12 @@ type TypeaheadProps = {
45
46
  getOptionLabel?: string | (() => string),
46
47
  getOptionValue?: string | (() => string),
47
48
  name?: string,
49
+ options?: Array<{ label: string; value?: string }>,
48
50
  marginBottom?: "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl",
49
51
  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",
52
+ optionsByContext?: Record<string, Array<{ label: string; value?: string }>>
53
+ searchContextSelector?: string,
54
+ clearOnContextChange?: boolean,
50
55
  } & GlobalProps
51
56
 
52
57
  export type SelectValueType = {
@@ -81,6 +86,9 @@ const Typeahead = ({
81
86
  loadOptions = noop,
82
87
  marginBottom = "sm",
83
88
  pillColor,
89
+ optionsByContext = {},
90
+ searchContextSelector,
91
+ clearOnContextChange = false,
84
92
  ...props
85
93
  }: TypeaheadProps) => {
86
94
  const selectProps = {
@@ -115,6 +123,32 @@ const Typeahead = ({
115
123
  ...props,
116
124
  }
117
125
 
126
+ const [contextValue, setContextValue] = useState("")
127
+
128
+ useEffect(() => {
129
+ if (searchContextSelector) {
130
+ const searchContextElement = document.getElementById(searchContextSelector)
131
+
132
+ setContextValue((searchContextElement as HTMLInputElement)?.value)
133
+ const handleContextChange = (e: Event) => {
134
+ const target = e.target as HTMLInputElement;
135
+ setContextValue(target.value);
136
+ if (clearOnContextChange) document.dispatchEvent(new CustomEvent(`pb-typeahead-kit-${selectProps.id}:clear`))
137
+ }
138
+
139
+ if (searchContextElement) searchContextElement.addEventListener('change', handleContextChange)
140
+
141
+ return () => {
142
+ if (searchContextElement) searchContextElement.removeEventListener('change', handleContextChange)
143
+ }
144
+ }
145
+ }, [searchContextSelector])
146
+
147
+ const contextArray = optionsByContext[contextValue]
148
+ if (Array.isArray(contextArray) && contextArray.length > 0) {
149
+ selectProps.options = contextArray
150
+ }
151
+
118
152
  const Tag = (
119
153
  createable
120
154
  ? (async ? AsyncCreateableSelect : CreateableSelect)
@@ -8,15 +8,17 @@ import { SelectValueType } from '../_typeahead'
8
8
  type Props = {
9
9
  data: SelectValueType,
10
10
  multiValueTemplate: any,
11
+ wrapped?: boolean,
11
12
  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",
12
13
  removeProps: any,
13
14
  selectProps: any,
14
15
  }
15
16
 
17
+
16
18
  const MultiValue = (props: Props) => {
17
19
  const { removeProps } = props
18
20
  const { imageUrl, label } = props.data
19
- const { dark, multiKit, pillColor, truncate } = props.selectProps
21
+ const { dark, multiKit, pillColor, truncate, wrapped } = props.selectProps
20
22
 
21
23
  const formPillProps = {
22
24
  marginRight: 'xs',
@@ -52,6 +54,7 @@ const MultiValue = (props: Props) => {
52
54
  size={multiKit === 'smallPill' ? 'small' : ''}
53
55
  text=''
54
56
  truncate={truncate}
57
+ wrapped={wrapped}
55
58
  {...props}
56
59
  />
57
60
  }
@@ -66,6 +69,7 @@ const MultiValue = (props: Props) => {
66
69
  size={multiKit === 'smallPill' ? 'small' : ''}
67
70
  text={label}
68
71
  truncate={truncate}
72
+ wrapped={wrapped}
69
73
  {...props}
70
74
  />
71
75
  }
@@ -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
@@ -20,6 +20,34 @@ export default class PbTypeahead extends PbEnhancedElement {
20
20
  this.searchInput.addEventListener('focus', () => this.debouncedSearch())
21
21
  this.searchInput.addEventListener('input', () => this.debouncedSearch())
22
22
  this.resultsElement.addEventListener('click', (event: MouseEvent) => this.optionSelected(event))
23
+
24
+ if (this.clearOnContextChange && this.searchContextElement) {
25
+ this.searchContextElement.addEventListener('change', () => {
26
+ this.searchInputClear()
27
+ this.resultsCacheClear()
28
+ this.clearResults()
29
+ })
30
+ }
31
+ }
32
+
33
+ get optionsByContext() {
34
+ return (this.element as HTMLElement).dataset.pbTypeaheadKitOptionsByContext
35
+ ? JSON.parse((this.element as HTMLElement).dataset.pbTypeaheadKitOptionsByContext)
36
+ : null
37
+ }
38
+
39
+ get searchContextElement() {
40
+ const selector = (this.element as HTMLElement).dataset.pbTypeaheadKitSearchContextSelector
41
+ if (!selector) return null
42
+
43
+ const found = this.element.parentNode?.querySelector(`#${selector}`)
44
+ || this.element.closest(selector)
45
+
46
+ return found || null
47
+ }
48
+
49
+ get clearOnContextChange() {
50
+ return (this.element as HTMLElement).dataset.pbTypeaheadKitClearOnContextChange === 'true'
23
51
  }
24
52
 
25
53
  handleKeydown(event: KeyboardEvent) {
@@ -40,14 +68,32 @@ export default class PbTypeahead extends PbEnhancedElement {
40
68
 
41
69
  const searchTerm = this.searchTerm
42
70
  const searchContext = this.searchContext
43
- const search = {
44
- searchingFor: searchTerm,
45
- searchingContext: searchContext,
46
- setResults: (results: Array<DocumentFragment>) => {
47
- this.resultsCacheUpdate(searchTerm, searchContext, results)
48
- },
71
+
72
+ if (this.optionsByContext && Object.keys(this.optionsByContext).length > 0) {
73
+ const contextArray = this.optionsByContext[searchContext] || []
74
+
75
+ const filteredResults = contextArray.filter((obj: { label: string, value: string }) => {
76
+ return obj.label
77
+ && obj.label.toLowerCase().includes(searchTerm.toLowerCase())
78
+ })
79
+
80
+ const optionFragments = filteredResults.map((obj: { label: string, value: string }) => {
81
+ const singleOption = document.createDocumentFragment()
82
+ singleOption.appendChild(document.createTextNode(obj.label))
83
+ return singleOption
84
+ })
85
+
86
+ this.resultsCacheUpdate(searchTerm, searchContext, optionFragments)
87
+ } else {
88
+ const search = {
89
+ searchingFor: searchTerm,
90
+ searchingContext: searchContext,
91
+ setResults: (results: Array<DocumentFragment>) => {
92
+ this.resultsCacheUpdate(searchTerm, searchContext, results)
93
+ },
94
+ }
95
+ this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-search', { bubbles: true, detail: search }))
49
96
  }
50
- this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-search', { bubbles: true, detail: search }))
51
97
  }
52
98
 
53
99
  resultsCacheUpdate(searchTerm: string, searchContext: string, results: Array<DocumentFragment>) {
@@ -87,11 +133,15 @@ export default class PbTypeahead extends PbEnhancedElement {
87
133
  const resultOption = (event.target as Element).closest('[data-result-option-item]')
88
134
  if (!resultOption) return
89
135
 
136
+ const selectedText = resultOption.textContent.trim()
137
+
90
138
  this._validSelection = true
91
139
  this.removeValidationError()
92
140
 
141
+ if (this.searchContextElement) this.searchInput.value = selectedText
142
+
93
143
  this.resultsCacheClear()
94
- this.searchInputClear()
144
+ if (!this.searchContextElement) this.searchInputClear()
95
145
  this.clearResults()
96
146
 
97
147
  this.element.dispatchEvent(new CustomEvent('pb-typeahead-kit-result-option-selected', { bubbles: true, detail: { selected: resultOption, typeahead: this } }))
@@ -173,6 +223,9 @@ export default class PbTypeahead extends PbEnhancedElement {
173
223
  this.element.parentNode.querySelector(selector) ||
174
224
  this.element.closest(selector)
175
225
  ) as HTMLInputElement).value
226
+ else if (this.searchContextElement) {
227
+ return (this.searchContextElement as HTMLInputElement).value
228
+ }
176
229
 
177
230
  return null
178
231
  }