shadcn_phlexcomponents 0.1.0

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 (168) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +39 -0
  3. data/Rakefile +12 -0
  4. data/app/assets/tailwind/tailwindcss-animate.css +318 -0
  5. data/app/assets/tailwind/vanilla-calendar-pro.css +461 -0
  6. data/app/javascript/controllers/accordion_controller.js +133 -0
  7. data/app/javascript/controllers/alert_dialog_controller.js +157 -0
  8. data/app/javascript/controllers/avatar_controller.js +15 -0
  9. data/app/javascript/controllers/checkbox_controller.js +28 -0
  10. data/app/javascript/controllers/collapsible_controller.js +35 -0
  11. data/app/javascript/controllers/combobox_controller.js +291 -0
  12. data/app/javascript/controllers/datepicker_controller.js +47 -0
  13. data/app/javascript/controllers/dialog_controller.js +159 -0
  14. data/app/javascript/controllers/dropdown_menu_controller.js +193 -0
  15. data/app/javascript/controllers/hover_card_controller.js +135 -0
  16. data/app/javascript/controllers/loading_button_controller.js +15 -0
  17. data/app/javascript/controllers/popover_controller.js +124 -0
  18. data/app/javascript/controllers/progress_controller.js +14 -0
  19. data/app/javascript/controllers/radio_group_controller.js +90 -0
  20. data/app/javascript/controllers/select_controller.js +294 -0
  21. data/app/javascript/controllers/sheet_controller.js +159 -0
  22. data/app/javascript/controllers/sidebar_controller.js +36 -0
  23. data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
  24. data/app/javascript/controllers/switch_controller.js +24 -0
  25. data/app/javascript/controllers/tabs_controller.js +73 -0
  26. data/app/javascript/controllers/theme_switcher_controller.js +32 -0
  27. data/app/javascript/controllers/toast_container_controller.js +22 -0
  28. data/app/javascript/controllers/toast_controller.js +45 -0
  29. data/app/javascript/controllers/tooltip_controller.js +135 -0
  30. data/lib/components/accordion.rb +38 -0
  31. data/lib/components/accordion_content.rb +28 -0
  32. data/lib/components/accordion_item.rb +26 -0
  33. data/lib/components/accordion_trigger.rb +45 -0
  34. data/lib/components/alert.rb +40 -0
  35. data/lib/components/alert_description.rb +11 -0
  36. data/lib/components/alert_dialog.rb +60 -0
  37. data/lib/components/alert_dialog_action.rb +22 -0
  38. data/lib/components/alert_dialog_action_to.rb +37 -0
  39. data/lib/components/alert_dialog_cancel.rb +22 -0
  40. data/lib/components/alert_dialog_content.rb +40 -0
  41. data/lib/components/alert_dialog_description.rb +22 -0
  42. data/lib/components/alert_dialog_footer.rb +11 -0
  43. data/lib/components/alert_dialog_header.rb +11 -0
  44. data/lib/components/alert_dialog_title.rb +22 -0
  45. data/lib/components/alert_dialog_trigger.rb +50 -0
  46. data/lib/components/alert_title.rb +11 -0
  47. data/lib/components/aspect_ratio.rb +19 -0
  48. data/lib/components/avatar.rb +31 -0
  49. data/lib/components/avatar_fallback.rb +21 -0
  50. data/lib/components/avatar_image.rb +20 -0
  51. data/lib/components/badge.rb +36 -0
  52. data/lib/components/base.rb +108 -0
  53. data/lib/components/breadcrumb.rb +51 -0
  54. data/lib/components/breadcrumb_ellipsis.rb +23 -0
  55. data/lib/components/breadcrumb_item.rb +11 -0
  56. data/lib/components/breadcrumb_link.rb +7 -0
  57. data/lib/components/breadcrumb_page.rb +21 -0
  58. data/lib/components/breadcrumb_separator.rb +26 -0
  59. data/lib/components/button.rb +53 -0
  60. data/lib/components/card.rb +31 -0
  61. data/lib/components/card_content.rb +11 -0
  62. data/lib/components/card_description.rb +11 -0
  63. data/lib/components/card_footer.rb +11 -0
  64. data/lib/components/card_header.rb +11 -0
  65. data/lib/components/card_title.rb +11 -0
  66. data/lib/components/checkbox.rb +65 -0
  67. data/lib/components/checkbox_group.rb +48 -0
  68. data/lib/components/collapsible.rb +32 -0
  69. data/lib/components/collapsible_content.rb +25 -0
  70. data/lib/components/collapsible_trigger.rb +50 -0
  71. data/lib/components/datepicker.rb +38 -0
  72. data/lib/components/dialog.rb +52 -0
  73. data/lib/components/dialog_close.rb +42 -0
  74. data/lib/components/dialog_content.rb +54 -0
  75. data/lib/components/dialog_description.rb +22 -0
  76. data/lib/components/dialog_footer.rb +11 -0
  77. data/lib/components/dialog_header.rb +11 -0
  78. data/lib/components/dialog_title.rb +22 -0
  79. data/lib/components/dialog_trigger.rb +50 -0
  80. data/lib/components/dropdown_menu.rb +50 -0
  81. data/lib/components/dropdown_menu_content.rb +49 -0
  82. data/lib/components/dropdown_menu_item.rb +57 -0
  83. data/lib/components/dropdown_menu_item_to.rb +25 -0
  84. data/lib/components/dropdown_menu_label.rb +12 -0
  85. data/lib/components/dropdown_menu_separator.rb +20 -0
  86. data/lib/components/dropdown_menu_trigger.rb +58 -0
  87. data/lib/components/hover_card.rb +33 -0
  88. data/lib/components/hover_card_content.rb +36 -0
  89. data/lib/components/hover_card_trigger.rb +50 -0
  90. data/lib/components/input.rb +32 -0
  91. data/lib/components/label.rb +15 -0
  92. data/lib/components/link.rb +26 -0
  93. data/lib/components/loading_button.rb +21 -0
  94. data/lib/components/pagination.rb +38 -0
  95. data/lib/components/pagination_ellipsis.rb +24 -0
  96. data/lib/components/pagination_link.rb +34 -0
  97. data/lib/components/pagination_next.rb +32 -0
  98. data/lib/components/pagination_previous.rb +32 -0
  99. data/lib/components/popover.rb +35 -0
  100. data/lib/components/popover_content.rb +37 -0
  101. data/lib/components/popover_trigger.rb +52 -0
  102. data/lib/components/progress.rb +37 -0
  103. data/lib/components/radio_group.rb +62 -0
  104. data/lib/components/radio_group_item.rb +66 -0
  105. data/lib/components/select.rb +189 -0
  106. data/lib/components/select_content.rb +59 -0
  107. data/lib/components/select_group.rb +23 -0
  108. data/lib/components/select_item.rb +58 -0
  109. data/lib/components/select_label.rb +23 -0
  110. data/lib/components/select_trigger.rb +54 -0
  111. data/lib/components/separator.rb +29 -0
  112. data/lib/components/sheet.rb +53 -0
  113. data/lib/components/sheet_close.rb +42 -0
  114. data/lib/components/sheet_content.rb +67 -0
  115. data/lib/components/sheet_description.rb +22 -0
  116. data/lib/components/sheet_footer.rb +11 -0
  117. data/lib/components/sheet_header.rb +11 -0
  118. data/lib/components/sheet_title.rb +22 -0
  119. data/lib/components/sheet_trigger.rb +50 -0
  120. data/lib/components/sidebar.rb +103 -0
  121. data/lib/components/sidebar_container.rb +11 -0
  122. data/lib/components/sidebar_content.rb +11 -0
  123. data/lib/components/sidebar_footer.rb +11 -0
  124. data/lib/components/sidebar_group.rb +11 -0
  125. data/lib/components/sidebar_group_content.rb +11 -0
  126. data/lib/components/sidebar_group_label.rb +16 -0
  127. data/lib/components/sidebar_header.rb +11 -0
  128. data/lib/components/sidebar_inset.rb +15 -0
  129. data/lib/components/sidebar_menu.rb +11 -0
  130. data/lib/components/sidebar_menu_button.rb +61 -0
  131. data/lib/components/sidebar_menu_item.rb +9 -0
  132. data/lib/components/sidebar_menu_sub.rb +14 -0
  133. data/lib/components/sidebar_menu_sub_button.rb +48 -0
  134. data/lib/components/sidebar_menu_sub_item.rb +9 -0
  135. data/lib/components/sidebar_trigger.rb +40 -0
  136. data/lib/components/skeleton.rb +11 -0
  137. data/lib/components/switch.rb +65 -0
  138. data/lib/components/table.rb +73 -0
  139. data/lib/components/table_body.rb +11 -0
  140. data/lib/components/table_caption.rb +11 -0
  141. data/lib/components/table_cell.rb +11 -0
  142. data/lib/components/table_footer.rb +11 -0
  143. data/lib/components/table_head.rb +14 -0
  144. data/lib/components/table_header.rb +11 -0
  145. data/lib/components/table_row.rb +11 -0
  146. data/lib/components/tabs.rb +38 -0
  147. data/lib/components/tabs_content.rb +35 -0
  148. data/lib/components/tabs_list.rb +23 -0
  149. data/lib/components/tabs_trigger.rb +45 -0
  150. data/lib/components/textarea.rb +28 -0
  151. data/lib/components/theme_switcher.rb +21 -0
  152. data/lib/components/toast.rb +100 -0
  153. data/lib/components/toast_action.rb +38 -0
  154. data/lib/components/toast_action_to.rb +25 -0
  155. data/lib/components/toast_container.rb +44 -0
  156. data/lib/components/toast_content.rb +11 -0
  157. data/lib/components/toast_description.rb +11 -0
  158. data/lib/components/toast_title.rb +11 -0
  159. data/lib/components/tooltip.rb +34 -0
  160. data/lib/components/tooltip_content.rb +42 -0
  161. data/lib/components/tooltip_trigger.rb +50 -0
  162. data/lib/install/install_shadcn_phlexcomponents.rb +12 -0
  163. data/lib/shadcn_phlexcomponents/alias.rb +132 -0
  164. data/lib/shadcn_phlexcomponents/engine.rb +11 -0
  165. data/lib/shadcn_phlexcomponents/version.rb +5 -0
  166. data/lib/shadcn_phlexcomponents.rb +9 -0
  167. data/lib/tasks/install.rake +10 -0
  168. metadata +264 -0
