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,200 @@
1
+ import DropdownMenuRootController from './dropdown_menu_root_controller'
2
+
3
+ export default class extends DropdownMenuRootController {
4
+ static targets = [
5
+ 'trigger',
6
+ 'contentContainer',
7
+ 'content',
8
+ 'item',
9
+ 'triggerText',
10
+ 'group',
11
+ 'label',
12
+ 'select',
13
+ ]
14
+
15
+ static values = {
16
+ isOpen: Boolean,
17
+ selected: String,
18
+ setEqualWidth: { type: Boolean, default: true },
19
+ closestContentSelector: {
20
+ type: String,
21
+ default: '[data-select-target="content"]',
22
+ },
23
+ }
24
+
25
+ declare selectedValue: string
26
+ declare searchString: string
27
+ declare searchTimeout: number
28
+ declare groupTargets: HTMLElement[]
29
+ declare triggerTextTarget: HTMLElement
30
+ declare selectTarget: HTMLSelectElement
31
+ declare itemsInnerText: string[]
32
+
33
+ connect() {
34
+ super.connect()
35
+ this.itemsInnerText = this.items.map((i) => i.innerText.trim())
36
+ this.setAriaLabelledby()
37
+ this.searchString = ''
38
+ }
39
+
40
+ setAriaLabelledby() {
41
+ this.groupTargets.forEach((g) => {
42
+ const label = g.querySelector(
43
+ '[data-select-target="label"]',
44
+ ) as HTMLElement
45
+
46
+ if (label) {
47
+ label.id = g.getAttribute('aria-labelledby') as string
48
+ }
49
+ })
50
+ }
51
+
52
+ onOpenFocusedElement(event: MouseEvent | KeyboardEvent) {
53
+ let itemIndex = null as number | null
54
+
55
+ if (this.selectedValue) {
56
+ const item = this.itemTargets.find(
57
+ (i) => i.dataset.value === this.selectedValue,
58
+ )
59
+
60
+ if (item && !item.dataset.disabled) {
61
+ itemIndex = this.items.indexOf(item)
62
+ }
63
+ } else {
64
+ if (event instanceof KeyboardEvent) {
65
+ const key = event.key
66
+
67
+ if (['ArrowDown', 'Enter', ' '].includes(key)) {
68
+ itemIndex = 0
69
+ }
70
+ }
71
+ }
72
+
73
+ if (itemIndex !== null) {
74
+ return this.items[itemIndex]
75
+ } else {
76
+ return this.contentTarget
77
+ }
78
+ }
79
+
80
+ onSelect(event: MouseEvent | KeyboardEvent) {
81
+ const item = event.currentTarget as HTMLElement
82
+ const value = item.dataset.value as string
83
+ this.selectedValue = value
84
+ }
85
+
86
+ onDOMKeydown(event: KeyboardEvent) {
87
+ super.onDOMKeydown(event)
88
+
89
+ const { key, altKey, ctrlKey, metaKey } = event
90
+
91
+ if (
92
+ key === 'Backspace' ||
93
+ key === 'Clear' ||
94
+ (key.length === 1 && key !== ' ' && !altKey && !ctrlKey && !metaKey)
95
+ ) {
96
+ this.handleSearch(key)
97
+ }
98
+ }
99
+
100
+ // https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
101
+ handleSearch(char: string) {
102
+ const searchString = this.getSearchString(char)
103
+ const focusedItem = this.items.find(
104
+ (item) => document.activeElement === item,
105
+ )
106
+ const focusedIndex = focusedItem ? this.items.indexOf(focusedItem) : 0
107
+ const searchIndex = this.getIndexByLetter(searchString, focusedIndex + 1)
108
+
109
+ // if a match was found, go to it
110
+ if (searchIndex >= 0) {
111
+ this.focusItemByIndex(null, searchIndex)
112
+ }
113
+ // if no matches, clear the timeout and search string
114
+ else {
115
+ window.clearTimeout(this.searchTimeout)
116
+ this.searchString = ''
117
+ }
118
+ }
119
+
120
+ filterItemsInnerText(items: string[], filter: string) {
121
+ return items.filter((item) => {
122
+ const matches = item.toLowerCase().indexOf(filter.toLowerCase()) === 0
123
+ return matches
124
+ })
125
+ }
126
+
127
+ getSearchString(char: string) {
128
+ // reset typing timeout and start new timeout
129
+ // this allows us to make multiple-letter matches, like a native select
130
+ if (typeof this.searchTimeout === 'number') {
131
+ window.clearTimeout(this.searchTimeout)
132
+ }
133
+
134
+ this.searchTimeout = window.setTimeout(() => {
135
+ this.searchString = ''
136
+ }, 500)
137
+
138
+ // add most recent letter to saved search string
139
+ this.searchString += char
140
+ return this.searchString
141
+ }
142
+
143
+ // return the index of an option from an array of options, based on a search string
144
+ // if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches
145
+ getIndexByLetter(filter: string, startIndex: number) {
146
+ const orderedItems = [
147
+ ...this.itemsInnerText.slice(startIndex),
148
+ ...this.itemsInnerText.slice(0, startIndex),
149
+ ]
150
+
151
+ const firstMatch = this.filterItemsInnerText(orderedItems, filter)[0]
152
+
153
+ const allSameLetter = (array: string[]) =>
154
+ array.every((letter) => letter === array[0])
155
+
156
+ // first check if there is an exact match for the typed string
157
+ if (firstMatch) {
158
+ const index = this.itemsInnerText.indexOf(firstMatch)
159
+ return index
160
+ }
161
+
162
+ // if the same letter is being repeated, cycle through first-letter matches
163
+ else if (allSameLetter(filter.split(''))) {
164
+ const matches = this.filterItemsInnerText(orderedItems, filter[0])
165
+ const index = this.itemsInnerText.indexOf(matches[0])
166
+ return index
167
+ }
168
+
169
+ // if no matches, return -1
170
+ else {
171
+ return -1
172
+ }
173
+ }
174
+
175
+ selectedValueChanged(value: string) {
176
+ const item = this.itemTargets.find((i) => i.dataset.value === value)
177
+
178
+ if (item) {
179
+ this.triggerTextTarget.textContent = item.textContent
180
+
181
+ this.itemTargets.forEach((i) => {
182
+ if (i.dataset.value === value) {
183
+ i.setAttribute('aria-selected', 'true')
184
+ } else {
185
+ i.setAttribute('aria-selected', 'false')
186
+ }
187
+ })
188
+
189
+ this.selectTarget.value = value
190
+ }
191
+
192
+ this.triggerTarget.dataset.hasValue = `${!!value && value.length > 0}`
193
+
194
+ const placeholder = this.triggerTarget.dataset.placeholder
195
+
196
+ if (placeholder && this.triggerTarget.dataset.hasValue === 'false') {
197
+ this.triggerTextTarget.textContent = placeholder
198
+ }
199
+ }
200
+ }
@@ -1,8 +1,12 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller {
3
+ export default class extends Controller<HTMLElement> {
4
4
  static targets = ['panel', 'panelOffset']
5
5
 
6
+ declare readonly panelTarget: HTMLElement
7
+ declare readonly panelOffsetTarget: HTMLElement
8
+ declare width: number
9
+
6
10
  connect() {
7
11
  this.width = this.element.offsetWidth
8
12
  }
@@ -26,7 +30,7 @@ export default class extends Controller {
26
30
  this.element.dataset.state = 'collapsed'
27
31
  this.element.dataset.collapsible = 'offcanvas'
28
32
  this.panelTarget.style.left = `-${this.width}px`
29
- this.panelOffsetTarget.style.width = 0
33
+ this.panelOffsetTarget.style.width = `${0}`
30
34
  }
31
35
 
32
36
  isOpen() {
@@ -0,0 +1,21 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import SidebarController from './sidebar_controller'
3
+
4
+ export default class extends Controller<HTMLElement> {
5
+ declare sidebarId: string | undefined
6
+
7
+ connect() {
8
+ this.sidebarId = this.element.dataset.sidebarId
9
+ }
10
+
11
+ toggle() {
12
+ const sidebar = this.application.getControllerForElementAndIdentifier(
13
+ document.querySelector(`#${this.sidebarId}`) as HTMLElement,
14
+ 'sidebar',
15
+ ) as SidebarController
16
+
17
+ if (sidebar) {
18
+ sidebar.toggle()
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,107 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import noUiSlider, { API, Options } from 'nouislider'
3
+
4
+ export default class extends Controller<HTMLElement> {
5
+ static targets = ['slider', 'hiddenInput', 'endHiddenInput']
6
+
7
+ declare readonly sliderTarget: HTMLInputElement
8
+ declare readonly hiddenInputTarget: HTMLInputElement
9
+ declare readonly endHiddenInputTarget: HTMLInputElement
10
+ declare readonly hasEndHiddenInputTarget: boolean
11
+ declare onUpdateValuesListener: (values: (string | number)[]) => void
12
+ declare DOMClickListener: (event: MouseEvent) => void
13
+ declare range: boolean
14
+ declare slider: API
15
+
16
+ connect() {
17
+ this.range = this.element.dataset.range === 'true'
18
+ this.DOMClickListener = this.onDOMClick.bind(this)
19
+ this.onUpdateValuesListener = this.onUpdateValues.bind(this)
20
+ const options = this.getOptions()
21
+ this.slider = noUiSlider.create(this.sliderTarget, options)
22
+
23
+ if (this.element.dataset.disabled === 'true') {
24
+ this.slider.disable()
25
+ }
26
+
27
+ if (this.element.dataset.id) {
28
+ const lowerHandle = this.slider.target.querySelector('.noUi-handle-lower')
29
+ if (lowerHandle) {
30
+ lowerHandle.id = this.element.dataset.id
31
+ }
32
+ }
33
+ this.slider.on('update', this.onUpdateValuesListener)
34
+
35
+ document.addEventListener('click', this.DOMClickListener)
36
+ }
37
+
38
+ onUpdateValues(values: (string | number)[]) {
39
+ this.hiddenInputTarget.value = `${values[0]}`
40
+
41
+ if (this.range && this.hasEndHiddenInputTarget) {
42
+ this.endHiddenInputTarget.value = `${values[1]}`
43
+ }
44
+ }
45
+
46
+ getOptions() {
47
+ const defaultOptions = {
48
+ connect: this.range ? true : 'lower',
49
+ tooltips: true,
50
+ } as Options
51
+
52
+ defaultOptions.range = {
53
+ min: [parseFloat(this.element.dataset.min || '0')],
54
+ max: [parseFloat(this.element.dataset.max || '100')],
55
+ }
56
+
57
+ defaultOptions.step = parseFloat(this.element.dataset.step || '1')
58
+
59
+ if (this.range) {
60
+ defaultOptions.start = [
61
+ parseFloat(this.element.dataset.value || '0'),
62
+ parseFloat(this.element.dataset.endValue || '0'),
63
+ ]
64
+
65
+ defaultOptions.handleAttributes = [
66
+ { 'aria-label': 'lower' },
67
+ { 'aria-label': 'upper' },
68
+ ]
69
+ } else {
70
+ defaultOptions.start = [parseFloat(this.element.dataset.value || '0')]
71
+ defaultOptions.handleAttributes = [{ 'aria-label': 'lower' }]
72
+ }
73
+
74
+ defaultOptions.orientation = (this.element.dataset.orientation ||
75
+ 'horizontal') as Options['orientation']
76
+
77
+ try {
78
+ return {
79
+ ...defaultOptions,
80
+ ...JSON.parse(this.element.dataset.options || ''),
81
+ }
82
+ } catch {
83
+ return defaultOptions
84
+ }
85
+ }
86
+
87
+ onDOMClick(event: MouseEvent) {
88
+ const target = event.target
89
+
90
+ // Focus handle of slider when label is clicked.
91
+ if (target instanceof HTMLLabelElement) {
92
+ const id = target.getAttribute('for')
93
+
94
+ if (id === this.element.dataset.id) {
95
+ const handle = document.querySelector(`#${id}`) as HTMLElement
96
+
97
+ if (handle) {
98
+ handle.focus()
99
+ }
100
+ }
101
+ }
102
+ }
103
+
104
+ disconnect() {
105
+ document.removeEventListener('click', this.DOMClickListener)
106
+ }
107
+ }
@@ -0,0 +1,30 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller<HTMLElement> {
4
+ static targets = ['input', 'thumb']
5
+ static values = {
6
+ isChecked: Boolean,
7
+ }
8
+
9
+ declare readonly inputTarget: HTMLInputElement
10
+ declare readonly thumbTarget: HTMLElement
11
+ declare isCheckedValue: boolean
12
+
13
+ toggle() {
14
+ this.isCheckedValue = !this.isCheckedValue
15
+ }
16
+
17
+ isCheckedValueChanged(value: boolean) {
18
+ if (value) {
19
+ this.element.ariaChecked = 'true'
20
+ this.element.dataset.state = 'checked'
21
+ this.thumbTarget.dataset.state = 'checked'
22
+ this.inputTarget.checked = true
23
+ } else {
24
+ this.element.ariaChecked = 'false'
25
+ this.element.dataset.state = 'unchecked'
26
+ this.thumbTarget.dataset.state = 'unchecked'
27
+ this.inputTarget.checked = false
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,79 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['trigger', 'content']
5
+ static values = {
6
+ active: String,
7
+ }
8
+
9
+ declare readonly triggerTargets: HTMLButtonElement[]
10
+ declare readonly contentTargets: HTMLElement[]
11
+ declare activeValue: string | undefined
12
+
13
+ connect() {
14
+ if (!this.activeValue) {
15
+ this.activeValue = this.triggerTargets[0].dataset.value
16
+ }
17
+ }
18
+
19
+ setActiveTab(event: MouseEvent | KeyboardEvent) {
20
+ const target = event.currentTarget as HTMLButtonElement
21
+
22
+ if (event instanceof MouseEvent) {
23
+ this.activeValue = target.dataset.value
24
+ } else {
25
+ const key = event.key
26
+
27
+ const focusableTriggers = this.triggerTargets.filter(
28
+ (t) => !t.disabled,
29
+ ) as HTMLButtonElement[]
30
+
31
+ const index = focusableTriggers.indexOf(target)
32
+ let newIndex = 0
33
+
34
+ if (key === 'ArrowLeft') {
35
+ newIndex = index - 1
36
+
37
+ if (newIndex < 0) {
38
+ newIndex = focusableTriggers.length - 1
39
+ }
40
+ } else {
41
+ newIndex = index + 1
42
+
43
+ if (newIndex > focusableTriggers.length - 1) {
44
+ newIndex = 0
45
+ }
46
+ }
47
+
48
+ this.activeValue = focusableTriggers[newIndex].dataset.value
49
+ focusableTriggers[newIndex].focus()
50
+ }
51
+ }
52
+
53
+ activeValueChanged(value: string) {
54
+ this.triggerTargets.forEach((trigger) => {
55
+ const triggerValue = trigger.dataset.value
56
+ const content = this.contentTargets.find((c) => {
57
+ return c.dataset.value === triggerValue
58
+ })
59
+
60
+ if (!content) {
61
+ throw new Error(
62
+ `Could not find TabsContent with value "${triggerValue}"`,
63
+ )
64
+ }
65
+
66
+ if (triggerValue === value) {
67
+ trigger.ariaSelected = 'true'
68
+ trigger.tabIndex = 0
69
+ trigger.dataset.state = 'active'
70
+ content.classList.remove('hidden')
71
+ } else {
72
+ trigger.ariaSelected = 'false'
73
+ trigger.tabIndex = -1
74
+ trigger.dataset.state = 'inactive'
75
+ content.classList.add('hidden')
76
+ }
77
+ })
78
+ }
79
+ }
@@ -2,31 +2,34 @@ import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
4
  initialize() {
5
- document.documentElement.classList.toggle(
6
- 'dark',
5
+ if (
7
6
  localStorage.theme === 'dark' ||
8
- (!('theme' in localStorage) &&
9
- window.matchMedia('(prefers-color-scheme: dark)').matches),
10
- )
7
+ (!('theme' in localStorage) &&
8
+ window.matchMedia('(prefers-color-scheme: dark)').matches)
9
+ ) {
10
+ this.setDarkMode()
11
+ } else {
12
+ this.setLightMode()
13
+ }
11
14
  }
12
15
 
13
16
  toggle() {
14
17
  if (document.documentElement.classList.contains('dark')) {
15
- localStorage.theme = 'light'
16
- document.documentElement.classList.remove('dark')
18
+ this.setLightMode()
17
19
  } else {
18
- localStorage.theme = 'dark'
19
- document.documentElement.classList.add('dark')
20
+ this.setDarkMode()
20
21
  }
21
22
  }
22
23
 
23
24
  setLightMode() {
24
25
  localStorage.theme = 'light'
25
26
  document.documentElement.classList.remove('dark')
27
+ document.documentElement.style.colorScheme = ''
26
28
  }
27
29
 
28
30
  setDarkMode() {
29
31
  localStorage.theme = 'dark'
30
32
  document.documentElement.classList.add('dark')
33
+ document.documentElement.style.colorScheme = 'dark'
31
34
  }
32
35
  }
@@ -0,0 +1,62 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import DOMPurify from 'dompurify'
3
+
4
+ export default class extends Controller<HTMLElement> {
5
+ addToast({
6
+ title,
7
+ description,
8
+ action,
9
+ variant = 'default',
10
+ duration = 5000,
11
+ }: {
12
+ title: string
13
+ description?: string
14
+ action?: string
15
+ variant: string
16
+ duration?: number
17
+ }) {
18
+ const template = (
19
+ variant === 'default'
20
+ ? this.element.querySelector('[data-variant="default"]')
21
+ : this.element.querySelector('[data-variant="destructive"]')
22
+ ) as HTMLTemplateElement
23
+
24
+ const clone = template.content.cloneNode(true) as HTMLElement
25
+
26
+ const toastTemplate = clone.querySelector(
27
+ '[data-shadcn-phlexcomponents="toast"]',
28
+ ) as HTMLElement
29
+ toastTemplate.dataset.duration = String(duration)
30
+
31
+ const titleTemplate = clone.querySelector(
32
+ '[data-shadcn-phlexcomponents="toast-title"]',
33
+ ) as HTMLElement
34
+ const descriptionTemplate = clone.querySelector(
35
+ '[data-shadcn-phlexcomponents="toast-description"]',
36
+ ) as HTMLElement
37
+ const actionTemplate = clone.querySelector(
38
+ '[data-shadcn-phlexcomponents="toast-action"]',
39
+ ) as HTMLElement
40
+
41
+ titleTemplate.innerHTML = DOMPurify.sanitize(title)
42
+
43
+ if (description) {
44
+ descriptionTemplate.innerHTML = DOMPurify.sanitize(description)
45
+ } else {
46
+ descriptionTemplate.remove()
47
+ }
48
+
49
+ if (action) {
50
+ const element = document.createElement('div')
51
+ element.innerHTML = DOMPurify.sanitize(action)
52
+ const actionElement = element.firstElementChild as HTMLElement
53
+ const classes = actionTemplate.classList
54
+ actionElement.classList.add(...classes)
55
+ actionTemplate.replaceWith(actionElement)
56
+ } else {
57
+ actionTemplate.remove()
58
+ }
59
+
60
+ this.element.append(clone)
61
+ }
62
+ }
@@ -0,0 +1,28 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { ANIMATION_OUT_DELAY } from '../utils'
3
+
4
+ export default class extends Controller<HTMLElement> {
5
+ declare duration: number
6
+ declare closeTimeout: number
7
+
8
+ connect() {
9
+ this.duration = Number(this.element.dataset.duration)
10
+ this.close()
11
+ }
12
+
13
+ cancelClose() {
14
+ window.clearTimeout(this.closeTimeout)
15
+ }
16
+
17
+ close() {
18
+ if (this.duration > 0) {
19
+ this.closeTimeout = window.setTimeout(() => {
20
+ this.element.dataset.state = 'closed'
21
+
22
+ setTimeout(() => {
23
+ this.element.remove()
24
+ }, ANIMATION_OUT_DELAY)
25
+ }, this.duration)
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,98 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { useHover } from 'stimulus-use'
3
+ import { initFloatingUi, ANIMATION_OUT_DELAY } from '../utils'
4
+
5
+ export default class extends Controller<HTMLElement> {
6
+ static targets = ['trigger', 'content', 'contentContainer', 'arrow']
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 readonly arrowTarget: HTMLElement
16
+ declare closeTimeout: number
17
+ declare cleanup: () => void
18
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
19
+
20
+ connect() {
21
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
22
+ useHover(this, { element: this.triggerTarget, dispatchEvent: false })
23
+ }
24
+
25
+ open() {
26
+ window.clearTimeout(this.closeTimeout)
27
+ this.isOpenValue = true
28
+ }
29
+
30
+ close() {
31
+ this.closeTimeout = window.setTimeout(() => {
32
+ this.isOpenValue = false
33
+ }, 250)
34
+ }
35
+
36
+ closeImmediately() {
37
+ this.isOpenValue = false
38
+ }
39
+
40
+ // for useHover
41
+ mouseEnter() {
42
+ this.open()
43
+ }
44
+
45
+ // for useHover
46
+ mouseLeave() {
47
+ this.close()
48
+ }
49
+
50
+ onDOMKeydown(event: KeyboardEvent) {
51
+ if (!this.isOpenValue) return
52
+
53
+ const key = event.key
54
+
55
+ if (['Escape', 'Enter', ' '].includes(key)) {
56
+ event.preventDefault()
57
+ event.stopImmediatePropagation()
58
+ this.closeImmediately()
59
+ }
60
+ }
61
+
62
+ isOpenValueChanged(isOpen: boolean) {
63
+ if (isOpen) {
64
+ this.contentContainerTarget.classList.remove('hidden')
65
+ this.contentTarget.dataset.state = 'open'
66
+ this.setupEventListeners()
67
+
68
+ this.cleanup = initFloatingUi({
69
+ referenceElement: this.triggerTarget,
70
+ floatingElement: this.contentContainerTarget,
71
+ arrowElement: this.arrowTarget,
72
+ side: this.contentTarget.dataset.side,
73
+ align: this.contentTarget.dataset.align,
74
+ sideOffset: 8,
75
+ })
76
+ } else {
77
+ this.contentTarget.dataset.state = 'closed'
78
+ this.cleanupEventListeners()
79
+
80
+ setTimeout(() => {
81
+ this.contentContainerTarget.classList.add('hidden')
82
+ }, ANIMATION_OUT_DELAY)
83
+ }
84
+ }
85
+
86
+ setupEventListeners() {
87
+ document.addEventListener('keydown', this.DOMKeydownListener)
88
+ }
89
+
90
+ cleanupEventListeners() {
91
+ if (this.cleanup) this.cleanup()
92
+ document.removeEventListener('keydown', this.DOMKeydownListener)
93
+ }
94
+
95
+ disconnect() {
96
+ this.cleanupEventListeners()
97
+ }
98
+ }