shadcn_phlexcomponents 0.1.11 → 0.1.16

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/javascript/controllers/accordion_controller.js +107 -0
  3. data/app/javascript/controllers/alert_dialog_controller.js +7 -0
  4. data/app/javascript/controllers/avatar_controller.js +14 -0
  5. data/app/javascript/controllers/checkbox_controller.js +29 -0
  6. data/app/javascript/controllers/collapsible_controller.js +39 -0
  7. data/app/javascript/controllers/combobox_controller.js +278 -0
  8. data/app/javascript/controllers/command_controller.js +207 -0
  9. data/app/javascript/controllers/date_picker_controller.js +258 -0
  10. data/app/javascript/controllers/date_range_picker_controller.js +200 -0
  11. data/app/javascript/controllers/dialog_controller.js +83 -0
  12. data/app/javascript/controllers/dropdown_menu_controller.js +238 -0
  13. data/app/javascript/controllers/dropdown_menu_sub_controller.js +118 -0
  14. data/app/javascript/controllers/form_field_controller.js +20 -0
  15. data/app/javascript/controllers/hover_card_controller.js +73 -0
  16. data/app/javascript/controllers/loading_button_controller.js +14 -0
  17. data/app/javascript/controllers/popover_controller.js +90 -0
  18. data/app/javascript/controllers/progress_controller.js +14 -0
  19. data/app/javascript/controllers/radio_group_controller.js +80 -0
  20. data/app/javascript/controllers/select_controller.js +265 -0
  21. data/app/javascript/controllers/sidebar_controller.js +29 -0
  22. data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
  23. data/app/javascript/controllers/slider_controller.js +82 -0
  24. data/app/javascript/controllers/switch_controller.js +26 -0
  25. data/app/javascript/controllers/tabs_controller.js +66 -0
  26. data/app/javascript/controllers/theme_switcher_controller.js +32 -0
  27. data/app/javascript/controllers/toast_container_controller.js +48 -0
  28. data/app/javascript/controllers/toast_controller.js +22 -0
  29. data/app/javascript/controllers/toggle_controller.js +20 -0
  30. data/app/javascript/controllers/toggle_group_controller.js +20 -0
  31. data/app/javascript/controllers/tooltip_controller.js +79 -0
  32. data/app/javascript/shadcn_phlexcomponents.js +60 -0
  33. data/app/javascript/utils/command.js +448 -0
  34. data/app/javascript/utils/floating_ui.js +160 -0
  35. data/app/javascript/utils/index.js +288 -0
  36. data/app/stylesheets/date_picker.css +118 -0
  37. data/app/typescript/controllers/accordion_controller.ts +136 -0
  38. data/app/typescript/controllers/alert_dialog_controller.ts +12 -0
  39. data/app/{javascript → typescript}/controllers/avatar_controller.ts +7 -2
  40. data/app/{javascript → typescript}/controllers/checkbox_controller.ts +11 -4
  41. data/app/{javascript → typescript}/controllers/collapsible_controller.ts +12 -5
  42. data/app/typescript/controllers/combobox_controller.ts +376 -0
  43. data/app/typescript/controllers/command_controller.ts +301 -0
  44. data/app/{javascript → typescript}/controllers/date_picker_controller.ts +185 -125
  45. data/app/{javascript → typescript}/controllers/date_range_picker_controller.ts +89 -79
  46. data/app/{javascript → typescript}/controllers/dialog_controller.ts +59 -57
  47. data/app/typescript/controllers/dropdown_menu_controller.ts +309 -0
  48. data/app/{javascript → typescript}/controllers/dropdown_menu_sub_controller.ts +31 -29
  49. data/app/{javascript → typescript}/controllers/form_field_controller.ts +6 -1
  50. data/app/{javascript → typescript}/controllers/hover_card_controller.ts +36 -26
  51. data/app/{javascript → typescript}/controllers/loading_button_controller.ts +6 -1
  52. data/app/{javascript → typescript}/controllers/popover_controller.ts +42 -65
  53. data/app/{javascript → typescript}/controllers/progress_controller.ts +9 -3
  54. data/app/{javascript → typescript}/controllers/radio_group_controller.ts +16 -9
  55. data/app/typescript/controllers/select_controller.ts +341 -0
  56. data/app/{javascript → typescript}/controllers/slider_controller.ts +23 -16
  57. data/app/{javascript → typescript}/controllers/switch_controller.ts +11 -4
  58. data/app/{javascript → typescript}/controllers/tabs_controller.ts +26 -18
  59. data/app/{javascript → typescript}/controllers/theme_switcher_controller.ts +6 -1
  60. data/app/{javascript → typescript}/controllers/toast_container_controller.ts +6 -1
  61. data/app/{javascript → typescript}/controllers/toast_controller.ts +7 -1
  62. data/app/typescript/controllers/toggle_controller.ts +28 -0
  63. data/app/typescript/controllers/toggle_group_controller.ts +28 -0
  64. data/app/{javascript → typescript}/controllers/tooltip_controller.ts +43 -31
  65. data/app/typescript/shadcn_phlexcomponents.ts +61 -0
  66. data/app/typescript/utils/command.ts +544 -0
  67. data/app/typescript/utils/floating_ui.ts +196 -0
  68. data/app/typescript/utils/index.ts +424 -0
  69. data/lib/install/install_shadcn_phlexcomponents.rb +10 -3
  70. data/lib/shadcn_phlexcomponents/alias.rb +3 -0
  71. data/lib/shadcn_phlexcomponents/components/accordion.rb +2 -1
  72. data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +18 -15
  73. data/lib/shadcn_phlexcomponents/components/base.rb +14 -0
  74. data/lib/shadcn_phlexcomponents/components/collapsible.rb +1 -2
  75. data/lib/shadcn_phlexcomponents/components/combobox.rb +87 -57
  76. data/lib/shadcn_phlexcomponents/components/command.rb +77 -47
  77. data/lib/shadcn_phlexcomponents/components/date_picker.rb +25 -81
  78. data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +21 -4
  79. data/lib/shadcn_phlexcomponents/components/dialog.rb +14 -12
  80. data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +5 -4
  81. data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +2 -1
  82. data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
  83. data/lib/shadcn_phlexcomponents/components/form.rb +14 -0
  84. data/lib/shadcn_phlexcomponents/components/hover_card.rb +3 -2
  85. data/lib/shadcn_phlexcomponents/components/popover.rb +3 -3
  86. data/lib/shadcn_phlexcomponents/components/select.rb +10 -25
  87. data/lib/shadcn_phlexcomponents/components/sheet.rb +15 -11
  88. data/lib/shadcn_phlexcomponents/components/table.rb +1 -1
  89. data/lib/shadcn_phlexcomponents/components/tabs.rb +1 -1
  90. data/lib/shadcn_phlexcomponents/components/toast_container.rb +1 -1
  91. data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
  92. data/lib/shadcn_phlexcomponents/components/tooltip.rb +3 -2
  93. data/lib/shadcn_phlexcomponents/engine.rb +1 -5
  94. data/lib/shadcn_phlexcomponents/version.rb +1 -1
  95. metadata +71 -32
  96. data/app/javascript/controllers/accordion_controller.ts +0 -133
  97. data/app/javascript/controllers/combobox_controller.ts +0 -145
  98. data/app/javascript/controllers/command_controller.ts +0 -129
  99. data/app/javascript/controllers/command_root_controller.ts +0 -355
  100. data/app/javascript/controllers/dropdown_menu_controller.ts +0 -133
  101. data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
  102. data/app/javascript/controllers/select_controller.ts +0 -200
  103. data/app/javascript/shadcn_phlexcomponents.ts +0 -57
  104. data/app/javascript/utils.ts +0 -437
  105. /data/app/{javascript → typescript}/controllers/sidebar_controller.ts +0 -0
  106. /data/app/{javascript → typescript}/controllers/sidebar_trigger_controller.ts +0 -0
