shadcn_phlexcomponents 0.1.5 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -0
  3. data/app/javascript/controllers/accordion_controller.js +7 -16
  4. data/app/javascript/controllers/alert_dialog_controller.js +5 -141
  5. data/app/javascript/controllers/combobox_controller.js +20 -0
  6. data/app/javascript/controllers/date_picker_controller.js +199 -64
  7. data/app/javascript/controllers/date_range_picker_controller.js +289 -176
  8. data/app/javascript/controllers/dialog_controller.js +19 -64
  9. data/app/javascript/controllers/dropdown_menu_controller.js +15 -37
  10. data/app/javascript/controllers/form_field_controller.js +24 -0
  11. data/app/javascript/controllers/hover_card_controller.js +1 -22
  12. data/app/javascript/controllers/popover_controller.js +20 -31
  13. data/app/javascript/controllers/select_controller.js +32 -52
  14. data/app/javascript/controllers/sidebar_trigger_controller.js +1 -1
  15. data/app/javascript/controllers/toast_controller.js +2 -2
  16. data/app/javascript/controllers/tooltip_controller.js +1 -2
  17. data/app/javascript/shadcn_phlexcomponents.js +53 -0
  18. data/app/javascript/utils.js +184 -0
  19. data/app/stylesheets/date_picker.css +212 -0
  20. data/lib/install/install_shadcn_phlexcomponents.rb +7 -7
  21. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion.rb +1 -1
  22. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_content.rb +1 -1
  23. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_item.rb +1 -1
  24. data/lib/{components → shadcn_phlexcomponents/components/accordion}/accordion_trigger.rb +5 -4
  25. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog.rb +1 -1
  26. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_action.rb +1 -1
  27. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_action_to.rb +1 -1
  28. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_cancel.rb +1 -1
  29. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_content.rb +2 -2
  30. data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_trigger.rb +2 -2
  31. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar.rb +1 -1
  32. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar_fallback.rb +1 -1
  33. data/lib/{components → shadcn_phlexcomponents/components/avatar}/avatar_image.rb +1 -1
  34. data/lib/{components → shadcn_phlexcomponents/components/badge}/badge.rb +1 -1
  35. data/lib/{components → shadcn_phlexcomponents/components}/base.rb +10 -0
  36. data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb.rb +2 -0
  37. data/lib/{components → shadcn_phlexcomponents/components/button}/button.rb +5 -5
  38. data/lib/{components → shadcn_phlexcomponents/components/checkbox}/checkbox.rb +5 -5
  39. data/lib/{components → shadcn_phlexcomponents/components/checkbox_group}/checkbox_group.rb +27 -15
  40. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible.rb +1 -1
  41. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible_content.rb +1 -1
  42. data/lib/{components → shadcn_phlexcomponents/components/collapsible}/collapsible_trigger.rb +2 -2
  43. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker.rb +87 -0
  44. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_content.rb +45 -0
  45. data/lib/shadcn_phlexcomponents/components/date_picker/date_picker_trigger.rb +64 -0
  46. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker.rb +105 -0
  47. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_content.rb +9 -0
  48. data/lib/shadcn_phlexcomponents/components/date_range_picker/date_range_picker_trigger.rb +9 -0
  49. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog.rb +8 -8
  50. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_close.rb +1 -1
  51. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_content.rb +3 -3
  52. data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_trigger.rb +2 -2
  53. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu.rb +1 -1
  54. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_content.rb +9 -9
  55. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_item.rb +8 -8
  56. data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_trigger.rb +5 -5
  57. data/lib/shadcn_phlexcomponents/components/form/form.rb +139 -0
  58. data/lib/shadcn_phlexcomponents/components/form/form_checkbox.rb +83 -0
  59. data/lib/shadcn_phlexcomponents/components/form/form_checkbox_group.rb +116 -0
  60. data/lib/shadcn_phlexcomponents/components/form/form_date_picker.rb +47 -0
  61. data/lib/shadcn_phlexcomponents/components/form/form_date_range_picker.rb +96 -0
  62. data/lib/{components → shadcn_phlexcomponents/components/form}/form_error.rb +6 -2
  63. data/lib/shadcn_phlexcomponents/components/form/form_helpers.rb +108 -0
  64. data/lib/{components → shadcn_phlexcomponents/components/form}/form_hint.rb +6 -2
  65. data/lib/shadcn_phlexcomponents/components/form/form_radio_group.rb +107 -0
  66. data/lib/shadcn_phlexcomponents/components/form/form_select.rb +65 -0
  67. data/lib/shadcn_phlexcomponents/components/form/form_switch.rb +66 -0
  68. data/lib/shadcn_phlexcomponents/components/form/form_textarea.rb +60 -0
  69. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card.rb +1 -1
  70. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card_content.rb +1 -1
  71. data/lib/{components → shadcn_phlexcomponents/components/hover_card}/hover_card_trigger.rb +1 -1
  72. data/lib/{components → shadcn_phlexcomponents/components/input}/input.rb +1 -1
  73. data/lib/{components → shadcn_phlexcomponents/components/loading_button}/loading_button.rb +1 -1
  74. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover.rb +1 -1
  75. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover_content.rb +6 -6
  76. data/lib/{components → shadcn_phlexcomponents/components/popover}/popover_trigger.rb +2 -3
  77. data/lib/{components → shadcn_phlexcomponents/components/progress}/progress.rb +3 -3
  78. data/lib/{components → shadcn_phlexcomponents/components/radio_group}/radio_group.rb +33 -7
  79. data/lib/{components → shadcn_phlexcomponents/components/radio_group}/radio_group_item.rb +7 -7
  80. data/lib/{components → shadcn_phlexcomponents/components/select}/select.rb +22 -12
  81. data/lib/{components → shadcn_phlexcomponents/components/select}/select_content.rb +6 -6
  82. data/lib/{components → shadcn_phlexcomponents/components/select}/select_group.rb +1 -1
  83. data/lib/{components → shadcn_phlexcomponents/components/select}/select_item.rb +8 -8
  84. data/lib/{components → shadcn_phlexcomponents/components/select}/select_label.rb +1 -1
  85. data/lib/{components → shadcn_phlexcomponents/components/select}/select_trigger.rb +10 -10
  86. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet.rb +1 -1
  87. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_close.rb +1 -1
  88. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_content.rb +3 -3
  89. data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_trigger.rb +2 -2
  90. data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar.rb +3 -3
  91. data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_trigger.rb +2 -2
  92. data/lib/{components → shadcn_phlexcomponents/components/switch}/switch.rb +4 -4
  93. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs.rb +2 -2
  94. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_content.rb +1 -1
  95. data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_trigger.rb +4 -4
  96. data/lib/{components → shadcn_phlexcomponents/components/textarea}/textarea.rb +3 -2
  97. data/lib/{components → shadcn_phlexcomponents/components/theme_switcher}/theme_switcher.rb +2 -2
  98. data/lib/{components → shadcn_phlexcomponents/components/toast}/toast.rb +7 -7
  99. data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_container.rb +1 -1
  100. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip.rb +3 -3
  101. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip_content.rb +1 -1
  102. data/lib/{components → shadcn_phlexcomponents/components/tooltip}/tooltip_trigger.rb +1 -1
  103. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  104. metadata +157 -144
  105. data/app/assets/tailwind/vanilla-calendar-pro.css +0 -466
  106. data/app/javascript/controllers/sheet_controller.js +0 -159
  107. data/lib/components/combobox.rb +0 -57
  108. data/lib/components/combobox_item.rb +0 -9
  109. data/lib/components/date_picker.rb +0 -94
  110. data/lib/components/date_range_picker.rb +0 -113
  111. data/lib/components/form.rb +0 -59
  112. /data/app/{assets/tailwind → stylesheets}/choices.css +0 -0
  113. /data/app/{assets/tailwind → stylesheets}/tailwindcss-animate.css +0 -0
  114. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert.rb +0 -0
  115. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert_description.rb +0 -0
  116. /data/lib/{components → shadcn_phlexcomponents/components/alert}/alert_title.rb +0 -0
  117. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_description.rb +0 -0
  118. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_footer.rb +0 -0
  119. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_header.rb +0 -0
  120. /data/lib/{components → shadcn_phlexcomponents/components/alert_dialog}/alert_dialog_title.rb +0 -0
  121. /data/lib/{components → shadcn_phlexcomponents/components/aspect_ratio}/aspect_ratio.rb +0 -0
  122. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_ellipsis.rb +0 -0
  123. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_item.rb +0 -0
  124. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_link.rb +0 -0
  125. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_page.rb +0 -0
  126. /data/lib/{components → shadcn_phlexcomponents/components/breadcrumb}/breadcrumb_separator.rb +0 -0
  127. /data/lib/{components → shadcn_phlexcomponents/components/card}/card.rb +0 -0
  128. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_content.rb +0 -0
  129. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_description.rb +0 -0
  130. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_footer.rb +0 -0
  131. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_header.rb +0 -0
  132. /data/lib/{components → shadcn_phlexcomponents/components/card}/card_title.rb +0 -0
  133. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_description.rb +0 -0
  134. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_footer.rb +0 -0
  135. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_header.rb +0 -0
  136. /data/lib/{components → shadcn_phlexcomponents/components/dialog}/dialog_title.rb +0 -0
  137. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_item_to.rb +0 -0
  138. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_label.rb +0 -0
  139. /data/lib/{components → shadcn_phlexcomponents/components/dropdown_menu}/dropdown_menu_separator.rb +0 -0
  140. /data/lib/{components → shadcn_phlexcomponents/components/form}/form_input.rb +0 -0
  141. /data/lib/{components → shadcn_phlexcomponents/components/label}/label.rb +0 -0
  142. /data/lib/{components → shadcn_phlexcomponents/components/link}/link.rb +0 -0
  143. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination.rb +0 -0
  144. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_ellipsis.rb +0 -0
  145. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_link.rb +0 -0
  146. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_next.rb +0 -0
  147. /data/lib/{components → shadcn_phlexcomponents/components/pagination}/pagination_previous.rb +0 -0
  148. /data/lib/{components → shadcn_phlexcomponents/components/separator}/separator.rb +0 -0
  149. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_description.rb +0 -0
  150. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_footer.rb +0 -0
  151. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_header.rb +0 -0
  152. /data/lib/{components → shadcn_phlexcomponents/components/sheet}/sheet_title.rb +0 -0
  153. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_container.rb +0 -0
  154. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_content.rb +0 -0
  155. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_footer.rb +0 -0
  156. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group.rb +0 -0
  157. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group_content.rb +0 -0
  158. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_group_label.rb +0 -0
  159. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_header.rb +0 -0
  160. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_inset.rb +0 -0
  161. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu.rb +0 -0
  162. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_button.rb +0 -0
  163. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_item.rb +0 -0
  164. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub.rb +0 -0
  165. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub_button.rb +0 -0
  166. /data/lib/{components → shadcn_phlexcomponents/components/sidebar}/sidebar_menu_sub_item.rb +0 -0
  167. /data/lib/{components → shadcn_phlexcomponents/components/skeleton}/skeleton.rb +0 -0
  168. /data/lib/{components → shadcn_phlexcomponents/components/table}/table.rb +0 -0
  169. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_body.rb +0 -0
  170. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_caption.rb +0 -0
  171. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_cell.rb +0 -0
  172. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_footer.rb +0 -0
  173. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_head.rb +0 -0
  174. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_header.rb +0 -0
  175. /data/lib/{components → shadcn_phlexcomponents/components/table}/table_row.rb +0 -0
  176. /data/lib/{components → shadcn_phlexcomponents/components/tabs}/tabs_list.rb +0 -0
  177. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_action.rb +0 -0
  178. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_action_to.rb +0 -0
  179. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_content.rb +0 -0
  180. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_description.rb +0 -0
  181. /data/lib/{components → shadcn_phlexcomponents/components/toast}/toast_title.rb +0 -0
