shadcn_phlexcomponents 0.1.11 → 0.1.14
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.
- checksums.yaml +4 -4
- data/app/javascript/controllers/accordion_controller.ts +65 -62
- data/app/javascript/controllers/alert_dialog_controller.ts +12 -0
- data/app/javascript/controllers/avatar_controller.ts +7 -2
- data/app/javascript/controllers/checkbox_controller.ts +11 -4
- data/app/javascript/controllers/collapsible_controller.ts +12 -5
- data/app/javascript/controllers/combobox_controller.ts +270 -39
- data/app/javascript/controllers/command_controller.ts +223 -51
- data/app/javascript/controllers/date_picker_controller.ts +185 -125
- data/app/javascript/controllers/date_range_picker_controller.ts +89 -79
- data/app/javascript/controllers/dialog_controller.ts +59 -57
- data/app/javascript/controllers/dropdown_menu_controller.ts +212 -36
- data/app/javascript/controllers/dropdown_menu_sub_controller.ts +31 -29
- data/app/javascript/controllers/form_field_controller.ts +6 -1
- data/app/javascript/controllers/hover_card_controller.ts +36 -26
- data/app/javascript/controllers/loading_button_controller.ts +6 -1
- data/app/javascript/controllers/popover_controller.ts +42 -65
- data/app/javascript/controllers/progress_controller.ts +9 -3
- data/app/javascript/controllers/radio_group_controller.ts +16 -9
- data/app/javascript/controllers/select_controller.ts +206 -65
- data/app/javascript/controllers/slider_controller.ts +23 -16
- data/app/javascript/controllers/switch_controller.ts +11 -4
- data/app/javascript/controllers/tabs_controller.ts +26 -18
- data/app/javascript/controllers/theme_switcher_controller.ts +6 -1
- data/app/javascript/controllers/toast_container_controller.ts +6 -1
- data/app/javascript/controllers/toast_controller.ts +7 -1
- data/app/javascript/controllers/toggle_controller.ts +28 -0
- data/app/javascript/controllers/toggle_group_controller.ts +28 -0
- data/app/javascript/controllers/tooltip_controller.ts +43 -31
- data/app/javascript/shadcn_phlexcomponents.ts +29 -25
- data/app/javascript/utils/command.ts +544 -0
- data/app/javascript/utils/floating_ui.ts +196 -0
- data/app/javascript/utils/index.ts +417 -0
- data/app/stylesheets/date_picker.css +118 -0
- data/lib/shadcn_phlexcomponents/alias.rb +3 -0
- data/lib/shadcn_phlexcomponents/components/accordion.rb +2 -1
- data/lib/shadcn_phlexcomponents/components/alert_dialog.rb +18 -15
- data/lib/shadcn_phlexcomponents/components/base.rb +14 -0
- data/lib/shadcn_phlexcomponents/components/collapsible.rb +1 -2
- data/lib/shadcn_phlexcomponents/components/combobox.rb +87 -57
- data/lib/shadcn_phlexcomponents/components/command.rb +77 -47
- data/lib/shadcn_phlexcomponents/components/date_picker.rb +25 -81
- data/lib/shadcn_phlexcomponents/components/date_range_picker.rb +21 -4
- data/lib/shadcn_phlexcomponents/components/dialog.rb +14 -12
- data/lib/shadcn_phlexcomponents/components/dropdown_menu.rb +5 -4
- data/lib/shadcn_phlexcomponents/components/dropdown_menu_sub.rb +2 -1
- data/lib/shadcn_phlexcomponents/components/form/form_combobox.rb +64 -0
- data/lib/shadcn_phlexcomponents/components/form.rb +14 -0
- data/lib/shadcn_phlexcomponents/components/hover_card.rb +3 -2
- data/lib/shadcn_phlexcomponents/components/popover.rb +3 -3
- data/lib/shadcn_phlexcomponents/components/select.rb +10 -25
- data/lib/shadcn_phlexcomponents/components/sheet.rb +15 -11
- data/lib/shadcn_phlexcomponents/components/table.rb +1 -1
- data/lib/shadcn_phlexcomponents/components/tabs.rb +1 -1
- data/lib/shadcn_phlexcomponents/components/toast_container.rb +1 -1
- data/lib/shadcn_phlexcomponents/components/toggle.rb +54 -0
- data/lib/shadcn_phlexcomponents/components/tooltip.rb +3 -2
- data/lib/shadcn_phlexcomponents/engine.rb +1 -5
- data/lib/shadcn_phlexcomponents/version.rb +1 -1
- metadata +9 -4
- data/app/javascript/controllers/command_root_controller.ts +0 -355
- data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
- data/app/javascript/utils.ts +0 -437
@@ -1,60 +1,239 @@
|
|
1
1
|
import hotkeys from 'hotkeys-js'
|
2
|
+
import { Controller } from '@hotwired/stimulus'
|
2
3
|
import {
|
3
|
-
openWithOverlay,
|
4
4
|
showContent,
|
5
|
-
closeWithOverlay,
|
6
5
|
hideContent,
|
7
6
|
focusTrigger,
|
7
|
+
ON_OPEN_FOCUS_DELAY,
|
8
|
+
setGroupLabelsId,
|
8
9
|
} from '../utils'
|
9
|
-
import
|
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'
|
10
24
|
|
11
25
|
declare global {
|
12
26
|
interface Window {
|
13
|
-
Turbo:
|
27
|
+
Turbo: {
|
28
|
+
visit: (path: string) => void
|
29
|
+
}
|
14
30
|
}
|
15
31
|
}
|
16
32
|
|
17
|
-
|
33
|
+
const CommandController = class extends Controller<HTMLElement> {
|
34
|
+
// targets
|
18
35
|
static targets = [
|
19
36
|
'trigger',
|
20
37
|
'content',
|
38
|
+
'overlay',
|
21
39
|
'item',
|
22
40
|
'group',
|
23
|
-
'label',
|
24
41
|
'searchInput',
|
25
|
-
'
|
42
|
+
'list',
|
43
|
+
'listContainer',
|
26
44
|
'empty',
|
27
45
|
'modifierKey',
|
46
|
+
'loading',
|
47
|
+
'error',
|
28
48
|
]
|
29
|
-
|
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
|
30
58
|
declare readonly modifierKeyTarget: HTMLElement
|
31
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
|
32
80
|
declare modifierKey?: string
|
33
81
|
declare shortcutKey?: string
|
34
|
-
declare resultsTarget: HTMLElement
|
35
|
-
declare searchInputTarget: HTMLInputElement
|
36
|
-
declare emptyTarget: HTMLElement
|
37
|
-
declare filteredItems: HTMLElement[]
|
38
|
-
declare hotkeyListener: (event: KeyboardEvent) => void
|
39
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']
|
40
92
|
|
41
93
|
connect() {
|
42
|
-
|
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 })
|
43
110
|
this.hotkeyListener = this.onHotkeyPressed.bind(this)
|
111
|
+
this.DOMKeydownListener = this.onDOMKeydown.bind(this)
|
44
112
|
this.setupHotkeys()
|
45
113
|
this.replaceModifierKeyIcon()
|
46
114
|
}
|
47
115
|
|
48
|
-
|
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() {
|
49
227
|
const modifierKey = this.element.dataset.modifierKey
|
50
228
|
const shortcutKey = this.element.dataset.shortcutKey
|
229
|
+
|
51
230
|
let keybinds = ''
|
52
231
|
|
53
232
|
if (modifierKey && shortcutKey) {
|
54
233
|
keybinds = `${modifierKey}+${shortcutKey}`
|
55
234
|
|
56
235
|
if (modifierKey === 'ctrl') {
|
57
|
-
keybinds
|
236
|
+
keybinds += `,cmd-${shortcutKey}`
|
58
237
|
}
|
59
238
|
} else if (shortcutKey) {
|
60
239
|
keybinds = shortcutKey
|
@@ -64,19 +243,24 @@ export default class extends CommandRootController {
|
|
64
243
|
hotkeys(keybinds, this.hotkeyListener)
|
65
244
|
}
|
66
245
|
|
67
|
-
onHotkeyPressed(event: KeyboardEvent) {
|
246
|
+
protected onHotkeyPressed(event: KeyboardEvent) {
|
68
247
|
event.preventDefault()
|
69
248
|
this.open()
|
70
249
|
}
|
71
250
|
|
72
|
-
replaceModifierKeyIcon() {
|
251
|
+
protected replaceModifierKeyIcon() {
|
73
252
|
if (this.hasModifierKeyTarget && this.isMac()) {
|
74
253
|
this.modifierKeyTarget.innerHTML = '⌘'
|
75
254
|
}
|
76
255
|
}
|
77
256
|
|
78
|
-
isMac() {
|
79
|
-
const navigator = window.navigator as
|
257
|
+
protected isMac() {
|
258
|
+
const navigator = window.navigator as unknown as {
|
259
|
+
platform: string
|
260
|
+
userAgentData: {
|
261
|
+
platform: string
|
262
|
+
}
|
263
|
+
}
|
80
264
|
|
81
265
|
if (navigator.userAgentData) {
|
82
266
|
return navigator.userAgentData.platform === 'macOS'
|
@@ -86,44 +270,32 @@ export default class extends CommandRootController {
|
|
86
270
|
return navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
87
271
|
}
|
88
272
|
|
89
|
-
|
90
|
-
|
91
|
-
}
|
273
|
+
protected onDOMKeydown(event: KeyboardEvent) {
|
274
|
+
if (!this.isOpenValue) return
|
92
275
|
|
93
|
-
|
94
|
-
super.disconnect()
|
276
|
+
const key = event.key
|
95
277
|
|
96
|
-
if (
|
97
|
-
|
278
|
+
if (['Tab', 'Enter'].includes(key)) event.preventDefault()
|
279
|
+
|
280
|
+
if (key === 'Escape') {
|
281
|
+
this.close()
|
98
282
|
}
|
99
283
|
}
|
100
284
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
showContent({
|
106
|
-
trigger: this.triggerTarget,
|
107
|
-
content: this.contentTarget,
|
108
|
-
contentContainer: this.contentTarget,
|
109
|
-
})
|
110
|
-
|
111
|
-
this.setupEventListeners()
|
112
|
-
} else {
|
113
|
-
closeWithOverlay(this.contentTarget.id)
|
114
|
-
|
115
|
-
hideContent({
|
116
|
-
trigger: this.triggerTarget,
|
117
|
-
content: this.contentTarget,
|
118
|
-
contentContainer: this.contentTarget,
|
119
|
-
})
|
285
|
+
protected setupEventListeners() {
|
286
|
+
document.addEventListener('keydown', this.DOMKeydownListener)
|
287
|
+
}
|
120
288
|
|
121
|
-
|
289
|
+
protected cleanupEventListeners() {
|
290
|
+
document.removeEventListener('keydown', this.DOMKeydownListener)
|
122
291
|
|
123
|
-
|
124
|
-
|
125
|
-
focusTrigger(this.triggerTarget)
|
126
|
-
}
|
292
|
+
if (this.abortController) {
|
293
|
+
this.abortController.abort()
|
127
294
|
}
|
128
295
|
}
|
129
296
|
}
|
297
|
+
|
298
|
+
type Command = InstanceType<typeof CommandController>
|
299
|
+
|
300
|
+
export { CommandController }
|
301
|
+
export type { Command }
|