@@ -0,0 +1,12 @@
1
+ import { DialogController } from './dialog_controller'
2
+
3
+ const AlertDialogController = class extends DialogController {
4
+ protected onDOMClick() {
5
+ return
6
+ }
7
+ }
8
+
9
+ type AlertDialog = InstanceType<typeof AlertDialogController>
10
+
11
+ export { AlertDialogController }
12
+ export type { AlertDialog }
@@ -1,8 +1,8 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller {
3
+ const AvatarController = class extends Controller {
4
+ // targets
4
5
  static targets = ['image', 'fallback']
5
-
6
6
  declare readonly imageTarget: HTMLElement
7
7
  declare readonly fallbackTarget: HTMLElement
8
8
  declare readonly hasFallbackTarget: boolean
@@ -17,3 +17,8 @@ export default class extends Controller {
17
17
  }
18
18
  }
19
19
  }
20
+
21
+ type Avatar = InstanceType<typeof AvatarController>
22
+
23
+ export { AvatarController }
24
+ export type { Avatar }
@@ -1,13 +1,15 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
- export default class extends Controller<HTMLElement> {
3
+ const CheckboxController = class extends Controller<HTMLElement> {
4
+ // targets
4
5
  static targets = ['input', 'indicator']
6
+ declare readonly inputTarget: HTMLInputElement
7
+ declare readonly indicatorTarget: HTMLInputElement
8
+
9
+ // values
5
10
  static values = {
6
11
  isChecked: Boolean,
7
12
  }
8
-
9
- declare readonly inputTarget: HTMLInputElement
10
- declare readonly indicatorTarget: HTMLInputElement
11
13
  declare isCheckedValue: boolean
12
14
 
13
15
  toggle() {
@@ -32,3 +34,8 @@ export default class extends Controller<HTMLElement> {
32
34
  }
33
35
  }
34
36
  }
37
+
38
+ type Checkbox = InstanceType<typeof CheckboxController>
39
+
40
+ export { CheckboxController }
41
+ export type { Checkbox }
@@ -1,14 +1,16 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
- import { hideContent, showContent } from '@shadcn_phlexcomponents/utils'
2
+ import { hideContent, showContent } from '../utils'
3
3
 
4
- export default class extends Controller {
4
+ const CollapsibleController = class extends Controller {
5
+ // targets
5
6
  static targets = ['trigger', 'content']
7
+ declare readonly triggerTarget: HTMLElement
8
+ declare readonly contentTarget: HTMLElement
9
+
10
+ // values
6
11
  static values = {
7
12
  isOpen: Boolean,
8
13
  }
9
-
10
- declare readonly triggerTarget: HTMLElement
11
- declare readonly contentTarget: HTMLElement
12
14
  declare isOpenValue: boolean
13
15
 
14
16
  toggle() {
@@ -43,3 +45,8 @@ export default class extends Controller {
43
45
  }
44
46
  }
45
47
  }
48
+
49
+ type Collapsible = InstanceType<typeof CollapsibleController>
50
+
51
+ export { CollapsibleController }
52
+ export type { Collapsible }
@@ -0,0 +1,376 @@
1
+ import {
2
+ ON_OPEN_FOCUS_DELAY,
3
+ lockScroll,
4
+ showContent,
5
+ unlockScroll,
6
+ hideContent,
7
+ focusTrigger,
8
+ setGroupLabelsId,
9
+ onClickOutside,
10
+ } from '../utils'
11
+ import { initFloatingUi } from '../utils/floating_ui'
12
+ import { Controller } from '@hotwired/stimulus'
13
+ import Fuse from 'fuse.js'
14
+ import {
15
+ scrollToItem,
16
+ highlightItem,
17
+ highlightItemByIndex,
18
+ filteredItemsChanged,
19
+ setItemsGroupId,
20
+ search,
21
+ clearRemoteResults,
22
+ resetState,
23
+ } from '../utils/command'
24
+
25
+ import { useClickOutside, useDebounce } from 'stimulus-use'
26
+
27
+ const ComboboxController = class extends Controller<HTMLElement> {
28
+ // targets
29
+ static targets = [
30
+ 'trigger',
31
+ 'triggerText',
32
+ 'contentContainer',
33
+ 'content',
34
+ 'item',
35
+ 'group',
36
+ 'hiddenInput',
37
+ 'searchInput',
38
+ 'list',
39
+ 'listContainer',
40
+ 'empty',
41
+ 'loading',
42
+ 'error',
43
+ ]
44
+ declare readonly triggerTarget: HTMLElement
45
+ declare readonly triggerTextTarget: HTMLElement
46
+ declare readonly contentContainerTarget: HTMLElement
47
+ declare readonly contentTarget: HTMLElement
48
+ declare readonly itemTargets: HTMLElement[]
49
+ declare readonly groupTargets: HTMLElement[]
50
+ declare readonly hiddenInputTarget: HTMLInputElement
51
+ declare readonly searchInputTarget: HTMLInputElement
52
+ declare readonly listTarget: HTMLElement
53
+ declare readonly listContainerTarget: HTMLElement
54
+ declare readonly emptyTarget: HTMLElement
55
+ declare readonly loadingTarget: HTMLElement
56
+ declare readonly errorTarget: HTMLElement
57
+
58
+ // values
59
+ static values = {
60
+ isOpen: Boolean,
61
+ selected: String,
62
+ filteredItemIndexes: Array,
63
+ }
64
+ declare isOpenValue: boolean
65
+ declare selectedValue: string
66
+ declare filteredItemIndexesValue: number[]
67
+
68
+ // custom properties
69
+ declare orderedItems: HTMLElement[]
70
+ declare itemsInnerText: string[]
71
+ declare filteredItems: HTMLElement[]
72
+ declare fuse: Fuse<string>
73
+ declare scrollingViaKeyboard: boolean
74
+ declare keyboardScrollTimeout: number
75
+ declare abortController?: AbortController
76
+ declare searchPath?: string
77
+ declare isDirty: boolean
78
+ declare isLoading: boolean
79
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
80
+ declare cleanup: () => void
81
+
82
+ static debounces = ['search']
83
+
84
+ connect() {
85
+ this.orderedItems = [...this.itemTargets]
86
+ this.itemsInnerText = this.itemTargets.map((i) => i.innerText.trim())
87
+ this.fuse = new Fuse(this.itemsInnerText)
88
+ this.filteredItemIndexesValue = Array.from(
89
+ { length: this.itemTargets.length },
90
+ (_, i) => i,
91
+ )
92
+ this.isLoading = false
93
+ this.filteredItems = this.itemTargets
94
+ this.isDirty = false
95
+ this.searchPath = this.element.dataset.searchPath
96
+ setGroupLabelsId(this)
97
+ setItemsGroupId(this)
98
+ useDebounce(this)
99
+ useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
100
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
101
+ }
102
+
103
+ toggle() {
104
+ if (this.isOpenValue) {
105
+ this.close()
106
+ } else {
107
+ this.open()
108
+ }
109
+ }
110
+
111
+ open() {
112
+ this.isOpenValue = true
113
+
114
+ setTimeout(() => {
115
+ this.searchInputTarget.focus()
116
+
117
+ let index = 0
118
+ console.log('this.selectedValue', this.selectedValue)
119
+ if (this.selectedValue) {
120
+ const item = this.filteredItems.find(
121
+ (i) => i.dataset.value === this.selectedValue,
122
+ )
123
+
124
+ if (item && !item.dataset.disabled) {
125
+ index = this.filteredItems.indexOf(item)
126
+ }
127
+ }
128
+
129
+ this.highlightItemByIndex(index)
130
+ }, ON_OPEN_FOCUS_DELAY)
131
+ }
132
+
133
+ close() {
134
+ this.isOpenValue = false
135
+ resetState(this)
136
+ }
137
+
138
+ scrollToItem(index: number) {
139
+ scrollToItem(this, index)
140
+ }
141
+
142
+ highlightItem(
143
+ event: MouseEvent | KeyboardEvent | null = null,
144
+ index: number | null = null,
145
+ ) {
146
+ highlightItem(this, event, index)
147
+ }
148
+
149
+ highlightItemByIndex(index: number) {
150
+ highlightItemByIndex(this, index)
151
+ }
152
+
153
+ select(event: MouseEvent | KeyboardEvent) {
154
+ let item = undefined as HTMLElement | undefined
155
+
156
+ if (event instanceof KeyboardEvent) {
157
+ item = this.filteredItems.find((i) => i.dataset.highlighted === 'true')
158
+ } else {
159
+ // mouse event
160
+ item = event.currentTarget as HTMLElement
161
+ }
162
+
163
+ if (item) {
164
+ this.selectedValue = item.dataset.value as string
165
+
166
+ // setTimeout is needed for selectedValueChanged to finish executing
167
+ setTimeout(() => {
168
+ this.close()
169
+ }, 100)
170
+ }
171
+ }
172
+
173
+ inputKeydown(event: KeyboardEvent) {
174
+ if (event.key === ' ' && this.searchInputTarget.value.length === 0) {
175
+ event.preventDefault()
176
+ }
177
+
178
+ this.hideError()
179
+ this.showList()
180
+ }
181
+
182
+ search(event: InputEvent) {
183
+ this.isDirty = true
184
+ clearRemoteResults(this)
185
+ search(this, event)
186
+ }
187
+
188
+ clickOutside(event: MouseEvent) {
189
+ onClickOutside(this, event)
190
+ }
191
+
192
+ selectedValueChanged(value: string) {
193
+ const item = this.itemTargets.find((i) => i.dataset.value === value)
194
+
195
+ if (item) {
196
+ this.triggerTextTarget.textContent = item.textContent
197
+
198
+ this.itemTargets.forEach((i) => {
199
+ if (i.dataset.value === value) {
200
+ i.setAttribute('aria-selected', 'true')
201
+ } else {
202
+ i.setAttribute('aria-selected', 'false')
203
+ }
204
+ })
205
+
206
+ this.hiddenInputTarget.value = value
207
+ }
208
+
209
+ this.triggerTarget.dataset.hasValue = `${!!value && value.length > 0}`
210
+
211
+ const placeholder = this.triggerTarget.dataset.placeholder
212
+
213
+ if (placeholder && this.triggerTarget.dataset.hasValue === 'false') {
214
+ this.triggerTextTarget.textContent = placeholder
215
+ }
216
+ }
217
+
218
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
219
+ if (isOpen) {
220
+ lockScroll(this.contentTarget.id)
221
+
222
+ showContent({
223
+ trigger: this.triggerTarget,
224
+ content: this.contentTarget,
225
+ contentContainer: this.contentContainerTarget,
226
+ setEqualWidth: true,
227
+ })
228
+
229
+ this.cleanup = initFloatingUi({
230
+ referenceElement: this.triggerTarget,
231
+ floatingElement: this.contentContainerTarget,
232
+ side: this.contentTarget.dataset.side,
233
+ align: this.contentTarget.dataset.align,
234
+ sideOffset: 4,
235
+ })
236
+
237
+ this.setupEventListeners()
238
+ } else {
239
+ unlockScroll(this.contentTarget.id)
240
+
241
+ hideContent({
242
+ trigger: this.triggerTarget,
243
+ content: this.contentTarget,
244
+ contentContainer: this.contentContainerTarget,
245
+ })
246
+
247
+ if (previousIsOpen) {
248
+ focusTrigger(this.triggerTarget)
249
+ }
250
+
251
+ this.cleanupEventListeners()
252
+ }
253
+ }
254
+
255
+ filteredItemIndexesValueChanged(filteredItemIndexes: number[]) {
256
+ filteredItemsChanged(this, filteredItemIndexes)
257
+ }
258
+
259
+ disconnect() {
260
+ this.cleanupEventListeners()
261
+ resetState(this)
262
+ }
263
+
264
+ showLoading() {
265
+ this.isLoading = true
266
+ this.loadingTarget.classList.remove('hidden')
267
+ }
268
+
269
+ hideLoading() {
270
+ this.isLoading = false
271
+ this.loadingTarget.classList.add('hidden')
272
+ }
273
+
274
+ showList() {
275
+ this.listTarget.classList.remove('hidden')
276
+ }
277
+
278
+ hideList() {
279
+ this.listTarget.classList.add('hidden')
280
+ }
281
+
282
+ showError() {
283
+ this.errorTarget.classList.remove('hidden')
284
+ }
285
+
286
+ hideError() {
287
+ this.errorTarget.classList.add('hidden')
288
+ }
289
+
290
+ showEmpty() {
291
+ this.emptyTarget.classList.remove('hidden')
292
+ }
293
+
294
+ hideEmpty() {
295
+ this.emptyTarget.classList.add('hidden')
296
+ }
297
+
298
+ showSelectedRemoteItems() {
299
+ const remoteItems = Array.from(
300
+ this.element.querySelectorAll(
301
+ `[data-shadcn-phlexcomponents="${this.identifier}-item"][data-remote='true']`,
302
+ ),
303
+ )
304
+
305
+ remoteItems.forEach((i) => {
306
+ const isInsideGroup =
307
+ i.parentElement?.dataset?.shadcnPhlexcomponents ===
308
+ `${this.identifier}-group`
309
+
310
+ if (isInsideGroup) {
311
+ const isRemoteGroup = i.parentElement.dataset.remote === 'true'
312
+
313
+ if (isRemoteGroup) {
314
+ i.parentElement.classList.remove('hidden')
315
+ }
316
+ }
317
+
318
+ i.ariaHidden = 'false'
319
+ i.classList.remove('hidden')
320
+ })
321
+ }
322
+
323
+ hideSelectedRemoteItems() {
324
+ const remoteItems = Array.from(
325
+ this.element.querySelectorAll(
326
+ `[data-shadcn-phlexcomponents="${this.identifier}-item"][data-remote='true']`,
327
+ ),
328
+ )
329
+
330
+ remoteItems.forEach((i) => {
331
+ const isInsideGroup =
332
+ i.parentElement?.dataset?.shadcnPhlexcomponents ===
333
+ `${this.identifier}-group`
334
+
335
+ if (isInsideGroup) {
336
+ const isRemoteGroup = i.parentElement.dataset.remote === 'true'
337
+
338
+ if (isRemoteGroup) {
339
+ i.parentElement.classList.add('hidden')
340
+ }
341
+ }
342
+
343
+ i.ariaHidden = 'true'
344
+ i.classList.add('hidden')
345
+ })
346
+ }
347
+
348
+ protected setupEventListeners() {
349
+ document.addEventListener('keydown', this.DOMKeydownListener)
350
+ }
351
+
352
+ protected cleanupEventListeners() {
353
+ document.removeEventListener('keydown', this.DOMKeydownListener)
354
+
355
+ if (this.abortController) {
356
+ this.abortController.abort()
357
+ }
358
+ }
359
+
360
+ protected onDOMKeydown(event: KeyboardEvent) {
361
+ if (!this.isOpenValue) return
362
+
363
+ const key = event.key
364
+
365
+ if (['Tab', 'Enter'].includes(key)) event.preventDefault()
366
+
367
+ if (key === 'Escape') {
368
+ this.close()
369
+ }
370
+ }
371
+ }
372
+
373
+ type Combobox = InstanceType<typeof ComboboxController>
374
+
375
+ export { ComboboxController }
376
+ export type { Combobox }