shadcn_phlexcomponents 0.1.5 → 0.1.9

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -0
  3. data/app/javascript/controllers/accordion_controller.js +7 -16
  4. data/app/javascript/controllers/alert_dialog_controller.js +5 -141
  5. data/app/javascript/controllers/combobox_controller.js +20 -0
  6. data/app/javascript/controllers/date_picker_controller.js +199 -64
  7. data/app/javascript/controllers/date_range_picker_controller.js +289 -176
  8. data/app/javascript/controllers/dialog_controller.js +19 -64
  9. data/app/javascript/controllers/dropdown_menu_controller.js +15 -37
  10. data/app/javascript/controllers/form_field_controller.js +24 -0
  11. data/app/javascript/controllers/hover_card_controller.js +1 -22
  12. data/app/javascript/controllers/popover_controller.js +20 -31
  13. data/app/javascript/controllers/select_controller.js +32 -52
  14. data/app/javascript/controllers/sidebar_trigger_controller.js +1 -1
  15. data/app/javascript/controllers/toast_controller.js +2 -2
  16. data/app/javascript/controllers/tooltip_controller.js +1 -2
  17. data/app/javascript/shadcn_phlexcomponents.js +53 -0
  18. data/app/javascript/utils.js +184 -0
  19. data/app/stylesheets/date_picker.css +212 -0
  20. data/lib/install/install_shadcn_phlexcomponents.rb +7 -7
  21. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion.rb +1 -1
  22. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_content.rb +1 -1
  23. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_item.rb +1 -1
  24. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_trigger.rb +5 -4
  25. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog.rb +1 -1
  26. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_action.rb +1 -1
  27. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_action_to.rb +1 -1
  28. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_cancel.rb +1 -1
  29. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_content.rb +2 -2
  30. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_trigger.rb +2 -2
  31. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar.rb +1 -1
  32. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar_fallback.rb +1 -1
  33. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar_image.rb +1 -1
  34. data/lib/{components → shadcn_phlexcomponents/components/badge}/badge.rb +1 -1
  35. data/lib/{components → shadcn_phlexcomponents/components}/base.rb +10 -0
  36. data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb.rb +2 -0
  37. data/lib/{components → shadcn_phlexcomponents/components/button}/button.rb +5 -5
  38. data/lib/{components → shadcn_phlexcomponents/components/checkbox}/checkbox.rb +5 -5
  39. data/lib/{components → shadcn_phlexcomponents/components/checkbox_group}/checkbox_group.rb +27 -15
  40. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible.rb +1 -1
  41. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible_content.rb +1 -1
  42. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible_trigger.rb +2 -2
  43. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker.rb +87 -0
  44. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_content.rb +45 -0
  45. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_trigger.rb +64 -0
  46. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker.rb +105 -0
  47. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_content.rb +9 -0
  48. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_trigger.rb +9 -0
  49. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog.rb +8 -8
  50. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_close.rb +1 -1
  51. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_content.rb +3 -3
  52. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_trigger.rb +2 -2
  53. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu.rb +1 -1
  54. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_content.rb +9 -9
  55. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_item.rb +8 -8
  56. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_trigger.rb +5 -5
  57. data/lib/shadcn_phlexcomponents/components/form/form.rb +139 -0
  58. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +83 -0
  59. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +116 -0
  60. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +47 -0
  61. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +96 -0
  62. data/lib/{components → shadcn_phlexcomponents/components/form}/form_error.rb +6 -2
  63. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +108 -0
  64. data/lib/{components → shadcn_phlexcomponents/components/form}/form_hint.rb +6 -2
  65. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +107 -0
  66. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +65 -0
  67. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +66 -0
  68. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +60 -0
  69. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card.rb +1 -1
  70. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card_content.rb +1 -1
  71. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card_trigger.rb +1 -1
  72. data/lib/{components → shadcn_phlexcomponents/components/input}/input.rb +1 -1
  73. data/lib/{components → shadcn_phlexcomponents/components/loading_button}/loading_button.rb +1 -1
  74. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover.rb +1 -1
  75. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover_content.rb +6 -6
  76. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover_trigger.rb +2 -3
  77. data/lib/{components → shadcn_phlexcomponents/components/progress}/progress.rb +3 -3
  78. data/lib/{components → shadcn_phlexcomponents/components/radio_group}/radio_group.rb +33 -7
  79. data/lib/{components → shadcn_phlexcomponents/components/radio_group}/radio_group_item.rb +7 -7
  80. data/lib/{components → shadcn_phlexcomponents/components/select}/select.rb +22 -12
  81. data/lib/{components → shadcn_phlexcomponents/components/select}/select_content.rb +6 -6
  82. data/lib/{components → shadcn_phlexcomponents/components/select}/select_group.rb +1 -1
  83. data/lib/{components → shadcn_phlexcomponents/components/select}/select_item.rb +8 -8
  84. data/lib/{components → shadcn_phlexcomponents/components/select}/select_label.rb +1 -1
  85. data/lib/{components → shadcn_phlexcomponents/components/select}/select_trigger.rb +10 -10
  86. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet.rb +1 -1
  87. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_close.rb +1 -1
  88. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_content.rb +3 -3
  89. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_trigger.rb +2 -2
  90. data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar.rb +3 -3
  91. data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_trigger.rb +2 -2
  92. data/lib/{components → shadcn_phlexcomponents/components/switch}/switch.rb +4 -4
  93. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs.rb +2 -2
  94. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_content.rb +1 -1
  95. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_trigger.rb +4 -4
  96. data/lib/{components → shadcn_phlexcomponents/components/textarea}/textarea.rb +3 -2
  97. data/lib/{components → shadcn_phlexcomponents/components/theme_switcher}/theme_switcher.rb +2 -2
  98. data/lib/{components → shadcn_phlexcomponents/components/toast}/toast.rb +7 -7
  99. data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_container.rb +1 -1
  100. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip.rb +3 -3
  101. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip_content.rb +1 -1
  102. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip_trigger.rb +1 -1
  103. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  104. metadata +157 -144
  105. data/app/assets/tailwind/vanilla-calendar-pro.css +0 -466
  106. data/app/javascript/controllers/sheet_controller.js +0 -159
  107. data/lib/components/combobox.rb +0 -57
  108. data/lib/components/combobox_item.rb +0 -9
  109. data/lib/components/date_picker.rb +0 -94
  110. data/lib/components/date_range_picker.rb +0 -113
  111. data/lib/components/form.rb +0 -59
  112. /data/app/{assets/tailwind → stylesheets}/choices.css +0 -0
  113. /data/app/{assets/tailwind → stylesheets}/tailwindcss-animate.css +0 -0
  114. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert.rb +0 -0
  115. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert_description.rb +0 -0
  116. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert_title.rb +0 -0
  117. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_description.rb +0 -0
  118. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_footer.rb +0 -0
  119. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_header.rb +0 -0
  120. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_title.rb +0 -0
  121. /data/lib/{components → shadcn_phlexcomponents/components/aspect_ratio}/aspect_ratio.rb +0 -0
  122. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_ellipsis.rb +0 -0
  123. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_item.rb +0 -0
  124. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_link.rb +0 -0
  125. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_page.rb +0 -0
  126. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_separator.rb +0 -0
  127. /data/lib/{components → shadcn_phlexcomponents/components/card}/card.rb +0 -0
  128. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_content.rb +0 -0
  129. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_description.rb +0 -0
  130. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_footer.rb +0 -0
  131. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_header.rb +0 -0
  132. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_title.rb +0 -0
  133. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_description.rb +0 -0
  134. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_footer.rb +0 -0
  135. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_header.rb +0 -0
  136. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_title.rb +0 -0
  137. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_item_to.rb +0 -0
  138. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_label.rb +0 -0
  139. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_separator.rb +0 -0
  140. /data/lib/{components → shadcn_phlexcomponents/components/form}/form_input.rb +0 -0
  141. /data/lib/{components → shadcn_phlexcomponents/components/label}/label.rb +0 -0
  142. /data/lib/{components → shadcn_phlexcomponents/components/link}/link.rb +0 -0
  143. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination.rb +0 -0
  144. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_ellipsis.rb +0 -0
  145. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_link.rb +0 -0
  146. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_next.rb +0 -0
  147. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_previous.rb +0 -0
  148. /data/lib/{components → shadcn_phlexcomponents/components/separator}/separator.rb +0 -0
  149. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_description.rb +0 -0
  150. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_footer.rb +0 -0
  151. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_header.rb +0 -0
  152. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_title.rb +0 -0
  153. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_container.rb +0 -0
  154. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_content.rb +0 -0
  155. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_footer.rb +0 -0
  156. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group.rb +0 -0
  157. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group_content.rb +0 -0
  158. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group_label.rb +0 -0
  159. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_header.rb +0 -0
  160. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_inset.rb +0 -0
  161. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu.rb +0 -0
  162. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_button.rb +0 -0
  163. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_item.rb +0 -0
  164. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub.rb +0 -0
  165. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub_button.rb +0 -0
  166. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub_item.rb +0 -0
  167. /data/lib/{components → shadcn_phlexcomponents/components/skeleton}/skeleton.rb +0 -0
  168. /data/lib/{components → shadcn_phlexcomponents/components/table}/table.rb +0 -0
  169. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_body.rb +0 -0
  170. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_caption.rb +0 -0
  171. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_cell.rb +0 -0
  172. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_footer.rb +0 -0
  173. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_head.rb +0 -0
  174. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_header.rb +0 -0
  175. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_row.rb +0 -0
  176. /data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_list.rb +0 -0
  177. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_action.rb +0 -0
  178. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_action_to.rb +0 -0
  179. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_content.rb +0 -0
  180. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_description.rb +0 -0
  181. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_title.rb +0 -0
