@hakumi-dev/hakumi-components 0.1.17-pre → 0.1.19-pre
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.
- package/README.md +218 -369
- package/app/javascript/hakumi_components/controllers/hakumi/admin_panel_controller.js +5 -7
- package/app/javascript/hakumi_components/controllers/hakumi/back_top_controller.js +1 -1
- package/app/javascript/hakumi_components/controllers/hakumi/button_controller.js +108 -2
- package/app/javascript/hakumi_components/controllers/hakumi/calendar_controller.js +183 -95
- package/app/javascript/hakumi_components/controllers/hakumi/color_picker_controller.js +23 -285
- package/app/javascript/hakumi_components/controllers/hakumi/date_picker_controller.js +274 -262
- package/app/javascript/hakumi_components/controllers/hakumi/float_button_group_controller.js +2 -2
- package/app/javascript/hakumi_components/controllers/hakumi/message_controller.js +4 -2
- package/app/javascript/hakumi_components/controllers/hakumi/modal_controller.js +119 -125
- package/app/javascript/hakumi_components/controllers/hakumi/table/editable.js +291 -0
- package/app/javascript/hakumi_components/controllers/hakumi/table_controller.js +166 -366
- package/app/javascript/hakumi_components/controllers/hakumi/tabs_controller.js +8 -2
- package/app/javascript/hakumi_components/controllers/hakumi/tag_controller.js +27 -25
- package/app/javascript/hakumi_components/controllers/hakumi/tag_group_controller.js +19 -18
- package/app/javascript/hakumi_components/controllers/hakumi/theme_controller.js +116 -117
- package/app/javascript/hakumi_components/controllers/hakumi/transfer_controller.js +17 -1
- package/app/javascript/hakumi_components/controllers/hakumi/tree_controller.js +363 -78
- package/app/javascript/hakumi_components/controllers/hakumi/typography_controller.js +3 -3
- package/app/javascript/hakumi_components/controllers/hakumi/upload_controller.js +320 -204
- package/app/javascript/hakumi_components/core/render_component.js +37 -11
- package/app/javascript/hakumi_components/utils/color_helper.js +262 -0
- package/app/javascript/stylesheets/_base.scss +9 -0
- package/app/javascript/stylesheets/_hakumi_components.scss +1 -0
- package/app/javascript/stylesheets/components/_breadcrumb.scss +2 -2
- package/app/javascript/stylesheets/components/_calendar.scss +13 -13
- package/app/javascript/stylesheets/components/_cascader.scss +5 -5
- package/app/javascript/stylesheets/components/_checkbox.scss +9 -11
- package/app/javascript/stylesheets/components/_color_picker.scss +11 -11
- package/app/javascript/stylesheets/components/_date_picker.scss +4 -4
- package/app/javascript/stylesheets/components/_descriptions.scss +2 -2
- package/app/javascript/stylesheets/components/_drawer.scss +3 -3
- package/app/javascript/stylesheets/components/_dropdown.scss +2 -2
- package/app/javascript/stylesheets/components/_flex.scss +1 -1
- package/app/javascript/stylesheets/components/_float_button.scss +5 -5
- package/app/javascript/stylesheets/components/_form_item.scss +92 -0
- package/app/javascript/stylesheets/components/_image.scss +15 -15
- package/app/javascript/stylesheets/components/_input.scss +23 -113
- package/app/javascript/stylesheets/components/_layout.scss +27 -26
- package/app/javascript/stylesheets/components/_menu.scss +15 -15
- package/app/javascript/stylesheets/components/_modal.scss +13 -13
- package/app/javascript/stylesheets/components/_notification.scss +3 -3
- package/app/javascript/stylesheets/components/_popover.scss +1 -1
- package/app/javascript/stylesheets/components/_segmented.scss +3 -3
- package/app/javascript/stylesheets/components/_select.scss +6 -6
- package/app/javascript/stylesheets/components/_slider.scss +1 -1
- package/app/javascript/stylesheets/components/_spin.scss +2 -2
- package/app/javascript/stylesheets/components/_steps.scss +10 -10
- package/app/javascript/stylesheets/components/_switch.scss +11 -10
- package/app/javascript/stylesheets/components/_table.scss +6 -6
- package/app/javascript/stylesheets/components/_tag.scss +2 -2
- package/app/javascript/stylesheets/components/_tooltip.scss +4 -4
- package/app/javascript/stylesheets/components/_tree_select.scss +3 -3
- package/app/javascript/stylesheets/components/_typography.scss +3 -3
- package/app/javascript/stylesheets/components/_upload.scss +4 -4
- package/package.json +2 -2
|
@@ -169,45 +169,19 @@ export default class extends RegistryController {
|
|
|
169
169
|
initializeTimeFromInput() {
|
|
170
170
|
// Use hidden input for ISO value (24-hour format)
|
|
171
171
|
const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
|
|
172
|
-
const timePart =
|
|
172
|
+
const timePart = this.extractTimePart(currentValue)
|
|
173
173
|
|
|
174
174
|
if (timePart) {
|
|
175
|
-
|
|
176
|
-
let period = undefined
|
|
177
|
-
|
|
178
|
-
// Convert 24h to 12h if needed
|
|
179
|
-
if (this.hasPeriodOptionTarget) {
|
|
180
|
-
if (hour === 0) {
|
|
181
|
-
hour = 12
|
|
182
|
-
period = "AM"
|
|
183
|
-
} else if (hour < 12) {
|
|
184
|
-
period = "AM"
|
|
185
|
-
} else if (hour === 12) {
|
|
186
|
-
period = "PM"
|
|
187
|
-
} else {
|
|
188
|
-
hour -= 12
|
|
189
|
-
period = "PM"
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
this.selectedTime = {
|
|
194
|
-
hour: String(hour).padStart(2, "0"),
|
|
195
|
-
minute: timePart[2],
|
|
196
|
-
second: timePart[3] || "00"
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (period) {
|
|
200
|
-
this.selectedTime.period = period
|
|
201
|
-
}
|
|
175
|
+
this.selectedTime = this.selectedTimeFromTimePart(timePart, this.hasPeriodOptionTarget)
|
|
202
176
|
} else {
|
|
203
|
-
this.selectedTime =
|
|
177
|
+
this.selectedTime = this.defaultSelectedTime()
|
|
204
178
|
}
|
|
205
179
|
|
|
206
180
|
// Also initialize selectedDate if we have a date in the input
|
|
207
181
|
if (currentValue) {
|
|
208
|
-
const datePart =
|
|
182
|
+
const datePart = this.extractDatePart(currentValue)
|
|
209
183
|
if (datePart) {
|
|
210
|
-
this.selectedDate = DateHelper.parseDataDate(datePart
|
|
184
|
+
this.selectedDate = DateHelper.parseDataDate(datePart).toDate()
|
|
211
185
|
}
|
|
212
186
|
}
|
|
213
187
|
|
|
@@ -257,7 +231,7 @@ export default class extends RegistryController {
|
|
|
257
231
|
const inputValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
|
|
258
232
|
|
|
259
233
|
|
|
260
|
-
const calendars = this.
|
|
234
|
+
const calendars = this.calendarElements()
|
|
261
235
|
|
|
262
236
|
calendars.forEach(calendarEl => {
|
|
263
237
|
|
|
@@ -287,41 +261,60 @@ export default class extends RegistryController {
|
|
|
287
261
|
|
|
288
262
|
try {
|
|
289
263
|
switch (picker) {
|
|
290
|
-
case "quarter":
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (isNaN(year)) return null
|
|
301
|
-
return DateHelper.parseDataDate(`${year}-01-01`).toDate()
|
|
302
|
-
}
|
|
303
|
-
case "month": {
|
|
304
|
-
const [year, month] = value.split("-").map(n => parseInt(n, 10))
|
|
305
|
-
if (isNaN(year) || isNaN(month)) return null
|
|
306
|
-
return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
|
|
307
|
-
}
|
|
308
|
-
case "week": {
|
|
309
|
-
const parts = value.split("-W")
|
|
310
|
-
const year = parseInt(parts[0], 10)
|
|
311
|
-
const week = parseInt(parts[1], 10)
|
|
312
|
-
if (isNaN(year) || isNaN(week)) return null
|
|
313
|
-
return DateHelper.getMondayOfISOWeek(year, week).toDate()
|
|
314
|
-
}
|
|
315
|
-
default: {
|
|
316
|
-
const parsed = DateHelper.parseDataDate(value)
|
|
317
|
-
return parsed.isValid() ? parsed.toDate() : null
|
|
318
|
-
}
|
|
264
|
+
case "quarter":
|
|
265
|
+
return this.parseQuarterInputValue(value)
|
|
266
|
+
case "year":
|
|
267
|
+
return this.parseYearInputValue(value)
|
|
268
|
+
case "month":
|
|
269
|
+
return this.parseMonthInputValue(value)
|
|
270
|
+
case "week":
|
|
271
|
+
return this.parseWeekInputValue(value)
|
|
272
|
+
default:
|
|
273
|
+
return this.parseDateInputValue(value)
|
|
319
274
|
}
|
|
320
275
|
} catch (e) {
|
|
321
276
|
return null
|
|
322
277
|
}
|
|
323
278
|
}
|
|
324
279
|
|
|
280
|
+
parseQuarterInputValue(value) {
|
|
281
|
+
const match = value.match(/Q(\d) (\d{4})/)
|
|
282
|
+
if (!match) return null
|
|
283
|
+
|
|
284
|
+
const quarter = parseInt(match[1], 10)
|
|
285
|
+
const year = parseInt(match[2], 10)
|
|
286
|
+
const month = (quarter - 1) * 3 + 1
|
|
287
|
+
return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
parseYearInputValue(value) {
|
|
291
|
+
const year = parseInt(value, 10)
|
|
292
|
+
if (isNaN(year)) return null
|
|
293
|
+
|
|
294
|
+
return DateHelper.parseDataDate(`${year}-01-01`).toDate()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
parseMonthInputValue(value) {
|
|
298
|
+
const [year, month] = value.split("-").map(n => parseInt(n, 10))
|
|
299
|
+
if (isNaN(year) || isNaN(month)) return null
|
|
300
|
+
|
|
301
|
+
return DateHelper.parseDataDate(`${year}-${String(month).padStart(2, "0")}-01`).toDate()
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
parseWeekInputValue(value) {
|
|
305
|
+
const parts = value.split("-W")
|
|
306
|
+
const year = parseInt(parts[0], 10)
|
|
307
|
+
const week = parseInt(parts[1], 10)
|
|
308
|
+
if (isNaN(year) || isNaN(week)) return null
|
|
309
|
+
|
|
310
|
+
return DateHelper.getMondayOfISOWeek(year, week).toDate()
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
parseDateInputValue(value) {
|
|
314
|
+
const parsed = DateHelper.parseDataDate(value)
|
|
315
|
+
return parsed.isValid() ? parsed.toDate() : null
|
|
316
|
+
}
|
|
317
|
+
|
|
325
318
|
close() {
|
|
326
319
|
if (!this.isOpen) return
|
|
327
320
|
|
|
@@ -348,7 +341,7 @@ export default class extends RegistryController {
|
|
|
348
341
|
updateRangeVisuals() {
|
|
349
342
|
if (!this.rangeValue || !this.hasPanelTarget) return
|
|
350
343
|
|
|
351
|
-
const calendars = this.
|
|
344
|
+
const calendars = this.calendarElements()
|
|
352
345
|
|
|
353
346
|
calendars.forEach(calendar => {
|
|
354
347
|
const api = calendar.hakumiComponent?.api
|
|
@@ -370,7 +363,7 @@ export default class extends RegistryController {
|
|
|
370
363
|
clearRangeVisuals() {
|
|
371
364
|
if (!this.hasPanelTarget) return
|
|
372
365
|
|
|
373
|
-
const calendars = this.
|
|
366
|
+
const calendars = this.calendarElements()
|
|
374
367
|
calendars.forEach(calendar => {
|
|
375
368
|
const api = calendar.hakumiComponent?.api
|
|
376
369
|
if (api) api.clearRange()
|
|
@@ -380,44 +373,25 @@ export default class extends RegistryController {
|
|
|
380
373
|
syncRangeCalendars() {
|
|
381
374
|
if (!this.rangeValue || !this.hasPanelTarget) return
|
|
382
375
|
|
|
383
|
-
const
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
const startCalendar = calendars[0]
|
|
387
|
-
const endCalendar = calendars[1]
|
|
388
|
-
|
|
389
|
-
const startApi = startCalendar.hakumiComponent?.api
|
|
390
|
-
const endApi = endCalendar.hakumiComponent?.api
|
|
376
|
+
const rangeCalendars = this.rangeCalendarElements()
|
|
377
|
+
if (!rangeCalendars) return
|
|
391
378
|
|
|
379
|
+
const [startCalendar, endCalendar] = rangeCalendars
|
|
380
|
+
const [startApi, endApi] = this.calendarApis(startCalendar, endCalendar)
|
|
392
381
|
if (!startApi || !endApi) return
|
|
393
382
|
|
|
394
383
|
// Get current month/year from first calendar
|
|
395
384
|
const startDate = startApi.getValue()
|
|
396
|
-
|
|
397
|
-
const startYear = startDate.getFullYear()
|
|
398
|
-
|
|
399
|
-
// Calculate next month for second calendar
|
|
400
|
-
let endMonth = startMonth + 1
|
|
401
|
-
let endYear = startYear
|
|
402
|
-
|
|
403
|
-
if (endMonth > 11) {
|
|
404
|
-
endMonth = 0
|
|
405
|
-
endYear += 1
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Set second calendar to next month
|
|
409
|
-
const endDate = new Date(endYear, endMonth, 1)
|
|
410
|
-
endApi.setValue(endDate, { navigate: true })
|
|
385
|
+
endApi.setValue(this.monthOffsetDate(startDate, 1), { navigate: true })
|
|
411
386
|
}
|
|
412
387
|
|
|
413
388
|
setupRangeCalendarListeners() {
|
|
414
389
|
if (!this.rangeValue || !this.hasPanelTarget) return
|
|
415
390
|
|
|
416
|
-
const
|
|
417
|
-
if (
|
|
391
|
+
const rangeCalendars = this.rangeCalendarElements()
|
|
392
|
+
if (!rangeCalendars) return
|
|
418
393
|
|
|
419
|
-
const startCalendar =
|
|
420
|
-
const endCalendar = calendars[1]
|
|
394
|
+
const [startCalendar, endCalendar] = rangeCalendars
|
|
421
395
|
|
|
422
396
|
// Remove previous listeners if they exist
|
|
423
397
|
if (this.boundSyncFromStart) {
|
|
@@ -428,68 +402,15 @@ export default class extends RegistryController {
|
|
|
428
402
|
}
|
|
429
403
|
|
|
430
404
|
// Create listeners
|
|
431
|
-
this.boundSyncFromStart = (
|
|
432
|
-
|
|
433
|
-
const endApi = endCalendar.hakumiComponent?.api
|
|
434
|
-
if (!startApi || !endApi) return
|
|
435
|
-
|
|
436
|
-
const startDate = startApi.getValue()
|
|
437
|
-
const startMonth = startDate.getMonth()
|
|
438
|
-
const startYear = startDate.getFullYear()
|
|
439
|
-
|
|
440
|
-
// Calculate next month
|
|
441
|
-
let endMonth = startMonth + 1
|
|
442
|
-
let endYear = startYear
|
|
443
|
-
|
|
444
|
-
if (endMonth > 11) {
|
|
445
|
-
endMonth = 0
|
|
446
|
-
endYear += 1
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const endDate = endApi.getValue()
|
|
450
|
-
const currentEndMonth = endDate.getMonth()
|
|
451
|
-
const currentEndYear = endDate.getFullYear()
|
|
452
|
-
|
|
453
|
-
// Only sync if second calendar is not already on the correct month
|
|
454
|
-
if (currentEndMonth !== endMonth || currentEndYear !== endYear) {
|
|
455
|
-
endApi.setValue(new Date(endYear, endMonth, 1), { navigate: true })
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
this.boundSyncFromEnd = (event) => {
|
|
460
|
-
const startApi = startCalendar.hakumiComponent?.api
|
|
461
|
-
const endApi = endCalendar.hakumiComponent?.api
|
|
462
|
-
if (!startApi || !endApi) return
|
|
463
|
-
|
|
464
|
-
const endDate = endApi.getValue()
|
|
465
|
-
const endMonth = endDate.getMonth()
|
|
466
|
-
const endYear = endDate.getFullYear()
|
|
467
|
-
|
|
468
|
-
// Calculate previous month
|
|
469
|
-
let startMonth = endMonth - 1
|
|
470
|
-
let startYear = endYear
|
|
471
|
-
|
|
472
|
-
if (startMonth < 0) {
|
|
473
|
-
startMonth = 11
|
|
474
|
-
startYear -= 1
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const startDate = startApi.getValue()
|
|
478
|
-
const currentStartMonth = startDate.getMonth()
|
|
479
|
-
const currentStartYear = startDate.getFullYear()
|
|
480
|
-
|
|
481
|
-
// Only sync if first calendar is not already on the correct month
|
|
482
|
-
if (currentStartMonth !== startMonth || currentStartYear !== startYear) {
|
|
483
|
-
startApi.setValue(new Date(startYear, startMonth, 1), { navigate: true })
|
|
484
|
-
}
|
|
485
|
-
}
|
|
405
|
+
this.boundSyncFromStart = () => this.syncAdjacentRangeCalendar(startCalendar, endCalendar, 1)
|
|
406
|
+
this.boundSyncFromEnd = () => this.syncAdjacentRangeCalendar(endCalendar, startCalendar, -1)
|
|
486
407
|
|
|
487
408
|
// Hover listener
|
|
488
409
|
this.boundHandleHover = (event) => {
|
|
489
410
|
const hoveredDate = event.detail?.date
|
|
490
411
|
if (!hoveredDate) return
|
|
491
412
|
|
|
492
|
-
const calendars = this.
|
|
413
|
+
const calendars = this.calendarElements()
|
|
493
414
|
calendars.forEach(calendar => {
|
|
494
415
|
const api = calendar.hakumiComponent?.api
|
|
495
416
|
if (api) {
|
|
@@ -498,13 +419,61 @@ export default class extends RegistryController {
|
|
|
498
419
|
})
|
|
499
420
|
}
|
|
500
421
|
|
|
501
|
-
//
|
|
422
|
+
// Add the listeners
|
|
502
423
|
startCalendar.addEventListener("hakumi--calendar:change", this.boundSyncFromStart)
|
|
503
424
|
endCalendar.addEventListener("hakumi--calendar:change", this.boundSyncFromEnd)
|
|
504
425
|
startCalendar.addEventListener("hakumi--calendar:cell-hover", this.boundHandleHover)
|
|
505
426
|
endCalendar.addEventListener("hakumi--calendar:cell-hover", this.boundHandleHover)
|
|
506
427
|
}
|
|
507
428
|
|
|
429
|
+
calendarElements() {
|
|
430
|
+
if (!this.hasPanelTarget) return []
|
|
431
|
+
|
|
432
|
+
return this.panelTarget.querySelectorAll(".hakumi-calendar")
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
rangeCalendarElements() {
|
|
436
|
+
const calendars = this.calendarElements()
|
|
437
|
+
if (calendars.length !== 2) return null
|
|
438
|
+
|
|
439
|
+
return [calendars[0], calendars[1]]
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
calendarApis(...calendars) {
|
|
443
|
+
return calendars.map(calendar => calendar.hakumiComponent?.api)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
monthOffsetDate(date, offset) {
|
|
447
|
+
let month = date.getMonth() + offset
|
|
448
|
+
let year = date.getFullYear()
|
|
449
|
+
|
|
450
|
+
if (month > 11) {
|
|
451
|
+
month = 0
|
|
452
|
+
year += 1
|
|
453
|
+
} else if (month < 0) {
|
|
454
|
+
month = 11
|
|
455
|
+
year -= 1
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return new Date(year, month, 1)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
syncAdjacentRangeCalendar(sourceCalendar, targetCalendar, offset) {
|
|
462
|
+
const [sourceApi, targetApi] = this.calendarApis(sourceCalendar, targetCalendar)
|
|
463
|
+
if (!sourceApi || !targetApi) return
|
|
464
|
+
|
|
465
|
+
const targetDate = this.monthOffsetDate(sourceApi.getValue(), offset)
|
|
466
|
+
const currentTargetDate = targetApi.getValue()
|
|
467
|
+
|
|
468
|
+
// Only sync if paired calendar is not already on the correct month
|
|
469
|
+
if (
|
|
470
|
+
currentTargetDate.getMonth() !== targetDate.getMonth() ||
|
|
471
|
+
currentTargetDate.getFullYear() !== targetDate.getFullYear()
|
|
472
|
+
) {
|
|
473
|
+
targetApi.setValue(targetDate, { navigate: true })
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
508
477
|
clear(event) {
|
|
509
478
|
if (event) event.stopPropagation()
|
|
510
479
|
if (this.disabledValue) return
|
|
@@ -527,6 +496,8 @@ export default class extends RegistryController {
|
|
|
527
496
|
if (this.disabledValue) return
|
|
528
497
|
if (this.rangeValue && this.hasStartInputTarget) {
|
|
529
498
|
this.startInputTarget.focus()
|
|
499
|
+
} else if (this.hasDisplayInputTarget) {
|
|
500
|
+
this.displayInputTarget.focus()
|
|
530
501
|
} else if (this.hasInputTarget) {
|
|
531
502
|
this.inputTarget.focus()
|
|
532
503
|
}
|
|
@@ -536,6 +507,8 @@ export default class extends RegistryController {
|
|
|
536
507
|
if (this.rangeValue) {
|
|
537
508
|
if (this.hasStartInputTarget) this.startInputTarget.blur()
|
|
538
509
|
if (this.hasEndInputTarget) this.endInputTarget.blur()
|
|
510
|
+
} else if (this.hasDisplayInputTarget) {
|
|
511
|
+
this.displayInputTarget.blur()
|
|
539
512
|
} else if (this.hasInputTarget) {
|
|
540
513
|
this.inputTarget.blur()
|
|
541
514
|
}
|
|
@@ -577,124 +550,96 @@ export default class extends RegistryController {
|
|
|
577
550
|
if (!(date instanceof Date)) return
|
|
578
551
|
if (this.isDateDisabled(date)) return
|
|
579
552
|
|
|
580
|
-
// When show_time is enabled, don't close panel or update input immediately
|
|
581
|
-
// Store the selected date and wait for OK button
|
|
582
553
|
if (this.showTimeValue) {
|
|
583
|
-
this.
|
|
584
|
-
// Initialize time from current value or defaults
|
|
585
|
-
if (!this.selectedTime) {
|
|
586
|
-
const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
|
|
587
|
-
const timePart = currentValue.match(/(\d{2}):(\d{2})(?::(\d{2}))?$/)
|
|
588
|
-
if (timePart) {
|
|
589
|
-
this.selectedTime = {
|
|
590
|
-
hour: timePart[1],
|
|
591
|
-
minute: timePart[2],
|
|
592
|
-
second: timePart[3] || "00"
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
595
|
-
this.selectedTime = { hour: "00", minute: "00", second: "00" }
|
|
596
|
-
}
|
|
597
|
-
this.updateTimeSelection()
|
|
598
|
-
}
|
|
554
|
+
this.handleDateTimeDateSelection(date)
|
|
599
555
|
return
|
|
600
556
|
}
|
|
601
557
|
|
|
602
558
|
const formatted = this.formatDate(date)
|
|
603
559
|
|
|
604
560
|
if (this.rangeValue) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
this.tempRangeStart = date
|
|
609
|
-
this.tempRangeEnd = null
|
|
610
|
-
|
|
611
|
-
// Update start input
|
|
612
|
-
if (this.hasStartInputTarget) {
|
|
613
|
-
this.startInputTarget.value = formatted
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// Clear end input
|
|
617
|
-
if (this.hasEndInputTarget) {
|
|
618
|
-
this.endInputTarget.value = ""
|
|
619
|
-
}
|
|
561
|
+
this.handleRangeDateSelection(date, formatted)
|
|
562
|
+
return // Early return for ranges
|
|
563
|
+
}
|
|
620
564
|
|
|
621
|
-
|
|
622
|
-
|
|
565
|
+
// Code for non-range pickers
|
|
566
|
+
// Use setValue to update both hidden and display inputs
|
|
567
|
+
this.setValue(formatted)
|
|
568
|
+
this.syncCalendarWithInput()
|
|
569
|
+
this.updateClearVisibility()
|
|
570
|
+
this.close()
|
|
571
|
+
}
|
|
623
572
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
})
|
|
628
|
-
} else if (!this.tempRangeEnd) {
|
|
629
|
-
// Second selection: set range end
|
|
630
|
-
const clickedDate = date
|
|
573
|
+
handleDateTimeDateSelection(date) {
|
|
574
|
+
// Store the selected date and wait for OK button.
|
|
575
|
+
this.selectedDate = date
|
|
631
576
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
577
|
+
if (!this.selectedTime) {
|
|
578
|
+
const currentValue = this.hasHiddenInputTarget ? this.hiddenInputTarget.value : ""
|
|
579
|
+
const timePart = this.extractTimePart(currentValue)
|
|
580
|
+
if (timePart) {
|
|
581
|
+
this.selectedTime = this.selectedTimeFromTimePart(timePart)
|
|
582
|
+
} else {
|
|
583
|
+
this.selectedTime = this.defaultSelectedTime()
|
|
584
|
+
}
|
|
585
|
+
this.updateTimeSelection()
|
|
586
|
+
}
|
|
587
|
+
}
|
|
636
588
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
589
|
+
handleRangeDateSelection(date, formatted) {
|
|
590
|
+
if (!this.tempRangeStart) {
|
|
591
|
+
this.startRangeSelection(date, formatted)
|
|
592
|
+
} else if (!this.tempRangeEnd) {
|
|
593
|
+
if (date < this.tempRangeStart) {
|
|
594
|
+
this.restartRangeSelection(date, formatted)
|
|
595
|
+
} else {
|
|
596
|
+
this.completeRangeSelection(date, formatted)
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
this.restartRangeSelection(date, formatted)
|
|
600
|
+
}
|
|
643
601
|
|
|
644
|
-
|
|
602
|
+
this.updateClearVisibility()
|
|
603
|
+
}
|
|
645
604
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
})
|
|
650
|
-
} else {
|
|
651
|
-
// Complete the range
|
|
652
|
-
this.tempRangeEnd = clickedDate
|
|
605
|
+
startRangeSelection(date, formatted) {
|
|
606
|
+
this.tempRangeStart = date
|
|
607
|
+
this.tempRangeEnd = null
|
|
653
608
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
609
|
+
if (this.hasStartInputTarget) {
|
|
610
|
+
this.startInputTarget.value = formatted
|
|
611
|
+
}
|
|
657
612
|
|
|
658
|
-
|
|
613
|
+
if (this.hasEndInputTarget) {
|
|
614
|
+
this.endInputTarget.value = ""
|
|
615
|
+
}
|
|
659
616
|
|
|
660
|
-
|
|
661
|
-
startValue: this.hasStartInputTarget ? this.startInputTarget.value : "",
|
|
662
|
-
endValue: formatted
|
|
663
|
-
})
|
|
617
|
+
this.updateRangeVisuals()
|
|
664
618
|
|
|
665
|
-
|
|
619
|
+
this.dispatchChangeEvent({
|
|
620
|
+
startValue: formatted,
|
|
621
|
+
endValue: ""
|
|
622
|
+
})
|
|
623
|
+
}
|
|
666
624
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
} else {
|
|
671
|
-
// Already have a complete range, restart with new selection
|
|
672
|
-
this.tempRangeStart = date
|
|
673
|
-
this.tempRangeEnd = null
|
|
625
|
+
restartRangeSelection(date, formatted) {
|
|
626
|
+
this.startRangeSelection(date, formatted)
|
|
627
|
+
}
|
|
674
628
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
if (this.hasEndInputTarget) {
|
|
679
|
-
this.endInputTarget.value = ""
|
|
680
|
-
}
|
|
629
|
+
completeRangeSelection(date, formatted) {
|
|
630
|
+
this.tempRangeEnd = date
|
|
681
631
|
|
|
682
|
-
|
|
632
|
+
if (this.hasEndInputTarget) {
|
|
633
|
+
this.endInputTarget.value = formatted
|
|
634
|
+
}
|
|
683
635
|
|
|
684
|
-
|
|
685
|
-
startValue: formatted,
|
|
686
|
-
endValue: ""
|
|
687
|
-
})
|
|
688
|
-
}
|
|
636
|
+
this.updateRangeVisuals()
|
|
689
637
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
638
|
+
this.dispatchChangeEvent({
|
|
639
|
+
startValue: this.hasStartInputTarget ? this.startInputTarget.value : "",
|
|
640
|
+
endValue: formatted
|
|
641
|
+
})
|
|
693
642
|
|
|
694
|
-
// Code for non-range pickers
|
|
695
|
-
// Use setValue to update both hidden and display inputs
|
|
696
|
-
this.setValue(formatted)
|
|
697
|
-
this.syncCalendarWithInput()
|
|
698
643
|
this.updateClearVisibility()
|
|
699
644
|
this.close()
|
|
700
645
|
}
|
|
@@ -738,33 +683,98 @@ export default class extends RegistryController {
|
|
|
738
683
|
|
|
739
684
|
switch (picker) {
|
|
740
685
|
case "year":
|
|
741
|
-
return
|
|
686
|
+
return this.formatYearValue(date)
|
|
742
687
|
|
|
743
688
|
case "month":
|
|
744
|
-
return
|
|
689
|
+
return this.formatMonthValue(date)
|
|
745
690
|
|
|
746
|
-
case "week":
|
|
747
|
-
|
|
748
|
-
const weekYear = DateHelper.getISOWeekYear(date)
|
|
749
|
-
return `${weekYear}-W${String(week).padStart(2, "0")}`
|
|
750
|
-
}
|
|
691
|
+
case "week":
|
|
692
|
+
return this.formatWeekValue(date)
|
|
751
693
|
|
|
752
|
-
case "quarter":
|
|
753
|
-
|
|
754
|
-
return `Q${quarter} ${date.getFullYear()}`
|
|
755
|
-
}
|
|
694
|
+
case "quarter":
|
|
695
|
+
return this.formatQuarterValue(date)
|
|
756
696
|
|
|
757
|
-
default:
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
697
|
+
default:
|
|
698
|
+
return this.formatDateValue(date, time)
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
formatYearValue(date) {
|
|
703
|
+
return String(date.getFullYear())
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
formatMonthValue(date) {
|
|
707
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
formatWeekValue(date) {
|
|
711
|
+
const week = DateHelper.getISOWeek(date)
|
|
712
|
+
const weekYear = DateHelper.getISOWeekYear(date)
|
|
713
|
+
return `${weekYear}-W${String(week).padStart(2, "0")}`
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
formatQuarterValue(date) {
|
|
717
|
+
const quarter = Math.floor(date.getMonth() / 3) + 1
|
|
718
|
+
return `Q${quarter} ${date.getFullYear()}`
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
formatDateValue(date, time = null) {
|
|
722
|
+
const dateStr = DateHelper.formatDataDate(date)
|
|
723
|
+
if (time && this.showTimeValue) {
|
|
724
|
+
return `${dateStr} ${this.formatTimeValue(time)}`
|
|
725
|
+
}
|
|
726
|
+
return dateStr
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
formatTimeValue(time) {
|
|
730
|
+
return this.showSecondsValue
|
|
731
|
+
? `${time.hour}:${time.minute}:${time.second}`
|
|
732
|
+
: `${time.hour}:${time.minute}`
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
extractDatePart(value) {
|
|
736
|
+
const datePart = value.match(/^(\d{4}-\d{2}-\d{2})/)
|
|
737
|
+
return datePart?.[1] || null
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
extractTimePart(value) {
|
|
741
|
+
return value.match(/(\d{2}):(\d{2})(?::(\d{2}))?$/)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
selectedTimeFromTimePart(timePart, convertToPeriod = false) {
|
|
745
|
+
let hour = parseInt(timePart[1], 10)
|
|
746
|
+
let period = undefined
|
|
747
|
+
|
|
748
|
+
// Convert 24h to 12h if needed
|
|
749
|
+
if (convertToPeriod) {
|
|
750
|
+
if (hour === 0) {
|
|
751
|
+
hour = 12
|
|
752
|
+
period = "AM"
|
|
753
|
+
} else if (hour < 12) {
|
|
754
|
+
period = "AM"
|
|
755
|
+
} else if (hour === 12) {
|
|
756
|
+
period = "PM"
|
|
757
|
+
} else {
|
|
758
|
+
hour -= 12
|
|
759
|
+
period = "PM"
|
|
766
760
|
}
|
|
767
761
|
}
|
|
762
|
+
|
|
763
|
+
const selectedTime = {
|
|
764
|
+
hour: String(hour).padStart(2, "0"),
|
|
765
|
+
minute: timePart[2],
|
|
766
|
+
second: timePart[3] || "00"
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (period) {
|
|
770
|
+
selectedTime.period = period
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return selectedTime
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
defaultSelectedTime() {
|
|
777
|
+
return { hour: "00", minute: "00", second: "00" }
|
|
768
778
|
}
|
|
769
779
|
|
|
770
780
|
handleClickOutside(event) {
|
|
@@ -838,6 +848,7 @@ export default class extends RegistryController {
|
|
|
838
848
|
|
|
839
849
|
setNow() {
|
|
840
850
|
const now = new Date()
|
|
851
|
+
if (this.isDateDisabled(now)) return
|
|
841
852
|
|
|
842
853
|
// Set both selected date and time
|
|
843
854
|
this.selectedDate = now
|
|
@@ -869,6 +880,7 @@ export default class extends RegistryController {
|
|
|
869
880
|
if (!selectedDateStr) return
|
|
870
881
|
date = DateHelper.parseDataDate(selectedDateStr).toDate()
|
|
871
882
|
}
|
|
883
|
+
if (this.isDateDisabled(date)) return
|
|
872
884
|
|
|
873
885
|
// Combine date and time
|
|
874
886
|
const time = this.selectedTime || { hour: "00", minute: "00", second: "00" }
|