shadcn_phlexcomponents 0.1.0 → 0.1.1

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/tailwind/choices.css +324 -0
  3. data/app/assets/tailwind/vanilla-calendar-pro.css +5 -0
  4. data/app/javascript/controllers/combobox_controller.js +19 -276
  5. data/app/javascript/controllers/date_picker_controller.js +118 -0
  6. data/app/javascript/controllers/date_range_picker_controller.js +231 -0
  7. data/app/javascript/controllers/dropdown_menu_controller.js +1 -1
  8. data/app/javascript/controllers/hover_card_controller.js +33 -126
  9. data/app/javascript/controllers/sidebar_controller.js +0 -1
  10. data/app/javascript/controllers/tooltip_controller.js +32 -126
  11. data/lib/components/accordion.rb +3 -3
  12. data/lib/components/accordion_content.rb +12 -10
  13. data/lib/components/accordion_item.rb +3 -3
  14. data/lib/components/accordion_trigger.rb +3 -3
  15. data/lib/components/alert_description.rb +1 -1
  16. data/lib/components/alert_dialog.rb +2 -2
  17. data/lib/components/alert_dialog_action.rb +2 -2
  18. data/lib/components/alert_dialog_action_to.rb +5 -2
  19. data/lib/components/alert_dialog_cancel.rb +1 -1
  20. data/lib/components/alert_dialog_content.rb +1 -1
  21. data/lib/components/alert_dialog_description.rb +1 -1
  22. data/lib/components/alert_dialog_footer.rb +1 -1
  23. data/lib/components/alert_dialog_header.rb +1 -1
  24. data/lib/components/alert_dialog_title.rb +1 -1
  25. data/lib/components/alert_dialog_trigger.rb +2 -2
  26. data/lib/components/aspect_ratio.rb +1 -1
  27. data/lib/components/avatar.rb +1 -1
  28. data/lib/components/avatar_fallback.rb +1 -1
  29. data/lib/components/avatar_image.rb +1 -2
  30. data/lib/components/badge.rb +2 -8
  31. data/lib/components/base.rb +5 -5
  32. data/lib/components/breadcrumb.rb +1 -1
  33. data/lib/components/breadcrumb_ellipsis.rb +1 -1
  34. data/lib/components/breadcrumb_item.rb +1 -1
  35. data/lib/components/breadcrumb_link.rb +1 -1
  36. data/lib/components/breadcrumb_page.rb +1 -1
  37. data/lib/components/breadcrumb_separator.rb +1 -1
  38. data/lib/components/button.rb +1 -1
  39. data/lib/components/card.rb +1 -1
  40. data/lib/components/card_content.rb +1 -1
  41. data/lib/components/card_description.rb +1 -1
  42. data/lib/components/card_footer.rb +1 -1
  43. data/lib/components/card_header.rb +1 -1
  44. data/lib/components/card_title.rb +1 -1
  45. data/lib/components/checkbox.rb +14 -10
  46. data/lib/components/checkbox_group.rb +9 -8
  47. data/lib/components/collapsible.rb +2 -3
  48. data/lib/components/collapsible_content.rb +2 -3
  49. data/lib/components/collapsible_trigger.rb +5 -5
  50. data/lib/components/combobox.rb +57 -0
  51. data/lib/components/combobox_item.rb +9 -0
  52. data/lib/components/date_picker.rb +94 -0
  53. data/lib/components/date_range_picker.rb +113 -0
  54. data/lib/components/dialog.rb +1 -1
  55. data/lib/components/dialog_close.rb +1 -1
  56. data/lib/components/dialog_content.rb +2 -2
  57. data/lib/components/dialog_description.rb +1 -1
  58. data/lib/components/dialog_footer.rb +1 -1
  59. data/lib/components/dialog_header.rb +1 -1
  60. data/lib/components/dialog_title.rb +1 -1
  61. data/lib/components/dialog_trigger.rb +2 -2
  62. data/lib/components/dropdown_menu.rb +5 -5
  63. data/lib/components/dropdown_menu_content.rb +12 -9
  64. data/lib/components/dropdown_menu_item.rb +5 -6
  65. data/lib/components/dropdown_menu_item_to.rb +6 -3
  66. data/lib/components/dropdown_menu_label.rb +2 -3
  67. data/lib/components/dropdown_menu_separator.rb +5 -5
  68. data/lib/components/dropdown_menu_trigger.rb +9 -10
  69. data/lib/components/hover_card.rb +6 -6
  70. data/lib/components/hover_card_content.rb +8 -12
  71. data/lib/components/hover_card_trigger.rb +5 -11
  72. data/lib/components/input.rb +1 -1
  73. data/lib/components/label.rb +1 -2
  74. data/lib/components/link.rb +5 -2
  75. data/lib/components/loading_button.rb +1 -1
  76. data/lib/components/pagination.rb +4 -4
  77. data/lib/components/pagination_ellipsis.rb +3 -3
  78. data/lib/components/pagination_link.rb +5 -5
  79. data/lib/components/pagination_next.rb +5 -5
  80. data/lib/components/pagination_previous.rb +4 -4
  81. data/lib/components/popover.rb +6 -7
  82. data/lib/components/popover_content.rb +13 -10
  83. data/lib/components/popover_trigger.rb +5 -6
  84. data/lib/components/progress.rb +7 -7
  85. data/lib/components/radio_group.rb +4 -4
  86. data/lib/components/radio_group_item.rb +8 -8
  87. data/lib/components/select.rb +67 -72
  88. data/lib/components/select_content.rb +12 -7
  89. data/lib/components/select_group.rb +3 -3
  90. data/lib/components/select_item.rb +9 -8
  91. data/lib/components/select_label.rb +6 -5
  92. data/lib/components/select_trigger.rb +12 -10
  93. data/lib/components/separator.rb +3 -3
  94. data/lib/components/sheet.rb +9 -9
  95. data/lib/components/sheet_close.rb +4 -4
  96. data/lib/components/sheet_content.rb +13 -15
  97. data/lib/components/sheet_description.rb +3 -3
  98. data/lib/components/sheet_footer.rb +2 -2
  99. data/lib/components/sheet_header.rb +2 -2
  100. data/lib/components/sheet_title.rb +3 -3
  101. data/lib/components/sheet_trigger.rb +6 -6
  102. data/lib/components/sidebar.rb +30 -25
  103. data/lib/components/sidebar_container.rb +1 -1
  104. data/lib/components/sidebar_content.rb +1 -1
  105. data/lib/components/sidebar_footer.rb +2 -2
  106. data/lib/components/sidebar_group.rb +1 -1
  107. data/lib/components/sidebar_group_content.rb +1 -1
  108. data/lib/components/sidebar_group_label.rb +2 -2
  109. data/lib/components/sidebar_header.rb +2 -2
  110. data/lib/components/sidebar_inset.rb +1 -1
  111. data/lib/components/sidebar_menu.rb +2 -2
  112. data/lib/components/sidebar_menu_button.rb +5 -5
  113. data/lib/components/sidebar_menu_item.rb +1 -1
  114. data/lib/components/sidebar_menu_sub.rb +2 -2
  115. data/lib/components/sidebar_menu_sub_button.rb +7 -7
  116. data/lib/components/sidebar_menu_sub_item.rb +1 -1
  117. data/lib/components/sidebar_trigger.rb +5 -5
  118. data/lib/components/skeleton.rb +2 -2
  119. data/lib/components/switch.rb +10 -9
  120. data/lib/components/table.rb +7 -5
  121. data/lib/components/table_body.rb +2 -2
  122. data/lib/components/table_caption.rb +2 -2
  123. data/lib/components/table_cell.rb +2 -2
  124. data/lib/components/table_footer.rb +2 -2
  125. data/lib/components/table_head.rb +3 -3
  126. data/lib/components/table_header.rb +2 -2
  127. data/lib/components/table_row.rb +2 -2
  128. data/lib/components/tabs.rb +3 -3
  129. data/lib/components/tabs_content.rb +5 -5
  130. data/lib/components/tabs_list.rb +4 -4
  131. data/lib/components/tabs_trigger.rb +3 -3
  132. data/lib/components/textarea.rb +1 -1
  133. data/lib/components/theme_switcher.rb +2 -2
  134. data/lib/components/toast.rb +15 -14
  135. data/lib/components/toast_action.rb +5 -4
  136. data/lib/components/toast_action_to.rb +5 -2
  137. data/lib/components/toast_container.rb +5 -5
  138. data/lib/components/toast_content.rb +1 -1
  139. data/lib/components/toast_description.rb +1 -1
  140. data/lib/components/toast_title.rb +1 -1
  141. data/lib/components/tooltip.rb +8 -8
  142. data/lib/components/tooltip_content.rb +8 -11
  143. data/lib/components/tooltip_trigger.rb +9 -11
  144. data/lib/shadcn_phlexcomponents/alias.rb +3 -1
  145. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  146. metadata +9 -4
  147. data/app/javascript/controllers/datepicker_controller.js +0 -47
  148. data/lib/components/datepicker.rb +0 -38