@@ -1,17 +1,17 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import {
3
- computePosition,
4
- flip,
5
- shift,
6
- offset,
7
- autoUpdate,
8
- } from '@floating-ui/dom'
3
+ ANIMATION_OUT_DELAY,
4
+ FOCUS_DELAY,
5
+ lockScroll,
6
+ unlockScroll,
7
+ initFloatingUi,
8
+ focusTrigger,
9
+ } from '../utils'
9
10
 
10
11
  export default class extends Controller {
11
12
  static targets = ['trigger', 'contentWrapper', 'content', 'item']
12
13
 
13
14
  connect() {
14
- this.contentTarget.dataset.state = this.isOpen() ? 'open' : 'closed'
15
15
  this.DOMClickListener = this.onDOMClick.bind(this)
16
16
  this.DOMKeydownListener = this.onDOMKeydown.bind(this)
17
17
  this.items = this.itemTargets.filter(
@@ -32,7 +32,7 @@ export default class extends Controller {
32
32
  if (['ArrowDown', 'Enter', ' '].includes(key)) {
33
33
  setTimeout(() => {
34
34
  this.focusItem(null, 0)
35
- }, 100)
35
+ }, FOCUS_DELAY)
36
36
  }
37
37
  }
38
38
  }
@@ -42,6 +42,7 @@ export default class extends Controller {
42
42
  }
