shadcn_phlexcomponents 0.1.9 → 0.1.11

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.ts +133 -0
  3. data/app/javascript/controllers/{avatar_controller.js → avatar_controller.ts} +4 -0
  4. data/app/javascript/controllers/checkbox_controller.ts +34 -0
  5. data/app/javascript/controllers/collapsible_controller.ts +45 -0
  6. data/app/javascript/controllers/combobox_controller.ts +145 -0
  7. data/app/javascript/controllers/command_controller.ts +129 -0
  8. data/app/javascript/controllers/command_root_controller.ts +355 -0
  9. data/app/javascript/controllers/date_picker_controller.ts +274 -0
  10. data/app/javascript/controllers/date_range_picker_controller.ts +243 -0
  11. data/app/javascript/controllers/dialog_controller.ts +113 -0
  12. data/app/javascript/controllers/dropdown_menu_controller.ts +133 -0
  13. data/app/javascript/controllers/dropdown_menu_root_controller.ts +234 -0
  14. data/app/javascript/controllers/dropdown_menu_sub_controller.ts +150 -0
  15. data/app/javascript/controllers/{form_field_controller.js → form_field_controller.ts} +4 -6
  16. data/app/javascript/controllers/hover_card_controller.ts +93 -0
  17. data/app/javascript/controllers/{loading_button_controller.js → loading_button_controller.ts} +2 -2
  18. data/app/javascript/controllers/popover_controller.ts +141 -0
  19. data/app/javascript/controllers/progress_controller.ts +17 -0
  20. data/app/javascript/controllers/radio_group_controller.ts +106 -0
  21. data/app/javascript/controllers/select_controller.ts +200 -0
  22. data/app/javascript/controllers/{sidebar_controller.js → sidebar_controller.ts} +6 -2
  23. data/app/javascript/controllers/sidebar_trigger_controller.ts +21 -0
  24. data/app/javascript/controllers/slider_controller.ts +107 -0
  25. data/app/javascript/controllers/switch_controller.ts +30 -0
  26. data/app/javascript/controllers/tabs_controller.ts +79 -0
  27. data/app/javascript/controllers/{theme_switcher_controller.js → theme_switcher_controller.ts} +12 -9
  28. data/app/javascript/controllers/toast_container_controller.ts +62 -0
  29. data/app/javascript/controllers/toast_controller.ts +28 -0
  30. data/app/javascript/controllers/tooltip_controller.ts +98 -0
  31. data/app/javascript/{shadcn_phlexcomponents.js → shadcn_phlexcomponents.ts} +6 -2
  32. data/app/javascript/utils.ts +437 -0
  33. data/app/stylesheets/date_picker.css +16 -154
  34. data/app/stylesheets/nouislider.css +173 -0
  35. data/app/stylesheets/tw-animate.css +486 -0
  36. data/lib/install/install_shadcn_phlexcomponents.rb +16 -3
  37. data/lib/shadcn_phlexcomponents/alias.rb +3 -1
  38. data/lib/shadcn_phlexcomponents/components/accordion.rb +129 -0
  39. data/lib/shadcn_phlexcomponents/components/alert.rb +59 -0
  40. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +276 -0
  41. data/lib/shadcn_phlexcomponents/components/{aspect_ratio/aspect_ratio.rb → aspect_ratio.rb} +2 -2
  42. data/lib/shadcn_phlexcomponents/components/avatar.rb +63 -0
  43. data/lib/shadcn_phlexcomponents/components/badge.rb +35 -0
  44. data/lib/shadcn_phlexcomponents/components/base.rb +34 -7
  45. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +150 -0
  46. data/lib/shadcn_phlexcomponents/components/button.rb +49 -0
  47. data/lib/shadcn_phlexcomponents/components/card.rb +88 -0
  48. data/lib/shadcn_phlexcomponents/components/{checkbox/checkbox.rb → checkbox.rb} +18 -14
  49. data/lib/shadcn_phlexcomponents/components/{checkbox_group/checkbox_group.rb → checkbox_group.rb} +7 -8
  50. data/lib/shadcn_phlexcomponents/components/collapsible.rb +91 -0
  51. data/lib/shadcn_phlexcomponents/components/combobox.rb +398 -0
  52. data/lib/shadcn_phlexcomponents/components/command.rb +351 -0
  53. data/lib/shadcn_phlexcomponents/components/date_picker.rb +264 -0
  54. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +126 -0
  55. data/lib/shadcn_phlexcomponents/components/dialog.rb +234 -0
  56. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +282 -0
  57. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +135 -0
  58. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +6 -7
  59. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +2 -2
  60. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +3 -4
  61. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +27 -41
  62. data/lib/shadcn_phlexcomponents/components/form/form_error.rb +1 -1
  63. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +43 -8
  64. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +1 -1
  65. data/lib/shadcn_phlexcomponents/components/form/form_input.rb +3 -4
  66. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +4 -5
  67. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +3 -4
  68. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +91 -0
  69. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +7 -6
  70. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +3 -4
  71. data/lib/shadcn_phlexcomponents/components/{form/form.rb → form.rb} +22 -4
  72. data/lib/shadcn_phlexcomponents/components/hover_card.rb +110 -0
  73. data/lib/shadcn_phlexcomponents/components/input.rb +31 -0
  74. data/lib/shadcn_phlexcomponents/components/label.rb +16 -0
  75. data/lib/shadcn_phlexcomponents/components/{link/link.rb → link.rb} +10 -3
  76. data/lib/shadcn_phlexcomponents/components/{loading_button/loading_button.rb → loading_button.rb} +9 -2
  77. data/lib/shadcn_phlexcomponents/components/pagination.rb +166 -0
  78. data/lib/shadcn_phlexcomponents/components/popover.rb +116 -0
  79. data/lib/shadcn_phlexcomponents/components/{progress/progress.rb → progress.rb} +4 -4
  80. data/lib/shadcn_phlexcomponents/components/radio_group.rb +155 -0
  81. data/lib/shadcn_phlexcomponents/components/select.rb +421 -0
  82. data/lib/shadcn_phlexcomponents/components/{separator/separator.rb → separator.rb} +9 -8
  83. data/lib/shadcn_phlexcomponents/components/sheet.rb +239 -0
  84. data/lib/shadcn_phlexcomponents/components/{skeleton/skeleton.rb → skeleton.rb} +1 -1
  85. data/lib/shadcn_phlexcomponents/components/slider.rb +72 -0
  86. data/lib/shadcn_phlexcomponents/components/switch.rb +75 -0
  87. data/lib/shadcn_phlexcomponents/components/table.rb +140 -0
  88. data/lib/shadcn_phlexcomponents/components/tabs.rb +135 -0
  89. data/lib/shadcn_phlexcomponents/components/textarea.rb +24 -0
  90. data/lib/shadcn_phlexcomponents/components/toast.rb +153 -0
  91. data/lib/shadcn_phlexcomponents/components/{toast/toast_container.rb → toast_container.rb} +23 -4
  92. data/lib/shadcn_phlexcomponents/components/tooltip.rb +131 -0
  93. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +25 -0
  94. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  95. data/lib/tasks/install.rake +1 -1
  96. metadata +78 -167
  97. data/app/javascript/controllers/accordion_controller.js +0 -124
  98. data/app/javascript/controllers/alert_dialog_controller.js +0 -21
  99. data/app/javascript/controllers/checkbox_controller.js +0 -28
  100. data/app/javascript/controllers/collapsible_controller.js +0 -35
  101. data/app/javascript/controllers/combobox_controller.js +0 -54
  102. data/app/javascript/controllers/date_picker_controller.js +0 -253
  103. data/app/javascript/controllers/date_range_picker_controller.js +0 -344
  104. data/app/javascript/controllers/dialog_controller.js +0 -114
  105. data/app/javascript/controllers/dropdown_menu_controller.js +0 -171
  106. data/app/javascript/controllers/hover_card_controller.js +0 -21
  107. data/app/javascript/controllers/popover_controller.js +0 -113
  108. data/app/javascript/controllers/progress_controller.js +0 -14
  109. data/app/javascript/controllers/radio_group_controller.js +0 -90
  110. data/app/javascript/controllers/select_controller.js +0 -274
  111. data/app/javascript/controllers/sidebar_trigger_controller.js +0 -15
  112. data/app/javascript/controllers/switch_controller.js +0 -24
  113. data/app/javascript/controllers/tabs_controller.js +0 -73
  114. data/app/javascript/controllers/toast_container_controller.js +0 -22
  115. data/app/javascript/controllers/toast_controller.js +0 -45
  116. data/app/javascript/controllers/tooltip_controller.js +0 -40
  117. data/app/javascript/utils.js +0 -184
  118. data/app/stylesheets/choices.css +0 -324
  119. data/app/stylesheets/tailwindcss-animate.css +0 -318
  120. data/lib/shadcn_phlexcomponents/components/accordion/accordion.rb +0 -38
  121. data/lib/shadcn_phlexcomponents/components/accordion/accordion_content.rb +0 -30
  122. data/lib/shadcn_phlexcomponents/components/accordion/accordion_item.rb +0 -26
  123. data/lib/shadcn_phlexcomponents/components/accordion/accordion_trigger.rb +0 -46
  124. data/lib/shadcn_phlexcomponents/components/alert/alert.rb +0 -40
  125. data/lib/shadcn_phlexcomponents/components/alert/alert_description.rb +0 -11
  126. data/lib/shadcn_phlexcomponents/components/alert/alert_title.rb +0 -11
  127. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog.rb +0 -60
  128. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_action.rb +0 -22
  129. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_action_to.rb +0 -40
  130. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_cancel.rb +0 -22
  131. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_content.rb +0 -40
  132. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_description.rb +0 -22
  133. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_footer.rb +0 -11
  134. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_header.rb +0 -11
  135. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_title.rb +0 -22
  136. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_trigger.rb +0 -50
  137. data/lib/shadcn_phlexcomponents/components/avatar/avatar.rb +0 -31
  138. data/lib/shadcn_phlexcomponents/components/avatar/avatar_fallback.rb +0 -21
  139. data/lib/shadcn_phlexcomponents/components/avatar/avatar_image.rb +0 -19
  140. data/lib/shadcn_phlexcomponents/components/badge/badge.rb +0 -30
  141. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb.rb +0 -53
  142. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_ellipsis.rb +0 -23
  143. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_item.rb +0 -11
  144. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_link.rb +0 -7
  145. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_page.rb +0 -21
  146. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_separator.rb +0 -26
  147. data/lib/shadcn_phlexcomponents/components/button/button.rb +0 -53
  148. data/lib/shadcn_phlexcomponents/components/card/card.rb +0 -31
  149. data/lib/shadcn_phlexcomponents/components/card/card_content.rb +0 -11
  150. data/lib/shadcn_phlexcomponents/components/card/card_description.rb +0 -11
  151. data/lib/shadcn_phlexcomponents/components/card/card_footer.rb +0 -11
  152. data/lib/shadcn_phlexcomponents/components/card/card_header.rb +0 -11
  153. data/lib/shadcn_phlexcomponents/components/card/card_title.rb +0 -11
  154. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible.rb +0 -31
  155. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible_content.rb +0 -24
  156. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible_trigger.rb +0 -50
  157. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker.rb +0 -87
  158. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_content.rb +0 -45
  159. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_trigger.rb +0 -64
  160. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker.rb +0 -105
  161. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_content.rb +0 -9
  162. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_trigger.rb +0 -9
  163. data/lib/shadcn_phlexcomponents/components/dialog/dialog.rb +0 -52
  164. data/lib/shadcn_phlexcomponents/components/dialog/dialog_close.rb +0 -42
  165. data/lib/shadcn_phlexcomponents/components/dialog/dialog_content.rb +0 -54
  166. data/lib/shadcn_phlexcomponents/components/dialog/dialog_description.rb +0 -22
  167. data/lib/shadcn_phlexcomponents/components/dialog/dialog_footer.rb +0 -11
  168. data/lib/shadcn_phlexcomponents/components/dialog/dialog_header.rb +0 -11
  169. data/lib/shadcn_phlexcomponents/components/dialog/dialog_title.rb +0 -22
  170. data/lib/shadcn_phlexcomponents/components/dialog/dialog_trigger.rb +0 -50
  171. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu.rb +0 -50
  172. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_content.rb +0 -52
  173. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_item.rb +0 -56
  174. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_item_to.rb +0 -28
  175. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_label.rb +0 -11
  176. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_separator.rb +0 -20
  177. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_trigger.rb +0 -57
  178. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card.rb +0 -33
  179. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card_content.rb +0 -32
  180. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card_trigger.rb +0 -44
  181. data/lib/shadcn_phlexcomponents/components/input/input.rb +0 -32
  182. data/lib/shadcn_phlexcomponents/components/label/label.rb +0 -14
  183. data/lib/shadcn_phlexcomponents/components/pagination/pagination.rb +0 -38
  184. data/lib/shadcn_phlexcomponents/components/pagination/pagination_ellipsis.rb +0 -24
  185. data/lib/shadcn_phlexcomponents/components/pagination/pagination_link.rb +0 -34
  186. data/lib/shadcn_phlexcomponents/components/pagination/pagination_next.rb +0 -32
  187. data/lib/shadcn_phlexcomponents/components/pagination/pagination_previous.rb +0 -32
  188. data/lib/shadcn_phlexcomponents/components/popover/popover.rb +0 -34
  189. data/lib/shadcn_phlexcomponents/components/popover/popover_content.rb +0 -40
  190. data/lib/shadcn_phlexcomponents/components/popover/popover_trigger.rb +0 -50
  191. data/lib/shadcn_phlexcomponents/components/radio_group/radio_group.rb +0 -88
  192. data/lib/shadcn_phlexcomponents/components/radio_group/radio_group_item.rb +0 -66
  193. data/lib/shadcn_phlexcomponents/components/select/select.rb +0 -194
  194. data/lib/shadcn_phlexcomponents/components/select/select_content.rb +0 -64
  195. data/lib/shadcn_phlexcomponents/components/select/select_group.rb +0 -23
  196. data/lib/shadcn_phlexcomponents/components/select/select_item.rb +0 -59
  197. data/lib/shadcn_phlexcomponents/components/select/select_label.rb +0 -24
  198. data/lib/shadcn_phlexcomponents/components/select/select_trigger.rb +0 -56
  199. data/lib/shadcn_phlexcomponents/components/sheet/sheet.rb +0 -53
  200. data/lib/shadcn_phlexcomponents/components/sheet/sheet_close.rb +0 -42
  201. data/lib/shadcn_phlexcomponents/components/sheet/sheet_content.rb +0 -65
  202. data/lib/shadcn_phlexcomponents/components/sheet/sheet_description.rb +0 -22
  203. data/lib/shadcn_phlexcomponents/components/sheet/sheet_footer.rb +0 -11
  204. data/lib/shadcn_phlexcomponents/components/sheet/sheet_header.rb +0 -11
  205. data/lib/shadcn_phlexcomponents/components/sheet/sheet_title.rb +0 -22
  206. data/lib/shadcn_phlexcomponents/components/sheet/sheet_trigger.rb +0 -50
  207. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar.rb +0 -108
  208. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_container.rb +0 -11
  209. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_content.rb +0 -11
  210. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_footer.rb +0 -11
  211. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group.rb +0 -11
  212. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group_content.rb +0 -11
  213. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group_label.rb +0 -16
  214. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_header.rb +0 -11
  215. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_inset.rb +0 -15
  216. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu.rb +0 -11
  217. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_button.rb +0 -61
  218. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_item.rb +0 -9
  219. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub.rb +0 -14
  220. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub_button.rb +0 -48
  221. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub_item.rb +0 -9
  222. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_trigger.rb +0 -40
  223. data/lib/shadcn_phlexcomponents/components/switch/switch.rb +0 -66
  224. data/lib/shadcn_phlexcomponents/components/table/table.rb +0 -75
  225. data/lib/shadcn_phlexcomponents/components/table/table_body.rb +0 -11
  226. data/lib/shadcn_phlexcomponents/components/table/table_caption.rb +0 -11
  227. data/lib/shadcn_phlexcomponents/components/table/table_cell.rb +0 -11
  228. data/lib/shadcn_phlexcomponents/components/table/table_footer.rb +0 -11
  229. data/lib/shadcn_phlexcomponents/components/table/table_head.rb +0 -14
  230. data/lib/shadcn_phlexcomponents/components/table/table_header.rb +0 -11
  231. data/lib/shadcn_phlexcomponents/components/table/table_row.rb +0 -11
  232. data/lib/shadcn_phlexcomponents/components/tabs/tabs.rb +0 -38
  233. data/lib/shadcn_phlexcomponents/components/tabs/tabs_content.rb +0 -35
  234. data/lib/shadcn_phlexcomponents/components/tabs/tabs_list.rb +0 -23
  235. data/lib/shadcn_phlexcomponents/components/tabs/tabs_trigger.rb +0 -45
  236. data/lib/shadcn_phlexcomponents/components/textarea/textarea.rb +0 -29
  237. data/lib/shadcn_phlexcomponents/components/toast/toast.rb +0 -101
  238. data/lib/shadcn_phlexcomponents/components/toast/toast_action.rb +0 -39
  239. data/lib/shadcn_phlexcomponents/components/toast/toast_action_to.rb +0 -28
  240. data/lib/shadcn_phlexcomponents/components/toast/toast_content.rb +0 -11
  241. data/lib/shadcn_phlexcomponents/components/toast/toast_description.rb +0 -11
  242. data/lib/shadcn_phlexcomponents/components/toast/toast_title.rb +0 -11
  243. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip.rb +0 -34
  244. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip_content.rb +0 -39
  245. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip_trigger.rb +0 -48
  246. /data/lib/shadcn_phlexcomponents/components/{theme_switcher/theme_switcher.rb → theme_switcher.rb} +0 -0
