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,6 +1,23 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
1
|
+
import { useClickOutside } from 'stimulus-use'
|
2
|
+
import { onKeydown, focusItemByIndex } from './dropdown_menu_controller'
|
3
|
+
import { initFloatingUi } from '../utils/floating_ui'
|
4
|
+
import {
|
5
|
+
getSameLevelItems,
|
6
|
+
focusTrigger,
|
7
|
+
hideContent,
|
8
|
+
showContent,
|
9
|
+
lockScroll,
|
10
|
+
unlockScroll,
|
11
|
+
onClickOutside,
|
12
|
+
setGroupLabelsId,
|
13
|
+
getNextEnabledIndex,
|
14
|
+
getPreviousEnabledIndex,
|
15
|
+
focusElement,
|
16
|
+
} from '../utils'
|
17
|
+
import { Controller } from '@hotwired/stimulus'
|
18
|
+
|
19
|
+
const SelectController = class extends Controller<HTMLElement> {
|
20
|
+
// targets
|
4
21
|
static targets = [
|
5
22
|
'trigger',
|
6
23
|
'contentContainer',
|
@@ -8,49 +25,57 @@ export default class extends DropdownMenuRootController {
|
|
8
25
|
'item',
|
9
26
|
'triggerText',
|
10
27
|
'group',
|
11
|
-
'label',
|
12
28
|
'select',
|
13
29
|
]
|
30
|
+
declare readonly triggerTarget: HTMLElement
|
31
|
+
declare readonly contentContainerTarget: HTMLElement
|
32
|
+
declare readonly contentTarget: HTMLElement
|
33
|
+
declare readonly itemTargets: HTMLElement[]
|
34
|
+
declare triggerTextTarget: HTMLElement
|
35
|
+
declare groupTargets: HTMLElement[]
|
36
|
+
declare selectTarget: HTMLSelectElement
|
14
37
|
|
38
|
+
// values
|
15
39
|
static values = {
|
16
40
|
isOpen: Boolean,
|
17
41
|
selected: String,
|
18
|
-
setEqualWidth: { type: Boolean, default: true },
|
19
|
-
closestContentSelector: {
|
20
|
-
type: String,
|
21
|
-
default: '[data-select-target="content"]',
|
22
|
-
},
|
23
42
|
}
|
24
|
-
|
43
|
+
declare isOpenValue: boolean
|
25
44
|
declare selectedValue: string
|
45
|
+
|
46
|
+
// custom properties
|
26
47
|
declare searchString: string
|
27
48
|
declare searchTimeout: number
|
28
|
-
declare groupTargets: HTMLElement[]
|
29
|
-
declare triggerTextTarget: HTMLElement
|
30
|
-
declare selectTarget: HTMLSelectElement
|
31
49
|
declare itemsInnerText: string[]
|
50
|
+
declare items: HTMLElement[]
|
51
|
+
declare DOMKeydownListener: (event: KeyboardEvent) => void
|
52
|
+
declare cleanup: () => void
|
32
53
|
|
33
54
|
connect() {
|
34
|
-
|
55
|
+
this.items = getSameLevelItems({
|
56
|
+
content: this.contentTarget,
|
57
|
+
items: this.itemTargets,
|
58
|
+
closestContentSelector: '[data-select-target="content"]',
|
59
|
+
})
|
35
60
|
this.itemsInnerText = this.items.map((i) => i.innerText.trim())
|
36
|
-
this.setAriaLabelledby()
|
37
61
|
this.searchString = ''
|
62
|
+
useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
|
63
|
+
this.DOMKeydownListener = this.onDOMKeydown.bind(this)
|
64
|
+
setGroupLabelsId(this)
|
38
65
|
}
|
39
66
|
|
40
|
-
|
41
|
-
this.
|
42
|
-
|
43
|
-
|
44
|
-
)
|
45
|
-
|
46
|
-
if (label) {
|
47
|
-
label.id = g.getAttribute('aria-labelledby') as string
|
48
|
-
}
|
49
|
-
})
|
67
|
+
toggle(event: MouseEvent) {
|
68
|
+
if (this.isOpenValue) {
|
69
|
+
this.close()
|
70
|
+
} else {
|
71
|
+
this.open(event)
|
72
|
+
}
|
50
73
|
}
|
51
74
|
|
52
|
-
|
53
|
-
|
75
|
+
open(event: MouseEvent | KeyboardEvent) {
|
76
|
+
this.isOpenValue = true
|
77
|
+
|
78
|
+
let elementToFocus = null as HTMLElement | null
|
54
79
|
|
55
80
|
if (this.selectedValue) {
|
56
81
|
const item = this.itemTargets.find(
|
@@ -58,33 +83,161 @@ export default class extends DropdownMenuRootController {
|
|
58
83
|
)
|
59
84
|
|
60
85
|
if (item && !item.dataset.disabled) {
|
61
|
-
|
86
|
+
elementToFocus = item
|
62
87
|
}
|
63
|
-
}
|
88
|
+
}
|
89
|
+
|
90
|
+
if (!elementToFocus) {
|
64
91
|
if (event instanceof KeyboardEvent) {
|
65
92
|
const key = event.key
|
66
93
|
|
67
94
|
if (['ArrowDown', 'Enter', ' '].includes(key)) {
|
68
|
-
|
95
|
+
elementToFocus = this.items[0]
|
69
96
|
}
|
97
|
+
} else {
|
98
|
+
elementToFocus = this.contentTarget
|
70
99
|
}
|
71
100
|
}
|
72
101
|
|
73
|
-
|
74
|
-
|
102
|
+
focusElement(elementToFocus)
|
103
|
+
}
|
104
|
+
|
105
|
+
close() {
|
106
|
+
this.isOpenValue = false
|
107
|
+
}
|
108
|
+
|
109
|
+
onItemFocus(event: FocusEvent) {
|
110
|
+
const item = event.currentTarget as HTMLElement
|
111
|
+
item.tabIndex = 0
|
112
|
+
}
|
113
|
+
|
114
|
+
onItemBlur(event: FocusEvent) {
|
115
|
+
const item = event.currentTarget as HTMLElement
|
116
|
+
item.tabIndex = -1
|
117
|
+
}
|
118
|
+
|
119
|
+
focusItemByIndex(
|
120
|
+
event: KeyboardEvent | null = null,
|
121
|
+
index: number | null = null,
|
122
|
+
) {
|
123
|
+
focusItemByIndex(this, event, index)
|
124
|
+
}
|
125
|
+
|
126
|
+
focusItem(event: MouseEvent | KeyboardEvent) {
|
127
|
+
const item = event.currentTarget as HTMLElement
|
128
|
+
const index = this.items.indexOf(item)
|
129
|
+
|
130
|
+
if (event instanceof KeyboardEvent) {
|
131
|
+
const key = event.key
|
132
|
+
let newIndex = 0
|
133
|
+
|
134
|
+
if (key === 'ArrowUp') {
|
135
|
+
newIndex = getPreviousEnabledIndex({
|
136
|
+
items: this.items,
|
137
|
+
currentIndex: index,
|
138
|
+
wrapAround: false,
|
139
|
+
})
|
140
|
+
} else {
|
141
|
+
newIndex = getNextEnabledIndex({
|
142
|
+
items: this.items,
|
143
|
+
currentIndex: index,
|
144
|
+
wrapAround: false,
|
145
|
+
})
|
146
|
+
}
|
147
|
+
|
148
|
+
this.items[newIndex].focus()
|
75
149
|
} else {
|
76
|
-
|
150
|
+
// item mouseover event
|
151
|
+
this.items[index].focus()
|
77
152
|
}
|
78
153
|
}
|
79
154
|
|
80
|
-
|
155
|
+
focusContent() {
|
156
|
+
this.contentTarget.focus()
|
157
|
+
}
|
158
|
+
|
159
|
+
select(event: MouseEvent | KeyboardEvent) {
|
81
160
|
const item = event.currentTarget as HTMLElement
|
82
161
|
const value = item.dataset.value as string
|
83
162
|
this.selectedValue = value
|
163
|
+
this.close()
|
164
|
+
}
|
165
|
+
|
166
|
+
clickOutside(event: MouseEvent) {
|
167
|
+
onClickOutside(this, event)
|
168
|
+
}
|
169
|
+
|
170
|
+
isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
|
171
|
+
if (isOpen) {
|
172
|
+
lockScroll(this.contentTarget.id)
|
173
|
+
|
174
|
+
showContent({
|
175
|
+
trigger: this.triggerTarget,
|
176
|
+
content: this.contentTarget,
|
177
|
+
contentContainer: this.contentContainerTarget,
|
178
|
+
setEqualWidth: true,
|
179
|
+
})
|
180
|
+
|
181
|
+
this.cleanup = initFloatingUi({
|
182
|
+
referenceElement: this.triggerTarget,
|
183
|
+
floatingElement: this.contentContainerTarget,
|
184
|
+
side: this.contentTarget.dataset.side,
|
185
|
+
align: this.contentTarget.dataset.align,
|
186
|
+
sideOffset: 4,
|
187
|
+
})
|
188
|
+
|
189
|
+
this.setupEventListeners()
|
190
|
+
} else {
|
191
|
+
unlockScroll(this.contentTarget.id)
|
192
|
+
|
193
|
+
hideContent({
|
194
|
+
trigger: this.triggerTarget,
|
195
|
+
content: this.contentTarget,
|
196
|
+
contentContainer: this.contentContainerTarget,
|
197
|
+
})
|
198
|
+
|
199
|
+
if (previousIsOpen) {
|
200
|
+
focusTrigger(this.triggerTarget)
|
201
|
+
}
|
202
|
+
|
203
|
+
this.cleanupEventListeners()
|
204
|
+
}
|
84
205
|
}
|
85
206
|
|
86
|
-
|
87
|
-
|
207
|
+
selectedValueChanged(value: string) {
|
208
|
+
const item = this.itemTargets.find((i) => i.dataset.value === value)
|
209
|
+
|
210
|
+
if (item) {
|
211
|
+
this.triggerTextTarget.textContent = item.textContent
|
212
|
+
|
213
|
+
this.itemTargets.forEach((i) => {
|
214
|
+
if (i.dataset.value === value) {
|
215
|
+
i.setAttribute('aria-selected', 'true')
|
216
|
+
} else {
|
217
|
+
i.setAttribute('aria-selected', 'false')
|
218
|
+
}
|
219
|
+
})
|
220
|
+
|
221
|
+
this.selectTarget.value = value
|
222
|
+
}
|
223
|
+
|
224
|
+
this.triggerTarget.dataset.hasValue = `${!!value && value.length > 0}`
|
225
|
+
|
226
|
+
const placeholder = this.triggerTarget.dataset.placeholder
|
227
|
+
|
228
|
+
if (placeholder && this.triggerTarget.dataset.hasValue === 'false') {
|
229
|
+
this.triggerTextTarget.textContent = placeholder
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
disconnect() {
|
234
|
+
this.cleanupEventListeners()
|
235
|
+
}
|
236
|
+
|
237
|
+
protected onDOMKeydown(event: KeyboardEvent) {
|
238
|
+
if (!this.isOpenValue) return
|
239
|
+
|
240
|
+
onKeydown(this, event)
|
88
241
|
|
89
242
|
const { key, altKey, ctrlKey, metaKey } = event
|
90
243
|
|
@@ -97,8 +250,17 @@ export default class extends DropdownMenuRootController {
|
|
97
250
|
}
|
98
251
|
}
|
99
252
|
|
253
|
+
protected setupEventListeners() {
|
254
|
+
document.addEventListener('keydown', this.DOMKeydownListener)
|
255
|
+
}
|
256
|
+
|
257
|
+
protected cleanupEventListeners() {
|
258
|
+
if (this.cleanup) this.cleanup()
|
259
|
+
document.removeEventListener('keydown', this.DOMKeydownListener)
|
260
|
+
}
|
261
|
+
|
100
262
|
// https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/
|
101
|
-
handleSearch(char: string) {
|
263
|
+
protected handleSearch(char: string) {
|
102
264
|
const searchString = this.getSearchString(char)
|
103
265
|
const focusedItem = this.items.find(
|
104
266
|
(item) => document.activeElement === item,
|
@@ -117,14 +279,14 @@ export default class extends DropdownMenuRootController {
|
|
117
279
|
}
|
118
280
|
}
|
119
281
|
|
120
|
-
filterItemsInnerText(items: string[], filter: string) {
|
282
|
+
protected filterItemsInnerText(items: string[], filter: string) {
|
121
283
|
return items.filter((item) => {
|
122
284
|
const matches = item.toLowerCase().indexOf(filter.toLowerCase()) === 0
|
123
285
|
return matches
|
124
286
|
})
|
125
287
|
}
|
126
288
|
|
127
|
-
getSearchString(char: string) {
|
289
|
+
protected getSearchString(char: string) {
|
128
290
|
// reset typing timeout and start new timeout
|
129
291
|
// this allows us to make multiple-letter matches, like a native select
|
130
292
|
if (typeof this.searchTimeout === 'number') {
|
@@ -142,7 +304,7 @@ export default class extends DropdownMenuRootController {
|
|
142
304
|
|
143
305
|
// return the index of an option from an array of options, based on a search string
|
144
306
|
// if the filter is multiple iterations of the same letter (e.g "aaa"), then cycle through first-letter matches
|
145
|
-
getIndexByLetter(filter: string, startIndex: number) {
|
307
|
+
protected getIndexByLetter(filter: string, startIndex: number) {
|
146
308
|
const orderedItems = [
|
147
309
|
...this.itemsInnerText.slice(startIndex),
|
148
310
|
...this.itemsInnerText.slice(0, startIndex),
|
@@ -171,30 +333,9 @@ export default class extends DropdownMenuRootController {
|
|
171
333
|
return -1
|
172
334
|
}
|
173
335
|
}
|
336
|
+
}
|
174
337
|
|
175
|
-
|
176
|
-
const item = this.itemTargets.find((i) => i.dataset.value === value)
|
177
|
-
|
178
|
-
if (item) {
|
179
|
-
this.triggerTextTarget.textContent = item.textContent
|
180
|
-
|
181
|
-
this.itemTargets.forEach((i) => {
|
182
|
-
if (i.dataset.value === value) {
|
183
|
-
i.setAttribute('aria-selected', 'true')
|
184
|
-
} else {
|
185
|
-
i.setAttribute('aria-selected', 'false')
|
186
|
-
}
|
187
|
-
})
|
188
|
-
|
189
|
-
this.selectTarget.value = value
|
190
|
-
}
|
191
|
-
|
192
|
-
this.triggerTarget.dataset.hasValue = `${!!value && value.length > 0}`
|
338
|
+
type Select = InstanceType<typeof SelectController>
|
193
339
|
|
194
|
-
|
195
|
-
|
196
|
-
if (placeholder && this.triggerTarget.dataset.hasValue === 'false') {
|
197
|
-
this.triggerTextTarget.textContent = placeholder
|
198
|
-
}
|
199
|
-
}
|
200
|
-
}
|
340
|
+
export { SelectController }
|
341
|
+
export type { Select }
|
@@ -1,17 +1,19 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
2
|
import noUiSlider, { API, Options } from 'nouislider'
|
3
3
|
|
4
|
-
|
4
|
+
const SliderController = class extends Controller<HTMLElement> {
|
5
|
+
// targets
|
5
6
|
static targets = ['slider', 'hiddenInput', 'endHiddenInput']
|
6
|
-
|
7
7
|
declare readonly sliderTarget: HTMLInputElement
|
8
8
|
declare readonly hiddenInputTarget: HTMLInputElement
|
9
9
|
declare readonly endHiddenInputTarget: HTMLInputElement
|
10
10
|
declare readonly hasEndHiddenInputTarget: boolean
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
// custom properties
|
13
13
|
declare range: boolean
|
14
14
|
declare slider: API
|
15
|
+
declare onUpdateValuesListener: (values: (string | number)[]) => void
|
16
|
+
declare DOMClickListener: (event: MouseEvent) => void
|
15
17
|
|
16
18
|
connect() {
|
17
19
|
this.range = this.element.dataset.range === 'true'
|
@@ -35,15 +37,11 @@ export default class extends Controller<HTMLElement> {
|
|
35
37
|
document.addEventListener('click', this.DOMClickListener)
|
36
38
|
}
|
37
39
|
|
38
|
-
|
39
|
-
this.
|
40
|
-
|
41
|
-
if (this.range && this.hasEndHiddenInputTarget) {
|
42
|
-
this.endHiddenInputTarget.value = `${values[1]}`
|
43
|
-
}
|
40
|
+
disconnect() {
|
41
|
+
document.removeEventListener('click', this.DOMClickListener)
|
44
42
|
}
|
45
43
|
|
46
|
-
getOptions() {
|
44
|
+
protected getOptions() {
|
47
45
|
const defaultOptions = {
|
48
46
|
connect: this.range ? true : 'lower',
|
49
47
|
tooltips: true,
|
@@ -84,7 +82,15 @@ export default class extends Controller<HTMLElement> {
|
|
84
82
|
}
|
85
83
|
}
|
86
84
|
|
87
|
-
|
85
|
+
protected onUpdateValues(values: (string | number)[]) {
|
86
|
+
this.hiddenInputTarget.value = `${values[0]}`
|
87
|
+
|
88
|
+
if (this.range && this.hasEndHiddenInputTarget) {
|
89
|
+
this.endHiddenInputTarget.value = `${values[1]}`
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
protected onDOMClick(event: MouseEvent) {
|
88
94
|
const target = event.target
|
89
95
|
|
90
96
|
// Focus handle of slider when label is clicked.
|
@@ -100,8 +106,9 @@ export default class extends Controller<HTMLElement> {
|
|
100
106
|
}
|
101
107
|
}
|
102
108
|
}
|
103
|
-
|
104
|
-
disconnect() {
|
105
|
-
document.removeEventListener('click', this.DOMClickListener)
|
106
|
-
}
|
107
109
|
}
|
110
|
+
|
111
|
+
type Slider = InstanceType<typeof SliderController>
|
112
|
+
|
113
|
+
export { SliderController }
|
114
|
+
export type { Slider }
|
@@ -1,13 +1,15 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
2
|
|
3
|
-
|
3
|
+
const SwitchController = class extends Controller<HTMLElement> {
|
4
|
+
// targets
|
4
5
|
static targets = ['input', 'thumb']
|
6
|
+
declare readonly inputTarget: HTMLInputElement
|
7
|
+
declare readonly thumbTarget: HTMLElement
|
8
|
+
|
9
|
+
// values
|
5
10
|
static values = {
|
6
11
|
isChecked: Boolean,
|
7
12
|
}
|
8
|
-
|
9
|
-
declare readonly inputTarget: HTMLInputElement
|
10
|
-
declare readonly thumbTarget: HTMLElement
|
11
13
|
declare isCheckedValue: boolean
|
12
14
|
|
13
15
|
toggle() {
|
@@ -28,3 +30,8 @@ export default class extends Controller<HTMLElement> {
|
|
28
30
|
}
|
29
31
|
}
|
30
32
|
}
|
33
|
+
|
34
|
+
type Switch = InstanceType<typeof SwitchController>
|
35
|
+
|
36
|
+
export { SwitchController }
|
37
|
+
export type { Switch }
|
@@ -1,18 +1,21 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
|
+
import { getNextEnabledIndex, getPreviousEnabledIndex } from '../utils'
|
2
3
|
|
3
|
-
|
4
|
+
const TabsController = class extends Controller {
|
5
|
+
// targets
|
4
6
|
static targets = ['trigger', 'content']
|
7
|
+
declare readonly triggerTargets: HTMLButtonElement[]
|
8
|
+
declare readonly contentTargets: HTMLElement[]
|
9
|
+
|
10
|
+
// values
|
5
11
|
static values = {
|
6
12
|
active: String,
|
7
13
|
}
|
8
|
-
|
9
|
-
declare readonly triggerTargets: HTMLButtonElement[]
|
10
|
-
declare readonly contentTargets: HTMLElement[]
|
11
|
-
declare activeValue: string | undefined
|
14
|
+
declare activeValue: string
|
12
15
|
|
13
16
|
connect() {
|
14
17
|
if (!this.activeValue) {
|
15
|
-
this.activeValue = this.triggerTargets[0].dataset.value
|
18
|
+
this.activeValue = this.triggerTargets[0].dataset.value as string
|
16
19
|
}
|
17
20
|
}
|
18
21
|
|
@@ -20,7 +23,7 @@ export default class extends Controller {
|
|
20
23
|
const target = event.currentTarget as HTMLButtonElement
|
21
24
|
|
22
25
|
if (event instanceof MouseEvent) {
|
23
|
-
this.activeValue = target.dataset.value
|
26
|
+
this.activeValue = target.dataset.value as string
|
24
27
|
} else {
|
25
28
|
const key = event.key
|
26
29
|
|
@@ -32,20 +35,20 @@ export default class extends Controller {
|
|
32
35
|
let newIndex = 0
|
33
36
|
|
34
37
|
if (key === 'ArrowLeft') {
|
35
|
-
newIndex =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
}
|
38
|
+
newIndex = getPreviousEnabledIndex({
|
39
|
+
items: focusableTriggers,
|
40
|
+
currentIndex: index,
|
41
|
+
wrapAround: true,
|
42
|
+
})
|
40
43
|
} else {
|
41
|
-
newIndex =
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}
|
44
|
+
newIndex = getNextEnabledIndex({
|
45
|
+
items: focusableTriggers,
|
46
|
+
currentIndex: index,
|
47
|
+
wrapAround: true,
|
48
|
+
})
|
46
49
|
}
|
47
50
|
|
48
|
-
this.activeValue = focusableTriggers[newIndex].dataset.value
|
51
|
+
this.activeValue = focusableTriggers[newIndex].dataset.value as string
|
49
52
|
focusableTriggers[newIndex].focus()
|
50
53
|
}
|
51
54
|
}
|
@@ -77,3 +80,8 @@ export default class extends Controller {
|
|
77
80
|
})
|
78
81
|
}
|
79
82
|
}
|
83
|
+
|
84
|
+
type Tabs = InstanceType<typeof TabsController>
|
85
|
+
|
86
|
+
export { TabsController }
|
87
|
+
export type { Tabs }
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
2
|
|
3
|
-
|
3
|
+
const ThemeSwitcherController = class extends Controller {
|
4
4
|
initialize() {
|
5
5
|
if (
|
6
6
|
localStorage.theme === 'dark' ||
|
@@ -33,3 +33,8 @@ export default class extends Controller {
|
|
33
33
|
document.documentElement.style.colorScheme = 'dark'
|
34
34
|
}
|
35
35
|
}
|
36
|
+
|
37
|
+
type ThemeSwitcher = InstanceType<typeof ThemeSwitcherController>
|
38
|
+
|
39
|
+
export { ThemeSwitcherController }
|
40
|
+
export type { ThemeSwitcher }
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
2
|
import DOMPurify from 'dompurify'
|
3
3
|
|
4
|
-
|
4
|
+
const ToastContainerController = class extends Controller<HTMLElement> {
|
5
5
|
addToast({
|
6
6
|
title,
|
7
7
|
description,
|
@@ -60,3 +60,8 @@ export default class extends Controller<HTMLElement> {
|
|
60
60
|
this.element.append(clone)
|
61
61
|
}
|
62
62
|
}
|
63
|
+
|
64
|
+
type ToastContainer = InstanceType<typeof ToastContainerController>
|
65
|
+
|
66
|
+
export { ToastContainerController }
|
67
|
+
export type { ToastContainer }
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus'
|
2
2
|
import { ANIMATION_OUT_DELAY } from '../utils'
|
3
3
|
|
4
|
-
|
4
|
+
const ToastController = class extends Controller<HTMLElement> {
|
5
|
+
// custom properties
|
5
6
|
declare duration: number
|
6
7
|
declare closeTimeout: number
|
7
8
|
|
@@ -26,3 +27,8 @@ export default class extends Controller<HTMLElement> {
|
|
26
27
|
}
|
27
28
|
}
|
28
29
|
}
|
30
|
+
|
31
|
+
type Toast = InstanceType<typeof ToastController>
|
32
|
+
|
33
|
+
export { ToastController }
|
34
|
+
export type { Toast }
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
const ToggleController = class extends Controller<HTMLElement> {
|
4
|
+
// values
|
5
|
+
static values = {
|
6
|
+
isOn: Boolean,
|
7
|
+
}
|
8
|
+
declare isOnValue: boolean
|
9
|
+
|
10
|
+
toggle() {
|
11
|
+
this.isOnValue = !this.isOnValue
|
12
|
+
}
|
13
|
+
|
14
|
+
isOnValueChanged(isOn: boolean) {
|
15
|
+
if (isOn) {
|
16
|
+
this.element.dataset.state = 'on'
|
17
|
+
this.element.ariaPressed = 'true'
|
18
|
+
} else {
|
19
|
+
this.element.dataset.state = 'off'
|
20
|
+
this.element.ariaPressed = 'false'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
type Toggle = InstanceType<typeof ToggleController>
|
26
|
+
|
27
|
+
export { ToggleController }
|
28
|
+
export type { Toggle }
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
const ToggleController = class extends Controller<HTMLElement> {
|
4
|
+
// values
|
5
|
+
static values = {
|
6
|
+
isOn: Boolean,
|
7
|
+
}
|
8
|
+
declare isOnValue: boolean
|
9
|
+
|
10
|
+
toggle() {
|
11
|
+
this.isOnValue = !this.isOnValue
|
12
|
+
}
|
13
|
+
|
14
|
+
isOnValueChanged(isOn: boolean) {
|
15
|
+
if (isOn) {
|
16
|
+
this.element.dataset.state = 'on'
|
17
|
+
this.element.ariaPressed = 'true'
|
18
|
+
} else {
|
19
|
+
this.element.dataset.state = 'off'
|
20
|
+
this.element.ariaPressed = 'false'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
type Toggle = InstanceType<typeof ToggleController>
|
26
|
+
|
27
|
+
export { ToggleController }
|
28
|
+
export type { Toggle }
|