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,301 @@
1
+ import hotkeys from 'hotkeys-js'
2
+ import { Controller } from '@hotwired/stimulus'
3
+ import {
4
+ showContent,
5
+ hideContent,
6
+ focusTrigger,
7
+ ON_OPEN_FOCUS_DELAY,
8
+ setGroupLabelsId,
9
+ } from '../utils'
10
+ import {
11
+ scrollToItem,
12
+ highlightItem,
13
+ highlightItemByIndex,
14
+ filteredItemsChanged,
15
+ setItemsGroupId,
16
+ search,
17
+ clearRemoteResults,
18
+ resetState,
19
+ hideError,
20
+ showList,
21
+ } from '../utils/command'
22
+ import { useDebounce, useClickOutside } from 'stimulus-use'
23
+ import Fuse from 'fuse.js'
24
+
25
+ declare global {
26
+ interface Window {
27
+ Turbo: {
28
+ visit: (path: string) => void
29
+ }
30
+ }
31
+ }
32
+
33
+ const CommandController = class extends Controller<HTMLElement> {
34
+ // targets
35
+ static targets = [
36
+ 'trigger',
37
+ 'content',
38
+ 'overlay',
39
+ 'item',
40
+ 'group',
41
+ 'searchInput',
42
+ 'list',
43
+ 'listContainer',
44
+ 'empty',
45
+ 'modifierKey',
46
+ 'loading',
47
+ 'error',
48
+ ]
49
+ declare readonly triggerTarget: HTMLElement
50
+ declare readonly contentTarget: HTMLElement
51
+ declare readonly overlayTarget: HTMLElement
52
+ declare readonly itemTargets: HTMLInputElement[]
53
+ declare readonly groupTargets: HTMLElement[]
54
+ declare readonly searchInputTarget: HTMLInputElement
55
+ declare readonly listTarget: HTMLElement
56
+ declare readonly listContainerTarget: HTMLElement
57
+ declare readonly emptyTarget: HTMLElement
58
+ declare readonly modifierKeyTarget: HTMLElement
59
+ declare readonly hasModifierKeyTarget: boolean
60
+ declare readonly loadingTarget: HTMLElement
61
+ declare readonly errorTarget: HTMLElement
62
+
63
+ // values
64
+ static values = {
65
+ isOpen: Boolean,
66
+ filteredItemIndexes: Array,
67
+ searchUrl: String,
68
+ }
69
+ declare isOpenValue: boolean
70
+ declare filteredItemIndexesValue: number[]
71
+
72
+ // custom properties
73
+ declare trigger: HTMLElement
74
+ declare orderedItems: HTMLElement[]
75
+ declare itemsInnerText: string[]
76
+ declare filteredItems: HTMLElement[]
77
+ declare fuse: Fuse<string>
78
+ declare scrollingViaKeyboard: boolean
79
+ declare keyboardScrollTimeout: number
80
+ declare modifierKey?: string
81
+ declare shortcutKey?: string
82
+ declare keybinds: string
83
+ declare abortController?: AbortController
84
+ declare searchPath?: string
85
+ declare isDirty: boolean
86
+ declare isLoading: boolean
87
+ declare hotkeyListener: (event: KeyboardEvent) => void
88
+ declare DOMKeydownListener: (event: KeyboardEvent) => void
89
+ declare DOMClickListener: (event: MouseEvent) => void
90
+
91
+ static debounces = ['search']
92
+
93
+ connect() {
94
+ this.orderedItems = [...this.itemTargets]
95
+ this.itemsInnerText = this.orderedItems.map((i) => i.innerText.trim())
96
+ this.fuse = new Fuse(this.itemsInnerText)
97
+ this.filteredItemIndexesValue = Array.from(
98
+ { length: this.itemTargets.length },
99
+ (_, i) => i,
100
+ )
101
+ this.isLoading = false
102
+ this.filteredItems = this.itemTargets
103
+ this.isDirty = false
104
+ this.searchPath = this.element.dataset.searchPath
105
+
106
+ setGroupLabelsId(this)
107
+ setItemsGroupId(this)
108
+ useDebounce(this)
109
+ useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
110
+ this.hotkeyListener = this.onHotkeyPressed.bind(this)
111
+ this.DOMKeydownListener = this.onDOMKeydown.bind(this)
112
+ this.setupHotkeys()
113
+ this.replaceModifierKeyIcon()
114
+ }
115
+
116
+ open() {
117
+ this.isOpenValue = true
118
+ this.highlightItemByIndex(0)
119
+
120
+ setTimeout(() => {
121
+ this.searchInputTarget.focus()
122
+ }, ON_OPEN_FOCUS_DELAY)
123
+ }
124
+
125
+ close() {
126
+ this.isOpenValue = false
127
+ resetState(this)
128
+ }
129
+
130
+ scrollToItem(index: number) {
131
+ scrollToItem(this, index)
132
+ }
133
+
134
+ highlightItem(
135
+ event: MouseEvent | KeyboardEvent | null = null,
136
+ index: number | null = null,
137
+ ) {
138
+ highlightItem(this, event, index)
139
+ }
140
+
141
+ highlightItemByIndex(index: number) {
142
+ highlightItemByIndex(this, index)
143
+ }
144
+
145
+ select(event: MouseEvent | KeyboardEvent) {
146
+ let value = null as null | string
147
+
148
+ if (event instanceof KeyboardEvent) {
149
+ const item = this.filteredItems.find(
150
+ (i) => i.dataset.highlighted === 'true',
151
+ )
152
+
153
+ if (item) {
154
+ value = item.dataset.value as string
155
+ }
156
+ } else {
157
+ // mouse event
158
+ const item = event.currentTarget as HTMLElement
159
+ value = item.dataset.value as string
160
+ }
161
+
162
+ if (value) {
163
+ window.Turbo.visit(value)
164
+ this.close()
165
+ }
166
+ }
167
+
168
+ inputKeydown(event: KeyboardEvent) {
169
+ if (event.key === ' ' && this.searchInputTarget.value.length === 0) {
170
+ event.preventDefault()
171
+ }
172
+
173
+ hideError(this)
174
+ showList(this)
175
+ }
176
+
177
+ search(event: InputEvent) {
178
+ this.isDirty = true
179
+ clearRemoteResults(this)
180
+ search(this, event)
181
+ }
182
+
183
+ clickOutside() {
184
+ this.close()
185
+ }
186
+
187
+ isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
188
+ if (isOpen) {
189
+ showContent({
190
+ trigger: this.triggerTarget,
191
+ content: this.contentTarget,
192
+ contentContainer: this.contentTarget,
193
+ overlay: this.overlayTarget,
194
+ })
195
+
196
+ this.setupEventListeners()
197
+ } else {
198
+ hideContent({
199
+ trigger: this.triggerTarget,
200
+ content: this.contentTarget,
201
+ contentContainer: this.contentTarget,
202
+ overlay: this.overlayTarget,
203
+ })
204
+
205
+ if (previousIsOpen) {
206
+ focusTrigger(this.triggerTarget)
207
+ }
208
+
209
+ this.cleanupEventListeners()
210
+ }
211
+ }
212
+
213
+ filteredItemIndexesValueChanged(filteredItemIndexes: number[]) {
214
+ filteredItemsChanged(this, filteredItemIndexes)
215
+ }
216
+
217
+ disconnect() {
218
+ this.cleanupEventListeners()
219
+ resetState(this)
220
+
221
+ if (this.keybinds) {
222
+ hotkeys.unbind(this.keybinds)
223
+ }
224
+ }
225
+
226
+ protected setupHotkeys() {
227
+ const modifierKey = this.element.dataset.modifierKey
228
+ const shortcutKey = this.element.dataset.shortcutKey
229
+
230
+ let keybinds = ''
231
+
232
+ if (modifierKey && shortcutKey) {
233
+ keybinds = `${modifierKey}+${shortcutKey}`
234
+
235
+ if (modifierKey === 'ctrl') {
236
+ keybinds += `,cmd-${shortcutKey}`
237
+ }
238
+ } else if (shortcutKey) {
239
+ keybinds = shortcutKey
240
+ }
241
+
242
+ this.keybinds = keybinds
243
+ hotkeys(keybinds, this.hotkeyListener)
244
+ }
245
+
246
+ protected onHotkeyPressed(event: KeyboardEvent) {
247
+ event.preventDefault()
248
+ this.open()
249
+ }
250
+
251
+ protected replaceModifierKeyIcon() {
252
+ if (this.hasModifierKeyTarget && this.isMac()) {
253
+ this.modifierKeyTarget.innerHTML = '⌘'
254
+ }
255
+ }
256
+
257
+ protected isMac() {
258
+ const navigator = window.navigator as unknown as {
259
+ platform: string
260
+ userAgentData: {
261
+ platform: string
262
+ }
263
+ }
264
+
265
+ if (navigator.userAgentData) {
266
+ return navigator.userAgentData.platform === 'macOS'
267
+ }
268
+
269
+ // Fallback to traditional methods
270
+ return navigator.platform.toUpperCase().indexOf('MAC') >= 0
271
+ }
272
+
273
+ protected onDOMKeydown(event: KeyboardEvent) {
274
+ if (!this.isOpenValue) return
275
+
276
+ const key = event.key
277
+
278
+ if (['Tab', 'Enter'].includes(key)) event.preventDefault()
279
+
280
+ if (key === 'Escape') {
281
+ this.close()
282
+ }
283
+ }
284
+
285
+ protected setupEventListeners() {
286
+ document.addEventListener('keydown', this.DOMKeydownListener)
287
+ }
288
+
289
+ protected cleanupEventListeners() {
290
+ document.removeEventListener('keydown', this.DOMKeydownListener)
291
+
292
+ if (this.abortController) {
293
+ this.abortController.abort()
294
+ }
295
+ }
296
+ }
297
+
298
+ type Command = InstanceType<typeof CommandController>
299
+
300
+ export { CommandController }
301
+ export type { Command }