shadcn_phlexcomponents 0.1.9 → 0.1.14

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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.ts +136 -0
  3. data/app/javascript/controllers/alert_dialog_controller.ts +12 -0
  4. data/app/javascript/controllers/avatar_controller.ts +24 -0
  5. data/app/javascript/controllers/checkbox_controller.ts +41 -0
  6. data/app/javascript/controllers/collapsible_controller.ts +52 -0
  7. data/app/javascript/controllers/combobox_controller.ts +376 -0
  8. data/app/javascript/controllers/command_controller.ts +301 -0
  9. data/app/javascript/controllers/date_picker_controller.ts +334 -0
  10. data/app/javascript/controllers/date_range_picker_controller.ts +253 -0
  11. data/app/javascript/controllers/dialog_controller.ts +115 -0
  12. data/app/javascript/controllers/dropdown_menu_controller.ts +309 -0
  13. data/app/javascript/controllers/dropdown_menu_sub_controller.ts +152 -0
  14. data/app/javascript/controllers/form_field_controller.ts +27 -0
  15. data/app/javascript/controllers/hover_card_controller.ts +103 -0
  16. data/app/javascript/controllers/{loading_button_controller.js → loading_button_controller.ts} +7 -2
  17. data/app/javascript/controllers/popover_controller.ts +118 -0
  18. data/app/javascript/controllers/progress_controller.ts +23 -0
  19. data/app/javascript/controllers/radio_group_controller.ts +113 -0
  20. data/app/javascript/controllers/select_controller.ts +341 -0
  21. data/app/javascript/controllers/{sidebar_controller.js → sidebar_controller.ts} +6 -2
  22. data/app/javascript/controllers/sidebar_trigger_controller.ts +21 -0
  23. data/app/javascript/controllers/slider_controller.ts +114 -0
  24. data/app/javascript/controllers/switch_controller.ts +37 -0
  25. data/app/javascript/controllers/tabs_controller.ts +87 -0
  26. data/app/javascript/controllers/theme_switcher_controller.ts +40 -0
  27. data/app/javascript/controllers/toast_container_controller.ts +67 -0
  28. data/app/javascript/controllers/toast_controller.ts +34 -0
  29. data/app/javascript/controllers/toggle_controller.ts +28 -0
  30. data/app/javascript/controllers/toggle_group_controller.ts +28 -0
  31. data/app/javascript/controllers/tooltip_controller.ts +110 -0
  32. data/app/javascript/shadcn_phlexcomponents.ts +61 -0
  33. data/app/javascript/utils/command.ts +544 -0
  34. data/app/javascript/utils/floating_ui.ts +196 -0
  35. data/app/javascript/utils/index.ts +417 -0
  36. data/app/stylesheets/date_picker.css +81 -101
  37. data/app/stylesheets/nouislider.css +173 -0
  38. data/app/stylesheets/tw-animate.css +486 -0
  39. data/lib/install/install_shadcn_phlexcomponents.rb +16 -3
  40. data/lib/shadcn_phlexcomponents/alias.rb +6 -1
  41. data/lib/shadcn_phlexcomponents/components/accordion.rb +130 -0
  42. data/lib/shadcn_phlexcomponents/components/alert.rb +59 -0
  43. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +279 -0
  44. data/lib/shadcn_phlexcomponents/components/{aspect_ratio/aspect_ratio.rb → aspect_ratio.rb} +2 -2
  45. data/lib/shadcn_phlexcomponents/components/avatar.rb +63 -0
  46. data/lib/shadcn_phlexcomponents/components/badge.rb +35 -0
  47. data/lib/shadcn_phlexcomponents/components/base.rb +48 -7
  48. data/lib/shadcn_phlexcomponents/components/breadcrumb.rb +150 -0
  49. data/lib/shadcn_phlexcomponents/components/button.rb +49 -0
  50. data/lib/shadcn_phlexcomponents/components/card.rb +88 -0
  51. data/lib/shadcn_phlexcomponents/components/{checkbox/checkbox.rb → checkbox.rb} +18 -14
  52. data/lib/shadcn_phlexcomponents/components/{checkbox_group/checkbox_group.rb → checkbox_group.rb} +7 -8
  53. data/lib/shadcn_phlexcomponents/components/collapsible.rb +90 -0
  54. data/lib/shadcn_phlexcomponents/components/combobox.rb +428 -0
  55. data/lib/shadcn_phlexcomponents/components/command.rb +381 -0
  56. data/lib/shadcn_phlexcomponents/components/date_picker.rb +208 -0
  57. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +143 -0
  58. data/lib/shadcn_phlexcomponents/components/dialog.rb +236 -0
  59. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +283 -0
  60. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +136 -0
  61. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +6 -7
  62. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +2 -2
  63. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
  64. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +3 -4
  65. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +27 -41
  66. data/lib/shadcn_phlexcomponents/components/form/form_error.rb +1 -1
  67. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +43 -8
  68. data/lib/shadcn_phlexcomponents/components/form/form_hint.rb +1 -1
  69. data/lib/shadcn_phlexcomponents/components/form/form_input.rb +3 -4
  70. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +4 -5
  71. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +3 -4
  72. data/lib/shadcn_phlexcomponents/components/form/form_slider.rb +91 -0
  73. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +7 -6
  74. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +3 -4
  75. data/lib/shadcn_phlexcomponents/components/{form/form.rb → form.rb} +36 -4
  76. data/lib/shadcn_phlexcomponents/components/hover_card.rb +111 -0
  77. data/lib/shadcn_phlexcomponents/components/input.rb +31 -0
  78. data/lib/shadcn_phlexcomponents/components/label.rb +16 -0
  79. data/lib/shadcn_phlexcomponents/components/{link/link.rb → link.rb} +10 -3
  80. data/lib/shadcn_phlexcomponents/components/{loading_button/loading_button.rb → loading_button.rb} +9 -2
  81. data/lib/shadcn_phlexcomponents/components/pagination.rb +166 -0
  82. data/lib/shadcn_phlexcomponents/components/popover.rb +116 -0
  83. data/lib/shadcn_phlexcomponents/components/{progress/progress.rb → progress.rb} +4 -4
  84. data/lib/shadcn_phlexcomponents/components/radio_group.rb +155 -0
  85. data/lib/shadcn_phlexcomponents/components/select.rb +406 -0
  86. data/lib/shadcn_phlexcomponents/components/{separator/separator.rb → separator.rb} +9 -8
  87. data/lib/shadcn_phlexcomponents/components/sheet.rb +243 -0
  88. data/lib/shadcn_phlexcomponents/components/{skeleton/skeleton.rb → skeleton.rb} +1 -1
  89. data/lib/shadcn_phlexcomponents/components/slider.rb +72 -0
  90. data/lib/shadcn_phlexcomponents/components/switch.rb +75 -0
  91. data/lib/shadcn_phlexcomponents/components/table.rb +140 -0
  92. data/lib/shadcn_phlexcomponents/components/tabs.rb +135 -0
  93. data/lib/shadcn_phlexcomponents/components/textarea.rb +24 -0
  94. data/lib/shadcn_phlexcomponents/components/toast.rb +153 -0
  95. data/lib/shadcn_phlexcomponents/components/{toast/toast_container.rb → toast_container.rb} +23 -4
  96. data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
  97. data/lib/shadcn_phlexcomponents/components/tooltip.rb +132 -0
  98. data/lib/shadcn_phlexcomponents/engine.rb +1 -5
  99. data/lib/shadcn_phlexcomponents/initializers/shadcn_phlexcomponents.rb +25 -0
  100. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  101. data/lib/tasks/install.rake +1 -1
  102. metadata +83 -167
  103. data/app/javascript/controllers/accordion_controller.js +0 -124
  104. data/app/javascript/controllers/alert_dialog_controller.js +0 -21
  105. data/app/javascript/controllers/avatar_controller.js +0 -15
  106. data/app/javascript/controllers/checkbox_controller.js +0 -28
  107. data/app/javascript/controllers/collapsible_controller.js +0 -35
  108. data/app/javascript/controllers/combobox_controller.js +0 -54
  109. data/app/javascript/controllers/date_picker_controller.js +0 -253
  110. data/app/javascript/controllers/date_range_picker_controller.js +0 -344
  111. data/app/javascript/controllers/dialog_controller.js +0 -114
  112. data/app/javascript/controllers/dropdown_menu_controller.js +0 -171
  113. data/app/javascript/controllers/form_field_controller.js +0 -24
  114. data/app/javascript/controllers/hover_card_controller.js +0 -21
  115. data/app/javascript/controllers/popover_controller.js +0 -113
  116. data/app/javascript/controllers/progress_controller.js +0 -14
  117. data/app/javascript/controllers/radio_group_controller.js +0 -90
  118. data/app/javascript/controllers/select_controller.js +0 -274
  119. data/app/javascript/controllers/sidebar_trigger_controller.js +0 -15
  120. data/app/javascript/controllers/switch_controller.js +0 -24
  121. data/app/javascript/controllers/tabs_controller.js +0 -73
  122. data/app/javascript/controllers/theme_switcher_controller.js +0 -32
  123. data/app/javascript/controllers/toast_container_controller.js +0 -22
  124. data/app/javascript/controllers/toast_controller.js +0 -45
  125. data/app/javascript/controllers/tooltip_controller.js +0 -40
  126. data/app/javascript/shadcn_phlexcomponents.js +0 -53
  127. data/app/javascript/utils.js +0 -184
  128. data/app/stylesheets/choices.css +0 -324
  129. data/app/stylesheets/tailwindcss-animate.css +0 -318
  130. data/lib/shadcn_phlexcomponents/components/accordion/accordion.rb +0 -38
  131. data/lib/shadcn_phlexcomponents/components/accordion/accordion_content.rb +0 -30
  132. data/lib/shadcn_phlexcomponents/components/accordion/accordion_item.rb +0 -26
  133. data/lib/shadcn_phlexcomponents/components/accordion/accordion_trigger.rb +0 -46
  134. data/lib/shadcn_phlexcomponents/components/alert/alert.rb +0 -40
  135. data/lib/shadcn_phlexcomponents/components/alert/alert_description.rb +0 -11
  136. data/lib/shadcn_phlexcomponents/components/alert/alert_title.rb +0 -11
  137. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog.rb +0 -60
  138. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_action.rb +0 -22
  139. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_action_to.rb +0 -40
  140. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_cancel.rb +0 -22
  141. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_content.rb +0 -40
  142. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_description.rb +0 -22
  143. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_footer.rb +0 -11
  144. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_header.rb +0 -11
  145. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_title.rb +0 -22
  146. data/lib/shadcn_phlexcomponents/components/alert_dialog/alert_dialog_trigger.rb +0 -50
  147. data/lib/shadcn_phlexcomponents/components/avatar/avatar.rb +0 -31
  148. data/lib/shadcn_phlexcomponents/components/avatar/avatar_fallback.rb +0 -21
  149. data/lib/shadcn_phlexcomponents/components/avatar/avatar_image.rb +0 -19
  150. data/lib/shadcn_phlexcomponents/components/badge/badge.rb +0 -30
  151. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb.rb +0 -53
  152. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_ellipsis.rb +0 -23
  153. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_item.rb +0 -11
  154. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_link.rb +0 -7
  155. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_page.rb +0 -21
  156. data/lib/shadcn_phlexcomponents/components/breadcrumb/breadcrumb_separator.rb +0 -26
  157. data/lib/shadcn_phlexcomponents/components/button/button.rb +0 -53
  158. data/lib/shadcn_phlexcomponents/components/card/card.rb +0 -31
  159. data/lib/shadcn_phlexcomponents/components/card/card_content.rb +0 -11
  160. data/lib/shadcn_phlexcomponents/components/card/card_description.rb +0 -11
  161. data/lib/shadcn_phlexcomponents/components/card/card_footer.rb +0 -11
  162. data/lib/shadcn_phlexcomponents/components/card/card_header.rb +0 -11
  163. data/lib/shadcn_phlexcomponents/components/card/card_title.rb +0 -11
  164. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible.rb +0 -31
  165. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible_content.rb +0 -24
  166. data/lib/shadcn_phlexcomponents/components/collapsible/collapsible_trigger.rb +0 -50
  167. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker.rb +0 -87
  168. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_content.rb +0 -45
  169. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_trigger.rb +0 -64
  170. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker.rb +0 -105
  171. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_content.rb +0 -9
  172. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_trigger.rb +0 -9
  173. data/lib/shadcn_phlexcomponents/components/dialog/dialog.rb +0 -52
  174. data/lib/shadcn_phlexcomponents/components/dialog/dialog_close.rb +0 -42
  175. data/lib/shadcn_phlexcomponents/components/dialog/dialog_content.rb +0 -54
  176. data/lib/shadcn_phlexcomponents/components/dialog/dialog_description.rb +0 -22
  177. data/lib/shadcn_phlexcomponents/components/dialog/dialog_footer.rb +0 -11
  178. data/lib/shadcn_phlexcomponents/components/dialog/dialog_header.rb +0 -11
  179. data/lib/shadcn_phlexcomponents/components/dialog/dialog_title.rb +0 -22
  180. data/lib/shadcn_phlexcomponents/components/dialog/dialog_trigger.rb +0 -50
  181. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu.rb +0 -50
  182. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_content.rb +0 -52
  183. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_item.rb +0 -56
  184. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_item_to.rb +0 -28
  185. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_label.rb +0 -11
  186. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_separator.rb +0 -20
  187. data/lib/shadcn_phlexcomponents/components/dropdown_menu/dropdown_menu_trigger.rb +0 -57
  188. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card.rb +0 -33
  189. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card_content.rb +0 -32
  190. data/lib/shadcn_phlexcomponents/components/hover_card/hover_card_trigger.rb +0 -44
  191. data/lib/shadcn_phlexcomponents/components/input/input.rb +0 -32
  192. data/lib/shadcn_phlexcomponents/components/label/label.rb +0 -14
  193. data/lib/shadcn_phlexcomponents/components/pagination/pagination.rb +0 -38
  194. data/lib/shadcn_phlexcomponents/components/pagination/pagination_ellipsis.rb +0 -24
  195. data/lib/shadcn_phlexcomponents/components/pagination/pagination_link.rb +0 -34
  196. data/lib/shadcn_phlexcomponents/components/pagination/pagination_next.rb +0 -32
  197. data/lib/shadcn_phlexcomponents/components/pagination/pagination_previous.rb +0 -32
  198. data/lib/shadcn_phlexcomponents/components/popover/popover.rb +0 -34
  199. data/lib/shadcn_phlexcomponents/components/popover/popover_content.rb +0 -40
  200. data/lib/shadcn_phlexcomponents/components/popover/popover_trigger.rb +0 -50
  201. data/lib/shadcn_phlexcomponents/components/radio_group/radio_group.rb +0 -88
  202. data/lib/shadcn_phlexcomponents/components/radio_group/radio_group_item.rb +0 -66
  203. data/lib/shadcn_phlexcomponents/components/select/select.rb +0 -194
  204. data/lib/shadcn_phlexcomponents/components/select/select_content.rb +0 -64
  205. data/lib/shadcn_phlexcomponents/components/select/select_group.rb +0 -23
  206. data/lib/shadcn_phlexcomponents/components/select/select_item.rb +0 -59
  207. data/lib/shadcn_phlexcomponents/components/select/select_label.rb +0 -24
  208. data/lib/shadcn_phlexcomponents/components/select/select_trigger.rb +0 -56
  209. data/lib/shadcn_phlexcomponents/components/sheet/sheet.rb +0 -53
  210. data/lib/shadcn_phlexcomponents/components/sheet/sheet_close.rb +0 -42
  211. data/lib/shadcn_phlexcomponents/components/sheet/sheet_content.rb +0 -65
  212. data/lib/shadcn_phlexcomponents/components/sheet/sheet_description.rb +0 -22
  213. data/lib/shadcn_phlexcomponents/components/sheet/sheet_footer.rb +0 -11
  214. data/lib/shadcn_phlexcomponents/components/sheet/sheet_header.rb +0 -11
  215. data/lib/shadcn_phlexcomponents/components/sheet/sheet_title.rb +0 -22
  216. data/lib/shadcn_phlexcomponents/components/sheet/sheet_trigger.rb +0 -50
  217. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar.rb +0 -108
  218. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_container.rb +0 -11
  219. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_content.rb +0 -11
  220. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_footer.rb +0 -11
  221. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group.rb +0 -11
  222. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group_content.rb +0 -11
  223. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_group_label.rb +0 -16
  224. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_header.rb +0 -11
  225. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_inset.rb +0 -15
  226. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu.rb +0 -11
  227. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_button.rb +0 -61
  228. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_item.rb +0 -9
  229. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub.rb +0 -14
  230. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub_button.rb +0 -48
  231. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_menu_sub_item.rb +0 -9
  232. data/lib/shadcn_phlexcomponents/components/sidebar/sidebar_trigger.rb +0 -40
  233. data/lib/shadcn_phlexcomponents/components/switch/switch.rb +0 -66
  234. data/lib/shadcn_phlexcomponents/components/table/table.rb +0 -75
  235. data/lib/shadcn_phlexcomponents/components/table/table_body.rb +0 -11
  236. data/lib/shadcn_phlexcomponents/components/table/table_caption.rb +0 -11
  237. data/lib/shadcn_phlexcomponents/components/table/table_cell.rb +0 -11
  238. data/lib/shadcn_phlexcomponents/components/table/table_footer.rb +0 -11
  239. data/lib/shadcn_phlexcomponents/components/table/table_head.rb +0 -14
  240. data/lib/shadcn_phlexcomponents/components/table/table_header.rb +0 -11
  241. data/lib/shadcn_phlexcomponents/components/table/table_row.rb +0 -11
  242. data/lib/shadcn_phlexcomponents/components/tabs/tabs.rb +0 -38
  243. data/lib/shadcn_phlexcomponents/components/tabs/tabs_content.rb +0 -35
  244. data/lib/shadcn_phlexcomponents/components/tabs/tabs_list.rb +0 -23
  245. data/lib/shadcn_phlexcomponents/components/tabs/tabs_trigger.rb +0 -45
  246. data/lib/shadcn_phlexcomponents/components/textarea/textarea.rb +0 -29
  247. data/lib/shadcn_phlexcomponents/components/toast/toast.rb +0 -101
  248. data/lib/shadcn_phlexcomponents/components/toast/toast_action.rb +0 -39
  249. data/lib/shadcn_phlexcomponents/components/toast/toast_action_to.rb +0 -28
  250. data/lib/shadcn_phlexcomponents/components/toast/toast_content.rb +0 -11
  251. data/lib/shadcn_phlexcomponents/components/toast/toast_description.rb +0 -11
  252. data/lib/shadcn_phlexcomponents/components/toast/toast_title.rb +0 -11
  253. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip.rb +0 -34
  254. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip_content.rb +0 -39
  255. data/lib/shadcn_phlexcomponents/components/tooltip/tooltip_trigger.rb +0 -48
  256. /data/lib/shadcn_phlexcomponents/components/{theme_switcher/theme_switcher.rb → theme_switcher.rb} +0 -0
