diff --git a/packages/docs/components/Datepicker.md b/packages/docs/components/Datepicker.md index 46b004c9f..92be59017 100644 --- a/packages/docs/components/Datepicker.md +++ b/packages/docs/components/Datepicker.md @@ -45,7 +45,7 @@ title: Datepicker | closeOnClick | Close dropdown on click | boolean | - |
From config:
datepicker: {
  closeOnClick: true
}
| | dateCreator | Date creator function, default is `new Date()` | () => Date | - |
From config:
datepicker: {
  dateCreator: () => new Date()
}
| | dateFormatter | Custom function to format a date into a string | (date: Date \| Date[]) => string | - |
From config:
datepicker: {
  dateFormatter: defaultFunction
}
| -| dateParser | Custom function to parse a string into a date | (date: string) => Date | - |
From config:
datepicker: {
  dateParser: defaultFunction
}
| +| dateParser | Custom function to parse a string into a date | (date: string) => Date \| Date[] | - |
From config:
datepicker: {
  dateParser: defaultFunction
}
| | dayNames | Set custom day names, else use names based on locale | string[] | - |
From config:
datepicker: {
  dayNames: undefined
}
| | disabled | Same as native disabled | boolean | - | false | | events | Events to display on picker | DatepickerEvent[] | - | | diff --git a/packages/oruga/src/components/datepicker/Datepicker.vue b/packages/oruga/src/components/datepicker/Datepicker.vue index d03ee830e..73912074a 100644 --- a/packages/oruga/src/components/datepicker/Datepicker.vue +++ b/packages/oruga/src/components/datepicker/Datepicker.vue @@ -116,9 +116,11 @@ const props = defineProps({ }, /** Custom function to parse a string into a date */ dateParser: { - type: Function as PropType<(date: string) => Date>, - default: (date: string, defaultFunction: (date: string) => Date) => - getOption("datepicker.dateParser", defaultFunction)(date), + type: Function as PropType<(date: string) => Date | Date[]>, + default: ( + date: string, + defaultFunction: (date: string) => Date | Date[], + ) => getOption("datepicker.dateParser", defaultFunction)(date), }, /** Date creator function, default is `new Date()` */ dateCreator: { @@ -616,34 +618,30 @@ const isTypeMonth = computed(() => props.type === "month"); /** * When v-model is changed: * 1. Update internal value. - * 2. If it's invalid, validate again. */ watch( () => props.modelValue, (value) => { - // updateInternalState - if (vmodel.value !== value) { - const isArray = Array.isArray(value); - const currentDate = isArray - ? !value.length - ? props.dateCreator() - : value[value.length - 1] - : !value - ? props.dateCreator() - : value; - if ( - !isArray || - (isArray && - Array.isArray(vmodel.value) && - value.length > vmodel.value.length) - ) { - focusedDateData.value = { - day: currentDate.getDate(), - month: currentDate.getMonth(), - year: currentDate.getFullYear(), - }; - } - } + const isArray = Array.isArray(value); + const currentDate = isArray + ? value.length + ? value[value.length - 1] + : props.dateCreator() + : value + ? value + : props.dateCreator(); + if ( + !isArray || + (isArray && + Array.isArray(vmodel.value) && + value.length > vmodel.value.length) + ) + // updateInternalState + focusedDateData.value = { + day: currentDate.getDate(), + month: currentDate.getMonth(), + year: currentDate.getFullYear(), + }; }, ); @@ -854,17 +852,14 @@ function formatNative(value: Date | Date[]): string { function onChange(value: string): void { const date = (props.dateParser as any)(value, defaultDateParser); - if ( + const isValid = isDate(date) || (Array.isArray(date) && date.length === 2 && isDate(date[0]) && - isDate(date[1])) - ) { - vmodel.value = date; - } else { - vmodel.value = null; - } + isDate(date[1])); + + vmodel.value = isValid ? date : null; } /** Parse date from string */ diff --git a/packages/oruga/src/components/datepicker/useDatepickerMixins.ts b/packages/oruga/src/components/datepicker/useDatepickerMixins.ts index 9095daa27..b94e59c8c 100644 --- a/packages/oruga/src/components/datepicker/useDatepickerMixins.ts +++ b/packages/oruga/src/components/datepicker/useDatepickerMixins.ts @@ -97,6 +97,7 @@ export function useDatepickerMixins(props: DatepickerProps) { const defaultDateFormatter = (date: Date | Date[]): string => { if (!date) return ""; const targetDates = Array.isArray(date) ? date : [date]; + if (!targetDates.length) return ""; const dates = targetDates.map((date) => { const d = new Date( date.getFullYear(), @@ -112,57 +113,63 @@ export function useDatepickerMixins(props: DatepickerProps) { }; /** Parse a string into a date */ - const defaultDateParser = (date: string): Date => { + const defaultDateParser = (date: string): Date[] | Date => { if (!date) return null; - if ( - dtf.value.formatToParts && - typeof dtf.value.formatToParts === "function" - ) { - const formatRegex = (isTypeMonth.value ? dtfMonth.value : dtf.value) - .formatToParts(sampleTime.value) - .map((part) => { - if (part.type === "literal") return part.value; - return `((?!=<${part.type}>)\\d+)`; - }) - .join(""); - const dateGroups = matchWithGroups(formatRegex, date); - - // We do a simple validation for the group. - // If it is not valid, it will fallback to Date.parse below + const targetDates = !props.multiple ? [date] : date.split(", "); + const dates = targetDates.map((date) => { if ( - dateGroups.year && - dateGroups.year.length === 4 && - dateGroups.month && - dateGroups.month <= 12 + dtf.value.formatToParts && + typeof dtf.value.formatToParts === "function" ) { - if (isTypeMonth.value) - return new Date(dateGroups.year, dateGroups.month - 1); - else if (dateGroups.day && dateGroups.day <= 31) { - return new Date( - dateGroups.year, - dateGroups.month - 1, - dateGroups.day, - 12, - ); + const formatRegex = ( + isTypeMonth.value ? dtfMonth.value : dtf.value + ) + .formatToParts(sampleTime.value) + .map((part) => { + if (part.type === "literal") return part.value; + return `((?!=<${part.type}>)\\d+)`; + }) + .join(""); + const dateGroups = matchWithGroups(formatRegex, date); + + // We do a simple validation for the group. + // If it is not valid, it will fallback to Date.parse below + if ( + dateGroups.year && + dateGroups.year.length === 4 && + dateGroups.month && + dateGroups.month <= 12 + ) { + if (isTypeMonth.value) + return new Date(dateGroups.year, dateGroups.month - 1); + else if (dateGroups.day && dateGroups.day <= 31) { + return new Date( + dateGroups.year, + dateGroups.month - 1, + dateGroups.day, + 12, + ); + } } } - } - // Fallback if formatToParts is not supported or if we were not able to parse a valid date - if (!isTypeMonth.value) return new Date(Date.parse(date)); - const s = date.split("/"); - const year = s[0].length === 4 ? s[0] : s[1]; - const month = s[0].length === 2 ? s[0] : s[1]; - if (year && month) { - return new Date( - parseInt(year, 10), - parseInt(month, 10) - 1, - 1, - 0, - 0, - 0, - 0, - ); - } + // Fallback if formatToParts is not supported or if we were not able to parse a valid date + if (!isTypeMonth.value) return new Date(Date.parse(date)); + const s = date.split("/"); + const year = s[0].length === 4 ? s[0] : s[1]; + const month = s[0].length === 2 ? s[0] : s[1]; + if (year && month) { + return new Date( + parseInt(year, 10), + parseInt(month, 10) - 1, + 1, + 0, + 0, + 0, + 0, + ); + } + }); + return props.multiple ? dates : dates[0]; }; return { isDateSelectable, defaultDateParser, defaultDateFormatter }; diff --git a/packages/oruga/src/components/datetimepicker/examples/base.vue b/packages/oruga/src/components/datetimepicker/examples/base.vue index 704f93bec..30b89c8bf 100644 --- a/packages/oruga/src/components/datetimepicker/examples/base.vue +++ b/packages/oruga/src/components/datetimepicker/examples/base.vue @@ -47,7 +47,6 @@ const locale = ref(); // Browser locale