@@ -0,0 +1,150 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import {
3
+ initFloatingUi,
4
+ ON_OPEN_FOCUS_DELAY,
5
+ getSameLevelItems,
6
+ showContent,
7
+ hideContent,
8
+ } from '../utils'
9
+
10
+ export default class extends Controller<HTMLElement> {
11
+ static targets = ['trigger', 'contentContainer', 'content']
12
+
13
+ static values = {
14
+ isOpen: Boolean,
15
+ }
16
+
17
+ declare isOpenValue: boolean
18
+ declare readonly triggerTarget: HTMLElement
19
+ declare readonly contentContainerTarget: HTMLElement
20
+ declare readonly contentTarget: HTMLElement
21
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
22
+ declare cleanup: () => void
23
+ declare closeTimeout: number
24
+ declare items: HTMLElement[]
25
+
26
+ connect() {
27
+ this.items = getSameLevelItems({
28
+ content: this.contentTarget,
29
+ items: Array.from(
30
+ this.contentTarget.querySelectorAll(
31
+ '[data-dropdown-menu-target="item"], [data-dropdown-menu-sub-target="trigger"]',
32
+ ),
33
+ ),
34
+ closestContentSelector: this.closestContentSelector(),
35
+ })
36
+ }
37
+
38
+ closestContentSelector() {
39
+ return '[data-dropdown-menu-sub-target="content"]'
40
+ }
41
+
42
+ open(event: MouseEvent | KeyboardEvent | null = null) {
43
+ clearTimeout(this.closeTimeout)
44
+ this.isOpenValue = true
45
+
46
+ setTimeout(() => {
47
+ if (event instanceof KeyboardEvent) {
48
+ const key = event.key
49
+
50
+ if (['ArrowRight', 'Enter', ' '].includes(key)) {
51
+ this.focusItemByIndex(null, 0)
52
+ }
53
+ }
54
+ }, ON_OPEN_FOCUS_DELAY)
55
+ }
56
+
57
+ close() {
58
+ this.closeTimeout = window.setTimeout(() => {
59
+ this.isOpenValue = false
60
+ }, 250)
61
+ }
62
+
63
+ closeOnLeftKeydown(event: KeyboardEvent) {
64
+ this.closeImmediately()
65
+ this.triggerTarget.focus()
66
+ }
67
+
68
+ focusItemByIndex(event: KeyboardEvent | null, index: number | null) {
69
+ if (event) {
70
+ const key = event.key
71
+
72
+ if (key === 'ArrowUp') {
73
+ this.items[this.items.length - 1].focus()
74
+ } else {
75
+ this.items[0].focus()
76
+ }
77
+ } else if (index !== null) {
78
+ this.items[index].focus()
79
+ }
80
+ }
81
+
82
+ closeParentSubMenu() {
83
+ const parentContent = this.triggerTarget.closest(
84
+ '[data-dropdown-menu-sub-target="content"]',
85
+ )
86
+
87
+ if (parentContent) {
88
+ const subMenu = parentContent.closest(
89
+ '[data-shadcn-phlexcomponents="dropdown-menu-sub"]',
90
+ )
91
+
92
+ if (subMenu) {
93
+ const subMenuController =
94
+ window.Stimulus.getControllerForElementAndIdentifier(
95
+ subMenu,
96
+ 'dropdown-menu-sub',
97
+ )
98
+
99
+ if (subMenuController) {
100
+ // @ts-ignore
101
+ subMenuController.closeImmediately()
102
+ setTimeout(() => {
103
+ // @ts-ignore
104
+ // weird bug where focus goes to body element after closing, and setting focus
105
+ // manually doesn't work
106
+ subMenuController.triggerTarget.focus()
107
+ }, 100)
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ closeImmediately() {
114
+ this.isOpenValue = false
115
+ }
116
+
117
+ cleanupEventListeners() {
118
+ if (this.cleanup) this.cleanup()
119
+ }
120
+
121
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
122
+ if (isOpen) {
123
+ showContent({
124
+ trigger: this.triggerTarget,
125
+ content: this.contentTarget,
126
+ contentContainer: this.contentContainerTarget,
127
+ })
128
+
129
+ this.cleanup = initFloatingUi({
130
+ referenceElement: this.triggerTarget,
131
+ floatingElement: this.contentContainerTarget,
132
+ side: this.contentTarget.dataset.side,
133
+ align: this.contentTarget.dataset.align,
134
+ sideOffset: -2,
135
+ })
136
+ } else {
137
+ this.closeTimeout = window.setTimeout(() => {
138
+ hideContent({
139
+ trigger: this.triggerTarget,
140
+ content: this.contentTarget,
141
+ contentContainer: this.contentContainerTarget,
142
+ })
143
+ })
144
+ }
145
+ }
146
+
147
+ disconnect() {
148
+ this.cleanupEventListeners()
149
+ }
150
+ }
@@ -1,22 +1,20 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ['trigger', 'content', 'item']
5
-
6
4
  connect() {
7
5
  const hintContainer = this.element.querySelector('[data-remove-label]')
8
6
  const labelContainer = this.element.querySelector('[data-remove-hint]')
9
7
 
10
8
  if (hintContainer) {
11
- const label = hintContainer.querySelector('label')
12
- const hint = hintContainer.querySelector('p')
9
+ const label = hintContainer.querySelector('label') as HTMLElement
10
+ const hint = hintContainer.querySelector('p') as HTMLElement
13
11
  label.remove()
14
12
  hintContainer.replaceWith(hint)
15
13
  }
16
14
 
17
15
  if (labelContainer) {
18
- const hint = labelContainer.querySelector('p')
19
- const label = labelContainer.querySelector('label')
16
+ const hint = labelContainer.querySelector('p') as HTMLElement
17
+ const label = labelContainer.querySelector('label') as HTMLElement
20
18
  hint.remove()
21
19
  labelContainer.replaceWith(label)
22
20
  }
@@ -0,0 +1,93 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { useHover } from 'stimulus-use'
3
+ import { initFloatingUi, showContent, hideContent } from '../utils'
4
+
5
+ export default class extends Controller<HTMLElement> {
6
+ static targets = ['trigger', 'content', 'contentContainer']
7
+ static values = {
8
+ isOpen: Boolean,
9
+ }
10
+
11
+ declare isOpenValue: boolean
12
+ declare readonly triggerTarget: HTMLElement
13
+ declare readonly contentTarget: HTMLElement
14
+ declare readonly contentContainerTarget: HTMLElement
15
+ declare cleanup: () => void
16
+ declare closeTimeout: number
17
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
18
+
19
+ connect() {
20
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
21
+ useHover(this, { element: this.triggerTarget, dispatchEvent: false })
22
+ }
23
+
24
+ open() {
25
+ window.clearTimeout(this.closeTimeout)
26
+ this.isOpenValue = true
27
+ }
28
+
29
+ close() {
30
+ this.closeTimeout = window.setTimeout(() => {
31
+ this.isOpenValue = false
32
+ }, 250)
33
+ }
34
+
35
+ // for useHover
36
+ mouseEnter() {
37
+ this.open()
38
+ }
39
+
40
+ // for useHover
41
+ mouseLeave() {
42
+ this.close()
43
+ }
44
+
45
+ setupEventListeners() {
46
+ document.addEventListener('keydown', this.DOMKeydownListener)
47
+ }
48
+
49
+ cleanupEventListeners() {
50
+ if (this.cleanup) this.cleanup()
51
+ document.removeEventListener('keydown', this.DOMKeydownListener)
52
+ }
53
+
54
+ onDOMKeydown(event: KeyboardEvent) {
55
+ if (!this.isOpenValue) return
56
+
57
+ const key = event.key
58
+
59
+ if (key === 'Escape') {
60
+ this.close()
61
+ }
62
+ }
63
+
64
+ isOpenValueChanged(isOpen: boolean) {
65
+ if (isOpen) {
66
+ showContent({
67
+ content: this.contentTarget,
68
+ contentContainer: this.contentContainerTarget,
69
+ })
70
+
71
+ this.setupEventListeners()
72
+
73
+ this.cleanup = initFloatingUi({
74
+ referenceElement: this.triggerTarget,
75
+ floatingElement: this.contentContainerTarget,
76
+ side: this.contentTarget.dataset.side,
77
+ align: this.contentTarget.dataset.align,
78
+ sideOffset: 4,
79
+ })
80
+ } else {
81
+ hideContent({
82
+ content: this.contentTarget,
83
+ contentContainer: this.contentContainerTarget,
84
+ })
85
+
86
+ this.cleanupEventListeners()
87
+ }
88
+ }
89
+
90
+ disconnect() {
91
+ this.cleanupEventListeners()
92
+ }
93
+ }
@@ -1,13 +1,13 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller {
3
+ export default class extends Controller<HTMLButtonElement> {
4
4
  connect() {
5
5
  const el = this.element
6
6
  const form = el.closest('form')
7
7
 
8
8
  if (form && form.dataset.turbo === 'false') {
9
9
  form.addEventListener('submit', () => {
10
- form.ariaBusy = true
10
+ form.ariaBusy = 'true'
11
11
  el.disabled = true
12
12
  })
13
13
  }
@@ -0,0 +1,141 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { useClickOutside } from 'stimulus-use'
3
+ import {
4
+ initFloatingUi,
5
+ focusTrigger,
6
+ ON_OPEN_FOCUS_DELAY,
7
+ getFocusableElements,
8
+ showContent,
9
+ hideContent,
10
+ } from '../utils'
11
+
12
+ export default class extends Controller<HTMLElement> {
13
+ static targets = ['trigger', 'contentContainer', 'content']
14
+ static values = { isOpen: Boolean }
15
+
16
+ declare isOpenValue: boolean
17
+ declare readonly triggerTarget: HTMLElement
18
+ declare readonly contentContainerTarget: HTMLElement
19
+ declare readonly contentTarget: HTMLElement
20
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
21
+ declare cleanup: () => void
22
+
23
+ connect() {
24
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
25
+ useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
26
+ }
27
+
28
+ toggle() {
29
+ if (this.isOpenValue) {
30
+ this.close()
31
+ } else {
32
+ this.open()
33
+ }
34
+ }
35
+
36
+ open() {
37
+ this.isOpenValue = true
38
+ this.onOpen()
39
+
40
+ setTimeout(() => {
41
+ this.onOpenFocusedElement().focus()
42
+ }, ON_OPEN_FOCUS_DELAY)
43
+ }
44
+
45
+ close() {
46
+ this.isOpenValue = false
47
+ this.onClose()
48
+ }
49
+
50
+ onDOMKeydown(event: KeyboardEvent) {
51
+ if (!this.isOpenValue) return
52
+
53
+ const key = event.key
54
+
55
+ if (key === 'Escape') {
56
+ this.close()
57
+ } else if (key === 'Tab') {
58
+ const focusableElements = getFocusableElements(this.contentTarget)
59
+
60
+ const firstElement = focusableElements[0]
61
+ const lastElement = focusableElements[focusableElements.length - 1]
62
+
63
+ // If Shift + Tab pressed on first element, go to last element
64
+ if (event.shiftKey && document.activeElement === firstElement) {
65
+ event.preventDefault()
66
+ lastElement.focus()
67
+ }
68
+ // If Tab pressed on last element, go to first element
69
+ else if (!event.shiftKey && document.activeElement === lastElement) {
70
+ event.preventDefault()
71
+ firstElement.focus()
72
+ }
73
+ }
74
+ }
75
+
76
+ clickOutside(event: MouseEvent) {
77
+ const target = event.target
78
+ // Let #toggle to handle state when clicked on trigger
79
+ if (target === this.triggerTarget) return
80
+
81
+ this.close()
82
+ }
83
+
84
+ onOpen() {}
85
+
86
+ onOpenFocusedElement() {
87
+ const focusableElements = getFocusableElements(this.contentTarget)
88
+ return focusableElements[0]
89
+ }
90
+
91
+ onClose() {}
92
+
93
+ referenceElement() {
94
+ return this.triggerTarget
95
+ }
96
+
97
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
98
+ if (isOpen === true) {
99
+ showContent({
100
+ trigger: this.triggerTarget,
101
+ content: this.contentTarget,
102
+ contentContainer: this.contentContainerTarget,
103
+ })
104
+
105
+ this.cleanup = initFloatingUi({
106
+ referenceElement: this.referenceElement(),
107
+ floatingElement: this.contentContainerTarget,
108
+ side: this.contentTarget.dataset.side,
109
+ align: this.contentTarget.dataset.align,
110
+ sideOffset: 4,
111
+ })
112
+ this.setupEventListeners()
113
+ } else {
114
+ hideContent({
115
+ trigger: this.triggerTarget,
116
+ content: this.contentTarget,
117
+ contentContainer: this.contentContainerTarget,
118
+ })
119
+
120
+ this.cleanupEventListeners()
121
+
122
+ // Only focus trigger when is previously opened
123
+ if (previousIsOpen) {
124
+ focusTrigger(this.triggerTarget)
125
+ }
126
+ }
127
+ }
128
+
129
+ setupEventListeners() {
130
+ document.addEventListener('keydown', this.DOMKeydownListener)
131
+ }
132
+
133
+ cleanupEventListeners() {
134
+ if (this.cleanup) this.cleanup()
135
+ document.removeEventListener('keydown', this.DOMKeydownListener)
136
+ }
137
+
138
+ disconnect() {
139
+ this.cleanupEventListeners()
140
+ }
141
+ }
@@ -0,0 +1,17 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['indicator']
5
+
6
+ static values = {
7
+ percent: Number,
8
+ }
9
+
10
+ declare readonly indicatorTarget: HTMLElement
11
+ declare percentValue: number
12
+
13
+ percentValueChanged(value: number) {
14
+ this.element.setAttribute('aria-valuenow', `${value}`)
15
+ this.indicatorTarget.style.transform = `translateX(-${100 - value}%)`
16
+ }
17
+ }
@@ -0,0 +1,106 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller<HTMLElement> {
4
+ static targets = ['item', 'input', 'indicator']
5
+ static values = {
6
+ selected: String,
7
+ }
8
+
9
+ declare readonly itemTargets: HTMLInputElement[]
10
+ declare readonly inputTargets: HTMLInputElement[]
11
+ declare readonly indicatorTargets: HTMLInputElement[]
12
+ declare selectedValue: string
13
+
14
+ connect() {
15
+ if (!this.selectedValue) {
16
+ this.itemTargets[0].tabIndex = 0
17
+ }
18
+ }
19
+
20
+ select(event: MouseEvent) {
21
+ const item = event.currentTarget as HTMLInputElement
22
+ this.selectedValue = item.dataset.value as string
23
+ }
24
+
25
+ preventDefault(event: KeyboardEvent) {
26
+ event.preventDefault()
27
+ }
28
+
29
+ selectItem(event: KeyboardEvent) {
30
+ const focusableItems = this.itemTargets.filter(
31
+ (t) => !t.disabled,
32
+ ) as HTMLInputElement[]
33
+
34
+ const item = event.currentTarget as HTMLInputElement
35
+ const index = focusableItems.indexOf(item)
36
+ const key = event.key
37
+ let newIndex = 0
38
+
39
+ if (['ArrowUp', 'ArrowLeft'].includes(key)) {
40
+ newIndex = index - 1
41
+
42
+ if (newIndex < 0) {
43
+ newIndex = focusableItems.length - 1
44
+ }
45
+ } else {
46
+ newIndex = index + 1
47
+
48
+ if (newIndex > focusableItems.length - 1) {
49
+ newIndex = 0
50
+ }
51
+ }
52
+
53
+ this.selectedValue = focusableItems[newIndex].dataset.value as string
54
+ }
55
+
56
+ focusItem() {
57
+ const item = this.itemTargets.find(
58
+ (i) => i.dataset.value === this.selectedValue,
59
+ )
60
+
61
+ if (!item) return
62
+
63
+ // Focus first item that is not disabled and allow it to be focused
64
+ if (item.disabled) {
65
+ item.tabIndex = -1
66
+
67
+ const focusableItems = this.itemTargets.filter(
68
+ (t) => !t.disabled,
69
+ ) as HTMLInputElement[]
70
+
71
+ if (focusableItems.length > 0) {
72
+ focusableItems[0].focus()
73
+ focusableItems[0].tabIndex = 0
74
+ }
75
+ } else {
76
+ item.focus()
77
+ }
78
+ }
79
+
80
+ selectedValueChanged(value: string) {
81
+ this.itemTargets.forEach((item) => {
82
+ const input = item.querySelector(
83
+ '[data-radio-group-target="input"]',
84
+ ) as HTMLInputElement
85
+ const indicator = item.querySelector(
86
+ '[data-radio-group-target="indicator"]',
87
+ ) as HTMLInputElement
88
+
89
+ if (value === item.dataset.value) {
90
+ input.checked = true
91
+ item.tabIndex = 0
92
+ item.ariaChecked = 'true'
93
+ item.dataset.state = 'checked'
94
+ indicator.classList.remove('hidden')
95
+ } else {
96
+ input.checked = false
97
+ item.tabIndex = -1
98
+ item.ariaChecked = 'false'
99
+ item.dataset.state = 'unchecked'
100
+ indicator.classList.add('hidden')
101
+ }
102
+ })
103
+
104
+ this.focusItem()
105
+ }
106
+ }