@@ -0,0 +1,301 @@
1
+ import hotkeys from 'hotkeys-js'
2
+ import { Controller } from '@hotwired/stimulus'
3
+ import {
4
+ showContent,
5
+ hideContent,
6
+ focusTrigger,
7
+ ON_OPEN_FOCUS_DELAY,
8
+ setGroupLabelsId,
9
+ } from '../utils'
10
+ import {
11
+ scrollToItem,
12
+ highlightItem,
13
+ highlightItemByIndex,
14
+ filteredItemsChanged,
15
+ setItemsGroupId,
16
+ search,
17
+ clearRemoteResults,
18
+ resetState,
19
+ hideError,
20
+ showList,
21
+ } from '../utils/command'
22
+ import { useDebounce, useClickOutside } from 'stimulus-use'
23
+ import Fuse from 'fuse.js'
24
+
25
+ declare global {
26
+ interface Window {
27
+ Turbo: {
28
+ visit: (path: string) => void
29
+ }
30
+ }
31
+ }
32
+
33
+ const CommandController = class extends Controller<HTMLElement> {
34
+ // targets
35
+ static targets = [
36
+ 'trigger',
37
+ 'content',
38
+ 'overlay',
39
+ 'item',
40
+ 'group',
41
+ 'searchInput',
42
+ 'list',
43
+ 'listContainer',
44
+ 'empty',
45
+ 'modifierKey',
46
+ 'loading',
47
+ 'error',
48
+ ]
49
+ declare readonly triggerTarget: HTMLElement
50
+ declare readonly contentTarget: HTMLElement
51
+ declare readonly overlayTarget: HTMLElement
52
+ declare readonly itemTargets: HTMLInputElement[]
53
+ declare readonly groupTargets: HTMLElement[]
54
+ declare readonly searchInputTarget: HTMLInputElement
55
+ declare readonly listTarget: HTMLElement
56
+ declare readonly listContainerTarget: HTMLElement
57
+ declare readonly emptyTarget: HTMLElement
58
+ declare readonly modifierKeyTarget: HTMLElement
59
+ declare readonly hasModifierKeyTarget: boolean
60
+ declare readonly loadingTarget: HTMLElement
61
+ declare readonly errorTarget: HTMLElement
62
+
63
+ // values
64
+ static values = {
65
+ isOpen: Boolean,
66
+ filteredItemIndexes: Array,
67
+ searchUrl: String,
68
+ }
69
+ declare isOpenValue: boolean
70
+ declare filteredItemIndexesValue: number[]
71
+
72
+ // custom properties
73
+ declare trigger: HTMLElement
74
+ declare orderedItems: HTMLElement[]
75
+ declare itemsInnerText: string[]
76
+ declare filteredItems: HTMLElement[]
77
+ declare fuse: Fuse<string>
78
+ declare scrollingViaKeyboard: boolean
79
+ declare keyboardScrollTimeout: number
80
+ declare modifierKey?: string
81
+ declare shortcutKey?: string
82
+ declare keybinds: string
83
+ declare abortController?: AbortController
84
+ declare searchPath?: string
85
+ declare isDirty: boolean
86
+ declare isLoading: boolean
87
+ declare hotkeyListener: (event: KeyboardEvent) => void
88
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
89
+ declare DOMClickListener: (event: MouseEvent) => void
90
+
91
+ static debounces = ['search']
92
+
93
+ connect() {
94
+ this.orderedItems = [...this.itemTargets]
95
+ this.itemsInnerText = this.orderedItems.map((i) => i.innerText.trim())
96
+ this.fuse = new Fuse(this.itemsInnerText)
97
+ this.filteredItemIndexesValue = Array.from(
98
+ { length: this.itemTargets.length },
99
+ (_, i) => i,
100
+ )
101
+ this.isLoading = false
102
+ this.filteredItems = this.itemTargets
103
+ this.isDirty = false
104
+ this.searchPath = this.element.dataset.searchPath
105
+
106
+ setGroupLabelsId(this)
107
+ setItemsGroupId(this)
108
+ useDebounce(this)
109
+ useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
110
+ this.hotkeyListener = this.onHotkeyPressed.bind(this)
111
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
112
+ this.setupHotkeys()
113
+ this.replaceModifierKeyIcon()
114
+ }
115
+
116
+ open() {
117
+ this.isOpenValue = true
118
+ this.highlightItemByIndex(0)
119
+
120
+ setTimeout(() => {
121
+ this.searchInputTarget.focus()
122
+ }, ON_OPEN_FOCUS_DELAY)
123
+ }
124
+
125
+ close() {
126
+ this.isOpenValue = false
127
+ resetState(this)
128
+ }
129
+
130
+ scrollToItem(index: number) {
131
+ scrollToItem(this, index)
132
+ }
133
+
134
+ highlightItem(
135
+ event: MouseEvent | KeyboardEvent | null = null,
136
+ index: number | null = null,
137
+ ) {
138
+ highlightItem(this, event, index)
139
+ }
140
+
141
+ highlightItemByIndex(index: number) {
142
+ highlightItemByIndex(this, index)
143
+ }
144
+
145
+ select(event: MouseEvent | KeyboardEvent) {
146
+ let value = null as null | string
147
+
148
+ if (event instanceof KeyboardEvent) {
149
+ const item = this.filteredItems.find(
150
+ (i) => i.dataset.highlighted === 'true',
151
+ )
152
+
153
+ if (item) {
154
+ value = item.dataset.value as string
155
+ }
156
+ } else {
157
+ // mouse event
158
+ const item = event.currentTarget as HTMLElement
159
+ value = item.dataset.value as string
160
+ }
161
+
162
+ if (value) {
163
+ window.Turbo.visit(value)
164
+ this.close()
165
+ }
166
+ }
167
+
168
+ inputKeydown(event: KeyboardEvent) {
169
+ if (event.key === ' ' && this.searchInputTarget.value.length === 0) {
170
+ event.preventDefault()
171
+ }
172
+
173
+ hideError(this)
174
+ showList(this)
175
+ }
176
+
177
+ search(event: InputEvent) {
178
+ this.isDirty = true
179
+ clearRemoteResults(this)
180
+ search(this, event)
181
+ }
182
+
183
+ clickOutside() {
184
+ this.close()
185
+ }
186
+
187
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
188
+ if (isOpen) {
189
+ showContent({
190
+ trigger: this.triggerTarget,
191
+ content: this.contentTarget,
192
+ contentContainer: this.contentTarget,
193
+ overlay: this.overlayTarget,
194
+ })
195
+
196
+ this.setupEventListeners()
197
+ } else {
198
+ hideContent({
199
+ trigger: this.triggerTarget,
200
+ content: this.contentTarget,
201
+ contentContainer: this.contentTarget,
202
+ overlay: this.overlayTarget,
203
+ })
204
+
205
+ if (previousIsOpen) {
206
+ focusTrigger(this.triggerTarget)
207
+ }
208
+
209
+ this.cleanupEventListeners()
210
+ }
211
+ }
212
+
213
+ filteredItemIndexesValueChanged(filteredItemIndexes: number[]) {
214
+ filteredItemsChanged(this, filteredItemIndexes)
215
+ }
216
+
217
+ disconnect() {
218
+ this.cleanupEventListeners()
219
+ resetState(this)
220
+
221
+ if (this.keybinds) {
222
+ hotkeys.unbind(this.keybinds)
223
+ }
224
+ }
225
+
226
+ protected setupHotkeys() {
227
+ const modifierKey = this.element.dataset.modifierKey
228
+ const shortcutKey = this.element.dataset.shortcutKey
229
+
230
+ let keybinds = ''
231
+
232
+ if (modifierKey && shortcutKey) {
233
+ keybinds = `${modifierKey}+${shortcutKey}`
234
+
235
+ if (modifierKey === 'ctrl') {
236
+ keybinds += `,cmd-${shortcutKey}`
237
+ }
238
+ } else if (shortcutKey) {
239
+ keybinds = shortcutKey
240
+ }
241
+
242
+ this.keybinds = keybinds
243
+ hotkeys(keybinds, this.hotkeyListener)
244
+ }
245
+
246
+ protected onHotkeyPressed(event: KeyboardEvent) {
247
+ event.preventDefault()
248
+ this.open()
249
+ }
250
+
251
+ protected replaceModifierKeyIcon() {
252
+ if (this.hasModifierKeyTarget && this.isMac()) {
253
+ this.modifierKeyTarget.innerHTML = '⌘'
254
+ }
255
+ }
256
+
257
+ protected isMac() {
258
+ const navigator = window.navigator as unknown as {
259
+ platform: string
260
+ userAgentData: {
261
+ platform: string
262
+ }
263
+ }
264
+
265
+ if (navigator.userAgentData) {
266
+ return navigator.userAgentData.platform === 'macOS'
267
+ }
268
+
269
+ // Fallback to traditional methods
270
+ return navigator.platform.toUpperCase().indexOf('MAC') >= 0
271
+ }
272
+
273
+ protected onDOMKeydown(event: KeyboardEvent) {
274
+ if (!this.isOpenValue) return
275
+
276
+ const key = event.key
277
+
278
+ if (['Tab', 'Enter'].includes(key)) event.preventDefault()
279
+
280
+ if (key === 'Escape') {
281
+ this.close()
282
+ }
283
+ }
284
+
285
+ protected setupEventListeners() {
286
+ document.addEventListener('keydown', this.DOMKeydownListener)
287
+ }
288
+
289
+ protected cleanupEventListeners() {
290
+ document.removeEventListener('keydown', this.DOMKeydownListener)
291
+
292
+ if (this.abortController) {
293
+ this.abortController.abort()
294
+ }
295
+ }
296
+ }
297
+
298
+ type Command = InstanceType<typeof CommandController>
299
+
300
+ export { CommandController }
301
+ export type { Command }
@@ -0,0 +1,334 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { useClickOutside } from 'stimulus-use'
3
+ import {
4
+ focusTrigger,
5
+ getFocusableElements,
6
+ showContent,
7
+ hideContent,
8
+ lockScroll,
9
+ unlockScroll,
10
+ handleTabNavigation,
11
+ focusElement,
12
+ } from '../utils'
13
+ import { initFloatingUi } from '../utils/floating_ui'
14
+ import { Calendar, Options } from 'vanilla-calendar-pro'
15
+ import Inputmask from 'inputmask'
16
+ import dayjs from 'dayjs'
17
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
18
+ import utc from 'dayjs/plugin/utc'
19
+ dayjs.extend(customParseFormat)
20
+ dayjs.extend(utc)
21
+
22
+ const DAYJS_FORMAT = 'YYYY-MM-DD'
23
+
24
+ const DatePickerController = class extends Controller<HTMLElement> {
25
+ // targets
26
+ static targets = [
27
+ 'trigger',
28
+ 'triggerText',
29
+ 'contentContainer',
30
+ 'content',
31
+ 'input',
32
+ 'hiddenInput',
33
+ 'inputContainer',
34
+ 'calendar',
35
+ 'overlay',
36
+ ]
37
+ declare readonly triggerTarget: HTMLElement
38
+ declare readonly triggerTextTarget: HTMLElement
39
+ declare readonly hasTriggerTextTarget: boolean
40
+ declare readonly contentContainerTarget: HTMLElement
41
+ declare readonly contentTarget: HTMLElement
42
+ declare readonly inputTarget: HTMLInputElement
43
+ declare readonly hasInputTarget: boolean
44
+ declare readonly hiddenInputTarget: HTMLInputElement
45
+ declare readonly inputContainerTarget: HTMLElement
46
+ declare readonly calendarTarget: HTMLElement
47
+ declare readonly overlayTarget: HTMLElement
48
+
49
+ // values
50
+ static values = { isOpen: Boolean, date: String }
51
+ declare isOpenValue: boolean
52
+ declare dateValue: string
53
+
54
+ // custom properties
55
+ declare format: string
56
+ declare mask: boolean
57
+ declare calendar: Calendar
58
+ declare isMobile: boolean
59
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
60
+ declare cleanup: () => void
61
+ declare onClickDateListener: (self: Calendar) => void
62
+
63
+ connect() {
64
+ this.format = this.element.dataset.format || 'DD/MM/YYYY'
65
+ this.mask = this.element.dataset.mask === 'true'
66
+
67
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
68
+ this.onClickDateListener = this.onClickDate.bind(this)
69
+
70
+ const options = this.getOptions()
71
+ this.calendar = new Calendar(this.calendarTarget, options)
72
+ this.calendar.init()
73
+
74
+ if (this.hasInputTarget && this.mask) {
75
+ this.setupInputMask()
76
+ }
77
+
78
+ this.calendarTarget.removeAttribute('tabindex')
79
+
80
+ useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
81
+ }
82
+
83
+ contentContainerTargetConnected() {
84
+ // Datepicker is shown as a dialog on small screens
85
+ const styles = window.getComputedStyle(this.contentContainerTarget)
86
+ this.isMobile = styles.translate === '-50%'
87
+ }
88
+
89
+ toggle() {
90
+ if (this.isOpenValue) {
91
+ this.close()
92
+ } else {
93
+ this.open()
94
+ }
95
+ }
96
+
97
+ open() {
98
+ this.isOpenValue = true
99
+ }
100
+
101
+ close() {
102
+ this.isOpenValue = false
103
+ }
104
+
105
+ inputBlur() {
106
+ let dateDisplay = ''
107
+ const date = this.calendar.context.selectedDates[0]
108
+ if (date) {
109
+ dateDisplay = dayjs(date).format(this.format)
110
+ }
111
+ this.inputTarget.value = dateDisplay
112
+ this.inputContainerTarget.dataset.focus = 'false'
113
+ }
114
+
115
+ inputDate(event: KeyboardEvent) {
116
+ const value = (event.target as HTMLInputElement).value
117
+ if (value.length === 0) {
118
+ this.calendar.set({
119
+ selectedDates: [],
120
+ })
121
+ this.dateValue = ''
122
+ }
123
+ if (value.length > 0 && dayjs(value, this.format, true).isValid()) {
124
+ const dayjsDate = dayjs(value, this.format).format(DAYJS_FORMAT)
125
+ this.calendar.set({
126
+ selectedDates: [dayjsDate],
127
+ })
128
+ this.dateValue = dayjsDate
129
+ }
130
+ }
131
+
132
+ setContainerFocus() {
133
+ this.inputContainerTarget.dataset.focus = 'true'
134
+ }
135
+
136
+ clickOutside(event: MouseEvent) {
137
+ const target = event.target as HTMLElement
138
+ // Let trigger handle state
139
+ if (target === this.triggerTarget) return
140
+ if (this.triggerTarget.contains(target)) return
141
+ if (this.triggerTarget.id === target.getAttribute('for')) return
142
+ this.close()
143
+ }
144
+
145
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
146
+ if (isOpen) {
147
+ showContent({
148
+ trigger: this.triggerTarget,
149
+ content: this.contentTarget,
150
+ contentContainer: this.contentContainerTarget,
151
+ })
152
+
153
+ // Prevent width from changing when changing to month/year view
154
+ if (!this.contentTarget.dataset.width) {
155
+ const contentWidth = this.contentTarget.offsetWidth
156
+ this.contentTarget.dataset.width = `${contentWidth}`
157
+
158
+ this.contentTarget.style.maxWidth = `${contentWidth}px`
159
+ this.contentTarget.style.minWidth = `${contentWidth}px`
160
+ }
161
+
162
+ if (this.isMobile) {
163
+ lockScroll(this.contentTarget.id)
164
+ this.overlayTarget.style.display = ''
165
+ this.overlayTarget.dataset.state = 'open'
166
+ } else {
167
+ this.cleanup = initFloatingUi({
168
+ referenceElement: this.hasInputTarget
169
+ ? this.inputTarget
170
+ : this.triggerTarget,
171
+ floatingElement: this.contentContainerTarget,
172
+ side: this.contentTarget.dataset.side,
173
+ align: this.contentTarget.dataset.align,
174
+ sideOffset: 4,
175
+ })
176
+ }
177
+
178
+ let elementToFocus = null as HTMLElement | null
179
+ const focusableElements = getFocusableElements(this.contentTarget)
180
+
181
+ const selectedElement = Array.from(focusableElements).find(
182
+ (e) => e.ariaSelected,
183
+ ) as HTMLElement
184
+
185
+ const currentElement = this.contentTarget.querySelector(
186
+ '[aria-current]',
187
+ ) as HTMLElement
188
+
189
+ if (selectedElement) {
190
+ elementToFocus = selectedElement
191
+ } else if (currentElement) {
192
+ const firstElementChild =
193
+ currentElement.firstElementChild as HTMLElement
194
+ elementToFocus = firstElementChild
195
+ } else {
196
+ elementToFocus = focusableElements[0]
197
+ }
198
+
199
+ focusElement(elementToFocus)
200
+
201
+ this.setupEventListeners()
202
+ } else {
203
+ hideContent({
204
+ trigger: this.triggerTarget,
205
+ content: this.contentTarget,
206
+ contentContainer: this.contentContainerTarget,
207
+ })
208
+
209
+ if (this.isMobile) {
210
+ unlockScroll(this.contentTarget.id)
211
+ this.overlayTarget.style.display = 'none'
212
+ this.overlayTarget.dataset.state = 'closed'
213
+ }
214
+
215
+ if (previousIsOpen) {
216
+ focusTrigger(this.triggerTarget)
217
+ }
218
+
219
+ this.cleanupEventListeners()
220
+ }
221
+ }
222
+
223
+ dateValueChanged(value: string) {
224
+ if (value && value.length > 0) {
225
+ const dayjsDate = dayjs(value)
226
+ const formattedDate = dayjsDate.format(this.format)
227
+
228
+ if (this.hasInputTarget) this.inputTarget.value = formattedDate
229
+ if (this.hasTriggerTextTarget) {
230
+ this.triggerTarget.dataset.hasValue = 'true'
231
+ this.triggerTextTarget.textContent = formattedDate
232
+ }
233
+
234
+ this.hiddenInputTarget.value = dayjsDate.utc().format()
235
+ } else {
236
+ if (this.hasInputTarget) this.inputTarget.value = ''
237
+
238
+ if (this.hasTriggerTextTarget) {
239
+ this.triggerTarget.dataset.hasValue = 'false'
240
+ if (this.triggerTarget.dataset.placeholder) {
241
+ this.triggerTextTarget.textContent =
242
+ this.triggerTarget.dataset.placeholder
243
+ } else {
244
+ this.triggerTextTarget.textContent = ''
245
+ }
246
+ }
247
+
248
+ this.hiddenInputTarget.value = ''
249
+ }
250
+ }
251
+
252
+ disconnect() {
253
+ this.cleanupEventListeners()
254
+ }
255
+
256
+ protected onClickDate(self: Calendar) {
257
+ const date = self.context.selectedDates[0]
258
+
259
+ if (date) {
260
+ this.dateValue = date
261
+ this.close()
262
+ } else {
263
+ this.dateValue = ''
264
+ }
265
+ }
266
+
267
+ protected setupEventListeners() {
268
+ document.addEventListener('keydown', this.DOMKeydownListener)
269
+ }
270
+
271
+ protected cleanupEventListeners() {
272
+ if (this.cleanup) this.cleanup()
273
+ document.removeEventListener('keydown', this.DOMKeydownListener)
274
+ }
275
+
276
+ protected getOptions() {
277
+ let options = {
278
+ type: 'default',
279
+ enableJumpToSelectedDate: true,
280
+ onClickDate: this.onClickDateListener,
281
+ } as Options
282
+
283
+ const date = this.element.dataset.value
284
+
285
+ if (date && dayjs(date).isValid()) {
286
+ const dayjsDate = dayjs(date).format(DAYJS_FORMAT)
287
+ options.selectedDates = [dayjsDate]
288
+ }
289
+
290
+ try {
291
+ options = {
292
+ ...options,
293
+ ...JSON.parse(this.element.dataset.options || ''),
294
+ }
295
+ } catch {
296
+ // noop
297
+ }
298
+
299
+ if (options.selectedDates && options.selectedDates.length > 0) {
300
+ this.dateValue = `${options.selectedDates[0]}`
301
+ }
302
+
303
+ return options
304
+ }
305
+
306
+ protected onDOMKeydown(event: KeyboardEvent) {
307
+ if (!this.isOpenValue) return
308
+
309
+ const key = event.key
310
+
311
+ if (key === 'Escape') {
312
+ this.close()
313
+ } else if (key === 'Tab') {
314
+ handleTabNavigation(this.contentTarget, event)
315
+ } else if (
316
+ ['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft'].includes(key) &&
317
+ document.activeElement != this.inputTarget
318
+ ) {
319
+ event.preventDefault()
320
+ }
321
+ }
322
+
323
+ protected setupInputMask() {
324
+ const im = new Inputmask(this.format.replace(/[^/]/g, '9'), {
325
+ showMaskOnHover: false,
326
+ })
327
+ im.mask(this.inputTarget)
328
+ }
329
+ }
330
+
331
+ type DatePicker = InstanceType<typeof DatePickerController>
332
+
333
+ export { DatePickerController }
334
+ export type { DatePicker }