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.
- checksums.yaml +4 -4
- data/app/javascript/controllers/accordion_controller.js +107 -0
- data/app/javascript/controllers/alert_dialog_controller.js +7 -0
- data/app/javascript/controllers/avatar_controller.js +14 -0
- data/app/javascript/controllers/checkbox_controller.js +29 -0
- data/app/javascript/controllers/collapsible_controller.js +39 -0
- data/app/javascript/controllers/combobox_controller.js +278 -0
- data/app/javascript/controllers/command_controller.js +207 -0
- data/app/javascript/controllers/date_picker_controller.js +258 -0
- data/app/javascript/controllers/date_range_picker_controller.js +200 -0
- data/app/javascript/controllers/dialog_controller.js +83 -0
- data/app/javascript/controllers/dropdown_menu_controller.js +238 -0
- data/app/javascript/controllers/dropdown_menu_sub_controller.js +118 -0
- data/app/javascript/controllers/form_field_controller.js +20 -0
- data/app/javascript/controllers/hover_card_controller.js +73 -0
- data/app/javascript/controllers/loading_button_controller.js +14 -0
- data/app/javascript/controllers/popover_controller.js +90 -0
- data/app/javascript/controllers/progress_controller.js +14 -0
- data/app/javascript/controllers/radio_group_controller.js +80 -0
- data/app/javascript/controllers/select_controller.js +265 -0
- data/app/javascript/controllers/sidebar_controller.js +29 -0
- data/app/javascript/controllers/sidebar_trigger_controller.js +15 -0
- data/app/javascript/controllers/slider_controller.js +82 -0
- data/app/javascript/controllers/switch_controller.js +26 -0
- data/app/javascript/controllers/tabs_controller.js +66 -0
- data/app/javascript/controllers/theme_switcher_controller.js +32 -0
- data/app/javascript/controllers/toast_container_controller.js +48 -0
- data/app/javascript/controllers/toast_controller.js +22 -0
- data/app/javascript/controllers/toggle_controller.js +20 -0
- data/app/javascript/controllers/toggle_group_controller.js +20 -0
- data/app/javascript/controllers/tooltip_controller.js +79 -0
- data/app/javascript/shadcn_phlexcomponents.js +60 -0
- data/app/javascript/utils/command.js +448 -0
- data/app/javascript/utils/floating_ui.js +160 -0
- data/app/javascript/utils/index.js +288 -0
- data/app/stylesheets/date_picker.css +118 -0
- data/app/typescript/controllers/accordion_controller.ts +136 -0
- data/app/typescript/controllers/alert_dialog_controller.ts +12 -0
- data/app/{javascript → typescript}/controllers/avatar_controller.ts +7 -2
- data/app/{javascript → typescript}/controllers/checkbox_controller.ts +11 -4
- data/app/{javascript → typescript}/controllers/collapsible_controller.ts +12 -5
- data/app/typescript/controllers/combobox_controller.ts +376 -0
- data/app/typescript/controllers/command_controller.ts +301 -0
- data/app/{javascript → typescript}/controllers/date_picker_controller.ts +185 -125
- data/app/{javascript → typescript}/controllers/date_range_picker_controller.ts +89 -79
- data/app/{javascript → typescript}/controllers/dialog_controller.ts +59 -57
- data/app/typescript/controllers/dropdown_menu_controller.ts +309 -0
- data/app/{javascript → typescript}/controllers/dropdown_menu_sub_controller.ts +31 -29
- data/app/{javascript → typescript}/controllers/form_field_controller.ts +6 -1
- data/app/{javascript → typescript}/controllers/hover_card_controller.ts +36 -26
- data/app/{javascript → typescript}/controllers/loading_button_controller.ts +6 -1
- data/app/{javascript → typescript}/controllers/popover_controller.ts +42 -65
- data/app/{javascript → typescript}/controllers/progress_controller.ts +9 -3
- data/app/{javascript → typescript}/controllers/radio_group_controller.ts +16 -9
- data/app/typescript/controllers/select_controller.ts +341 -0
- data/app/{javascript → typescript}/controllers/slider_controller.ts +23 -16
- data/app/{javascript → typescript}/controllers/switch_controller.ts +11 -4
- data/app/{javascript → typescript}/controllers/tabs_controller.ts +26 -18
- data/app/{javascript → typescript}/controllers/theme_switcher_controller.ts +6 -1
- data/app/{javascript → typescript}/controllers/toast_container_controller.ts +6 -1
- data/app/{javascript → typescript}/controllers/toast_controller.ts +7 -1
- data/app/typescript/controllers/toggle_controller.ts +28 -0
- data/app/typescript/controllers/toggle_group_controller.ts +28 -0
- data/app/{javascript → typescript}/controllers/tooltip_controller.ts +43 -31
- data/app/typescript/shadcn_phlexcomponents.ts +61 -0
- data/app/typescript/utils/command.ts +544 -0
- data/app/typescript/utils/floating_ui.ts +196 -0
- data/app/typescript/utils/index.ts +424 -0
- data/lib/install/install_shadcn_phlexcomponents.rb +10 -3
- 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 +71 -32
- data/app/javascript/controllers/accordion_controller.ts +0 -133
- data/app/javascript/controllers/combobox_controller.ts +0 -145
- data/app/javascript/controllers/command_controller.ts +0 -129
- data/app/javascript/controllers/command_root_controller.ts +0 -355
- data/app/javascript/controllers/dropdown_menu_controller.ts +0 -133
- data/app/javascript/controllers/dropdown_menu_root_controller.ts +0 -234
- data/app/javascript/controllers/select_controller.ts +0 -200
- data/app/javascript/shadcn_phlexcomponents.ts +0 -57
- data/app/javascript/utils.ts +0 -437
- /data/app/{javascript → typescript}/controllers/sidebar_controller.ts +0 -0
- /data/app/{javascript → typescript}/controllers/sidebar_trigger_controller.ts +0 -0
@@ -1,15 +1,18 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
import { useClickOutside } from 'stimulus-use'
|
1
3
|
import {
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
focusTrigger,
|
5
|
+
getFocusableElements,
|
6
|
+
showContent,
|
7
|
+
hideContent,
|
5
8
|
lockScroll,
|
6
9
|
unlockScroll,
|
7
|
-
|
8
|
-
|
10
|
+
handleTabNavigation,
|
11
|
+
focusElement,
|
9
12
|
} from '../utils'
|
13
|
+
import { initFloatingUi } from '../utils/floating_ui'
|
10
14
|
import { Calendar, Options } from 'vanilla-calendar-pro'
|
11
15
|
import Inputmask from 'inputmask'
|
12
|
-
import PopoverController from './popover_controller'
|
13
16
|
import dayjs from 'dayjs'
|
14
17
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
15
18
|
import utc from 'dayjs/plugin/utc'
|
@@ -18,7 +21,8 @@ dayjs.extend(utc)
|
|
18
21
|
|
19
22
|
const DAYJS_FORMAT = 'YYYY-MM-DD'
|
20
23
|
|
21
|
-
|
24
|
+
const DatePickerController = class extends Controller<HTMLElement> {
|
25
|
+
// targets
|
22
26
|
static targets = [
|
23
27
|
'trigger',
|
24
28
|
'triggerText',
|
@@ -28,31 +32,42 @@ export default class extends PopoverController {
|
|
28
32
|
'hiddenInput',
|
29
33
|
'inputContainer',
|
30
34
|
'calendar',
|
35
|
+
'overlay',
|
31
36
|
]
|
32
|
-
|
33
|
-
static values = { isOpen: Boolean, date: String }
|
34
|
-
|
37
|
+
declare readonly triggerTarget: HTMLElement
|
35
38
|
declare readonly triggerTextTarget: HTMLElement
|
39
|
+
declare readonly hasTriggerTextTarget: boolean
|
40
|
+
declare readonly contentContainerTarget: HTMLElement
|
41
|
+
declare readonly contentTarget: HTMLElement
|
36
42
|
declare readonly inputTarget: HTMLInputElement
|
43
|
+
declare readonly hasInputTarget: boolean
|
37
44
|
declare readonly hiddenInputTarget: HTMLInputElement
|
38
45
|
declare readonly inputContainerTarget: HTMLElement
|
39
46
|
declare readonly calendarTarget: HTMLElement
|
40
|
-
declare
|
47
|
+
declare readonly overlayTarget: HTMLElement
|
48
|
+
|
49
|
+
// values
|
50
|
+
static values = { isOpen: Boolean, date: String }
|
51
|
+
declare isOpenValue: boolean
|
52
|
+
declare dateValue: string
|
53
|
+
|
54
|
+
// custom properties
|
41
55
|
declare format: string
|
42
56
|
declare mask: boolean
|
43
|
-
declare dateValue: string
|
44
57
|
declare calendar: Calendar
|
45
|
-
declare
|
46
|
-
declare
|
58
|
+
declare isMobile: boolean
|
59
|
+
declare DOMKeydownListener: (event: KeyboardEvent) => void
|
60
|
+
declare cleanup: () => void
|
61
|
+
declare onClickDateListener: (self: Calendar) => void
|
47
62
|
|
48
63
|
connect() {
|
49
|
-
super.connect()
|
50
|
-
this.onClickDateListener = this.onClickDate.bind(this)
|
51
64
|
this.format = this.element.dataset.format || 'DD/MM/YYYY'
|
52
65
|
this.mask = this.element.dataset.mask === 'true'
|
53
66
|
|
54
|
-
|
67
|
+
this.DOMKeydownListener = this.onDOMKeydown.bind(this)
|
68
|
+
this.onClickDateListener = this.onClickDate.bind(this)
|
55
69
|
|
70
|
+
const options = this.getOptions()
|
56
71
|
this.calendar = new Calendar(this.calendarTarget, options)
|
57
72
|
this.calendar.init()
|
58
73
|
|
@@ -61,30 +76,50 @@ export default class extends PopoverController {
|
|
61
76
|
}
|
62
77
|
|
63
78
|
this.calendarTarget.removeAttribute('tabindex')
|
79
|
+
|
80
|
+
useClickOutside(this, { element: this.contentTarget, dispatchEvent: false })
|
81
|
+
}
|
82
|
+
|
83
|
+
contentContainerTargetConnected() {
|
84
|
+
// Datepicker is shown as a dialog on small screens
|
85
|
+
const styles = window.getComputedStyle(this.contentContainerTarget)
|
86
|
+
this.isMobile = styles.translate === '-50%'
|
87
|
+
}
|
88
|
+
|
89
|
+
toggle() {
|
90
|
+
if (this.isOpenValue) {
|
91
|
+
this.close()
|
92
|
+
} else {
|
93
|
+
this.open()
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
open() {
|
98
|
+
this.isOpenValue = true
|
99
|
+
}
|
100
|
+
|
101
|
+
close() {
|
102
|
+
this.isOpenValue = false
|
64
103
|
}
|
65
104
|
|
66
105
|
inputBlur() {
|
67
106
|
let dateDisplay = ''
|
68
107
|
const date = this.calendar.context.selectedDates[0]
|
69
|
-
|
70
108
|
if (date) {
|
71
109
|
dateDisplay = dayjs(date).format(this.format)
|
72
110
|
}
|
73
|
-
|
74
111
|
this.inputTarget.value = dateDisplay
|
75
112
|
this.inputContainerTarget.dataset.focus = 'false'
|
76
113
|
}
|
77
114
|
|
78
115
|
inputDate(event: KeyboardEvent) {
|
79
116
|
const value = (event.target as HTMLInputElement).value
|
80
|
-
|
81
117
|
if (value.length === 0) {
|
82
118
|
this.calendar.set({
|
83
119
|
selectedDates: [],
|
84
120
|
})
|
85
121
|
this.dateValue = ''
|
86
122
|
}
|
87
|
-
|
88
123
|
if (value.length > 0 && dayjs(value, this.format, true).isValid()) {
|
89
124
|
const dayjsDate = dayjs(value, this.format).format(DAYJS_FORMAT)
|
90
125
|
this.calendar.set({
|
@@ -98,38 +133,23 @@ export default class extends PopoverController {
|
|
98
133
|
this.inputContainerTarget.dataset.focus = 'true'
|
99
134
|
}
|
100
135
|
|
101
|
-
|
102
|
-
const
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
)
|
107
|
-
|
108
|
-
const currentElement = this.contentTarget.querySelector(
|
109
|
-
'[aria-current]',
|
110
|
-
) as HTMLElement
|
111
|
-
|
112
|
-
if (selectedElement) {
|
113
|
-
return selectedElement
|
114
|
-
} else if (currentElement) {
|
115
|
-
const firstElementChild = currentElement.firstElementChild as HTMLElement
|
116
|
-
return firstElementChild
|
117
|
-
} else {
|
118
|
-
return focusableElements[0]
|
119
|
-
}
|
136
|
+
clickOutside(event: MouseEvent) {
|
137
|
+
const target = event.target as HTMLElement
|
138
|
+
// Let trigger handle state
|
139
|
+
if (target === this.triggerTarget) return
|
140
|
+
if (this.triggerTarget.contains(target)) return
|
141
|
+
if (this.triggerTarget.id === target.getAttribute('for')) return
|
142
|
+
this.close()
|
120
143
|
}
|
121
144
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
showOverlay({ elementId: this.contentTarget.id })
|
130
|
-
}
|
145
|
+
isOpenValueChanged(isOpen: boolean, previousIsOpen: boolean) {
|
146
|
+
if (isOpen) {
|
147
|
+
showContent({
|
148
|
+
trigger: this.triggerTarget,
|
149
|
+
content: this.contentTarget,
|
150
|
+
contentContainer: this.contentContainerTarget,
|
151
|
+
})
|
131
152
|
|
132
|
-
setTimeout(() => {
|
133
153
|
// Prevent width from changing when changing to month/year view
|
134
154
|
if (!this.contentTarget.dataset.width) {
|
135
155
|
const contentWidth = this.contentTarget.offsetWidth
|
@@ -139,32 +159,121 @@ export default class extends PopoverController {
|
|
139
159
|
this.contentTarget.style.minWidth = `${contentWidth}px`
|
140
160
|
}
|
141
161
|
|
142
|
-
if (this.isMobile
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
this.
|
162
|
+
if (this.isMobile) {
|
163
|
+
lockScroll(this.contentTarget.id)
|
164
|
+
this.overlayTarget.style.display = ''
|
165
|
+
this.overlayTarget.dataset.state = 'open'
|
166
|
+
} else {
|
167
|
+
this.cleanup = initFloatingUi({
|
168
|
+
referenceElement: this.hasInputTarget
|
169
|
+
? this.inputTarget
|
170
|
+
: this.triggerTarget,
|
171
|
+
floatingElement: this.contentContainerTarget,
|
172
|
+
side: this.contentTarget.dataset.side,
|
173
|
+
align: this.contentTarget.dataset.align,
|
174
|
+
sideOffset: 4,
|
175
|
+
})
|
176
|
+
}
|
177
|
+
|
178
|
+
let elementToFocus = null as HTMLElement | null
|
179
|
+
const focusableElements = getFocusableElements(this.contentTarget)
|
180
|
+
|
181
|
+
const selectedElement = Array.from(focusableElements).find(
|
182
|
+
(e) => e.ariaSelected,
|
183
|
+
) as HTMLElement
|
184
|
+
|
185
|
+
const currentElement = this.contentTarget.querySelector(
|
186
|
+
'[aria-current]',
|
187
|
+
) as HTMLElement
|
188
|
+
|
189
|
+
if (selectedElement) {
|
190
|
+
elementToFocus = selectedElement
|
191
|
+
} else if (currentElement) {
|
192
|
+
const firstElementChild =
|
193
|
+
currentElement.firstElementChild as HTMLElement
|
194
|
+
elementToFocus = firstElementChild
|
195
|
+
} else {
|
196
|
+
elementToFocus = focusableElements[0]
|
197
|
+
}
|
198
|
+
|
199
|
+
focusElement(elementToFocus)
|
200
|
+
|
201
|
+
this.setupEventListeners()
|
202
|
+
} else {
|
203
|
+
hideContent({
|
204
|
+
trigger: this.triggerTarget,
|
205
|
+
content: this.contentTarget,
|
206
|
+
contentContainer: this.contentContainerTarget,
|
207
|
+
})
|
208
|
+
|
209
|
+
if (this.isMobile) {
|
210
|
+
unlockScroll(this.contentTarget.id)
|
211
|
+
this.overlayTarget.style.display = 'none'
|
212
|
+
this.overlayTarget.dataset.state = 'closed'
|
213
|
+
}
|
214
|
+
|
215
|
+
if (previousIsOpen) {
|
216
|
+
focusTrigger(this.triggerTarget)
|
217
|
+
}
|
218
|
+
|
219
|
+
this.cleanupEventListeners()
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
dateValueChanged(value: string) {
|
224
|
+
if (value && value.length > 0) {
|
225
|
+
const dayjsDate = dayjs(value)
|
226
|
+
const formattedDate = dayjsDate.format(this.format)
|
227
|
+
|
228
|
+
if (this.hasInputTarget) this.inputTarget.value = formattedDate
|
229
|
+
if (this.hasTriggerTextTarget) {
|
230
|
+
this.triggerTarget.dataset.hasValue = 'true'
|
231
|
+
this.triggerTextTarget.textContent = formattedDate
|
232
|
+
}
|
233
|
+
|
234
|
+
this.hiddenInputTarget.value = dayjsDate.utc().format()
|
235
|
+
} else {
|
236
|
+
if (this.hasInputTarget) this.inputTarget.value = ''
|
237
|
+
|
238
|
+
if (this.hasTriggerTextTarget) {
|
239
|
+
this.triggerTarget.dataset.hasValue = 'false'
|
240
|
+
if (this.triggerTarget.dataset.placeholder) {
|
241
|
+
this.triggerTextTarget.textContent =
|
242
|
+
this.triggerTarget.dataset.placeholder
|
243
|
+
} else {
|
244
|
+
this.triggerTextTarget.textContent = ''
|
149
245
|
}
|
150
246
|
}
|
151
|
-
|
247
|
+
|
248
|
+
this.hiddenInputTarget.value = ''
|
249
|
+
}
|
250
|
+
}
|
251
|
+
|
252
|
+
disconnect() {
|
253
|
+
this.cleanupEventListeners()
|
152
254
|
}
|
153
255
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
256
|
+
protected onClickDate(self: Calendar) {
|
257
|
+
const date = self.context.selectedDates[0]
|
258
|
+
|
259
|
+
if (date) {
|
260
|
+
this.dateValue = date
|
261
|
+
this.close()
|
262
|
+
} else {
|
263
|
+
this.dateValue = ''
|
158
264
|
}
|
159
265
|
}
|
160
266
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
267
|
+
protected setupEventListeners() {
|
268
|
+
document.addEventListener('keydown', this.DOMKeydownListener)
|
269
|
+
}
|
270
|
+
|
271
|
+
protected cleanupEventListeners() {
|
272
|
+
if (this.cleanup) this.cleanup()
|
273
|
+
document.removeEventListener('keydown', this.DOMKeydownListener)
|
165
274
|
}
|
166
275
|
|
167
|
-
getOptions() {
|
276
|
+
protected getOptions() {
|
168
277
|
let options = {
|
169
278
|
type: 'default',
|
170
279
|
enableJumpToSelectedDate: true,
|
@@ -184,7 +293,7 @@ export default class extends PopoverController {
|
|
184
293
|
...JSON.parse(this.element.dataset.options || ''),
|
185
294
|
}
|
186
295
|
} catch {
|
187
|
-
|
296
|
+
// noop
|
188
297
|
}
|
189
298
|
|
190
299
|
if (options.selectedDates && options.selectedDates.length > 0) {
|
@@ -194,29 +303,15 @@ export default class extends PopoverController {
|
|
194
303
|
return options
|
195
304
|
}
|
196
305
|
|
197
|
-
onDOMKeydown(event: KeyboardEvent) {
|
306
|
+
protected onDOMKeydown(event: KeyboardEvent) {
|
198
307
|
if (!this.isOpenValue) return
|
199
308
|
|
200
309
|
const key = event.key
|
201
310
|
|
202
|
-
const focusableElements = getFocusableElements(this.contentTarget)
|
203
|
-
|
204
|
-
const firstElement = focusableElements[0]
|
205
|
-
const lastElement = focusableElements[focusableElements.length - 1]
|
206
|
-
|
207
311
|
if (key === 'Escape') {
|
208
312
|
this.close()
|
209
313
|
} else if (key === 'Tab') {
|
210
|
-
|
211
|
-
if (event.shiftKey && document.activeElement === firstElement) {
|
212
|
-
event.preventDefault()
|
213
|
-
lastElement.focus()
|
214
|
-
}
|
215
|
-
// If Tab pressed on last element, go to first element
|
216
|
-
else if (!event.shiftKey && document.activeElement === lastElement) {
|
217
|
-
event.preventDefault()
|
218
|
-
firstElement.focus()
|
219
|
-
}
|
314
|
+
handleTabNavigation(this.contentTarget, event)
|
220
315
|
} else if (
|
221
316
|
['ArrowUp', 'ArrowDown', 'ArrowRight', 'ArrowLeft'].includes(key) &&
|
222
317
|
document.activeElement != this.inputTarget
|
@@ -225,50 +320,15 @@ export default class extends PopoverController {
|
|
225
320
|
}
|
226
321
|
}
|
227
322
|
|
228
|
-
|
229
|
-
const
|
230
|
-
|
231
|
-
if (date) {
|
232
|
-
this.dateValue = date
|
233
|
-
this.close()
|
234
|
-
} else {
|
235
|
-
this.dateValue = ''
|
236
|
-
}
|
237
|
-
}
|
238
|
-
|
239
|
-
setupInputMask() {
|
240
|
-
const im = new Inputmask(this.format.replace(/[^\/]/g, '9'), {
|
323
|
+
protected setupInputMask() {
|
324
|
+
const im = new Inputmask(this.format.replace(/[^/]/g, '9'), {
|
241
325
|
showMaskOnHover: false,
|
242
326
|
})
|
243
327
|
im.mask(this.inputTarget)
|
244
328
|
}
|
329
|
+
}
|
245
330
|
|
246
|
-
|
247
|
-
if (value && value.length > 0) {
|
248
|
-
const dayjsDate = dayjs(value)
|
249
|
-
const formattedDate = dayjsDate.format(this.format)
|
250
|
-
|
251
|
-
if (this.hasInputTarget) this.inputTarget.value = formattedDate
|
252
|
-
if (this.hasTriggerTextTarget) {
|
253
|
-
this.triggerTarget.dataset.hasValue = 'true'
|
254
|
-
this.triggerTextTarget.textContent = formattedDate
|
255
|
-
}
|
256
|
-
|
257
|
-
this.hiddenInputTarget.value = dayjsDate.utc().format()
|
258
|
-
} else {
|
259
|
-
if (this.hasInputTarget) this.inputTarget.value = ''
|
260
|
-
|
261
|
-
if (this.hasTriggerTextTarget) {
|
262
|
-
this.triggerTarget.dataset.hasValue = 'false'
|
263
|
-
if (this.triggerTarget.dataset.placeholder) {
|
264
|
-
this.triggerTextTarget.textContent =
|
265
|
-
this.triggerTarget.dataset.placeholder
|
266
|
-
} else {
|
267
|
-
this.triggerTextTarget.textContent = ''
|
268
|
-
}
|
269
|
-
}
|
331
|
+
type DatePicker = InstanceType<typeof DatePickerController>
|
270
332
|
|
271
|
-
|
272
|
-
|
273
|
-
}
|
274
|
-
}
|
333
|
+
export { DatePickerController }
|
334
|
+
export type { DatePicker }
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Calendar, Options } from 'vanilla-calendar-pro'
|
2
|
-
import DatePickerController from './date_picker_controller'
|
2
|
+
import { DatePickerController } from './date_picker_controller'
|
3
3
|
import dayjs from 'dayjs'
|
4
4
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
5
5
|
import utc from 'dayjs/plugin/utc'
|
@@ -9,26 +9,28 @@ dayjs.extend(utc)
|
|
9
9
|
const DELIMITER = ' - '
|
10
10
|
const DAYJS_FORMAT = 'YYYY-MM-DD'
|
11
11
|
|
12
|
-
|
12
|
+
const DateRangePickerController = class extends DatePickerController {
|
13
|
+
// targets
|
13
14
|
static targets = [
|
14
15
|
'trigger',
|
15
16
|
'triggerText',
|
16
|
-
'
|
17
|
+
'contentContainer',
|
17
18
|
'content',
|
18
19
|
'input',
|
19
20
|
'hiddenInput',
|
20
21
|
'endHiddenInput',
|
21
22
|
'inputContainer',
|
22
23
|
'calendar',
|
24
|
+
'overlay',
|
23
25
|
]
|
26
|
+
declare readonly endHiddenInputTarget: HTMLInputElement
|
24
27
|
|
28
|
+
// values
|
25
29
|
static values = {
|
26
30
|
isOpen: Boolean,
|
27
31
|
date: String,
|
28
32
|
endDate: String,
|
29
33
|
}
|
30
|
-
|
31
|
-
declare readonly endHiddenInputTarget: HTMLInputElement
|
32
34
|
declare endDateValue: string
|
33
35
|
|
34
36
|
inputBlur() {
|
@@ -88,80 +90,6 @@ export default class extends DatePickerController {
|
|
88
90
|
}
|
89
91
|
}
|
90
92
|
|
91
|
-
getOptions() {
|
92
|
-
let options = {
|
93
|
-
type: 'multiple',
|
94
|
-
selectionDatesMode: 'multiple-ranged',
|
95
|
-
displayMonthsCount: 2,
|
96
|
-
monthsToSwitch: 1,
|
97
|
-
displayDatesOutside: false,
|
98
|
-
enableJumpToSelectedDate: true,
|
99
|
-
onClickDate: this.onClickDateListener,
|
100
|
-
} as Options
|
101
|
-
|
102
|
-
const selectedDates = []
|
103
|
-
|
104
|
-
const startDate = this.element.dataset.value
|
105
|
-
const endDate = this.element.dataset.endValue
|
106
|
-
|
107
|
-
if (startDate && dayjs(startDate).isValid()) {
|
108
|
-
const date = dayjs(startDate).format(DAYJS_FORMAT)
|
109
|
-
selectedDates.push(date)
|
110
|
-
}
|
111
|
-
|
112
|
-
if (endDate && dayjs(endDate).isValid()) {
|
113
|
-
const date = dayjs(endDate).format(DAYJS_FORMAT)
|
114
|
-
selectedDates.push(date)
|
115
|
-
}
|
116
|
-
|
117
|
-
options.selectedDates = selectedDates
|
118
|
-
|
119
|
-
try {
|
120
|
-
options = {
|
121
|
-
...options,
|
122
|
-
...JSON.parse(this.element.dataset.options || ''),
|
123
|
-
}
|
124
|
-
} catch {
|
125
|
-
options = options
|
126
|
-
}
|
127
|
-
|
128
|
-
if (options.selectedDates && options.selectedDates.length > 0) {
|
129
|
-
this.dateValue = `${options.selectedDates[0]}`
|
130
|
-
this.endDateValue = `${options.selectedDates[1]}`
|
131
|
-
}
|
132
|
-
|
133
|
-
return options
|
134
|
-
}
|
135
|
-
|
136
|
-
onClickDate(self: Calendar) {
|
137
|
-
const dates = self.context.selectedDates
|
138
|
-
|
139
|
-
if (dates.length > 0) {
|
140
|
-
const startDate = dates[0]
|
141
|
-
const endDate = dates[1]
|
142
|
-
|
143
|
-
this.dateValue = startDate
|
144
|
-
|
145
|
-
if (endDate) {
|
146
|
-
this.endDateValue = endDate
|
147
|
-
this.close()
|
148
|
-
} else {
|
149
|
-
this.endDateValue = ''
|
150
|
-
}
|
151
|
-
} else {
|
152
|
-
this.dateValue = ''
|
153
|
-
this.endDateValue = ''
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
setupInputMask() {
|
158
|
-
const pattern = this.format.replace(/[^\/]/g, '9')
|
159
|
-
const im = new Inputmask(`${pattern}${DELIMITER}${pattern}`, {
|
160
|
-
showMaskOnHover: false,
|
161
|
-
})
|
162
|
-
im.mask(this.inputTarget)
|
163
|
-
}
|
164
|
-
|
165
93
|
dateValueChanged(value: string) {
|
166
94
|
this.onClickDateListener = this.onClickDate.bind(this)
|
167
95
|
|
@@ -228,6 +156,7 @@ export default class extends DatePickerController {
|
|
228
156
|
}
|
229
157
|
|
230
158
|
if (this.hasInputTarget) this.inputTarget.value = datesDisplay
|
159
|
+
|
231
160
|
if (this.hasTriggerTextTarget) {
|
232
161
|
const hasValue = (!!value && value.length > 0) || !!startDate
|
233
162
|
this.triggerTarget.dataset.hasValue = `${hasValue}`
|
@@ -240,4 +169,85 @@ export default class extends DatePickerController {
|
|
240
169
|
}
|
241
170
|
}
|
242
171
|
}
|
172
|
+
|
173
|
+
protected getOptions() {
|
174
|
+
let options = {
|
175
|
+
type: 'multiple',
|
176
|
+
selectionDatesMode: 'multiple-ranged',
|
177
|
+
displayMonthsCount: 2,
|
178
|
+
monthsToSwitch: 1,
|
179
|
+
displayDatesOutside: false,
|
180
|
+
enableJumpToSelectedDate: true,
|
181
|
+
onClickDate: this.onClickDateListener,
|
182
|
+
} as Options
|
183
|
+
|
184
|
+
const selectedDates = []
|
185
|
+
|
186
|
+
const startDate = this.element.dataset.value
|
187
|
+
const endDate = this.element.dataset.endValue
|
188
|
+
|
189
|
+
if (startDate && dayjs(startDate).isValid()) {
|
190
|
+
const date = dayjs(startDate).format(DAYJS_FORMAT)
|
191
|
+
selectedDates.push(date)
|
192
|
+
}
|
193
|
+
|
194
|
+
if (endDate && dayjs(endDate).isValid()) {
|
195
|
+
const date = dayjs(endDate).format(DAYJS_FORMAT)
|
196
|
+
selectedDates.push(date)
|
197
|
+
}
|
198
|
+
|
199
|
+
options.selectedDates = selectedDates
|
200
|
+
|
201
|
+
try {
|
202
|
+
options = {
|
203
|
+
...options,
|
204
|
+
...JSON.parse(this.element.dataset.options || ''),
|
205
|
+
}
|
206
|
+
} catch {
|
207
|
+
// noop
|
208
|
+
}
|
209
|
+
|
210
|
+
if (options.selectedDates && options.selectedDates.length > 0) {
|
211
|
+
this.dateValue = `${options.selectedDates[0]}`
|
212
|
+
if (options.selectedDates[1]) {
|
213
|
+
this.endDateValue = `${options.selectedDates[1]}`
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
return options
|
218
|
+
}
|
219
|
+
|
220
|
+
protected onClickDate(self: Calendar) {
|
221
|
+
const dates = self.context.selectedDates
|
222
|
+
|
223
|
+
if (dates.length > 0) {
|
224
|
+
const startDate = dates[0]
|
225
|
+
const endDate = dates[1]
|
226
|
+
|
227
|
+
this.dateValue = startDate
|
228
|
+
|
229
|
+
if (endDate) {
|
230
|
+
this.endDateValue = endDate
|
231
|
+
this.close()
|
232
|
+
} else {
|
233
|
+
this.endDateValue = ''
|
234
|
+
}
|
235
|
+
} else {
|
236
|
+
this.dateValue = ''
|
237
|
+
this.endDateValue = ''
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
protected setupInputMask() {
|
242
|
+
const pattern = this.format.replace(/[^\/]/g, '9')
|
243
|
+
const im = new Inputmask(`${pattern}${DELIMITER}${pattern}`, {
|
244
|
+
showMaskOnHover: false,
|
245
|
+
})
|
246
|
+
im.mask(this.inputTarget)
|
247
|
+
}
|
243
248
|
}
|
249
|
+
|
250
|
+
type DateRangePicker = InstanceType<typeof DateRangePickerController>
|
251
|
+
|
252
|
+
export { DateRangePickerController }
|
253
|
+
export type { DateRangePicker }
|