43
43
 
44
44
  open() {
45
+ lockScroll()
45
46
  this.contentWrapperTarget.classList.remove('hidden')
46
47
  this.triggerTarget.ariaExpanded = true
47
48
  this.triggerTarget.dataset.state = 'open'
@@ -49,53 +50,30 @@ export default class extends Controller {
49
50
 
50
51
  setTimeout(() => {
51
52
  this.focusContent()
52
- }, 100)
53
+ }, FOCUS_DELAY * 1.25)
53
54
 
54
55
  this.setupEventListeners()
55
56
 
56
- if (window.innerHeight < document.documentElement.scrollHeight) {
57
- document.body.dataset.scrollLocked = 1
58
- }
59
-
60
- this.cleanup = autoUpdate(
57
+ this.cleanup = initFloatingUi(
61
58
  this.triggerTarget,
62
59
  this.contentWrapperTarget,
63
- () => {
64
- computePosition(this.triggerTarget, this.contentWrapperTarget, {
65
- placement: this.element.dataset.side,
66
- strategy: 'fixed',
67
- middleware: [flip(), shift(), offset(4)],
68
- }).then(({ x, y }) => {
69
- Object.assign(this.contentWrapperTarget.style, {
70
- left: `${x}px`,
71
- top: `${y}px`,
72
- })
73
- })
74
- },
60
+ this.element.dataset.side,
75
61
  )
76
62
  }
77
63
 
