@@ -288,15 +288,17 @@ import {
288288 getFirstDay ,
289289} from ' @nextcloud/l10n'
290290import VueDatePicker from ' @vuepic/vue-datepicker'
291+ import { parse } from ' date-fns/parse'
291292import { computed , toRef , useTemplateRef } from ' vue'
292293import NcIconSvgWrapper from ' ../NcIconSvgWrapper/NcIconSvgWrapper.vue'
293294import NcTimezonePicker from ' ../NcTimezonePicker/NcTimezonePicker.vue'
294295import { t } from ' ../../l10n.ts'
295296import NcButton from ' ../NcButton/index.ts'
296- import { getDateFormat , getDateRangeFormat , getDateTimeFormat , getDateTimeRangeFormat , getMonthFormat , getTimeFormat , getTimeRangeFormat , getYearFormat } from ' ./format.ts'
297+ import { getDateFormat , getDateTimeFormat , getMonthFormat , getTimeFormat , getWeekFormat , getYearFormat } from ' ./format.ts'
297298import useDateFnsLocale from ' ./useDateFnsLocale.ts'
298299
299300type LibraryFormatOptions = VueDatePickerProps [' format' ]
301+ type LibraryTextInputOptions = VueDatePickerProps [' textInput' ]
300302
301303/**
302304 * The preselected IANA time zone ID for the time zone picker,
@@ -344,11 +346,13 @@ const props = withDefaults(defineProps<{
344346 confirm? : boolean
345347
346348 /**
347- * Preview format for the picker input field.
349+ * Format for the picker input field.
348350 * Can either be a string of Unicode tokens or a function that takes a Date object
349351 * or for range picker an array of two dates, and returns the formatted date as string.
350352 *
351- * @default Intl.DateTimeFormat is used to format dates and times
353+ * If no format is provided, localized date and time formats are used.
354+ * If a function is provided users cannot use text input.
355+ *
352356 * @see https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
353357 */
354358 format? : string | ((date : Date ) => string ) | ((dates : [Date , Date ]) => string )
@@ -533,47 +537,80 @@ const placeholderFallback = computed(() => {
533537})
534538
535539const dateFnsLocale = useDateFnsLocale (toRef (() => props .locale ))
536- const dateFnsLocaleCode = computed (() => dateFnsLocale .value ? .code || ' default ' )
540+ const dateFnsLocaleCode = computed (() => dateFnsLocale .value .code )
537541
538542/**
539543 * The date (time) formatting to be used by the library.
540- * We use the provided format if possible, otherwise we provide a formatting function
541- * which uses the browsers Intl API to format the date / time in the current users locale .
544+ * We use the provided format if possible, otherwise we provide a localized formatting
545+ * using ` date-fns/locale' which is mostly similiar to Intl.DateTime formating .
542546 */
543547const realFormat = computed <LibraryFormatOptions >(() => {
544548 if (props .format ) {
545549 // we can cast the type here as in this case its either string
546550 // function `(Date) => string` or `([Date, Date]) => string` where we cast to `(Date[]) => string` here.
547551 return props .format as LibraryFormatOptions
548- } else if (props .type === ' week' ) {
549- // cannot format weeks with Intl.
550- return ' RR-II'
551552 }
552553
553- const locale = getCanonicalLocale ()
554554 switch (props .type ) {
555555 case ' date' :
556- return getDateFormat (locale )
557556 case ' date-range' :
558- return getDateRangeFormat ( locale )
557+ return getDateFormat ( dateFnsLocale . value )
559558 case ' time' :
560- return getTimeFormat (locale )
561559 case ' time-range' :
562- return getTimeRangeFormat ( locale )
560+ return getTimeFormat ( )
563561 case ' datetime' :
564- return getDateTimeFormat (locale )
565562 case ' datetime-range' :
566- return getDateTimeRangeFormat ( locale )
563+ return getDateTimeFormat ( dateFnsLocale . value )
567564 case ' month' :
568- return getMonthFormat (locale )
565+ return getMonthFormat (dateFnsLocale . value )
569566 case ' year' :
570- return getYearFormat (locale )
567+ return getYearFormat (dateFnsLocale .value )
568+ case ' week' :
569+ return getWeekFormat ()
571570 }
572571
573572 // fallback to default formatting
574573 return undefined
575574})
576575
576+ const textInput = computed <LibraryTextInputOptions >(() => {
577+ const realFormatVal = realFormat .value
578+ if (typeof realFormatVal === ' function' ) {
579+ // NOTE We could provide a format string through `textInput.format`.
580+ // Then Vuepic uses it when the user focuses the input.
581+ // But this feature has a bug in v11 because it does not respect the provided locale when formatting.
582+ // This then breaks the parsing, because parsing respects the locale.
583+ // See https://github.com/Vuepic/vue-datepicker/blob/v11.0.3/src/VueDatePicker/composables/external-internal-mapper.ts#L299
584+ //
585+ // It might be something worth considering if this is fixed in newer versions of Vuepic.
586+ //
587+ // Possible workarounds that seem not to be worth implementing for now:
588+ // - switch locale to enUS when `format` is a function and `textInput.format` is provided
589+ // - replace input value when user focuses the input by ourselves
590+
591+ // Disable text input if user provided a format function.
592+ // We do not know how to parse such values.
593+ return false
594+ }
595+
596+ if (typeof realFormatVal === ' string' ) {
597+ return {
598+ // This should not be required in Vuepic because `format` is automatically used for parsing.
599+ // But v11 has a bug because the format string ist cut off wrongly.
600+ // See https://github.com/Vuepic/vue-datepicker/blob/v11.0.3/src/VueDatePicker/utils/date-utils.ts#L58
601+ // E.g. It breaks for `pattern = "y-MM-dd HH:mm"` and `value = "2025-01-01 10:10"`.
602+ //
603+ // Might be worth simplifying if this is fixed in newer versions of Vuepic.
604+ // Or parseFn could be extended to be extendended to allow for more lenient or smart parsing.
605+ format : (value : string ) => {
606+ return parse (value , realFormatVal , new Date (), { locale: (dateFnsLocale .value ) })
607+ },
608+ }
609+ }
610+
611+ return true
612+ })
613+
577614const pickerType = computed (() => ({
578615 timePicker: props .type === ' time' || props .type === ' time-range' ,
579616 yearPicker: props .type === ' year' ,
@@ -788,7 +825,6 @@ function sameDay(a: Date, b: Date): boolean {
788825 :placeholder =" placeholder ?? placeholderFallback "
789826 :format =" realFormat "
790827 :formatLocale =" dateFnsLocale "
791- :locale
792828 :minDate =" calcMinMaxTime .minDate "
793829 :maxDate =" calcMinMaxTime .maxDate "
794830 :minTime =" calcMinMaxTime .minTime "
@@ -800,7 +836,7 @@ function sameDay(a: Date, b: Date): boolean {
800836 sixWeeks="fair"
801837 :inline
802838 :teleport =" appendToBody ? (targetElement || undefined ) : false "
803- textInput
839+ : textInput
804840 :weekNumName
805841 :weekNumbers =" showWeekNumber ? { type: ' iso' } : undefined "
806842 :weekStart
0 commit comments