vueless 0.0.482 → 0.0.484

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.
@@ -1,146 +1,10 @@
1
- <template>
2
- <div ref="wrapperRef" tabindex="1" v-bind="wrapperAttrs" @keydown="onKeydown">
3
- <div v-bind="navigationAttrs">
4
- <UButton
5
- square
6
- no-ring
7
- size="sm"
8
- color="grayscale"
9
- variant="thirdary"
10
- :left-icon="config.defaults.prevIcon"
11
- v-bind="nextPrevButtonAttrs"
12
- @mousedown.prevent.capture
13
- @click="onClickPrevButton"
14
- />
15
-
16
- <UButton
17
- no-ring
18
- size="sm"
19
- color="grayscale"
20
- variant="thirdary"
21
- v-bind="viewSwitchButtonAttrs"
22
- @mousedown.prevent.capture
23
- @click="onClickViewSwitch"
24
- >
25
- <template v-if="isCurrentView.day">
26
- {{ viewSwitchLabel.month }} {{ viewSwitchLabel.year }}
27
- </template>
28
- <template v-if="isCurrentView.month">{{ viewSwitchLabel.year }}</template>
29
- <template v-if="isCurrentView.year">{{ viewSwitchLabel.yearsRange }}</template>
30
- </UButton>
31
-
32
- <UButton
33
- square
34
- no-ring
35
- size="sm"
36
- color="grayscale"
37
- variant="thirdary"
38
- :left-icon="config.defaults.nextIcon"
39
- v-bind="nextPrevButtonAttrs"
40
- @mousedown.prevent.capture
41
- @click="onClickNextButton"
42
- />
43
- </div>
44
-
45
- <DayView
46
- v-if="isCurrentView.day"
47
- :selected-date="selectedDate"
48
- :selected-date-to="selectedDateTo"
49
- :range="range"
50
- :active-month="activeMonth"
51
- :active-date="activeDate"
52
- :min-date="minDate"
53
- :max-date="maxDate"
54
- :date-format="actualDateFormat"
55
- :locale="locale"
56
- :config="config"
57
- @input="onInputDate"
58
- />
59
-
60
- <MonthView
61
- v-if="isCurrentView.month"
62
- :selected-date="selectedDate"
63
- :selected-date-to="selectedDateTo"
64
- :range="range"
65
- :active-month="activeMonth"
66
- :active-date="activeDate"
67
- :min-date="minDate"
68
- :max-date="maxDate"
69
- :date-format="actualDateFormat"
70
- :locale="locale"
71
- :config="config"
72
- @input="onInput"
73
- />
74
-
75
- <YearView
76
- v-if="isCurrentView.year"
77
- :selected-date="selectedDate"
78
- :selected-date-to="selectedDateTo"
79
- :range="range"
80
- :active-month="activeMonth"
81
- :active-date="activeDate"
82
- :min-date="minDate"
83
- :max-date="maxDate"
84
- :date-format="actualDateFormat"
85
- :locale="locale"
86
- :config="config"
87
- @input="onInput"
88
- />
89
-
90
- <div v-if="isTimepickerEnabled" v-bind="timepickerAttrs">
91
- <span v-bind="timepickerLabelAttrs" v-text="locale.timeLabel" />
92
-
93
- <div v-bind="timepickerInputWrapperAttrs">
94
- <input
95
- ref="hoursRef"
96
- placeholder="00"
97
- type="text"
98
- v-bind="timepickerInputHoursAttrs"
99
- @input.prevent="onTimeInput($event, INPUT_TYPE.hours, MAX_HOURS, MIN_HOURS)"
100
- @keydown="onTimeKeydown"
101
- />
102
- &#8282;
103
- <input
104
- ref="minutesRef"
105
- placeholder="00"
106
- type="text"
107
- v-bind="timepickerInputMinutesAttrs"
108
- @input.prevent="onTimeInput($event, INPUT_TYPE.minutes, MAX_MINUTES, MIN_MINUTES)"
109
- @keydown="onTimeKeydown"
110
- />
111
- &#8282;
112
- <input
113
- ref="secondsRef"
114
- placeholder="00"
115
- type="text"
116
- v-bind="timepickerInputSecondsAttrs"
117
- @input.prevent="onTimeInput($event, INPUT_TYPE.seconds, MAX_SECONDS, MIN_SECONDS)"
118
- @keydown="onTimeKeydown"
119
- />
120
- </div>
121
-
122
- <UButton
123
- variant="thirdary"
124
- size="sm"
125
- square
126
- filled
127
- no-ring
128
- color="grayscale"
129
- v-bind="timepickerSubmitButtonAttrs"
130
- @click="onClickSubmit"
131
- >
132
- {{ locale.okLabel }}
133
- </UButton>
134
- </div>
135
- </div>
136
- </template>
137
-
138
- <script setup>
139
- import { computed, ref, watch } from "vue";
1
+ <script setup lang="ts">
2
+ import { computed, ref, watch, useTemplateRef } from "vue";
140
3
  import { merge } from "lodash-es";
141
4
 
142
5
  import UButton from "../ui.button/UButton.vue";
143
6
  import { getDefault } from "../utils/ui.ts";
