@@ -89,11 +89,13 @@ Meaning an array with two dates is used, the first date is the range start and t
8989 <div>
9090 <fieldset class="type-select">
9191 <legend>Picker mode</legend>
92- <NcCheckboxRadioSwitch v-model="type" type="radio" value="range">Date</NcCheckboxRadioSwitch>
93- <NcCheckboxRadioSwitch v-model="type" type="radio" value="range-datetime">Date and time</NcCheckboxRadioSwitch>
92+ <NcCheckboxRadioSwitch v-model="type" type="radio" value="date-range">Date</NcCheckboxRadioSwitch>
93+ <NcCheckboxRadioSwitch v-model="type" type="radio" value="time-range">Time</NcCheckboxRadioSwitch>
94+ <NcCheckboxRadioSwitch v-model="type" type="radio" value="datetime-range">Date and time</NcCheckboxRadioSwitch>
9495 </fieldset>
9596
9697 <NcDateTimePicker
98+ :key="type"
9799 v-model="time"
98100 :type />
99101 <div>
@@ -106,17 +108,20 @@ Meaning an array with two dates is used, the first date is the range start and t
106108export default {
107109 data() {
108110 return {
109- time: [new Date(2025, 3, 18), new Date(2025, 3, 21)],
110- type: 'range',
111+ time: [new Date(2025, 3, 18, 12, 30 ), new Date(2025, 3, 21, 13, 30 )],
112+ type: 'date- range',
111113 }
112114 },
113115 methods: {
114116 formatDate(date) {
115- const text = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
116- if (this.type === 'range') {
117- return text
117+ const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
118+ const timeString = `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
119+ if (this.type === 'date-range') {
120+ return dateString
121+ } else if (this.type === 'time-range') {
122+ return timeString
118123 }
119- return `${text } ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0') }`
124+ return `${dateString } ${timeString }`
120125 },
121126 },
122127}
@@ -161,6 +166,13 @@ export default {
161166</docs >
162167
163168<script setup lang="ts">
169+ import type {
170+ // The emitted object for time picker
171+ TimeObj as LibraryTimeObject ,
172+ // The accepted model value
173+ ModelValue as LibraryModelValue ,
174+ } from ' @vuepic/vue-datepicker'
175+
164176import {
165177 mdiCalendarBlank ,
166178 mdiChevronDown ,
@@ -249,7 +261,7 @@ const props = withDefaults(defineProps<{
249261 * while 'range-datetime' will allow picking a date range with times.
250262 * @default ' date'
251263 */
252- type? : ' date' | ' datetime' | ' time' | ' week' | ' month' | ' year' | ' range' | ' range- datetime'
264+ type? : ' date' | ' datetime' | ' time' | ' week' | ' month' | ' year' | ' date- range' | ' time-range ' | ' datetime-range '
253265
254266 appendToBody? : boolean
255267
@@ -281,8 +293,9 @@ const emit = defineEmits<{
281293 /**
282294 * If range picker is enabled then an array containing start and end date are emitted.
283295 * Otherwise the selected date is emitted.
296+ * `null` is emitted if `clearable` is set to `true` and the value was cleared.
284297 */
285- ' update:modelValue' : [Date | [Date , Date ]]
298+ ' update:modelValue' : [Date | [Date , Date ] | null ]
286299 ' update:timezoneId' : [string ]
287300}>()
288301
@@ -296,20 +309,32 @@ const value = computed(() => {
296309 const end = new Date (date )
297310 end .setUTCDate (date .getUTCDate () + 6 )
298311 return [date , end ]
299- } else if (props .type .startsWith (' range' )) {
312+ } else if (props .type === ' year' ) {
313+ const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
314+ return date .getUTCFullYear ()
315+ } else if (props .type === ' month' ) {
316+ const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
317+ return { year: date .getUTCFullYear (), month: date .getUTCMonth () }
318+ } else if (props .type === ' time' || props .type === ' time-range' ) {
319+ const time = [props .modelValue ?? (props .type === ' time-range' ? [new Date (), new Date ()] : new Date ())].flat ()
320+ // default time range is 1 hour
321+ if (props .modelValue === undefined && props .type === ' time-range' ) {
322+ time [1 ].setHours (time [1 ].getHours () + 1 )
323+ }
324+ const timeValue = time .map ((date ) => ({
325+ hours: date .getHours (),
326+ minutes: date .getMinutes (),
327+ seconds: date .getSeconds (),
328+ } as LibraryTimeObject ))
329+ return props .type === ' time' ? timeValue [0 ] : timeValue
330+ } else if (props .type .endsWith (' -range' )) {
300331 if (props .modelValue === undefined ) {
301332 const start = new Date ()
302333 const end = new Date (start )
303334 end .setUTCDate (start .getUTCDate () + 7 )
304335 return [start , end ]
305336 }
306337 return props .modelValue
307- } else if (props .type === ' year' ) {
308- const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
309- return date .getUTCFullYear ()
310- } else if (props .type === ' month' ) {
311- const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
312- return { year: date .getUTCFullYear (), month: date .getUTCMonth () }
313338 }
314339
315340 // no special handling for other types needed
@@ -350,10 +375,12 @@ const realFormat = computed(() => {
350375 }
351376
352377 let formatter: Intl .DateTimeFormat | undefined
353- if (props .type === ' datetime' || props .type === ' range-datetime' ) {
354- formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { dateStyle: ' medium' , timeStyle: ' short' })
355- } else if (props .type === ' date' || props .type === ' range' ) {
378+ if (props .type === ' date' || props .type === ' date-range' ) {
356379 formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { dateStyle: ' medium' })
380+ } else if (props .type === ' time' || props .type === ' time-range' ) {
381+ formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { timeStyle: ' short' })
382+ } else if (props .type === ' datetime' || props .type === ' datetime-range' ) {
383+ formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { dateStyle: ' medium' , timeStyle: ' short' })
357384 } else if (props .type === ' month' ) {
358385 formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { year: ' numeric' , month: ' 2-digit' })
359386 } else if (props .type === ' year' ) {
@@ -371,12 +398,12 @@ const realFormat = computed(() => {
371398})
372399
373400const pickerType = computed (() => ({
374- timePicker: props .type === ' time' ,
401+ timePicker: props .type === ' time' || props . type === ' time-range ' ,
375402 yearPicker: props .type === ' year' ,
376403 monthPicker: props .type === ' month' ,
377404 weekPicker: props .type === ' week' ,
378- range: props .type .startsWith ( ' range' ),
379- enableTimePicker: ! (props .type === ' date' || props .type === ' range' ),
405+ range: props .type .endsWith ( ' - range' ),
406+ enableTimePicker: ! (props .type === ' date' || props .type === ' date- range' ),
380407 flow: props .type === ' datetime'
381408 ? [' calendar' , ' time' ] as [' calendar' , ' time' ]
382409 : undefined ,
@@ -386,17 +413,50 @@ const pickerType = computed(() => ({
386413 * Called on model value update of the library.
387414 * @param value The value emitted from the underlying library
388415 */
389- function onUpdateModelValue(value : Date | [Date , Date ] | number | { month: number , year: number }): void {
390- let date = value as Date | [Date , Date ]
391- if (props .type === ' month' ) {
416+ function onUpdateModelValue(value : LibraryModelValue ): void {
417+ if (value === null ) {
418+ return emit (' update:modelValue' , null )
419+ }
420+
421+ if (props .type === ' time' ) {
422+ // time is provided as an object
423+ emit (' update:modelValue' , formatLibraryTime (value as LibraryTimeObject ))
424+ } else if (props .type === ' time-range' ) {
425+ // same as time but as an array with two elements
426+ const start = formatLibraryTime (value [0 ])
427+ const end = formatLibraryTime (value [1 ])
428+ // ensure end is beyond the start
429+ if (end .getTime () < start .getTime ()) {
430+ end .setDate (end .getDate () + 1 )
431+ }
432+ emit (' update:modelValue' , [start , end ])
433+ } else if (props .type === ' month' ) {
434+ // month is emitted as an object with month and year attribute
392435 const data = value as { month: number , year: number }
393- date = new Date (data .year , data .month , 1 )
436+ emit ( ' update:modelValue ' , new Date (data .year , data .month , 1 ) )
394437 } else if (props .type === ' year' ) {
395- date = new Date (value as number , 0 )
438+ // Years are emitted as the numeric year e.g. 2022
439+ emit (' update:modelValue' , new Date (value as number , 0 ))
396440 } else if (props .type === ' week' ) {
397- date = value [0 ]
441+ // weeks are emitted as [Date, Date]
442+ emit (' update:modelValue' , value [0 ])
443+ } else {
444+ // otherwise it already emits the correct format
445+ emit (' update:modelValue' , value as Date | [Date , Date ])
398446 }
399- emit (' update:modelValue' , date )
447+ }
448+
449+ /**
450+ * Format a vuepick time object to native JS Date object.
451+ *
452+ * @param time - The library time value object
453+ */
454+ function formatLibraryTime(time : LibraryTimeObject ): Date {
455+ const date = new Date ()
456+ date .setHours (time .hours )
457+ date .setMinutes (time .minutes )
458+ date .setSeconds (time .seconds )
459+ return date
400460}
401461
402462// Localization
0 commit comments