From 77af0c68a45029fa5df2ef0974378b08c7ddf50b Mon Sep 17 00:00:00 2001 From: John Leider <9064066+johnleider@users.noreply.github.com> Date: Tue, 9 Jul 2024 11:10:37 -0500 Subject: [PATCH] fix(calendar): add missing first-day-of-week property support (#20096) --- .../VDatePicker/VDatePickerMonth.tsx | 2 +- packages/vuetify/src/composables/calendar.ts | 15 ++++++++-- .../src/composables/date/DateAdapter.ts | 6 ++-- .../src/composables/date/adapters/vuetify.ts | 29 ++++++++++--------- .../vuetify/src/labs/VCalendar/VCalendar.tsx | 12 ++++---- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/packages/vuetify/src/components/VDatePicker/VDatePickerMonth.tsx b/packages/vuetify/src/components/VDatePicker/VDatePickerMonth.tsx index 3dadf81a037..384f9b762bc 100644 --- a/packages/vuetify/src/components/VDatePicker/VDatePickerMonth.tsx +++ b/packages/vuetify/src/components/VDatePicker/VDatePickerMonth.tsx @@ -173,7 +173,7 @@ export const VDatePickerMonth = genericComponent()({ key={ daysInMonth.value[0].date?.toString() } class="v-date-picker-month__days" > - { !props.hideWeekdays && adapter.getWeekdays().map(weekDay => ( + { !props.hideWeekdays && adapter.getWeekdays(props.firstDayOfWeek).map(weekDay => (
void) | undefined 'onUpdate:month': ((value: number) => void) | undefined @@ -47,6 +48,7 @@ export const makeCalendarProps = propsFactory({ type: String as PropType<'dynamic' | 'static'>, default: 'dynamic', }, + firstDayOfWeek: [Number, String], }, 'calendar') export function useCalendar (props: CalendarProps) { @@ -91,8 +93,14 @@ export function useCalendar (props: CalendarProps) { v => adapter.getMonth(v) ) + const weekDays = computed(() => { + const firstDayOfWeek = Number(props.firstDayOfWeek ?? 0) + + return props.weekdays.map(day => (day + firstDayOfWeek) % 7) + }) + const weeksInMonth = computed(() => { - const weeks = adapter.getWeekArray(month.value) + const weeks = adapter.getWeekArray(month.value, props.firstDayOfWeek) const days = weeks.flat() @@ -118,7 +126,7 @@ export function useCalendar (props: CalendarProps) { function genDays (days: unknown[], today: unknown) { return days.filter(date => { - return props.weekdays.includes(adapter.toJsDate(date).getDay()) + return weekDays.value.includes(adapter.toJsDate(date).getDay()) }).map((date, index) => { const isoDate = adapter.toISO(date) const isAdjacent = !adapter.isSameMonth(date, month.value) @@ -148,7 +156,7 @@ export function useCalendar (props: CalendarProps) { } const daysInWeek = computed(() => { - const lastDay = adapter.startOfWeek(displayValue.value) + const lastDay = adapter.startOfWeek(displayValue.value, props.firstDayOfWeek) const week = [] for (let day = 0; day <= 6; day++) { week.push(adapter.addDays(lastDay, day)) @@ -198,6 +206,7 @@ export function useCalendar (props: CalendarProps) { genDays, model, weeksInMonth, + weekDays, weekNumbers, } } diff --git a/packages/vuetify/src/composables/date/DateAdapter.ts b/packages/vuetify/src/composables/date/DateAdapter.ts index 2ffd37fdbc4..f5be4fa97d0 100644 --- a/packages/vuetify/src/composables/date/DateAdapter.ts +++ b/packages/vuetify/src/composables/date/DateAdapter.ts @@ -7,7 +7,7 @@ export interface DateAdapter { startOfDay (date: T): T endOfDay (date: T): T - startOfWeek (date: T): T + startOfWeek (date: T, firstDayOfWeek?: number | string): T endOfWeek (date: T): T startOfMonth (date: T): T endOfMonth (date: T): T @@ -35,8 +35,8 @@ export interface DateAdapter { getYear (date: T): number setYear (date: T, year: number): T getDiff (date: T, comparing: T | string, unit?: string): number - getWeekArray (date: T): T[][] - getWeekdays (): string[] + getWeekArray (date: T, firstDayOfWeek?: number | string): T[][] + getWeekdays (firstDayOfWeek?: number | string): string[] getMonth (date: T): number setMonth (date: T, month: number): T getDate (date: T): number diff --git a/packages/vuetify/src/composables/date/adapters/vuetify.ts b/packages/vuetify/src/composables/date/adapters/vuetify.ts index 0066134a5e0..e6a9b4abcae 100644 --- a/packages/vuetify/src/composables/date/adapters/vuetify.ts +++ b/packages/vuetify/src/composables/date/adapters/vuetify.ts @@ -160,13 +160,14 @@ const firstDay: Record = { ZW: 0, } -function getWeekArray (date: Date, locale: string) { +function getWeekArray (date: Date, locale: string, firstDayOfWeek?: number) { const weeks = [] let currentWeek = [] const firstDayOfMonth = startOfMonth(date) const lastDayOfMonth = endOfMonth(date) - const firstDayWeekIndex = (firstDayOfMonth.getDay() - firstDay[locale.slice(-2).toUpperCase()] + 7) % 7 - const lastDayWeekIndex = (lastDayOfMonth.getDay() - firstDay[locale.slice(-2).toUpperCase()] + 7) % 7 + const first = firstDayOfWeek ?? firstDay[locale.slice(-2).toUpperCase()] ?? 0 + const firstDayWeekIndex = (firstDayOfMonth.getDay() - first + 7) % 7 + const lastDayWeekIndex = (lastDayOfMonth.getDay() - first + 7) % 7 for (let i = 0; i < firstDayWeekIndex; i++) { const adjacentDay = new Date(firstDayOfMonth) @@ -200,9 +201,11 @@ function getWeekArray (date: Date, locale: string) { return weeks } -function startOfWeek (date: Date, locale: string) { +function startOfWeek (date: Date, locale: string, firstDayOfWeek?: number) { + const day = firstDayOfWeek ?? firstDay[locale.slice(-2).toUpperCase()] ?? 0 + const d = new Date(date) - while (d.getDay() !== (firstDay[locale.slice(-2).toUpperCase()] ?? 0)) { + while (d.getDay() !== day) { d.setDate(d.getDate() - 1) } return d @@ -256,8 +259,8 @@ function date (value?: any): Date | null { const sundayJanuarySecond2000 = new Date(2000, 0, 2) -function getWeekdays (locale: string) { - const daysFromSunday = firstDay[locale.slice(-2).toUpperCase()] +function getWeekdays (locale: string, firstDayOfWeek?: number) { + const daysFromSunday = firstDayOfWeek ?? firstDay[locale.slice(-2).toUpperCase()] ?? 0 return createRange(7).map(i => { const weekday = new Date(sundayJanuarySecond2000) @@ -601,12 +604,12 @@ export class VuetifyDateAdapter implements DateAdapter { return addMonths(date, amount) } - getWeekArray (date: Date) { - return getWeekArray(date, this.locale) + getWeekArray (date: Date, firstDayOfWeek?: number | string) { + return getWeekArray(date, this.locale, firstDayOfWeek ? Number(firstDayOfWeek) : undefined) } - startOfWeek (date: Date): Date { - return startOfWeek(date, this.locale) + startOfWeek (date: Date, firstDayOfWeek?: number | string): Date { + return startOfWeek(date, this.locale, firstDayOfWeek ? Number(firstDayOfWeek) : undefined) } endOfWeek (date: Date): Date { @@ -685,8 +688,8 @@ export class VuetifyDateAdapter implements DateAdapter { return getDiff(date, comparing, unit) } - getWeekdays () { - return getWeekdays(this.locale) + getWeekdays (firstDayOfWeek?: number | string) { + return getWeekdays(this.locale, firstDayOfWeek ? Number(firstDayOfWeek) : undefined) } getYear (date: Date) { diff --git a/packages/vuetify/src/labs/VCalendar/VCalendar.tsx b/packages/vuetify/src/labs/VCalendar/VCalendar.tsx index d28f37919d6..23616521f31 100644 --- a/packages/vuetify/src/labs/VCalendar/VCalendar.tsx +++ b/packages/vuetify/src/labs/VCalendar/VCalendar.tsx @@ -42,7 +42,7 @@ export const VCalendar = genericComponent()({ setup (props, { emit, slots }) { const adapter = useDate() - const { daysInMonth, daysInWeek, genDays, model, displayValue, weekNumbers } = useCalendar(props as any) + const { daysInMonth, daysInWeek, genDays, model, displayValue, weekNumbers, weekDays } = useCalendar(props as any) const dayNames = adapter.getWeekdays() @@ -109,13 +109,13 @@ export const VCalendar = genericComponent()({ )}
-
+
{ props.viewMode === 'month' && !props.hideDayHeader && (
()({ > { !props.hideWeekNumber ?
: '' } { - props.weekdays.map(weekday => ( + weekDays.value.map(weekday => (
{ dayNames[weekday] }
@@ -138,12 +138,12 @@ export const VCalendar = genericComponent()({ class={ [ 'v-calendar-month__days', - `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${props.weekdays.length}`, + `days${!props.hideWeekNumber ? '-with-weeknumbers' : ''}__${weekDays.value.length}`, ...(!props.hideWeekNumber ? ['v-calendar-month__weeknumbers'] : []), ] } > - { chunkArray(daysInMonth.value, props.weekdays.length) + { chunkArray(daysInMonth.value, weekDays.value.length) .map((week, wi) => ( [ !props.hideWeekNumber ?
{ weekNumbers.value[wi] }
: '',