7
+ import { isRangeDate } from "./types.ts";
144
8
 
145
9
  import {
146
10
  parseDate,
@@ -148,134 +12,54 @@ import {
148
12
  getYearsRange,
149
13
  dateIsOutOfRange,
150
14
  isNumeric,
151
- } from "./utilCalendar.js";
15
+ } from "./utilCalendar.ts";
152
16
 
153
- import { getDateWithoutTime, addMonths, addDays, addYears, getSortedLocale } from "./utilDate.js";
17
+ import { getDateWithoutTime, addMonths, addDays, addYears, getSortedLocale } from "./utilDate.ts";
154
18
 
155
- import useAttrs from "./useAttrs.js";
19
+ import useAttrs from "./useAttrs.ts";
156
20
  import { useLocale } from "../composables/useLocale.ts";
157
21
 
158
22
  import {
159
23
  UCalendar,
160
- VIEW,
161
- KEY_CODE,
24
+ ARROW_KEYS,
162
25
  YEARS_PER_VIEW,
163
26
  MAX_HOURS,
164
27
  MIN_HOURS,
165
28
  MAX_MINUTES,
166
29
  MIN_MINUTES,
167
30
  SEPARATOR,
168
- INPUT_TYPE,
169
- LOCALE_TYPE,
170
31
  MAX_SECONDS,
171
32
  MIN_SECONDS,
172
- } from "./constants.js";
33
+ KeyCode,
34
+ LocaleType,
35
+ View,
36
+ InputType,
37
+ } from "./constants.ts";
38
+
39
+ import defaultConfig from "./config.ts";
173
40
 
174
- import defaultConfig from "./config.js";
41
+ import type { UCalendarProps, DateValue, RangeDate, Locale } from "./types.ts";
42
+ import type { ComputedRef, Ref } from "vue";
175
43
 
176
44
  import DayView from "./UCalendarDayView.vue";
177
45
  import MonthView from "./UCalendarMonthView.vue";
178
46
  import YearView from "./UCalendarYearView.vue";
47
+ import type { DateLocale } from "./utilFormatting.ts";
179
48
 