@@ -0,0 +1,90 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['item']
5
+ static values = {
6
+ selected: String,
7
+ }
8
+
9
+ connect() {
10
+ if (!this.selectedValue) {
11
+ this.itemTargets[0].tabIndex = 0
12
+ }
13
+
14
+ this.items = this.itemTargets.filter((i) => !i.disabled)
15
+ }
16
+
17
+ setChecked(event) {
18
+ const item = event.currentTarget
19
+ this.selectedValue = item.dataset.value
20
+ }
21
+
22
+ preventDefault(event) {
23
+ event.preventDefault()
24
+ }
25
+
26
+ setCheckedToNext(event) {
27
+ const item = event.currentTarget
28
+ const index = this.items.indexOf(item)
29
+ let nextIndex = index + 1
30
+
31
+ if (index === this.items.length - 1) {
32
+ nextIndex = 0
33
+ }
34
+
35
+ this.selectedValue = this.items[nextIndex].dataset.value
36
+ }
37
+
38
+ setCheckedToPrev(event) {
39
+ const item = event.currentTarget
40
+ const index = this.items.indexOf(item)
41
+ let prevIndex = index - 1
42
+
43
+ if (index === 0) {
44
+ prevIndex = this.items.length - 1
45
+ }
46
+
47
+ this.selectedValue = this.items[prevIndex].dataset.value
48
+ }
49
+
50
+ focusItem() {
51
+ const item = this.itemTargets.find(
52
+ (i) => i.dataset.value === this.selectedValue,
53
+ )
54
+
55
+ if (!item) return
56
+
57
+ // Focus first item that is not disabled and allow it to be focused
58
+ if (item.disabled) {
59
+ item.tabIndex = -1
60
+ const items = this.items || this.itemTargets.filter((i) => !i.disabled)
61
+
62
+ if (items.length > 0) {
63
+ items[0].focus()
64
+ items[0].tabIndex = 0
65
+ }
66
+ } else {
67
+ item.focus()
68
+ }
69
+ }
70
+
71
+ selectedValueChanged(value) {
72
+ this.itemTargets.forEach((item) => {
73
+ const input = item.querySelector('[data-input]')
74
+
75
+ if (value === item.dataset.value) {
76
+ input.checked = true
77
+ item.tabIndex = 0
78
+ item.ariaChecked = true
79
+ item.dataset.checked = true
80
+ } else {
81
+ input.checked = false
82
+ item.tabIndex = -1
83
+ item.ariaChecked = false
84
+ item.dataset.checked = false
85
+ }
86
+ })
87
+
88
+ this.focusItem()
89
+ }
90
+ }
@@ -0,0 +1,294 @@
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 = [
12
+ 'trigger',
13
+ 'contentWrapper',
14
+ 'content',
15
+ 'item',
16
+ 'triggerText',
17
+ 'group',
18
+ 'label',
19
+ 'select',
20
+ ]
21
+
22
+ static values = {
23
+ selected: String,
24
+ }
25
+
26
+ connect() {
27
+ this.DOMClickListener = this.onDOMClick.bind(this)
28
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
29
+
30
+ this.setAriaLabelledby()
31
+
32
+ this.items = [
33
+ ...this.element.querySelectorAll(
34
+ '[data-shadcn-phlexcomponents--select-target="item"]:not([data-disabled])',
35
+ ),
36
+ ]
37
+
38
+ this.search = ''
39
+ this.searchTimer = null
40
+ this.resetSearchTimer = null
41
+ }
42
+
43
+ // Methods
44
+ toggle() {
45
+ if (this.isOpen()) {
46
+ this.close()
47
+ } else {
48
+ this.open()
49
+ }
50
+ }
51
+
52
+ open() {
53
+ const triggerWidth = this.triggerTarget.offsetWidth
54
+ this.contentWrapperTarget.classList.remove('hidden')
55
+
56
+ const contentWrapperWidth = this.contentWrapperTarget.offsetWidth
57
+
58
+ if (contentWrapperWidth < triggerWidth) {
59
+ this.contentWrapperTarget.style.width = `${triggerWidth}px`
60
+ }
61
+
62
+ this.triggerTarget.ariaExpanded = true
63
+ this.contentTarget.dataset.state = 'open'
64
+
65
+ if (this.selectedValue) {
66
+ const item = this.itemTargets.find(
67
+ (i) => i.dataset.value === this.selectedValue,
68
+ )
69
+
70
+ if (item && !item.dataset.disabled) {
71
+ item.focus()
72
+ } else {
73
+ this.contentTarget.focus()
74
+ }
75
+ } else {
76
+ this.focusItem(null, 0)
77
+ }
78
+
79
+ if (window.innerHeight < document.documentElement.scrollHeight) {
80
+ document.body.dataset.scrollLocked = 1
81
+ }
82
+ this.setupEventListeners()
83
+
84
+ this.cleanup = autoUpdate(
85
+ this.triggerTarget,
86
+ this.contentWrapperTarget,
87
+ () => {
88
+ computePosition(this.triggerTarget, this.contentWrapperTarget, {
89
+ placement: 'bottom',
90
+ strategy: 'fixed',
91
+ middleware: [flip(), shift(), offset(4)],
92
+ }).then(({ x, y }) => {
93
+ Object.assign(this.contentWrapperTarget.style, {
94
+ left: `${x}px`,
95
+ top: `${y}px`,
96
+ })
97
+ })
98
+ },
99
+ )
100
+ }
101
+
102
+ close() {
103
+ this.contentTarget.dataset.state = 'closed'
104
+ this.triggerTarget.ariaExpanded = false
105
+ this.cleanup()
106
+ this.cleanupEventListeners()
107
+ delete document.body.dataset.scrollLocked
108
+
109
+ setTimeout(() => {
110
+ this.contentWrapperTarget.classList.add('hidden')
111
+ }, 100)
112
+
113
+ if (this.triggerTarget.nodeName === 'DIV') {
114
+ this.triggerTarget.firstElementChild.focus()
115
+ } else {
116
+ this.triggerTarget.focus()
117
+ }
118
+ }
119
+
120
+ setAriaLabelledby() {
121
+ this.groupTargets.forEach((g) => {
122
+ const label = g.querySelector(
123
+ '[data-shadcn-phlexcomponents--select-target="label"]',
124
+ )
125
+
126
+ if (label) {
127
+ const ariaId = this.element.dataset.ariaId
128
+ const labelledby = `${ariaId}-${label.textContent
129
+ .toLowerCase()
130
+ .trim()
131
+ .replace(/ /, '-')}`
132
+ label.setAttribute('id', labelledby)
133
+ g.setAttribute('aria-labelledby', labelledby)
134
+ }
135
+ })
136
+ }
137
+
138
+ isOpen() {
139
+ return this.triggerTarget.ariaExpanded === 'true'
140
+ }
141
+
142
+ focusItem(event = null, index = null) {
143
+ let itemIndex = index
144
+
145
+ if (event) {
146
+ const item = event.currentTarget || event.target
147
+ itemIndex = this.items.indexOf(item)
148
+ }
149
+
150
+ const item = this.items[itemIndex]
151
+ item.tabIndex = 0
152
+ item.focus()
153
+
154
+ this.items.forEach((item, index) => {
155
+ if (index !== itemIndex) {
156
+ item.tabIndex = -1
157
+ }
158
+ })
159
+ }
160
+
161
+ focusFirstItem() {
162
+ this.focusItem(null, 0)
163
+ }
164
+
165
+ focusLastItem() {
166
+ this.focusItem(null, this.items.length - 1)
167
+ }
168
+
169
+ focusPrevItem(event) {
170
+ const item = event.currentTarget || event.target
171
+ const index = this.items.indexOf(item)
172
+
173
+ if (index - 1 >= 0) {
174
+ this.focusItem(null, index - 1)
175
+ }
176
+ }
177
+
178
+ focusNextItem(event) {
179
+ const item = event.currentTarget || event.target
180
+ const index = this.items.indexOf(item)
181
+
182
+ if (index + 1 < this.items.length) {
183
+ this.focusItem(null, index + 1)
184
+ }
185
+ }
186
+
187
+ selectItem(event) {
188
+ const item = event.currentTarget || event.target
189
+ const value = item.dataset.value
190
+ this.selectedValue = value
191
+ this.close()
192
+ }
193
+
194
+ focusContent() {
195
+ this.items.forEach((item) => {
196
+ item.blur()
197
+ })
198
+
199
+ this.contentTarget.focus()
200
+ }
201
+
202
+ selectedValueChanged(value) {
203
+ const item = this.itemTargets.find((i) => i.dataset.value === value)
204
+
205
+ if (item) {
206
+ this.triggerTextTarget.textContent = item.textContent
207
+
208
+ this.itemTargets.forEach((i) => {
209
+ if (i.dataset.value === value) {
210
+ i.setAttribute('aria-selected', 'true')
211
+ } else {
212
+ i.setAttribute('aria-selected', 'false')
213
+ }
214
+ })
215
+
216
+ this.selectTarget.value = value
217
+ }
218
+
219
+ delete this.triggerTarget.dataset.placeholder
220
+ const hasPlaceholder = this.triggerTarget.dataset.placeholderText
221
+
222
+ if (hasPlaceholder) {
223
+ if (!value || value.length === 0) {
224
+ this.triggerTarget.dataset.placeholder = true
225
+ this.triggerTextTarget.textContent = hasPlaceholder
226
+ }
227
+ }
228
+ }
229
+
230
+ handleSearchChange(search) {
231
+ const item = this.items.find((i) =>
232
+ i.textContent.toLowerCase().startsWith(search.toLowerCase()),
233
+ )
234
+
235
+ if (item) {
236
+ item.focus()
237
+ }
238
+ }
239
+
240
+ handleSearch(key) {
241
+ const search = this.search + key
242
+ this.search = search
243
+
244
+ window.clearTimeout(this.searchTimer)
245
+ window.clearTimeout(this.resetSearchTimer)
246
+
247
+ if (search !== '') {
248
+ this.searchTimer = window.setTimeout(() => {
249
+ this.handleSearchChange(search)
250
+ }, 150)
251
+
252
+ this.resetSearchTimer = window.setTimeout(() => {
253
+ this.search = ''
254
+ }, 300)
255
+ }
256
+ }
257
+
258
+ // Global listeners
259
+ onDOMClick(event) {
260
+ const htmlFor = event.target.htmlFor
261
+
262
+ if (htmlFor === this.triggerTarget.id) return
263
+ if (event.target) if (this.element.contains(event.target)) return
264
+
265
+ this.close()
266
+ }
267
+
268
+ onDOMKeydown(event) {
269
+ if (!this.isOpen()) return
270
+
271
+ const key = event.key
272
+ const isModifierKey = event.ctrlKey || event.altKey || event.metaKey
273
+
274
+ if (key === 'Tab') event.preventDefault()
275
+
276
+ if (!isModifierKey && key.length === 1) this.handleSearch(key)
277
+
278
+ if (key === 'Escape') {
279
+ this.close()
280
+ }
281
+ }
282
+
283
+ setupEventListeners() {
284
+ document.addEventListener('click', this.DOMClickListener)
285
+ document.addEventListener('keydown', this.DOMKeydownListener)
286
+ }
287
+
288
+ cleanupEventListeners() {
289
+ document.removeEventListener('click', this.DOMClickListener)
290
+ document.removeEventListener('keydown', this.DOMKeydownListener)
291
+ window.clearTimeout(this.searchTimer)
292
+ window.clearTimeout(this.resetSearchTimer)
293
+ }
294
+ }
@@ -0,0 +1,159 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['trigger', 'content']
5
+
6
+ connect() {
7
+ this.DOMClickListener = this.onDOMClick.bind(this)
8
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
9
+
10
+ this.focusableElements = this.contentTarget.querySelectorAll(
11
+ 'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
12
+ )
13
+
14
+ this.firstElement = this.focusableElements[0]
15
+ this.lastElement = this.focusableElements[this.focusableElements.length - 1]
16
+ this.contentElement = document.querySelector(`#${this.contentTarget.id}`)
17
+ }
18
+
19
+ open() {
20
+ this.showOverlay()
21
+ this.contentElement.classList.remove('hidden')
22
+ this.contentElement.dataset.state = 'open'
23
+ this.triggerTarget.ariaExpanded = true
24
+ this.setupEventListeners()
25
+ this.addInert()
26
+
27
+ if (window.innerHeight < document.documentElement.scrollHeight) {
28
+ document.body.dataset.scrollLocked = 1
29
+ }
30
+ document.body.appendChild(this.contentElement)
31
+ this.firstElement.focus() // must be after appendChild
32
+ }
33
+
34
+ close() {
35
+ this.contentElement.dataset.state = 'closed'
36
+ this.triggerTarget.ariaExpanded = false
37
+ this.cleanupEventListeners()
38
+ this.removeInert()
39
+ this.element.appendChild(this.contentElement)
40
+
41
+ setTimeout(() => {
42
+ this.contentElement.classList.add('hidden')
43
+ }, 100)
44
+
45
+ setTimeout(() => {
46
+ delete document.body.dataset.scrollLocked
47
+ this.removeOverlay()
48
+ }, 150)
49
+
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
+ }
59
+ }
60
+
61
+ isOpen() {
62
+ return this.triggerTarget.ariaExpanded === 'true'
63
+ }
64
+
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
+ // Global listeners
91
+ onDOMClick(event) {
92
+ if (!this.isOpen()) return
93
+
94
+ const target = event.target
95
+ const trigger = event.target.closest(
96
+ '[data-shadcn-phlexcomponents--sheet-target="trigger"]',
97
+ )
98
+
99
+ if (trigger) return
100
+
101
+ const close = target.closest(
102
+ '[data-action*="shadcn-phlexcomponents--sheet#close"]',
103
+ )
104
+
105
+ if (
106
+ close ||
107
+ (target.dataset.action &&
108
+ target.dataset.action.includes('shadcn-phlexcomponents--sheet#close'))
109
+ )
110
+ this.close()
111
+
112
+ if (this.contentElement.contains(event.target)) return
113
+
114
+ this.close()
115
+ }
116
+
117
+ onDOMKeydown(event) {
118
+ if (!this.isOpen()) return
119
+
120
+ const key = event.key
121
+
122
+ if (key === 'Escape') {
123
+ this.close()
124
+ } else if (key === 'Tab') {
125
+ // If Shift + Tab pressed on first element, go to last element
126
+ if (event.shiftKey && document.activeElement === this.firstElement) {
127
+ event.preventDefault()
128
+ this.lastElement.focus()
129
+ }
130
+ // If Tab pressed on last element, go to first element
131
+ else if (!event.shiftKey && document.activeElement === this.lastElement) {
132
+ event.preventDefault()
133
+ this.firstElement.focus()
134
+ }
135
+ }
136
+ }
137
+
138
+ setupEventListeners() {
139
+ document.addEventListener('click', this.DOMClickListener)
140
+ document.addEventListener('keydown', this.DOMKeydownListener)
141
+ }
142
+
143
+ cleanupEventListeners() {
144
+ document.removeEventListener('click', this.DOMClickListener)
145
+ document.removeEventListener('keydown', this.DOMKeydownListener)
146
+ }
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
+ }
@@ -0,0 +1,36 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['panel', 'panelOffset']
5
+
6
+ connect() {
7
+ console.log('sidebar coonnected')
8
+ this.width = this.element.offsetWidth
9
+ }
10
+
11
+ toggle() {
12
+ if (this.isOpen()) {
13
+ this.close()
14
+ } else {
15
+ this.open()
16
+ }
17
+ }
18
+
19
+ open() {
20
+ this.element.dataset.state = 'expanded'
21
+ this.element.dataset.collapsible = ''
22
+ this.panelTarget.style.removeProperty('left')
23
+ this.panelOffsetTarget.style.removeProperty('width')
24
+ }
25
+
26
+ close() {
27
+ this.element.dataset.state = 'collapsed'
28
+ this.element.dataset.collapsible = 'offcanvas'
29
+ this.panelTarget.style.left = `-${this.width}px`
30
+ this.panelOffsetTarget.style.width = 0
31
+ }
32
+
33
+ isOpen() {
34
+ return this.element.dataset.state === 'expanded'
35
+ }
36
+ }
@@ -0,0 +1,15 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ this.sidebarId = this.element.dataset.sidebarId
6
+ }
7
+
8
+ toggle() {
9
+ const sidebar = this.application.getControllerForElementAndIdentifier(
10
+ document.querySelector(`#${this.sidebarId}`),
11
+ 'shadcn-phlexcomponents--sidebar',
12
+ )
13
+ sidebar.toggle()
14
+ }
15
+ }
@@ -0,0 +1,24 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['input']
5
+ static values = {
6
+ checked: Boolean,
7
+ }
8
+
9
+ toggle() {
10
+ this.checkedValue = !this.checkedValue
11
+ }
12
+
13
+ checkedValueChanged(value) {
14
+ if (value === true) {
15
+ this.element.ariaChecked = true
16
+ this.element.dataset.checked = true
17
+ this.inputTarget.checked = true
18
+ } else {
19
+ this.element.ariaChecked = false
20
+ this.element.dataset.checked = false
21
+ this.inputTarget.checked = false
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,73 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static targets = ['trigger', 'content']
5
+ static values = {
6
+ selected: String,
7
+ }
8
+
9
+ connect() {
10
+ this.triggers = this.triggerTargets.filter((t) => !t.disabled)
11
+
12
+ if (!this.selectedValue) {
13
+ this.selectedValue = this.triggerTargets[0].dataset.value
14
+ }
15
+ }
16
+
17
+ setActiveTab(event) {
18
+ this.selectedValue = event.target.dataset.value
19
+ }
20
+
21
+ setActiveToPrev(event) {
22
+ const trigger = event.currentTarget
23
+ const index = this.triggers.indexOf(trigger)
24
+ let prevIndex = index - 1
25
+
26
+ if (index === 0) {
27
+ prevIndex = this.triggers.length - 1
28
+ }
29
+
30
+ this.selectedValue = this.triggers[prevIndex].dataset.value
31
+ this.triggers[prevIndex].focus()
32
+ }
33
+
34
+ setActiveToNext(event) {
35
+ const trigger = event.currentTarget
36
+ const index = this.triggers.indexOf(trigger)
37
+ let nextIndex = index + 1
38
+
39
+ if (index === this.triggers.length - 1) {
40
+ nextIndex = 0
41
+ }
42
+
43
+ this.selectedValue = this.triggers[nextIndex].dataset.value
44
+ this.triggers[nextIndex].focus()
45
+ }
46
+
47
+ selectedValueChanged(value) {
48
+ this.triggerTargets.forEach((trigger) => {
49
+ const triggerValue = trigger.dataset.value
50
+ const content = this.contentTargets.find((c) => {
51
+ return c.dataset.value === triggerValue
52
+ })
53
+
54
+ if (!content) {
55
+ throw new Error(
56
+ `Could not find TabsContent with value "${triggerValue}"`,
57
+ )
58
+ }
59
+
60
+ if (triggerValue === value) {
61
+ trigger.ariaSelected = true
62
+ trigger.tabIndex = 0
63
+ trigger.dataset.state = 'active'
64
+ content.classList.remove('hidden')
65
+ } else {
66
+ trigger.ariaSelected = false
67
+ trigger.tabIndex = -1
68
+ trigger.dataset.state = 'inactive'
69
+ content.classList.add('hidden')
70
+ }
71
+ })
72
+ }
73
+ }