@@ -0,0 +1,118 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { Calendar } from 'vanilla-calendar-pro'
3
+ import dayjs from 'dayjs'
4
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
5
+ import utc from 'dayjs/plugin/utc'
6
+ dayjs.extend(customParseFormat)
7
+ dayjs.extend(utc)
8
+
9
+ export default class extends Controller {
10
+ static targets = ['dateInput', 'hiddenInput', 'clearButton', 'calendarIcon']
11
+
12
+ connect() {
13
+ this.date = this.element.dataset.value
14
+ this.format = this.element.dataset.format
15
+ const settings = this.getSettings()
16
+ settings.type = 'default'
17
+
18
+ if (this.date && dayjs(this.date).isValid()) {
19
+ const dayjsDate = dayjs(this.date)
20
+ const formattedDate = dayjsDate.format(this.format)
21
+ this.dateInputTarget.value = formattedDate
22
+ settings.selectedDates = [dayjsDate.format('YYYY-MM-DD')]
23
+ }
24
+
25
+ const calendar = new Calendar(this.dateInputTarget, {
26
+ inputMode: true,
27
+ enableJumpToSelectedDate: true,
28
+ ...settings,
29
+ onClickDate(self, event) {
30
+ const date = self.context.selectedDates[0]
31
+
32
+ const controllerElement = self.context.inputElement.closest(
33
+ '[data-controller="shadcn-phlexcomponents--date-picker"]',
34
+ )
35
+
36
+ const hiddenInput = controllerElement.querySelector(
37
+ '[data-shadcn-phlexcomponents--date-picker-target="hiddenInput"]',
38
+ )
39
+
40
+ if (date) {
41
+ const formattedDate = dayjs(date).format(
42
+ controllerElement.dataset.format,
43
+ )
44
+ self.context.inputElement.value = formattedDate
45
+ controllerElement.dataset.hasValue = 'true'
46
+ const utcDate = dayjs(date).utc().format()
47
+ hiddenInput.value = utcDate
48
+ } else {
49
+ self.context.inputElement.value = ''
50
+ controllerElement.dataset.hasValue = 'false'
51
+ hiddenInput.value = ''
52
+ }
53
+ },
54
+ })
55
+ calendar.init()
56
+
57
+ this.calendar = calendar
58
+ }
59
+
60
+ inputBlur() {
61
+ const date = this.calendar.selectedDates[0]
62
+
63
+ if (date) {
64
+ const formattedDate = dayjs(date).format(this.format)
65
+ this.dateInputTarget.value = formattedDate
66
+ }
67
+ }
68
+
69
+ changeDate(event) {
70
+ const value = event.target.value
71
+
72
+ if (value.length > 0 && dayjs(value, this.format, true).isValid()) {
73
+ const dayjsDate = dayjs(value, this.format)
74
+ this.calendar.set({
75
+ selectedDates: [dayjsDate.format('YYYY-MM-DD')],
76
+ })
77
+ }
78
+ }
79
+
80
+ closeCalendar(event) {
81
+ const key = event.key
82
+
83
+ if (key === 'Escape' || key === 'Tab') {
84
+ this.calendar.hide()
85
+ }
86
+ }
87
+
88
+ clear() {
89
+ this.dateInputTarget.value = ''
90
+ this.hiddenInputTarget.value = ''
91
+
92
+ this.calendar.set({
93
+ selectedDates: [],
94
+ })
95
+
96
+ this.element.dataset.hasValue = 'false'
97
+ }
98
+
99
+ showClearButton() {
100
+ if (this.element.dataset.hasValue === 'true') {
101
+ this.clearButtonTarget.classList.remove('!hidden')
102
+ this.calendarIconTarget.classList.add('hidden')
103
+ }
104
+ }
105
+
106
+ hideClearButton() {
107
+ this.clearButtonTarget.classList.add('!hidden')
108
+ this.calendarIconTarget.classList.remove('hidden')
109
+ }
110
+
111
+ getSettings() {
112
+ try {
113
+ return JSON.parse(this.element.dataset.settings)
114
+ } catch {
115
+ return {}
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,231 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { Calendar } from 'vanilla-calendar-pro'
3
+ import dayjs from 'dayjs'
4
+ import customParseFormat from 'dayjs/plugin/customParseFormat'
5
+ import utc from 'dayjs/plugin/utc'
6
+ dayjs.extend(customParseFormat)
7
+ dayjs.extend(utc)
8
+
9
+ // window.dayjs = dayjs
10
+ export default class extends Controller {
11
+ static targets = [
12
+ 'startDateInput',
13
+ 'endDateInput',
14
+ 'startDateHiddenInput',
15
+ 'endDateHiddenInput',
16
+ 'clearButton',
17
+ 'calendarIcon',
18
+ ]
19
+
20
+ connect() {
21
+ this.startDate = this.element.dataset.startDate
22
+ this.endDate = this.element.dataset.endDate
23
+ this.format = this.element.dataset.format
24
+ this.type = this.element.dataset.type
25
+ 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')]
37
+ }
38
+
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
+ ]
47
+ }
48
+
49
+ const calendar = new Calendar(this.element, {
50
+ inputMode: true,
51
+ enableJumpToSelectedDate: true,
52
+ ...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
+ },
124
+ })
125
+ calendar.init()
126
+
127
+ this.calendar = calendar
128
+ }
129
+
130
+ openCalendar(event) {
131
+ this.calendar.show()
132
+ }
133
+
134
+ closeCalendar(event) {
135
+ const key = event.key
136
+
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
159
+ }
160
+ }
161
+
162
+ changeDate(event) {
163
+ const value = event.target.value
164
+ const dates = this.calendar.selectedDates.filter((date) => !!date)
165
+
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'
178
+ }
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'
188
+ }
189
+ }
190
+ }
191
+
192
+ openCalendar() {
193
+ setTimeout(() => {
194
+ this.calendar.show()
195
+ }, 125)
196
+ }
197
+
198
+ clear() {
199
+ this.startDateInputTarget.value = ''
200
+
201
+ if (this.hasEndDateInputTarget) {
202
+ this.endDateInputTarget.value = ''
203
+ }
204
+
205
+ this.calendar.set({
206
+ selectedDates: [],
207
+ })
208
+
209
+ this.element.dataset.hasValue = 'false'
210
+ }
211
+
212
+ showClearButton() {
213
+ if (this.element.dataset.hasValue === 'true') {
214
+ this.clearButtonTarget.classList.remove('!hidden')
215
+ this.calendarIconTarget.classList.add('hidden')
216
+ }
217
+ }
218
+
219
+ hideClearButton() {
220
+ this.clearButtonTarget.classList.add('!hidden')
221
+ this.calendarIconTarget.classList.remove('hidden')
222
+ }
223
+
224
+ getSettings() {
225
+ try {
226
+ return JSON.parse(this.element.dataset.settings)
227
+ } catch {
228
+ return {}
229
+ }
230
+ }
231
+ }
@@ -174,7 +174,7 @@ export default class extends Controller {
174
174
 
175
175
  const key = event.key
176
176
 
177
- if (key === 'Tab') event.preventDefault()
177
+ if (['Tab', 'Enter', ' '].includes(key)) event.preventDefault()
178
178
 
179
179
  if (key === 'Escape') {
180
180
  this.close()
@@ -1,135 +1,42 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
- import {
3
- computePosition,
4
- flip,
5
- shift,
6
- offset,
7
- autoUpdate,
8
- } from '@floating-ui/dom'
9
-
10
- export default class extends Controller {
11
- static targets = ['trigger', 'contentWrapper', 'content']
12
-
13
- connect() {
14
- this.DOMClickListener = this.onDOMClick.bind(this)
15
- this.DOMKeydownListener = this.onDOMKeydown.bind(this)
16
-
17
- if (this.triggerTarget.dataset.asChild === 'false') {
18
- this.triggerTarget.firstElementChild.addEventListener('focus', () => {
19
- this.open()
20
- })
21
-
22
- this.triggerTarget.firstElementChild.addEventListener('blur', () => {
23
- this.close()
24
- })
25
- } else {
26
- this.triggerTarget.addEventListener('focus', () => {
27
- this.open()
28
- })
29
-
30
- this.triggerTarget.addEventListener('blur', () => {
31
- this.close()
32
- })
33
- }
34
- }
35
-
36
- toggle() {
37
- if (this.isOpen()) {
38
- this.close()
39
- } else {
40
- this.openWithDelay()
2
+ import tippy from 'tippy.js'
3
+
4
+ const hideOnEsc = {
5
+ name: 'hideOnEsc',
6
+ defaultValue: true,
7
+ fn({ hide }) {
8
+ function onKeyDown(event) {
9
+ if (event.keyCode === 27) {
10
+ hide()
11
+ }
41
12
  }
42
- }
43
-
44
- isOpen() {
45
- return this.triggerTarget.dataset.state === 'open'
46
- }
47
13
 
48
- open() {
49
- this.contentWrapperTarget.classList.remove('hidden')
50
- this.triggerTarget.dataset.state = 'open'
51
- this.contentTarget.dataset.state = 'open'
52
- this.setupEventListeners()
53
-
54
- this.cleanup = autoUpdate(
55
- this.triggerTarget,
56
- this.contentWrapperTarget,
57
- () => {
58
- computePosition(this.triggerTarget, this.contentWrapperTarget, {
59
- placement: this.element.dataset.side,
60
- strategy: 'fixed',
61
- middleware: [flip(), shift(), offset(4)],
62
- }).then(({ x, y }) => {
63
- Object.assign(this.contentWrapperTarget.style, {
64
- left: `${x}px`,
65
- top: `${y}px`,
66
- })
67
- })
14
+ return {
15
+ onShow() {
16
+ document.addEventListener('keydown', onKeyDown)
17
+ },
18
+ onHide() {
19
+ document.removeEventListener('keydown', onKeyDown)
68
20
  },
69
- )
70
- }
71
-
72
- close() {
73
- if (!this.isOpen()) return
74
-
75
- this.triggerTarget.dataset.state = 'closed'
76
- this.contentTarget.dataset.state = 'closed'
77
- this.cleanup()
78
- this.cleanupEventListeners()
79
-
80
- setTimeout(() => {
81
- this.contentWrapperTarget.classList.add('hidden')
82
- }, 100)
83
- }
84
-
85
- clearOpenTimer() {
86
- window.clearTimeout(this.openTimer)
87
- }
88
-
89
- clearCloseTimer() {
90
- window.clearTimeout(this.closeTimer)
91
- }
92
-
93
- openWithDelay() {
94
- this.clearCloseTimer()
95
-
96
- this.openTimer = setTimeout(() => {
97
- this.open()
98
- }, 500)
99
- }
100
-
101
- closeWithDelay() {
102
- window.clearTimeout(this.openTimer)
103
-
104
- this.closeTimer = setTimeout(() => {
105
- this.close()
106
- }, 300)
107
- }
108
-
109
- // Global listeners
110
- onDOMClick(event) {
111
- if (!this.isOpen()) return
112
- if (this.element.contains(event.target)) return
113
-
114
- this.close()
115
- }
116
-
117
- onDOMKeydown(event) {
118
- if (!this.isOpen()) return
119
- const key = event.key
120
-
121
- if (key === 'Escape') {
122
- this.close()
123
21
  }
124
- }
22
+ },
23
+ }
125
24
 
126
- setupEventListeners() {
127
- document.addEventListener('click', this.DOMClickListener)
128
- document.addEventListener('keydown', this.DOMKeydownListener)
129
- }
25
+ export default class extends Controller {
26
+ static targets = ['trigger', 'content']
130
27
 
131
- cleanupEventListeners() {
132
- document.removeEventListener('click', this.DOMClickListener)
133
- document.removeEventListener('keydown', this.DOMKeydownListener)
28
+ connect() {
29
+ const content = this.contentTarget.innerHTML
30
+
31
+ this.tippy = tippy(this.triggerTarget, {
32
+ content: content,
33
+ allowHTML: true,
34
+ interactive: true,
35
+ arrow: false,
36
+ placement: this.element.dataset.side,
37
+ plugins: [hideOnEsc],
38
+ offset: [0, 4],
39
+ delay: 250,
40
+ })
134
41
  }
135
42
  }
@@ -4,7 +4,6 @@ export default class extends Controller {
4
4
  static targets = ['panel', 'panelOffset']
5
5
 
6
6
  connect() {
7
- console.log('sidebar coonnected')
8
7
  this.width = this.element.offsetWidth
9
8
  }
10
9
 
@@ -1,135 +1,41 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
- import {
3
- computePosition,
4
- flip,
5
- shift,
6
- offset,
7
- autoUpdate,
8
- } from '@floating-ui/dom'
9
-
10
- export default class extends Controller {
11
- static targets = ['trigger', 'contentWrapper', 'content']
12
-
13
- connect() {
14
- this.DOMClickListener = this.onDOMClick.bind(this)
15
- this.DOMKeydownListener = this.onDOMKeydown.bind(this)
16
-
17
- if (this.triggerTarget.dataset.asChild === 'false') {
18
- this.triggerTarget.firstElementChild.addEventListener('focus', () => {
19
- this.open()
20
- })
21
-
22
- this.triggerTarget.firstElementChild.addEventListener('blur', () => {
23
- this.close()
24
- })
25
- } else {
26
- this.triggerTarget.addEventListener('focus', () => {
27
- this.open()
28
- })
29
-
30
- this.triggerTarget.addEventListener('blur', () => {
31
- this.close()
32
- })
33
- }
34
- }
35
-
36
- toggle() {
37
- if (this.isOpen()) {
38
- this.close()
39
- } else {
40
- this.openWithDelay()
2
+ import tippy from 'tippy.js'
3
+
4
+ const hideOnEsc = {
5
+ name: 'hideOnEsc',
6
+ defaultValue: true,
7
+ fn({ hide }) {
8
+ function onKeyDown(event) {
9
+ if (event.keyCode === 27) {
10
+ hide()
11
+ }
41
12
  }
42
- }
43
-
44
- isOpen() {
45
- return this.triggerTarget.dataset.state === 'open'
46
- }
47
13
 
48
- open() {
49
- this.contentWrapperTarget.classList.remove('hidden')
50
- this.triggerTarget.dataset.state = 'open'
51
- this.contentTarget.dataset.state = 'open'
52
- this.setupEventListeners()
53
-
54
- this.cleanup = autoUpdate(
55
- this.triggerTarget,
56
- this.contentWrapperTarget,
57
- () => {
58
- computePosition(this.triggerTarget, this.contentWrapperTarget, {
59
- placement: this.element.dataset.side,
60
- strategy: 'fixed',
61
- middleware: [flip(), shift(), offset(4)],
62
- }).then(({ x, y }) => {
63
- Object.assign(this.contentWrapperTarget.style, {
64
- left: `${x}px`,
65
- top: `${y}px`,
66
- })
67
- })
14
+ return {
15
+ onShow() {
16
+ document.addEventListener('keydown', onKeyDown)
17
+ },
18
+ onHide() {
19
+ document.removeEventListener('keydown', onKeyDown)
68
20
  },
69
- )
70
- }
71
-
72
- close() {
73
- if (!this.isOpen()) return
74
-
75
- this.contentTarget.dataset.state = 'closed'
76
- this.triggerTarget.dataset.state = 'closed'
77
- this.cleanup()
78
- this.cleanupEventListeners()
79
-
80
- setTimeout(() => {
81
- this.contentWrapperTarget.classList.add('hidden')
82
- }, 100)
83
- }
84
-
85
- clearOpenTimer() {
86
- window.clearTimeout(this.openTimer)
87
- }
88
-
89
- clearCloseTimer() {
90
- window.clearTimeout(this.closeTimer)
91
- }
92
-
93
- openWithDelay() {
94
- this.clearCloseTimer()
95
-
96
- this.openTimer = setTimeout(() => {
97
- this.open()
98
- }, 300)
99
- }
100
-
101
- closeWithDelay() {
102
- window.clearTimeout(this.openTimer)
103
-
104
- this.closeTimer = setTimeout(() => {
105
- this.close()
106
- }, 150)
107
- }
108
-
109
- // Global listeners
110
- onDOMClick(event) {
111
- if (!this.isOpen()) return
112
- if (this.element.contains(event.target)) return
113
-
114
- this.close()
115
- }
116
-
117
- onDOMKeydown(event) {
118
- if (!this.isOpen()) return
119
- const key = event.key
120
-
121
- if (key === 'Escape') {
122
- this.close()
123
21
  }
124
- }
22
+ },
23
+ }
125
24
 
126
- setupEventListeners() {
127
- document.addEventListener('click', this.DOMClickListener)
128
- document.addEventListener('keydown', this.DOMKeydownListener)
129
- }
25
+ export default class extends Controller {
26
+ static targets = ['trigger', 'content']
130
27
 
131
- cleanupEventListeners() {
132
- document.removeEventListener('click', this.DOMClickListener)
133
- document.removeEventListener('keydown', this.DOMKeydownListener)
28
+ connect() {
29
+ const content = this.contentTarget.innerHTML
30
+
31
+ tippy(this.triggerTarget, {
32
+ content: content,
33
+ allowHTML: true,
34
+ interactive: true,
35
+ arrow: false,
36
+ placement: this.element.dataset.side,
37
+ plugins: [hideOnEsc],
38
+ offset: [0, 4],
39
+ })
134
40
  }
135
41
  }