@@ -1,231 +1,344 @@
1
- import { Controller } from '@hotwired/stimulus'
1
+ import { FOCUS_DELAY, initFloatingUi, showOverlay, hideOverlay } from '../utils'
2
2
  import { Calendar } from 'vanilla-calendar-pro'
3
+ import PopoverController from './popover_controller'
3
4
  import dayjs from 'dayjs'
5
+ import Inputmask from 'inputmask'
4
6
  import customParseFormat from 'dayjs/plugin/customParseFormat'
5
7
  import utc from 'dayjs/plugin/utc'
6
8
  dayjs.extend(customParseFormat)
7
9
  dayjs.extend(utc)
8
10
 
9
- // window.dayjs = dayjs
10
- export default class extends Controller {
11
+ const DELIMITER = ' - '
12
+
13
+ export default class extends PopoverController {
11
14
  static targets = [
12
- 'startDateInput',
13
- 'endDateInput',
15
+ 'trigger',
16
+ 'triggerText',
17
+ 'contentWrapper',
18
+ 'content',
19
+ 'input',
14
20
  'startDateHiddenInput',
15
21
  'endDateHiddenInput',
16
- 'clearButton',
17
- 'calendarIcon',
22
+ 'inputContainer',
23
+ 'calendar',
18
24
  ]
19
25
 
26
+ static values = {
27
+ startDate: String,
28
+ endDate: String,
29
+ }
30
+
20
31
  connect() {
21
- this.startDate = this.element.dataset.startDate
22
- this.endDate = this.element.dataset.endDate
32
+ super.connect()
33
+
23
34
  this.format = this.element.dataset.format
24
- this.type = this.element.dataset.type
25
35
  const settings = this.getSettings()
26
- settings.type = 'multiple'
27
- settings.selectionDatesMode = 'multiple-ranged'
28
- settings.displayMonthsCount = 2
29
- settings.monthsToSwitch = 1
30
- settings.displayDatesOutside = false
31
-
32
- if (this.startDate && dayjs(this.startDate).isValid()) {
33
- const dayjsDate = dayjs(this.startDate)
34
- const formattedDate = dayjsDate.format(this.format)
35
- this.startDateInputTarget.value = formattedDate
36
- settings.selectedDates = [dayjsDate.format('YYYY-MM-DD')]
36
+ settings.selectedDates = []
37
+ const startDate = this.element.dataset.startDate
38
+ const endDate = this.element.dataset.endDate
39
+
40
+ this.onClickDateListener = this.onClickDate.bind(this)
41
+
42
+ if (startDate && dayjs(startDate).isValid()) {
43
+ const date = dayjs(startDate).format('YYYY-MM-DD')
44
+ settings.selectedDates.push(date)
45
+ this.startDateValue = date
37
46
  }
38
47
 
39
- if (this.endDate && dayjs(this.endDate).isValid()) {
40
- const dayjsDate = dayjs(this.endDate)
41
- const formattedDate = dayjsDate.format(this.format)
42
- this.endDateInputTarget.value = formattedDate
43
- settings.selectedDates = [
44
- ...settings.selectedDates,
45
- dayjsDate.format('YYYY-MM-DD'),
46
- ]
48
+ if (endDate && dayjs(endDate).isValid()) {
49
+ const date = dayjs(endDate).format('YYYY-MM-DD')
50
+ settings.selectedDates.push(date)
51
+ this.endDateValue = date
47
52
  }
48
53
 
49
- const calendar = new Calendar(this.element, {
50
- inputMode: true,
54
+ this.calendar = new Calendar(this.calendarTarget, {
51
55
  enableJumpToSelectedDate: true,
52
56
  ...settings,
53
- onShow(self) {
54
- const controllerElement = self.context.inputElement
55
-
56
- const startDateInput = controllerElement.querySelector(
57
- '[data-shadcn-phlexcomponents--date-range-picker-target="startDateInput"]',
58
- )
59
-
60
- const endDateInput = controllerElement.querySelector(
61
- '[data-shadcn-phlexcomponents--date-range-picker-target="endDateInput"]',
62
- )
63
-
64
- controllerElement.dataset.focus = 'true'
65
- if (document.activeElement !== endDateInput) {
66
- startDateInput.focus()
67
- }
68
- },
69
- onHide(self) {
70
- const controllerElement = self.context.inputElement
71
- controllerElement.dataset.focus = 'false'
72
- },
73
- onClickDate(self, event) {
74
- const dates = self.context.selectedDates.filter((date) => !!date)
75
- const controllerElement = self.context.inputElement
76
-
77
- const startDateInput = controllerElement.querySelector(
78
- '[data-shadcn-phlexcomponents--date-range-picker-target="startDateInput"]',
79
- )
80
-
81
- const endDateInput = controllerElement.querySelector(
82
- '[data-shadcn-phlexcomponents--date-range-picker-target="endDateInput"]',
83
- )
84
-
85
- const startDateHiddenInput = controllerElement.querySelector(
86
- '[data-shadcn-phlexcomponents--date-range-picker-target="startDateHiddenInput"]',
87
- )
88
-
89
- const endDateHiddenInput = controllerElement.querySelector(
90
- '[data-shadcn-phlexcomponents--date-range-picker-target="endDateHiddenInput"]',
91
- )
92
-
93
- if (dates.length > 0) {
94
- const startDate = dates[0]
95
- const endDate = dates[1]
96
-
97
- const formattedStartDate = dayjs(startDate).format(
98
- controllerElement.dataset.format,
99
- )
100
- startDateInput.value = formattedStartDate
101
- controllerElement.dataset.hasValue = 'true'
102
- const utcStartDate = dayjs(startDate).utc().format()
103
- startDateHiddenInput.value = utcStartDate
104
-
105
- if (endDate) {
106
- const formattedEndDate = dayjs(endDate).format(
107
- controllerElement.dataset.format,
108
- )
109
- endDateInput.value = formattedEndDate
110
- const utcEndDate = dayjs(endDate).utc().format()
111
- endDateHiddenInput.value = utcEndDate
112
- } else {
113
- endDateInput.value = ''
114
- endDateHiddenInput.value = ''
115
- }
116
- } else {
117
- self.context.inputElement.value = ''
118
- endDateInput.value = ''
119
- controllerElement.dataset.hasValue = 'false'
120
- startDateHiddenInput.value = ''
121
- endDateHiddenInput.value = ''
122
- }
123
- },
57
+ onClickDate: this.onClickDateListener,
124
58
  })
125
- calendar.init()
126
59
 
127
- this.calendar = calendar
128
- }
60
+ this.calendar.init()
61
+
62
+ if (this.hasInputTarget) {
63
+ const pattern = this.format.replace(/[^\/]/g, '9')
64
+ const im = new Inputmask(`${pattern}${DELIMITER}${pattern}`, {
65
+ showMaskOnHover: false,
66
+ })
67
+ im.mask(this.inputTarget)
68
+ }
129
69
 
130
- openCalendar(event) {
131
- this.calendar.show()
70
+ this.calendarTarget.removeAttribute('tabindex')
132
71
  }
133
72
 
134
- closeCalendar(event) {
135
- const key = event.key
73
+ inputBlur() {
74
+ const dates = this.calendar.context.selectedDates
75
+ const startDate = dates[0]
76
+ const endDate = dates[1]
77
+ let datesDisplay = ''
78
+
79
+ if (startDate) {
80
+ datesDisplay = `${dayjs(startDate).format(this.format)}${DELIMITER}`
81
+ }
136
82
 
137
- switch (key) {
138
- case 'Tab':
139
- if (
140
- event.target.dataset[
141
- 'shadcnPhlexcomponents-DateRangePickerTarget'
142
- ] === 'startDateInput'
143
- ) {
144
- if (event.shiftKey) {
145
- this.calendar.hide()
146
- }
147
- } else {
148
- if (!event.shiftKey) {
149
- this.calendar.hide()
150
- }
151
- }
152
-
153
- break
154
- case 'Escape':
155
- this.calendar.hide()
156
- break
157
- default:
158
- break
83
+ if (endDate) {
84
+ datesDisplay = `${datesDisplay}${dayjs(endDate).format(this.format)}`
159
85
  }
86
+
87
+ this.inputTarget.value = datesDisplay
88
+ this.inputContainerTarget.dataset.focus = false
160
89
  }
161
90
 
162
91
  changeDate(event) {
163
92
  const value = event.target.value
164
- const dates = this.calendar.selectedDates.filter((date) => !!date)
93
+ const dates = value.split(DELIMITER).filter((d) => d.length > 0)
165
94
 
166
- if (
167
- event.target.dataset['shadcnPhlexcomponents-DateRangePickerTarget'] ===
168
- 'startDateInput'
169
- ) {
170
- if (dayjs(value, this.format, true).isValid()) {
171
- const dayjsDate = dayjs(value, this.format)
172
- dates[0] = dayjsDate.format('YYYY-MM-DD')
173
-
174
- this.calendar.set({
175
- selectedDates: dates,
176
- })
177
- this.element.dataset.hasValue = 'true'
95
+ if (dates.length > 0) {
96
+ const startDate = dates[0]
97
+ const endDate = dates[1]
98
+ let selectedDates = this.calendar.context.selectedDates
99
+
100
+ if (dayjs(startDate, this.format, true).isValid()) {
101
+ const dayjsDate = dayjs(value, this.format).format('YYYY-MM-DD')
102
+ selectedDates[0] = dayjsDate
178
103
  }
179
- } else {
180
- if (dayjs(value, this.format, true).isValid()) {
181
- const dayjsDate = dayjs(value, this.format)
182
- dates[1] = dayjsDate.format('YYYY-MM-DD')
183
-
184
- this.calendar.set({
185
- selectedDates: dates,
186
- })
187
- this.element.dataset.hasValue = 'true'
104
+
105
+ if (dayjs(endDate, this.format, true).isValid()) {
106
+ const dayjsDate = dayjs(endDate, this.format).format('YYYY-MM-DD')
107
+ selectedDates[1] = dayjsDate
108
+ }
109
+
110
+ selectedDates = selectedDates.filter((d) => !!d)
111
+
112
+ this.calendar.set({
113
+ selectedDates: selectedDates,
114
+ })
115
+ if (selectedDates[0]) {
116
+ this.startDateValue = selectedDates[0]
117
+ }
118
+ if (selectedDates[1]) {
119
+ this.endDateValue = selectedDates[1]
188
120
  }
121
+ } else {
122
+ this.calendar.set({
123
+ selectedDates: [],
124
+ })
125
+ this.startDateValue = ''
126
+ this.endDateValue = ''
189
127
  }
190
128
  }
191
129
 
192
- openCalendar() {
130
+ onOpen() {
193
131
  setTimeout(() => {
194
- this.calendar.show()
195
- }, 125)
132
+ this.focusCalendar()
133
+ }, FOCUS_DELAY * 1.5)
134
+
135
+ this.cleanup = initFloatingUi(
136
+ this.hasInputTarget ? this.inputTarget : this.triggerTarget,
137
+ this.contentWrapperTarget,
138
+ 'bottom-start',
139
+ )
140
+
141
+ if (!this.contentTarget.dataset.width) {
142
+ const contentWidth = this.contentTarget.offsetWidth
143
+ this.contentTarget.dataset.width = contentWidth
144
+
145
+ this.contentTarget.style.maxWidth = `${contentWidth}px`
146
+ this.contentTarget.style.minWidth = `${contentWidth}px`
147
+ }
148
+
149
+ showOverlay('md:hidden')
196
150
  }
197
151
 
198
- clear() {
199
- this.startDateInputTarget.value = ''
152
+ focusCalendar() {
153
+ const focusableElements = this.contentTarget.querySelectorAll(
154
+ 'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
155
+ )
156
+
157
+ const selectedElement = Array.from(focusableElements).find(
158
+ (e) => e.ariaSelected,
159
+ )
160
+
161
+ const currentElement = this.contentTarget.querySelector('[aria-current]')
162
+
163
+ if (selectedElement) {
164
+ selectedElement.focus()
165
+ } else if (currentElement) {
166
+ currentElement.firstElementChild.focus()
167
+ }
168
+ }
200
169
 
201
- if (this.hasEndDateInputTarget) {
202
- this.endDateInputTarget.value = ''
170
+ getSettings() {
171
+ const defaultSettings = {
172
+ type: 'multiple',
173
+ selectionDatesMode: 'multiple-ranged',
174
+ displayMonthsCount: 2,
175
+ monthsToSwitch: 1,
176
+ displayDatesOutside: false,
203
177
  }
178
+ try {
179
+ return {
180
+ ...defaultSettings,
181
+ ...JSON.parse(this.element.dataset.settings),
182
+ }
183
+ } catch {
184
+ return defaultSettings
185
+ }
186
+ }
204
187
 
205
- this.calendar.set({
206
- selectedDates: [],
207
- })
188
+ onDOMKeydown(event) {
189
+ if (!this.isOpen()) return
190
+
191
+ const key = event.key
208
192
 
209
- this.element.dataset.hasValue = 'false'
193
+ const focusableElements = this.contentTarget.querySelectorAll(
194
+ 'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
195
+ )
196
+
197
+ const firstElement = focusableElements[0]
198
+ const lastElement = focusableElements[focusableElements.length - 1]
199
+
200
+ if (key === 'Escape') {
201
+ this.close()
202
+ } else if (key === 'Tab') {
203
+ // If Shift + Tab pressed on first element, go to last element
204
+ if (event.shiftKey && document.activeElement === firstElement) {
205
+ event.preventDefault()
206
+ lastElement.focus()
207
+ }
208
+ // If Tab pressed on last element, go to first element
209
+ else if (!event.shiftKey && document.activeElement === lastElement) {
210
+ event.preventDefault()
211
+ firstElement.focus()
212
+ }
213
+ } else if (
214
+ ['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft'].includes(key) &&
215
+ document.activeElement != this.inputTarget
216
+ ) {
217
+ event.preventDefault()
218
+ }
219
+ }
220
+
221
+ onDOMClick(event) {
222
+ if (!this.isOpen()) return
223
+ if (this.element.contains(event.target)) return
224
+
225
+ // Fix bug with clicking/pressing on Month/Year button will cause popover to close
226
+ if (
227
+ event.target.dataset.vcMonth ||
228
+ event.target.dataset.vcYear ||
229
+ event.target.dataset.vcYearsYear ||
230
+ event.target.dataset.vcMonthsMonth ||
231
+ event.target.dataset.vcArrow ||
232
+ event.target.dataset.vcGrid
233
+ )
234
+ return
235
+
236
+ this.close()
237
+ }
238
+
239
+ setContainerFocus() {
240
+ this.inputContainerTarget.dataset.focus = true
241
+ }
242
+
243
+ onClose() {
244
+ hideOverlay()
210
245
  }
211
246
 
212
- showClearButton() {
213
- if (this.element.dataset.hasValue === 'true') {
214
- this.clearButtonTarget.classList.remove('!hidden')
215
- this.calendarIconTarget.classList.add('hidden')
247
+ onClickDate(self, event) {
248
+ const dates = self.context.selectedDates
249
+
250
+ if (dates.length > 0) {
251
+ const startDate = dates[0]
252
+ const endDate = dates[1]
253
+
254
+ this.startDateValue = startDate
255
+
256
+ if (endDate) {
257
+ this.endDateValue = endDate
258
+ this.close()
259
+ } else {
260
+ this.endDateValue = ''
261
+ }
262
+ } else {
263
+ this.startDateValue = ''
264
+ this.endDateValue = ''
216
265
  }
217
266
  }
218
267
 
219
- hideClearButton() {
220
- this.clearButtonTarget.classList.add('!hidden')
221
- this.calendarIconTarget.classList.remove('hidden')
268
+ startDateValueChanged(value) {
269
+ const endDate = this.endDateValue
270
+ let datesDisplay = ''
271
+
272
+ if (value && value.length > 0) {
273
+ const dayjsDate = dayjs(value)
274
+ const formattedDate = dayjsDate.format(this.format)
275
+ this.startDateHiddenInputTarget.value = dayjsDate.utc().format()
276
+
277
+ if (endDate) {
278
+ datesDisplay = `${formattedDate}${DELIMITER}${dayjs(endDate).format(
279
+ this.format,
280
+ )}`
281
+ } else {
282
+ datesDisplay = `${formattedDate}${DELIMITER}`
283
+ }
284
+ } else {
285
+ this.startDateHiddenInputTarget.value = ''
286
+
287
+ if (endDate) {
288
+ datesDisplay = `${DELIMITER}${dayjs(endDate).format(this.format)}`
289
+ }
290
+ }
291
+
292
+ if (this.hasInputTarget) this.inputTarget.value = datesDisplay
293
+ if (this.hasTriggerTextTarget) {
294
+ const hasValue = (!!value && value.length > 0) || !!endDate
295
+
296
+ this.triggerTarget.dataset.hasValue = hasValue
297
+
298
+ if (this.triggerTarget.dataset.placeholder && !hasValue) {
299
+ this.triggerTextTarget.textContent =
300
+ this.triggerTarget.dataset.placeholder
301
+ } else {
302
+ this.triggerTextTarget.textContent = datesDisplay
303
+ }
304
+ }
222
305
  }
223
306
 
224
- getSettings() {
225
- try {
226
- return JSON.parse(this.element.dataset.settings)
227
- } catch {
228
- return {}
307
+ endDateValueChanged(value) {
308
+ const startDate = this.startDateValue
309
+ let datesDisplay = ''
310
+
311
+ if (value && value.length > 0) {
312
+ const dayjsDate = dayjs(value)
313
+ const formattedDate = dayjsDate.format(this.format)
314
+ this.endDateHiddenInputTarget.value = dayjsDate.utc().format()
315
+
316
+ if (startDate) {
317
+ datesDisplay = `${dayjs(startDate).format(
318
+ this.format,
319
+ )}${DELIMITER}${formattedDate}`
320
+ } else {
321
+ datesDisplay = `${DELIMITER}${formattedDate}`
322
+ }
323
+ } else {
324
+ this.endDateHiddenInputTarget.value = ''
325
+
326
+ if (startDate) {
327
+ datesDisplay = `${dayjs(startDate).format(this.format)}${DELIMITER}`
328
+ }
329
+ }
330
+
331
+ if (this.hasInputTarget) this.inputTarget.value = datesDisplay
332
+ if (this.hasTriggerTextTarget) {
333
+ const hasValue = (!!value && value.length > 0) || !!startDate
334
+ this.triggerTarget.dataset.hasValue = hasValue
335
+
336
+ if (this.triggerTarget.dataset.placeholder && !hasValue) {
337
+ this.triggerTextTarget.textContent =
338
+ this.triggerTarget.dataset.placeholder
339
+ } else {
340
+ this.triggerTextTarget.textContent = datesDisplay
341
+ }
229
342
  }
230
343
  }
231
344
  }
@@ -1,4 +1,11 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
+ import {
3
+ ANIMATION_OUT_DELAY,
4
+ openWithOverlay,
5
+ closeWithOverlay,
6
+ focusTrigger,
7
+ FOCUS_DELAY,
8
+ } from '../utils'
2
9
 
3
10
  export default class extends Controller {
4
11
  static targets = ['trigger', 'content']
@@ -17,95 +24,55 @@ export default class extends Controller {
17
24
  }
18
25
 
19
26
  open() {
20
- this.showOverlay()
27
+ openWithOverlay([this.contentElement])
21
28
  this.contentElement.classList.remove('hidden')
22
29
  this.contentElement.dataset.state = 'open'
23
30
  this.triggerTarget.ariaExpanded = true
24
31
  this.setupEventListeners()
25
- this.addInert()
26
32
 
27
- if (window.innerHeight < document.documentElement.scrollHeight) {
28
- document.body.dataset.scrollLocked = 1
29
- }
30
33
  document.body.appendChild(this.contentElement)
31
- this.firstElement.focus() // must be after appendChild
34
+
35
+ setTimeout(() => {
36
+ // must be after appendChild
37
+ this.firstElement.focus()
38
+ }, FOCUS_DELAY * 1.25)
32
39
  }
33
40
 
34
41
  close() {
42
+ closeWithOverlay()
35
43
  this.contentElement.dataset.state = 'closed'
36
44
  this.triggerTarget.ariaExpanded = false
37
45
  this.cleanupEventListeners()
38
- this.removeInert()
39
46
  this.element.appendChild(this.contentElement)
40
47
 
41
48
  setTimeout(() => {
42
49
  this.contentElement.classList.add('hidden')
43
- }, 100)
44
-
45
- setTimeout(() => {
46
- delete document.body.dataset.scrollLocked
47
- this.removeOverlay()
48
- }, 150)
50
+ }, ANIMATION_OUT_DELAY)
49
51
 
50
- this.focusTrigger()
51
- }
52
-
53
- focusTrigger() {
54
- if (this.triggerTarget.dataset.asChild === 'false') {
55
- this.triggerTarget.firstElementChild.focus()
56
- } else {
57
- this.triggerTarget.focus()
58
- }
52
+ focusTrigger(this.triggerTarget)
59
53
  }
60
54
 
61
55
  isOpen() {
62
56
  return this.triggerTarget.ariaExpanded === 'true'
63
57
  }
64
58
 
65
- showOverlay() {
66
- const elem = document.createElement('div')
67
- elem.classList.add(
68
- 'fixed',
69
- 'inset-0',
70
- 'z-50',
71
- 'bg-black/80',
72
- 'data-[state=open]:animate-in',
73
- 'data-[state=closed]:animate-out',
74
- 'data-[state=closed]:fade-out-0',
75
- 'data-[state=open]:fade-in-0',
76
- 'pointer-events-auto',
77
- )
78
- elem.dataset.state = 'open'
79
- elem.ariaHidden = true
80
- elem.dataset.overlay = true
81
- document.body.appendChild(elem)
82
- }
83
-
84
- removeOverlay() {
85
- if (document.querySelector('[data-overlay]')) {
86
- document.querySelector('[data-overlay]').remove()
87
- }
88
- }
89
-
90
59
  // Global listeners
91
60
  onDOMClick(event) {
92
61
  if (!this.isOpen()) return
93
62
 
94
63
  const target = event.target
95
64
  const trigger = event.target.closest(
96
- '[data-shadcn-phlexcomponents--dialog-target="trigger"]',
65
+ `[data-${this.identifier}-target="trigger"]`,
97
66
  )
98
67
 
99
68
  if (trigger) return
100
69
 
101
- const close = target.closest(
102
- '[data-action*="shadcn-phlexcomponents--dialog#close"]',
103
- )
70
+ const close = target.closest(`[data-action*="${this.identifier}#close"]`)
104
71
 
105
72
  if (
106
73
  close ||
107
74
  (target.dataset.action &&
108
- target.dataset.action.includes('shadcn-phlexcomponents--dialog#close'))
75
+ target.dataset.action.includes(`${this.identifier}#close`))
109
76
  )
110
77
  this.close()
111
78
 
@@ -144,16 +111,4 @@ export default class extends Controller {
144
111
  document.removeEventListener('click', this.DOMClickListener)
145
112
  document.removeEventListener('keydown', this.DOMKeydownListener)
146
113
  }
147
-
148
- addInert() {
149
- Array.from(document.body.children)
150
- .filter((el) => el !== this.contentElement)
151
- .forEach((el) => el.setAttribute('inert', ''))
152
- }
153
-
154
- removeInert() {
155
- Array.from(document.body.children)
156
- .filter((el) => el.hasAttribute('inert'))
157
- .forEach((el) => el.removeAttribute('inert'))
158
- }
159
114
  }