180
- defineOptions({ inheritAttrs: false });
181
-
182
- const props = defineProps({
183
- /**
184
- * Calendar value (JavaScript Date object or string formatted in given `dateFormat` or object when `range` enabled).
185
- */
186
- modelValue: {
187
- type: [Date, String, Object],
188
- default: null,
189
- },
190
-
191
- /**
192
- * Calendar view variant.
193
- * @values day, month, year
194
- */
195
- view: {
196
- type: String,
197
- default: VIEW.day,
198
- },
199
-
200
- /**
201
- * Enable date range selection.
202
- */
203
- range: {
204
- type: Boolean,
205
- default: getDefault(defaultConfig, UCalendar).range,
206
- },
207
-
208
- /**
209
- * Show timepicker.
210
- */
211
- timepicker: {
212
- type: Boolean,
213
- default: getDefault(defaultConfig, UCalendar).timepicker,
214
- },
215
-
216
- /**
217
- * Date string format.
218
- */
219
- dateFormat: {
220
- type: String,
221
- default: getDefault(defaultConfig, UCalendar).dateFormat,
222
- },
49
+ type DefaultLocale = typeof defaultConfig.i18n;
223
50
 
224
- /**
225
- * Same as date format, but used when timepicker is enabled.
226
- */
227
- dateTimeFormat: {
228
- type: String,
229
- default: getDefault(defaultConfig, UCalendar).dateTimeFormat,
230
- },
231
-
232
- /**
233
- * User-friendly date format (it will be shown in UI).
234
- */
235
- userDateFormat: {
236
- type: String,
237
- default: getDefault(defaultConfig, UCalendar).userDateFormat,
238
- },
239
-
240
- /**
241
- * Same as user format, but used when timepicker is enabled.
242
- */
243
- userDateTimeFormat: {
244
- type: String,
245
- default: getDefault(defaultConfig, UCalendar).userDateTimeFormat,
246
- },
247
-
248
- /**
249
- * Min date (JavaScript Date object or string formatted in given `dateFormat`).
250
- */
251
- minDate: {
252
- type: [Date, String],
253
- default: getDefault(defaultConfig, UCalendar).minDate,
254
- },
255
-
256
- /**
257
- * Max date (JavaScript Date object or string formatted in given `dateFormat`).
258
- */
259
- maxDate: {
260
- type: [Date, String],
261
- default: getDefault(defaultConfig, UCalendar).maxDate,
262
- },
263
-
264
- /**
265
- * Component config object.
266
- */
267
- config: {
268
- type: Object,
269
- default: () => ({}),
270
- },
51
+ defineOptions({ inheritAttrs: false });
271
52
 
272
- /**
273
- * Data-test attribute for automated testing.
274
- */
275
- dataTest: {
276
- type: String,
277
- default: "",
278
- },
53
+ const props = withDefaults(defineProps<UCalendarProps>(), {
54
+ view: View.Day,
55
+ range: getDefault<UCalendarProps>(defaultConfig, UCalendar).range,
56
+ timepicker: getDefault<UCalendarProps>(defaultConfig, UCalendar).timepicker,
57
+ dateFormat: getDefault<UCalendarProps>(defaultConfig, UCalendar).dateFormat,
58
+ dateTimeFormat: getDefault<UCalendarProps>(defaultConfig, UCalendar).dateTimeFormat,
59
+ userDateFormat: getDefault<UCalendarProps>(defaultConfig, UCalendar).userDateFormat,
60
+ userDateTimeFormat: getDefault<UCalendarProps>(defaultConfig, UCalendar).userDateTimeFormat,
61
+ dataTest: "",
62
+ config: () => ({}),
279
63
  });
280
64
 
281
65
  const emit = defineEmits([
@@ -284,29 +68,24 @@ const emit = defineEmits([
284
68
  * @property {object} newDate
285
69
  */
286
70
  "update:modelValue",
287
-
288
71
  /**
289
72
  * Triggers when calendar view changes.
290
73
  * @property {string} view
291
74
  */
292
75
  "update:view",
293
-
294
76
  /**
295
77
  * Triggers when date value changes.
296
78
  * @property {object} value
297
79
  */
298
80
  "input",
299
-
300
81
  /**
301
82
  * Triggers when calendar date is selected by clicking "Enter".
302
83
  */
303
84
  "submit",
304
-
305
85
  /**
306
86
  * Triggers when arrow keys are used to change calendar date.
307
87
  */
308
88
  "keydown",
309
-
310
89
  /**
311
90
  * Triggers when the user changes the date input value.
312
91
  * @property {string} value
@@ -331,13 +110,13 @@ const {
331
110
  timepickerSubmitButtonAttrs,
332
111
  } = useAttrs(props);
333
112
 
334
- const wrapperRef = ref(null);
335
- const hoursRef = ref(null);
336
- const minutesRef = ref(null);
337
- const secondsRef = ref(null);
113
+ const wrapperRef = useTemplateRef<HTMLInputElement>("wrapper");
114
+ const hoursRef = useTemplateRef<HTMLInputElement>("hours-input");
115
+ const minutesRef = useTemplateRef<HTMLInputElement>("minutes-input");
116
+ const secondsRef = useTemplateRef<HTMLInputElement>("seconds-input");
338
117
 
339
- const activeDate = ref(null);
340
- const activeMonth = ref(null);
118
+ const activeDate: Ref<Date | null> = ref(null);
119
+ const activeMonth: Ref<Date | null> = ref(null);
341
120
 
342
121
  const currentView = ref(props.view);
343
122
 
@@ -352,18 +131,21 @@ watch(
352
131
 
353
132
  watch(currentView, () => {
354
133
  if (props.view !== currentView.value) {
355
- emit("update:view", currentView.value);
134
+ emit("update:view", currentView.value as View);
356
135
  }
357
136
  });
358
137
 
359
138
  const isCurrentView = computed(() => ({
360
- day: currentView.value === VIEW.day,
361
- month: currentView.value === VIEW.month,
362
- year: currentView.value === VIEW.year,
139
+ day: currentView.value === View.Day,
140
+ month: currentView.value === View.Month,
141
+ year: currentView.value === View.Year,
363
142
  }));
364
143
 
365
- const i18nGlobal = tm(UCalendar);
366
- const currentLocale = computed(() => merge(defaultConfig.i18n, i18nGlobal, props.config.i18n));
144
+ const i18nGlobal = tm<DefaultLocale>(UCalendar);
145
+
146
+ const currentLocale: ComputedRef<Locale> = computed(() =>
147
+ merge(defaultConfig.i18n, i18nGlobal, props.config?.i18n),
148
+ );
367
149
 
368
150
  const locale = computed(() => {
369
151
  const { months, weekdays } = currentLocale.value;
@@ -372,16 +154,18 @@ const locale = computed(() => {
372
154
  return {
373
155
  ...currentLocale.value,
374
156
  months: {
375
- shorthand: getSortedLocale(months.shorthand, LOCALE_TYPE.month),
376
- longhand: getSortedLocale(months.longhand, LOCALE_TYPE.month),
157
+ shorthand: getSortedLocale(months.shorthand, LocaleType.Month),
158
+ longhand: getSortedLocale(months.longhand, LocaleType.Month),
377
159
  },
378
160
  weekdays: {
379
- shorthand: getSortedLocale(weekdays.shorthand, LOCALE_TYPE.day),
380
- longhand: getSortedLocale(weekdays.longhand, LOCALE_TYPE.day),
161
+ shorthand: getSortedLocale(weekdays.shorthand, LocaleType.Day),
162
+ longhand: getSortedLocale(weekdays.longhand, LocaleType.Day),
381
163
  },
382
- };
164
+ } as DateLocale;
383
165
  });
384
166
 
167
+ const isInputRefs = computed(() => Boolean(hoursRef.value && minutesRef.value && secondsRef.value));
168
+
385
169
  const isTimepickerEnabled = computed(() => {
386
170
  return props.timepicker && !props.range;
387
171
  });
@@ -398,12 +182,12 @@ const userFormatLocale = computed(() => {
398
182
  const { months, weekdays } = currentLocale.value;
399
183
 
400
184
  const monthsLonghand =
401
- Boolean(props.config.i18n?.months?.userFormat) || Boolean(i18nGlobal?.months?.userFormat)
185
+ Boolean(props.config?.i18n?.months?.userFormat) || Boolean(i18nGlobal?.months?.userFormat)
402
186
  ? months.userFormat
403
187
  : months.longhand;
404
188
 
405
189
  const weekdaysLonghand =
406
- Boolean(props.config.i18n?.weekdays?.userFormat) || Boolean(i18nGlobal?.weekdays?.userFormat)
190
+ Boolean(props.config?.i18n?.weekdays?.userFormat) || Boolean(i18nGlobal?.weekdays?.userFormat)
407
191
  ? weekdays.userFormat
408
192
  : weekdays.longhand;
409
193
 
@@ -411,30 +195,21 @@ const userFormatLocale = computed(() => {
411
195
  return {
412
196
  ...currentLocale,
413
197
  months: {
414
- shorthand: getSortedLocale(months.shorthand, LOCALE_TYPE.month),
415
- longhand: getSortedLocale(monthsLonghand, LOCALE_TYPE.month),
198
+ shorthand: getSortedLocale(months.shorthand, LocaleType.Month),
199
+ longhand: getSortedLocale(monthsLonghand, LocaleType.Month),
416
200
  },
417
201
  weekdays: {
418
- shorthand: getSortedLocale(weekdays.shorthand, LOCALE_TYPE.day),
419
- longhand: getSortedLocale(weekdaysLonghand, LOCALE_TYPE.day),
202
+ shorthand: getSortedLocale(weekdays.shorthand, LocaleType.Day),
203
+ longhand: getSortedLocale(weekdaysLonghand, LocaleType.Day),
420
204
  },
421
205
  };
422
206
  });
423
207
 
424
- const isModelRangeType = computed(() => {
425
- return (
426
- props.modelValue !== null &&
427
- !(props.modelValue instanceof Date) &&
428
- typeof props.modelValue === "object"
429
- );
430
- });
431
-
432
208
  const localValue = computed({
433
209
  get: () => {
434
210
  if (props.range) {
435
- const from = isModelRangeType.value ? props.modelValue.from : props.modelValue || null;
436
-
437
- const to = isModelRangeType.value ? props.modelValue.to : null;
211
+ const from = isRangeDate(props.modelValue) ? props.modelValue.from : props.modelValue || null;
212
+ const to = isRangeDate(props.modelValue) ? props.modelValue.to : null;
438
213
 
439
214
  return {
440
215
  from: parseDate(from || null, actualDateFormat.value, locale.value),
@@ -442,27 +217,29 @@ const localValue = computed({
442
217
  };
443
218
  }
444
219
 
445
- return isModelRangeType.value
446
- ? parseDate(props.modelValue.from || null, actualDateFormat.value, locale.value)
447
- : parseDate(props.modelValue || null, actualDateFormat.value, locale.value);
220
+ if (!isRangeDate(props.modelValue)) {
221
+ return parseDate(props.modelValue || null, actualDateFormat.value, locale.value);
222
+ }
223
+
224
+ return parseDate(props.modelValue.from || null, actualDateFormat.value, locale.value);
448
225
  },
449
226
  set(value) {
450
- value = getCurrentValueType(value);
227
+ const newDateValue = getCurrentValueType(value);
451
228
 
452
229
  const parsedDate = parseDate(
453
- props.range ? value.from : value,
230
+ isRangeDate(newDateValue) ? newDateValue.from : newDateValue,
454
231
  actualDateFormat.value,
455
232
  locale.value,
456
233
  );
457
234
  const parsedDateTo =
458
- isModelRangeType.value && props.range
235
+ isRangeDate(value) && props.range
459
236
  ? parseDate(value.to, actualDateFormat.value, locale.value)
460
- : undefined;
237
+ : null;
461
238
 
462
239
  if (parsedDate && isTimepickerEnabled.value) {
463
- parsedDate.setHours(Number(hoursRef.value.value));
464
- parsedDate.setMinutes(Number(minutesRef.value.value));
465
- parsedDate.setSeconds(Number(secondsRef.value.value));
240
+ parsedDate.setHours(Number(hoursRef.value?.value));
241
+ parsedDate.setMinutes(Number(minutesRef.value?.value));
242
+ parsedDate.setSeconds(Number(secondsRef.value?.value));
466
243
  }
467
244
 
468
245
  const isOutOfRange = dateIsOutOfRange(
@@ -485,37 +262,39 @@ const localValue = computed({
485
262
  ? formatDate(parsedDateTo || null, actualDateFormat.value, locale.value)
486
263
  : parsedDateTo;
487
264
 
488
- emit("update:modelValue", props.range ? { from: newDate, to: newDateTo } : newDate);
265
+ const newRangeDate = { from: newDate, to: newDateTo };
489
266
 
490
- if (parsedDate === null && isTimepickerEnabled.value) {
267
+ emit("update:modelValue", props.range ? (newRangeDate as RangeDate) : newDate);
268
+
269
+ if (parsedDate === null && isTimepickerEnabled.value && isInputRefs.value) {
491
270
  const currentDate = new Date();
492
271
 
493
- hoursRef.value.value = String(currentDate.getHours()).padStart(2, "0");
494
- minutesRef.value.value = String(currentDate.getMinutes()).padStart(2, "0");
495
- secondsRef.value.value = String(currentDate.getSeconds()).padStart(2, "0");
272
+ hoursRef.value!.value = String(currentDate.getHours()).padStart(2, "0");
273
+ minutesRef.value!.value = String(currentDate.getMinutes()).padStart(2, "0");
274
+ secondsRef.value!.value = String(currentDate.getSeconds()).padStart(2, "0");
496
275
  }
497
276
  },
498
277
  });
499
278
 
500
279
  const selectedDate = computed(() => {
501
280
  return parseDate(
502
- props.range ? localValue.value.from : localValue.value,
281
+ isRangeDate(localValue.value) ? localValue.value.from : localValue.value,
503
282
  actualDateFormat.value,
504
283
  locale.value,
505
284
  );
506
285
  });
507
286
 
508
287
  const selectedDateTo = computed(() => {
509
- return props.range
288
+ return isRangeDate(localValue.value)
510
289
  ? parseDate(localValue.value.to, actualDateFormat.value, locale.value)
511
- : undefined;
290
+ : null;
512
291
  });
513
292
 
514
293
  const userFormattedDate = computed(() => {
515
294
  const date = formatDate(selectedDate.value, actualUserFormat.value, userFormatLocale.value);
516
295
  const dateTo = props.range
517
296
  ? formatDate(selectedDateTo.value, actualUserFormat.value, userFormatLocale.value)
518
- : undefined;
297
+ : null;
519
298
 
520
299
  return props.range ? `${date} ${SEPARATOR} ${dateTo}` : date;
521
300
  });
@@ -540,7 +319,7 @@ watch(
540
319
  () => props.range,
541
320
  (newValue, oldValue) => {
542
321
  if (newValue !== oldValue) {
543
- localValue.value = getCurrentValueType(localValue.value);
322
+ localValue.value = localValue.value;
544
323
  }
545
324
  },
546
325
  );
@@ -552,10 +331,10 @@ const unwatchInit = watch(
552
331
  () => {
553
332
  if (isInit) unwatchInit();
554
333
 
555
- if (selectedDate.value && isTimepickerEnabled.value) {
556
- hoursRef.value.value = String(selectedDate.value.getHours()).padStart(2, "0");
557
- minutesRef.value.value = String(selectedDate.value.getMinutes()).padStart(2, "0");
558
- secondsRef.value.value = String(selectedDate.value.getSeconds()).padStart(2, "0");
334
+ if (selectedDate.value && isTimepickerEnabled.value && isInputRefs.value) {
335
+ hoursRef.value!.value = String(selectedDate.value.getHours()).padStart(2, "0");
336
+ minutesRef.value!.value = String(selectedDate.value.getMinutes()).padStart(2, "0");
337
+ secondsRef.value!.value = String(selectedDate.value.getSeconds()).padStart(2, "0");
559
338
 
560
339
  emit("userDateChange", userFormattedDate.value);
561
340
 
@@ -565,23 +344,23 @@ const unwatchInit = watch(
565
344
  { deep: true },
566
345
  );
567
346
 
568
- function getCurrentValueType(value) {
347
+ function getCurrentValueType(value: DateValue): DateValue {
569
348
  if (props.range && value === null) {
570
- value = { from: null, to: null };
349
+ return { from: null, to: null };
571
350
  }
572
351
 
573
- if (isModelRangeType.value && !props.range) {
574
- value = value.from || value;
352
+ if (isRangeDate(value) && !props.range) {
353
+ return value.from || value;
575
354
  }
576
355
 
577
356
  if (typeof value !== "object" && value !== null && props.range) {
578
- value = { from: value, to: null };
357
+ return { from: value, to: null };
579
358
  }
580
359
 
581
360
  return value;
582
361
  }
583
362
 
584
- function onInputDate(newDate) {
363
+ function onInputDate(newDate: Date | null) {
585
364
  if (newDate === null) {
586
365
  localValue.value = newDate;
587
366
 
@@ -595,7 +374,7 @@ function onInputDate(newDate) {
595
374
 
596
375
  const date = new Date(newDate.valueOf());
597
376
 
598
- if (props.range) {
377
+ if (props.range && isRangeDate(localValue.value)) {
599
378
  const isFullReset =
600
379
  localValue.value.to || !localValue.value.from || date <= localValue.value.from;
601
380
 
@@ -617,17 +396,17 @@ function onInputDate(newDate) {
617
396
  activeMonth.value = null;
618
397
  }
619
398
 
620
- wrapperRef.value.focus();
399
+ wrapperRef.value?.focus();
621
400
  }
622
401
 
623
- function onKeydown(event) {
402
+ function onKeydown(event: KeyboardEvent) {
624
403
  if (props.range) {
625
404
  emit("keydown", event);
626
405
 
627
406
  return;
628
407
  }
629
408
 
630
- if ([KEY_CODE.left, KEY_CODE.up, KEY_CODE.right, KEY_CODE.down].includes(event.keyCode)) {
409
+ if (ARROW_KEYS.includes(event.code)) {
631
410
  arrowKeyHandler(event);
632
411
 
633
412
  minutesRef.value?.blur();
@@ -635,8 +414,8 @@ function onKeydown(event) {
635
414
  secondsRef.value?.blur();
636
415
  }
637
416
 
638
- if (event.keyCode === KEY_CODE.enter) {
639
- enterKeyHandler(event);
417
+ if (event.code === KeyCode.Enter) {
418
+ enterKeyHandler();
640
419
  }
641
420
 
642
421
  const isActiveTimeInput =
@@ -649,59 +428,61 @@ function onKeydown(event) {
649
428
  emit("keydown", event);
650
429
  }
651
430
 
652
- function onInput(date) {
431
+ function onInput(date: Date | null): void {
653
432
  activeDate.value = null;
654
433
  activeMonth.value = date;
655
434
 
656
- if (isCurrentView.value.month) currentView.value = VIEW.day;
657
- if (isCurrentView.value.year) currentView.value = VIEW.month;
435
+ if (isCurrentView.value.month) currentView.value = View.Day;
436
+ if (isCurrentView.value.year) currentView.value = View.Month;
658
437
  }
659
438
 
660
- function arrowKeyHandler(event) {
439
+ function arrowKeyHandler(event: KeyboardEvent) {
661
440
  const currentActiveDate =
662
441
  activeDate.value || activeMonth.value || selectedDate.value || getDateWithoutTime();
663
442
 
664
443
  let newActiveDate;
665
444
 
666
- if (currentView.value === VIEW.day) {
667
- if (event.keyCode === KEY_CODE.down) {
445
+ if (currentView.value === View.Day) {
446
+ if (event.code === KeyCode.ArrowDown) {
668
447
  newActiveDate = addDays(currentActiveDate, 7);
669
- } else if (event.keyCode === KEY_CODE.left) {
448
+ } else if (event.code === KeyCode.ArrowLeft) {
670
449
  newActiveDate = addDays(currentActiveDate, -1);
671
- } else if (event.keyCode === KEY_CODE.up) {
450
+ } else if (event.code === KeyCode.ArrowUp) {
672
451
  newActiveDate = addDays(currentActiveDate, -7);
673
- } else if (event.keyCode === KEY_CODE.right) {
452
+ } else if (event.code === KeyCode.ArrowRight) {
674
453
  newActiveDate = addDays(currentActiveDate, 1);
675
454
  }
676
- } else if (currentView.value === VIEW.month) {
677
- if (event.keyCode === KEY_CODE.down) {
455
+ } else if (currentView.value === View.Month) {
456
+ if (event.code === KeyCode.ArrowDown) {
678
457
  newActiveDate = addMonths(currentActiveDate, 4);
679
- } else if (event.keyCode === KEY_CODE.left) {
458
+ } else if (event.code === KeyCode.ArrowLeft) {
680
459
  newActiveDate = addMonths(currentActiveDate, -1);
681
- } else if (event.keyCode === KEY_CODE.up) {
460
+ } else if (event.code === KeyCode.ArrowUp) {
682
461
  newActiveDate = addMonths(currentActiveDate, -4);
683
- } else if (event.keyCode === KEY_CODE.right) {
462
+ } else if (event.code === KeyCode.ArrowRight) {
684
463
  newActiveDate = addMonths(currentActiveDate, 1);
685
464
  }
686
- } else if (currentView.value === VIEW.year) {
687
- if (event.keyCode === KEY_CODE.down) {
465
+ } else if (currentView.value === View.Year) {
466
+ if (event.code === KeyCode.ArrowDown) {
688
467
  newActiveDate = addYears(currentActiveDate, 4);
689
- } else if (event.keyCode === KEY_CODE.left) {
468
+ } else if (event.code === KeyCode.ArrowLeft) {
690
469
  newActiveDate = addYears(currentActiveDate, -1);
691
- } else if (event.keyCode === KEY_CODE.up) {
470
+ } else if (event.code === KeyCode.ArrowUp) {
692
471
  newActiveDate = addYears(currentActiveDate, -4);
693
- } else if (event.keyCode === KEY_CODE.right) {
472
+ } else if (event.code === KeyCode.ArrowRight) {
694
473
  newActiveDate = addYears(currentActiveDate, 1);
695
474
  }
696
475
  }
697
476
 
698
- const isOutOfRange = dateIsOutOfRange(
699
- newActiveDate,
700
- props.minDate,
701
- props.maxDate,
702
- locale.value,
703
- actualDateFormat.value,
704
- );
477
+ const isOutOfRange =
478
+ newActiveDate &&
479
+ dateIsOutOfRange(
480
+ newActiveDate,
481
+ props.minDate,
482
+ props.maxDate,
483
+ locale.value,
484
+ actualDateFormat.value,
485
+ );
705
486
 
706
487
  if (newActiveDate && !isOutOfRange) {
707
488
  activeDate.value = newActiveDate;
@@ -709,14 +490,14 @@ function arrowKeyHandler(event) {
709
490
  }
710
491
  }
711
492
 
712
- function addActiveMonth(amount) {
493
+ function addActiveMonth(amount: number) {
713
494
  const currentActiveMonth = activeMonth.value || selectedDate.value || getDateWithoutTime();
714
495
  const newActiveMonth = addMonths(currentActiveMonth, amount);
715
496
 
716
497
  activeMonth.value = newActiveMonth;
717
498
  }
718
499
 
719
- function addActiveYear(amount) {
500
+ function addActiveYear(amount: number) {
720
501
  const currentActiveMonth = activeMonth.value || selectedDate.value || getDateWithoutTime();
721
502
  const newActiveMonth = addYears(currentActiveMonth, amount);
722
503
 
@@ -749,33 +530,33 @@ function enterKeyHandler() {
749
530
  emit("input", localValue.value);
750
531
  }
751
532
 
752
- if (isCurrentView.value.month) currentView.value = VIEW.day;
753
- if (isCurrentView.value.year) currentView.value = VIEW.month;
533
+ if (isCurrentView.value.month) currentView.value = View.Day;
534
+ if (isCurrentView.value.year) currentView.value = View.Month;
754
535
  }
755
536
 
756
537
  function onClickViewSwitch() {
757
- const views = Object.values(VIEW);
538
+ const views: string[] = Object.values(View);
758
539
  const currentViewIndex = views.indexOf(currentView.value);
759
540
  const nextViewIndex = currentViewIndex + 1;
760
541
 
761
542
  activeDate.value = null;
762
543
 
763
- currentView.value = views[nextViewIndex] || views.at(0);
544
+ currentView.value = (views[nextViewIndex] || views.at(0)) as View;
764
545
  }
765
546
 
766
- let lastValidHourValue = "";
767
- let lastValidMinuteValue = "";
768
- let lastValidSecondValue = "";
547
+ let lastValidHourValue: string | number = "";
548
+ let lastValidMinuteValue: string | number = "";
549
+ let lastValidSecondValue: string | number = "";
769
550
 
770
- function onTimeKeydown(event) {
771
- if ([KEY_CODE.left, KEY_CODE.up, KEY_CODE.right, KEY_CODE.down].includes(event.keyCode)) {
772
- wrapperRef.value.focus();
551
+ function onTimeKeydown(event: KeyboardEvent) {
552
+ if (ARROW_KEYS.includes(event.code)) {
553
+ wrapperRef.value?.focus();
773
554
 
774
555
  return;
775
556
  }
776
557
 
777
- if (event.keyCode === KEY_CODE.enter) {
778
- enterKeyHandler(event);
558
+ if (event.code === KeyCode.Enter) {
559
+ enterKeyHandler();
779
560
 
780
561
  emit("submit");
781
562
  }
@@ -785,26 +566,44 @@ function onClickSubmit() {
785
566
  emit("submit");
786
567
  }
787
568
 
788
- function onTimeInput(event, type, maxValue, minValue) {
789
- const input = event.target;
569
+ function onTimeInput(event: InputEvent, type: InputType) {
570
+ const input = event.target as HTMLInputElement;
790
571
  const value = input.value;
791
572
  const numericValue = Number(value);
792
573
 
793
- const isHours = type === INPUT_TYPE.hours;
794
- const isMinutes = type === INPUT_TYPE.minutes;
795
- const isSeconds = type === INPUT_TYPE.seconds;
574
+ const isHours = type === InputType.Hours;
575
+ const isMinutes = type === InputType.Minutes;
576
+ const isSeconds = type === InputType.Seconds;
577
+
578
+ let minValue = 0;
579
+ let maxValue = 0;
796
580
 
797
- if (!isNumeric(event.data) && event.data !== null) {
581
+ if (isHours) {
582
+ minValue = MIN_HOURS;
583
+ maxValue = MAX_HOURS;
584
+ }
585
+
586
+ if (isMinutes) {
587
+ minValue = MIN_MINUTES;
588
+ maxValue = MAX_MINUTES;
589
+ }
590
+
591
+ if (isSeconds) {
592
+ minValue = MIN_SECONDS;
593
+ maxValue = MAX_SECONDS;
594
+ }
595
+
596
+ if (event.data !== null && !isNumeric(event.data)) {
798
597
  if (isHours) {
799
- input.value = lastValidHourValue;
598
+ input.value = String(lastValidHourValue);
800
599
  }
801
600
 
802
601
  if (isMinutes) {
803
- input.value = lastValidMinuteValue;
602
+ input.value = String(lastValidMinuteValue);
804
603
  }
805
604
 
806
605
  if (isSeconds) {
807
- input.value = lastValidSecondValue;
606
+ input.value = String(lastValidSecondValue);
808
607
  }
809
608
 
810
609
  return;
@@ -831,7 +630,11 @@ function onTimeInput(event, type, maxValue, minValue) {
831
630
  if (selectedDate.value) {
832
631
  const date = new Date(selectedDate.value.valueOf());
833
632
 
834
- date.setHours(lastValidHourValue, lastValidMinuteValue, lastValidSecondValue);
633
+ date.setHours(
634
+ Number(lastValidHourValue),
635
+ Number(lastValidMinuteValue),
636
+ Number(lastValidSecondValue),
637
+ );
835
638
  localValue.value = date;
836
639
  }
837
640
 
@@ -846,3 +649,140 @@ defineExpose({
846
649
  wrapperRef,
847
650
  });
848
651
  </script>
652
+
653
+ <template>
654
+ <div ref="wrapper" tabindex="1" v-bind="wrapperAttrs" @keydown="onKeydown">
655
+ <div v-bind="navigationAttrs">
656
+ <UButton
657
+ square
658
+ no-ring
659
+ size="sm"
660
+ color="grayscale"
661
+ variant="thirdary"
662
+ :left-icon="config?.defaults?.prevIcon"
663
+ v-bind="nextPrevButtonAttrs"
664
+ @mousedown.prevent.capture
665
+ @click="onClickPrevButton"
666
+ />
667
+
668
+ <UButton
669
+ no-ring
670
+ size="sm"
671
+ color="grayscale"
672
+ variant="thirdary"
673
+ v-bind="viewSwitchButtonAttrs"
674
+ @mousedown.prevent.capture
675
+ @click="onClickViewSwitch"
676
+ >
677
+ <template v-if="isCurrentView.day">
678
+ {{ viewSwitchLabel.month }} {{ viewSwitchLabel.year }}
679
+ </template>
680
+ <template v-if="isCurrentView.month">{{ viewSwitchLabel.year }}</template>
681
+ <template v-if="isCurrentView.year">{{ viewSwitchLabel.yearsRange }}</template>
682
+ </UButton>
683
+
684
+ <UButton
685
+ square
686
+ no-ring
687
+ size="sm"
688
+ color="grayscale"
689
+ variant="thirdary"
690
+ :left-icon="config?.defaults?.nextIcon"
691
+ v-bind="nextPrevButtonAttrs"
692
+ @mousedown.prevent.capture
693
+ @click="onClickNextButton"
694
+ />
695
+ </div>
696
+
697
+ <DayView
698
+ v-if="isCurrentView.day"
699
+ :selected-date="selectedDate"
700
+ :selected-date-to="selectedDateTo"
701
+ :range="range"
702
+ :active-month="activeMonth"
703
+ :active-date="activeDate"
704
+ :min-date="minDate"
705
+ :max-date="maxDate"
706
+ :date-format="actualDateFormat"
707
+ :locale="locale"
708
+ :config="config"
709
+ @input="onInputDate"
710
+ />
711
+
712
+ <MonthView
713
+ v-if="isCurrentView.month"
714
+ :selected-date="selectedDate"
715
+ :selected-date-to="selectedDateTo"
716
+ :range="range"
717
+ :active-month="activeMonth"
718
+ :active-date="activeDate"
719
+ :min-date="minDate"
720
+ :max-date="maxDate"
721
+ :date-format="actualDateFormat"
722
+ :locale="locale"
723
+ :config="config"
724
+ @input="onInput"
725
+ />
726
+
727
+ <YearView
728
+ v-if="isCurrentView.year"
729
+ :selected-date="selectedDate"
730
+ :selected-date-to="selectedDateTo"
731
+ :range="range"
732
+ :active-month="activeMonth"
733
+ :active-date="activeDate"
734
+ :min-date="minDate"
735
+ :max-date="maxDate"
736
+ :date-format="actualDateFormat"
737
+ :locale="locale"
738
+ :config="config"
739
+ @input="onInput"
740
+ />
741
+
742
+ <div v-if="isTimepickerEnabled" v-bind="timepickerAttrs">
743
+ <span v-bind="timepickerLabelAttrs" v-text="currentLocale.timeLabel" />
744
+
745
+ <div v-bind="timepickerInputWrapperAttrs">
746
+ <input
747
+ ref="hours-input"
748
+ placeholder="00"
749
+ type="text"
750
+ v-bind="timepickerInputHoursAttrs"
751
+ @input.prevent="onTimeInput($event as InputEvent, InputType.Hours)"
752
+ @keydown="onTimeKeydown"
753
+ />
754
+ &#8282;
755
+ <input
756
+ ref="minutes-input"
757
+ placeholder="00"
758
+ type="text"
759
+ v-bind="timepickerInputMinutesAttrs"
760
+ @input.prevent="onTimeInput($event as InputEvent, InputType.Minutes)"
761
+ @keydown="onTimeKeydown"
762
+ />
763
+ &#8282;
764
+ <input
765
+ ref="seconds-input"
766
+ placeholder="00"
767
+ type="text"
768
+ v-bind="timepickerInputSecondsAttrs"
769
+ @input.prevent="onTimeInput($event as InputEvent, InputType.Seconds)"
770
+ @keydown="onTimeKeydown"
771
+ />
772
+ </div>
773
+
774
+ <UButton
775
+ variant="thirdary"
776
+ size="sm"
777
+ square
778
+ filled
779
+ no-ring
780
+ color="grayscale"
781
+ v-bind="timepickerSubmitButtonAttrs"
782
+ @click="onClickSubmit"
783
+ >
784
+ {{ currentLocale.okLabel }}
785
+ </UButton>
786
+ </div>
787
+ </div>
788
+ </template>