78
64
  close() {
65
+ unlockScroll()
79
66
  this.triggerTarget.ariaExpanded = false
80
67
  this.triggerTarget.dataset.state = 'closed'
81
68
  this.contentTarget.dataset.state = 'closed'
82
69
  this.cleanup()
83
70
  this.cleanupEventListeners()
84
- delete document.body.dataset.scrollLocked
85
71
 
86
72
  setTimeout(() => {
87
73
  this.contentWrapperTarget.classList.add('hidden')
88
- }, 100)
89
-
90
- this.focusTrigger()
91
- }
74
+ }, ANIMATION_OUT_DELAY)
92
75
 
93
- focusTrigger() {
94
- if (this.triggerTarget.dataset.asChild === 'false') {
95
- this.triggerTarget.firstElementChild.focus()
96
- } else {
97
- this.triggerTarget.focus()
98
- }
76
+ focusTrigger(this.triggerTarget)
99
77
  }
100
78
 
101
79
  focusItem(event = null, index = null) {
@@ -0,0 +1,24 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['trigger', 'content', 'item']
5
+
6
+ connect() {
7
+ const hintContainer = this.element.querySelector('[data-remove-label]')
8
+ const labelContainer = this.element.querySelector('[data-remove-hint]')
9
+
10
+ if (hintContainer) {
11
+ const label = hintContainer.querySelector('label')
12
+ const hint = hintContainer.querySelector('p')
13
+ label.remove()
14
+ hintContainer.replaceWith(hint)
15
+ }
16
+
17
+ if (labelContainer) {
18
+ const hint = labelContainer.querySelector('p')
19
+ const label = labelContainer.querySelector('label')
20
+ hint.remove()
21
+ labelContainer.replaceWith(label)
22
+ }
23
+ }
24
+ }
@@ -1,26 +1,6 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import tippy from 'tippy.js'
3
-
4
- const hideOnEsc = {
5
- name: 'hideOnEsc',
6
- defaultValue: true,
7
- fn({ hide }) {
8
- function onKeyDown(event) {
9
- if (event.keyCode === 27) {
10
- hide()
11
- }
12
- }
13
-
14
- return {
15
- onShow() {
16
- document.addEventListener('keydown', onKeyDown)
17
- },
18
- onHide() {
19
- document.removeEventListener('keydown', onKeyDown)
20
- },
21
- }
22
- },
23
- }
3
+ import { hideOnEsc } from '../utils'
24
4
 
25
5
  export default class extends Controller {
26
6
  static targets = ['trigger', 'content']
@@ -35,7 +15,6 @@ export default class extends Controller {
35
15
  arrow: false,
36
16
  placement: this.element.dataset.side,
37
17
  plugins: [hideOnEsc],
38
- offset: [0, 4],
39
18
  delay: 250,
40
19
  })
41
20
  }
@@ -1,14 +1,13 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import {
3
- computePosition,
4
- flip,
5
- shift,
6
- offset,
7
- autoUpdate,
8
- } from '@floating-ui/dom'
3
+ ANIMATION_OUT_DELAY,
4
+ FOCUS_DELAY,
5
+ initFloatingUi,
6
+ focusTrigger,
7
+ } from '../utils'
9
8
 
10
9
  export default class extends Controller {
11
- static targets = ['trigger', 'contentWrapper', 'content', 'menuItem']
10
+ static targets = ['trigger', 'contentWrapper', 'content']
12
11
 
13
12
  connect() {
14
13
  this.DOMClickListener = this.onDOMClick.bind(this)
@@ -33,7 +32,6 @@ export default class extends Controller {
33
32
  onDOMClick(event) {
34
33
  if (!this.isOpen()) return
35
34
  if (this.element.contains(event.target)) return
36
-
37
35
  this.close()
38
36
  }
39
37
 
@@ -68,25 +66,20 @@ export default class extends Controller {
68
66
  this.contentTarget.dataset.state = 'open'
69
67
  this.setupEventListeners()
70
68
 
69
+ this.onOpen()
70
+ }
71
+
72
+ onOpen() {
71
73
  setTimeout(() => {
72
- this.firstElement.focus()
73
- }, 100)
74
+ if (this.firstElement) {
75
+ this.firstElement.focus()
76
+ }
77
+ }, FOCUS_DELAY * 1.25)
74
78
 
75
- this.cleanup = autoUpdate(
79
+ this.cleanup = initFloatingUi(
76
80
  this.triggerTarget,
77
81
  this.contentWrapperTarget,
78
- () => {
79
- computePosition(this.triggerTarget, this.contentWrapperTarget, {
80
- placement: this.element.dataset.side,
81
- strategy: 'fixed',
82
- middleware: [flip(), shift(), offset(4)],
83
- }).then(({ x, y }) => {
84
- Object.assign(this.contentWrapperTarget.style, {
85
- left: `${x}px`,
86
- top: `${y}px`,
87
- })
88
- })
89
- },
82
+ this.element.dataset.side,
90
83
  )
91
84
  }
92
85
 
@@ -98,17 +91,13 @@ export default class extends Controller {
98
91
 
99
92
  setTimeout(() => {
100
93
  this.contentWrapperTarget.classList.add('hidden')
101
- }, 100)
94
+ }, ANIMATION_OUT_DELAY)
102
95
 
103
- this.focusTrigger()
96
+ this.onClose()
104
97
  }
