@@ -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 ,
@@ -245,11 +257,14 @@ const props = withDefaults(defineProps<{
245257
246258 /**
247259 * Type of the picker.
248- * The 'range' type will enable a range picker for dates,
249- * while 'range-datetime' will allow picking a date range with times.
260+ * There is some special handling for ranges as those types require a `[Date, Date]` model value.
261+ * - The 'date-range' type will enable a range picker for dates
262+ * - The 'time-range' allows picking a time range.
263+ * - The 'datetime-range' allows picking dates with times assigned.
264+ *
250265 * @default ' date'
251266 */
252- type? : ' date' | ' datetime' | ' time' | ' week' | ' month' | ' year' | ' range' | ' range- datetime'
267+ type? : ' date' | ' datetime' | ' time' | ' week' | ' month' | ' year' | ' date- range' | ' time-range ' | ' datetime-range '
253268
254269 appendToBody? : boolean
255270
@@ -281,8 +296,9 @@ const emit = defineEmits<{
281296 /**
282297 * If range picker is enabled then an array containing start and end date are emitted.
283298 * Otherwise the selected date is emitted.
299+ * `null` is emitted if `clearable` is set to `true` and the value was cleared.
284300 */
285- ' update:modelValue' : [Date | [Date , Date ]]
301+ ' update:modelValue' : [Date | [Date , Date ] | null ]
286302 ' update:timezoneId' : [string ]
287303}>()
288304
@@ -296,20 +312,32 @@ const value = computed(() => {
296312 const end = new Date (date )
297313 end .setUTCDate (date .getUTCDate () + 6 )
298314 return [date , end ]
299- } else if (props .type .startsWith (' range' )) {
315+ } else if (props .type === ' year' ) {
316+ const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
317+ return date .getUTCFullYear ()
318+ } else if (props .type === ' month' ) {
319+ const date = props .modelValue instanceof Date ? props .modelValue : new Date ()
320+ return { year: date .getUTCFullYear (), month: date .getUTCMonth () }
321+ } else if (props .type === ' time' || props .type === ' time-range' ) {
322+ const time = [props .modelValue ?? (props .type === ' time-range' ? [new Date (), new Date ()] : new Date ())].flat ()
323+ // default time range is 1 hour
324+ if (props .modelValue === undefined && props .type === ' time-range' ) {
325+ time [1 ].setHours (time [1 ].getHours () + 1 )
326+ }
327+ const timeValue = time .map ((date ) => ({
328+ hours: date .getHours (),
329+ minutes: date .getMinutes (),
330+ seconds: date .getSeconds (),
331+ } as LibraryTimeObject ))
332+ return props .type === ' time' ? timeValue [0 ] : timeValue
333+ } else if (props .type .endsWith (' -range' )) {
300334 if (props .modelValue === undefined ) {
301335 const start = new Date ()
302336 const end = new Date (start )
303337 end .setUTCDate (start .getUTCDate () + 7 )
304338 return [start , end ]
305339 }
306340 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 () }
313341 }
314342
315343 // no special handling for other types needed
@@ -329,7 +357,7 @@ const placeholderFallback = computed(() => {
329357 return t (' Select month' )
330358 } else if (props .type === ' year' ) {
331359 return t (' Select year' )
332- } else if (props .type .startsWith ( ' range' )) {
360+ } else if (props .type .endsWith ( ' - range' )) {
333361 return t (' Select time range' )
334362 }
335363 // should not be reached
@@ -350,10 +378,12 @@ const realFormat = computed(() => {
350378 }
351379
352380 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' ) {
381+ if (props .type === ' date' || props .type === ' date-range' ) {
356382 formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { dateStyle: ' medium' })
383+ } else if (props .type === ' time' || props .type === ' time-range' ) {
384+ formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { timeStyle: ' short' })
385+ } else if (props .type === ' datetime' || props .type === ' datetime-range' ) {
386+ formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { dateStyle: ' medium' , timeStyle: ' short' })
357387 } else if (props .type === ' month' ) {
358388 formatter = new Intl .DateTimeFormat (getCanonicalLocale (), { year: ' numeric' , month: ' 2-digit' })
359389 } else if (props .type === ' year' ) {
@@ -371,12 +401,12 @@ const realFormat = computed(() => {
371401})
372402
373403const pickerType = computed (() => ({
374- timePicker: props .type === ' time' ,
404+ timePicker: props .type === ' time' || props . type === ' time-range ' ,
375405 yearPicker: props .type === ' year' ,
376406 monthPicker: props .type === ' month' ,
377407 weekPicker: props .type === ' week' ,
378- range: props .type .startsWith ( ' range' ),
379- enableTimePicker: ! (props .type === ' date' || props .type === ' range' ),
408+ range: props .type .endsWith ( ' - range' ),
409+ enableTimePicker: ! (props .type === ' date' || props .type === ' date- range' ),
380410 flow: props .type === ' datetime'
381411 ? [' calendar' , ' time' ] as [' calendar' , ' time' ]
382412 : undefined ,
@@ -386,17 +416,50 @@ const pickerType = computed(() => ({
386416 * Called on model value update of the library.
387417 * @param value The value emitted from the underlying library
388418 */
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' ) {
419+ function onUpdateModelValue(value : LibraryModelValue ): void {
420+ if (value === null ) {
421+ return emit (' update:modelValue' , null )
422+ }
423+
424+ if (props .type === ' time' ) {
425+ // time is provided as an object
426+ emit (' update:modelValue' , formatLibraryTime (value as LibraryTimeObject ))
427+ } else if (props .type === ' time-range' ) {
428+ // same as time but as an array with two elements
429+ const start = formatLibraryTime (value [0 ])
430+ const end = formatLibraryTime (value [1 ])
431+ // ensure end is beyond the start
432+ if (end .getTime () < start .getTime ()) {
433+ end .setDate (end .getDate () + 1 )
434+ }
435+ emit (' update:modelValue' , [start , end ])
436+ } else if (props .type === ' month' ) {
437+ // month is emitted as an object with month and year attribute
392438 const data = value as { month: number , year: number }
393- date = new Date (data .year , data .month , 1 )
439+ emit ( ' update:modelValue ' , new Date (data .year , data .month , 1 ) )
394440 } else if (props .type === ' year' ) {
395- date = new Date (value as number , 0 )
441+ // Years are emitted as the numeric year e.g. 2022
442+ emit (' update:modelValue' , new Date (value as number , 0 ))
396443 } else if (props .type === ' week' ) {
397- date = value [0 ]
444+ // weeks are emitted as [Date, Date]
445+ emit (' update:modelValue' , value [0 ])
446+ } else {
447+ // otherwise it already emits the correct format
448+ emit (' update:modelValue' , value as Date | [Date , Date ])
398449 }
399- emit (' update:modelValue' , date )
450+ }
451+
452+ /**
453+ * Format a vuepick time object to native JS Date object.
454+ *
455+ * @param time - The library time value object
456+ */
457+ function formatLibraryTime(time : LibraryTimeObject ): Date {
458+ const date = new Date ()
459+ date .setHours (time .hours )
460+ date .setMinutes (time .minutes )
461+ date .setSeconds (time .seconds )
462+ return date
400463}
401464
402465// Localization
0 commit comments