105
98
 
106
- focusTrigger() {
107
- if (this.triggerTarget.dataset.asChild === 'false') {
108
- this.triggerTarget.firstElementChild.focus()
109
- } else {
110
- this.triggerTarget.focus()
111
- }
99
+ onClose() {
100
+ focusTrigger(this.triggerTarget)
112
101
  }
113
102
 
114
103
  // Global listeners
@@ -1,11 +1,12 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import {
3
- computePosition,
4
- flip,
5
- shift,
6
- offset,
7
- autoUpdate,
8
- } from '@floating-ui/dom'
3
+ ANIMATION_OUT_DELAY,
4
+ FOCUS_DELAY,
5
+ lockScroll,
6
+ unlockScroll,
7
+ initFloatingUi,
8
+ focusTrigger,
9
+ } from '../utils'
9
10
 
10
11
  export default class extends Controller {
11
12
  static targets = [
@@ -31,7 +32,7 @@ export default class extends Controller {
31
32
 
32
33
  this.items = [
33
34
  ...this.element.querySelectorAll(
34
- '[data-shadcn-phlexcomponents--select-target="item"]:not([data-disabled])',
35
+ '[data-select-target="item"]:not([data-disabled])',
35
36
  ),
36
37
  ]
37
38
 
@@ -40,7 +41,6 @@ export default class extends Controller {
40
41
  this.resetSearchTimer = null
41
42
  }
42
43
 
43
- // Methods
44
44
  toggle() {
45
45
  if (this.isOpen()) {
46
46
  this.close()
@@ -50,6 +50,7 @@ export default class extends Controller {
50
50
  }
51
51
 
52
52
  open() {
53
+ lockScroll()
53
54
  const triggerWidth = this.triggerTarget.offsetWidth
54
55
  this.contentWrapperTarget.classList.remove('hidden')
55
56
 
@@ -62,66 +63,47 @@ export default class extends Controller {
62
63
  this.triggerTarget.ariaExpanded = true
63
64
  this.contentTarget.dataset.state = 'open'
64
65
 
65
- if (this.selectedValue) {
66
- const item = this.itemTargets.find(
67
- (i) => i.dataset.value === this.selectedValue,
68
- )
66
+ setTimeout(() => {
67
+ if (this.selectedValue) {
68
+ const item = this.itemTargets.find(
69
+ (i) => i.dataset.value === this.selectedValue,
70
+ )
69
71
 
70
- if (item && !item.dataset.disabled) {
71
- item.focus()
72
+ if (item && !item.dataset.disabled) {
73
+ item.focus()
74
+ } else {
75
+ this.contentTarget.focus()
76
+ }
72
77
  } else {
73
- this.contentTarget.focus()
78
+ this.focusItem(null, 0)
74
79
  }
75
- } else {
76
- this.focusItem(null, 0)
77
- }
80
+ }, FOCUS_DELAY * 1.25)
78
81
 
79
- if (window.innerHeight < document.documentElement.scrollHeight) {
80
- document.body.dataset.scrollLocked = 1
81
- }
82
82
  this.setupEventListeners()
83
-
84
- this.cleanup = autoUpdate(
83
+ this.cleanup = initFloatingUi(
85
84
  this.triggerTarget,
86
85
  this.contentWrapperTarget,
87
- () => {
88
- computePosition(this.triggerTarget, this.contentWrapperTarget, {
89
- placement: 'bottom',
90
- strategy: 'fixed',
91
- middleware: [flip(), shift(), offset(4)],
92
- }).then(({ x, y }) => {
93
- Object.assign(this.contentWrapperTarget.style, {
94
- left: `${x}px`,
95
- top: `${y}px`,
96
- })
97
- })
98
- },
86
+ 'bottom',
99
87
  )
100
88
  }
101
89
 
102
90
  close() {
91
+ unlockScroll()
103
92
  this.contentTarget.dataset.state = 'closed'
104
93
  this.triggerTarget.ariaExpanded = false
105
94
  this.cleanup()
106
95
  this.cleanupEventListeners()
107
- delete document.body.dataset.scrollLocked
108
96
 
109
97
  setTimeout(() => {
110
98
  this.contentWrapperTarget.classList.add('hidden')
111
- }, 100)
99
+ }, ANIMATION_OUT_DELAY)
112
100
 
113
- if (this.triggerTarget.nodeName === 'DIV') {
114
- this.triggerTarget.firstElementChild.focus()
115
- } else {
116
- this.triggerTarget.focus()
117
- }
101
+ focusTrigger(this.triggerTarget)
118
102
  }
119
103
 
120
104
  setAriaLabelledby() {
121
105
  this.groupTargets.forEach((g) => {
122
- const label = g.querySelector(
123
- '[data-shadcn-phlexcomponents--select-target="label"]',
124
- )
106
+ const label = g.querySelector('[data-select-target="label"]')
125
107
 
126
108
  if (label) {
127
109
  const ariaId = this.element.dataset.ariaId
@@ -216,14 +198,12 @@ export default class extends Controller {
216
198
  this.selectTarget.value = value
217
199
  }
218
200
 
219
- delete this.triggerTarget.dataset.placeholder
220
- const hasPlaceholder = this.triggerTarget.dataset.placeholderText
201
+ this.triggerTarget.dataset.hasValue = !!value && value.length > 0
221
202
 
222
- if (hasPlaceholder) {
223
- if (!value || value.length === 0) {
224
- this.triggerTarget.dataset.placeholder = true
225
- this.triggerTextTarget.textContent = hasPlaceholder
226
- }
203
+ const placeholder = this.triggerTarget.dataset.placeholder
204
+
205
+ if (placeholder && this.triggerTarget.dataset.hasValue === 'false') {
206
+ this.triggerTextTarget.textContent = placeholder
227
207
  }
228
208
  }
229
209
 
@@ -8,7 +8,7 @@ export default class extends Controller {
8
8
  toggle() {
9
9
  const sidebar = this.application.getControllerForElementAndIdentifier(
10
10
  document.querySelector(`#${this.sidebarId}`),
11
- 'shadcn-phlexcomponents--sidebar',
11
+ 'sidebar',
12
12
  )
13
13
  sidebar.toggle()
14
14
  }
@@ -1,5 +1,5 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
-
2
+ import { ANIMATION_OUT_DELAY } from '../utils'
3
3
  export default class extends Controller {
4
4
  connect() {
5
5
  this.focusableElements = this.element.querySelectorAll('button')
@@ -26,7 +26,7 @@ export default class extends Controller {
26
26
 
27
27
  setTimeout(() => {
28
28
  this.element.remove()
29
- }, 100)
29
+ }, ANIMATION_OUT_DELAY)
30
30
  }
31
31
 
32
32
  dismiss(event) {
@@ -28,14 +28,13 @@ export default class extends Controller {
28
28
  connect() {
29
29
  const content = this.contentTarget.innerHTML
30
30
 
31
- tippy(this.triggerTarget, {
31
+ this.tippy = tippy(this.triggerTarget, {
32
32
  content: content,
33
33
  allowHTML: true,
34
34
  interactive: true,
35
35
  arrow: false,
36
36
  placement: this.element.dataset.side,
37
37
  plugins: [hideOnEsc],
38
- offset: [0, 4],
39
38
  })
40
39
  }
41
40
  }
@@ -0,0 +1,53 @@
1
+ import AccordionController from './controllers/accordion_controller'
2
+ import AlertDialogController from './controllers/alert_dialog_controller'
3
+ import AvatarController from './controllers/avatar_controller'
4
+ import CheckboxController from './controllers/checkbox_controller'
5
+ import CollapsibleController from './controllers/collapsible_controller'
6
+ import ComboboxController from './controllers/combobox_controller'
7
+ import DatePickerController from './controllers/date_picker_controller'
8
+ import DateRangePickerController from './controllers/date_range_picker_controller'
9
+ import DialogController from './controllers/dialog_controller'
10
+ import DropdownMenuController from './controllers/dropdown_menu_controller'
11
+ import FormFieldController from './controllers/form_field_controller'
12
+ import HoverCardController from './controllers/hover_card_controller'
13
+ import LoadingButtonController from './controllers/loading_button_controller'
14
+ import PopoverController from './controllers/popover_controller'
15
+ import ProgressController from './controllers/progress_controller'
16
+ import RadioGroupController from './controllers/radio_group_controller'
17
+ import SelectController from './controllers/select_controller'
18
+ import SidebarController from './controllers/sidebar_controller'
19
+ import SidebarTriggerController from './controllers/sidebar_trigger_controller'
20
+ import SwitchController from './controllers/switch_controller'
21
+ import TabsController from './controllers/tabs_controller'
22
+ import ThemeSwitcherController from './controllers/theme_switcher_controller'
23
+ import ToastContainerController from './controllers/toast_container_controller'
24
+ import ToastController from './controllers/toast_controller'
25
+ import TooltipController from './controllers/tooltip_controller'
26
+
27
+ export default {
28
+ accordion: AccordionController,
29
+ 'alert-dialog': AlertDialogController,
30
+ avatar: AvatarController,
31
+ checkbox: CheckboxController,
32
+ collapsible: CollapsibleController,
33
+ combobox: ComboboxController,
34
+ 'date-picker': DatePickerController,
35
+ 'date-range-picker': DateRangePickerController,
36
+ dialog: DialogController,
37
+ 'dropdown-menu': DropdownMenuController,
38
+ 'form-field': FormFieldController,
39
+ 'hover-card': HoverCardController,
40
+ 'loading-button': LoadingButtonController,
41
+ popover: PopoverController,
42
+ progress: ProgressController,
43
+ 'radio-group': RadioGroupController,
44
+ select: SelectController,
45
+ sidebar: SidebarController,
46
+ 'sidebar-trigger': SidebarTriggerController,
47
+ switch: SwitchController,
48
+ tabs: TabsController,
49
+ 'theme-switcher': ThemeSwitcherController,
50
+ 'toast-container': ToastContainerController,
51
+ toast: ToastController,
52
+ tooltip: TooltipController,
53
+ }
@@ -0,0 +1,184 @@
1
+ import {
2
+ computePosition,
3
+ flip,
4
+ shift,
5
+ offset,
6
+ autoUpdate,
7
+ } from '@floating-ui/dom'
8
+
9
+ const ANIMATION_OUT_DELAY = 125
10
+ const FOCUS_DELAY = 100
11
+
12
+ const getScrollbarWidth = () => {
13
+ // Create a temporary div container and append it into the body
14
+ const outer = document.createElement('div')
15
+ outer.style.visibility = 'hidden'
16
+ outer.style.overflow = 'scroll' // force scrollbars
17
+ outer.style.msOverflowStyle = 'scrollbar' // needed for some older versions of Edge
18
+ outer.style.width = '100px'
19
+ outer.style.position = 'absolute'
20
+ outer.style.top = '-9999px'
21
+ document.body.appendChild(outer)
22
+
23
+ // Create an inner div and place it inside the outer div
24
+ const inner = document.createElement('div')
25
+ inner.style.width = '100%'
26
+ outer.appendChild(inner)
27
+
28
+ // Calculate the scrollbar width
29
+ const scrollbarWidth = outer.offsetWidth - inner.offsetWidth
30
+
31
+ // Clean up
32
+ outer.remove()
33
+
34
+ return scrollbarWidth
35
+ }
36
+
37
+ const showOverlay = (classNames = '') => {
38
+ const element = document.createElement('div')
39
+ let defaultClassNames = [
40
+ 'fixed',
41
+ 'inset-0',
42
+ 'z-50',
43
+ 'bg-black/80',
44
+ 'data-[state=open]:animate-in',
45
+ 'data-[state=closed]:animate-out',
46
+ 'data-[state=closed]:fade-out-0',
47
+ 'data-[state=open]:fade-in-0',
48
+ 'pointer-events-auto',
49
+ ]
50
+ defaultClassNames = defaultClassNames.concat(
51
+ classNames.split(' ').filter((c) => !!c),
52
+ )
53
+
54
+ element.classList.add(...defaultClassNames)
55
+ element.dataset.state = 'open'
56
+ element.dataset.shadcnPhlexcomponentsOverlay = true
57
+ element.ariaHidden = true
58
+ document.body.appendChild(element)
59
+ }
60
+
61
+ const hideOverlay = () => {
62
+ const element = document.querySelector(
63
+ '[data-shadcn-phlexcomponents-overlay]',
64
+ )
65
+
66
+ if (element) {
67
+ element.dataset.state = 'closed'
68
+
69
+ setTimeout(() => {
70
+ element.remove()
71
+ }, ANIMATION_OUT_DELAY)
72
+ }
73
+ }
74
+
75
+ const lockScroll = () => {
76
+ if (window.innerHeight < document.documentElement.scrollHeight) {
77
+ document.body.dataset.scrollLocked = 1
78
+ const style = getComputedStyle(document.body)
79
+ const originalMarginRight = style.marginRight
80
+ document.body.dataset.marginRight = originalMarginRight
81
+ document.body.style.marginRight = `${getScrollbarWidth()}px`
82
+ }
83
+ }
84
+
85
+ const unlockScroll = () => {
86
+ if (document.body.dataset.scrollLocked) {
87
+ delete document.body.dataset.scrollLocked
88
+ const originalMarginRight = document.body.dataset.marginRight
89
+
90
+ if (parseInt(originalMarginRight) === 0) {
91
+ document.body.style.marginRight = ''
92
+ } else {
93
+ document.body.style.marginRight = originalMarginRight
94
+ }
95
+
96
+ delete document.body.dataset.marginRight
97
+ }
98
+ }
99
+
100
+ const addInert = (ignoredElements = []) => {
101
+ Array.from(document.body.children)
102
+ .filter((el) => !ignoredElements.includes(el))
103
+ .forEach((el) => el.setAttribute('inert', ''))
104
+ }
105
+
106
+ const removeInert = () => {
107
+ Array.from(document.body.children)
108
+ .filter((el) => el.hasAttribute('inert'))
109
+ .forEach((el) => el.removeAttribute('inert'))
110
+ }
111
+
112
+ const openWithOverlay = (ignoredElements = []) => {
113
+ showOverlay()
114
+ lockScroll()
115
+ addInert(ignoredElements)
116
+ }
117
+
118
+ const closeWithOverlay = () => {
119
+ hideOverlay()
120
+ unlockScroll()
121
+ removeInert()
122
+ }
123
+
124
+ const hideOnEsc = {
125
+ name: 'hideOnEsc',
126
+ defaultValue: true,
127
+ fn({ hide }) {
128
+ function onKeyDown(event) {
129
+ if (event.keyCode === 27) {
130
+ hide()
131
+ }
132
+ }
133
+
134
+ return {
135
+ onShow() {
136
+ document.addEventListener('keydown', onKeyDown)
137
+ },
138
+ onHide() {
139
+ document.removeEventListener('keydown', onKeyDown)
140
+ },
141
+ }
142
+ },
143
+ }
144
+
145
+ const initFloatingUi = (referenceElement, floatingElement, side) => {
146
+ return autoUpdate(referenceElement, floatingElement, () => {
147
+ computePosition(referenceElement, floatingElement, {
148
+ placement: side,
149
+ strategy: 'fixed',
150
+ middleware: [flip(), shift(), offset(4)],
151
+ }).then(({ x, y }) => {
152
+ Object.assign(floatingElement.style, {
153
+ left: `${x}px`,
154
+ top: `${y}px`,
155
+ })
156
+ })
157
+ })
158
+ }
159
+
160
+ const focusTrigger = (triggerTarget) => {
161
+ setTimeout(() => {
162
+ if (triggerTarget.dataset.asChild === 'false') {
163
+ triggerTarget.firstElementChild.focus()
164
+ } else {
165
+ triggerTarget.focus()
166
+ }
167
+ }, FOCUS_DELAY)
168
+ }
169
+
170
+ export {
171
+ ANIMATION_OUT_DELAY,
172
+ FOCUS_DELAY,
173
+ hideOnEsc,
174
+ showOverlay,
175
+ hideOverlay,
176
+ lockScroll,
177
+ unlockScroll,
178
+ addInert,
179
+ removeInert,
180
+ openWithOverlay,
181
+ closeWithOverlay,
182
+ initFloatingUi,
183
+ focusTrigger,
184
+ }