Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions packages/@internationalized/date/src/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ export function endOfWeek(date: DateValue, locale: string, firstDayOfWeek?: DayO
}

const cachedRegions = new Map<string, string>();
const cachedWeekInfo = new Map<string, {firstDay: number}>();

function getRegion(locale: string): string | undefined {
// If the Intl.Locale API is available, use it to get the region for the locale.
Expand Down Expand Up @@ -251,8 +252,47 @@ function getRegion(locale: string): string | undefined {
function getWeekStart(locale: string): number {
// TODO: use Intl.Locale for this once browsers support the weekInfo property
// https://github.com/tc39/proposal-intl-locale-info
let region = getRegion(locale);
return region ? weekStartData[region] || 0 : 0;
let weekInfo = cachedWeekInfo.get(locale);
if (!weekInfo) {
if (Intl.Locale) {
// @ts-ignore
let localeInst = new Intl.Locale(locale);
if ('getWeekInfo' in localeInst) {
// @ts-expect-error
weekInfo = localeInst.getWeekInfo();
if (weekInfo) {
cachedWeekInfo.set(locale, weekInfo);
return weekInfo.firstDay;
}
}
}
let region = getRegion(locale);
if (locale.includes('-fw-')) {
let day = locale.split('-fw-')[1];
if (day === 'mon') {
weekInfo = {firstDay: 1};
} else if (day === 'tue') {
weekInfo = {firstDay: 2};
} else if (day === 'wed') {
weekInfo = {firstDay: 3};
} else if (day === 'thu') {
weekInfo = {firstDay: 4};
} else if (day === 'fri') {
weekInfo = {firstDay: 5};
} else if (day === 'sat') {
weekInfo = {firstDay: 6};
} else {
weekInfo = {firstDay: 0};
}
} else if (locale.includes('u-ca-iso8601')) {
weekInfo = {firstDay: 1};
} else {
weekInfo = {firstDay: region ? weekStartData[region] || 0 : 0};
}
cachedWeekInfo.set(locale, weekInfo);
}

return weekInfo.firstDay;
}

/** Returns the number of weeks in the given month and locale. */
Expand Down
9 changes: 9 additions & 0 deletions packages/@internationalized/date/tests/queries.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,15 @@ describe('queries', function () {
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'fr-FR', 'sun')).toEqual(new CalendarDate(2021, 8, 1));
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US', 'thu')).toEqual(new CalendarDate(2021, 7, 29));
});

it('should return the start of the week in en-US-u-ca-iso8601', function () {
// start of week is monday
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601')).toEqual(new CalendarDate(2021, 8, 2));
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'fr-FR-u-ca-iso8601')).toEqual(new CalendarDate(2021, 8, 2));

// override first day of week
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601-fw-tue')).toEqual(new CalendarDate(2021, 8, 3));
});
});

describe('endOfWeek', function () {
Expand Down
48 changes: 47 additions & 1 deletion packages/react-aria-components/stories/Calendar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@
* governing permissions and limitations under the License.
*/

import {Button, Calendar, CalendarCell, CalendarGrid, CalendarStateContext, Heading, RangeCalendar} from 'react-aria-components';
import {
Button,
Calendar,
CalendarCell,
CalendarGrid,
CalendarProps,
CalendarStateContext,
DateValue,
Heading,
I18nProvider,
RangeCalendar
} from 'react-aria-components';
import {CalendarDate, parseDate} from '@internationalized/date';
import {Meta, StoryObj} from '@storybook/react';
import React, {useContext} from 'react';
Expand Down Expand Up @@ -120,6 +131,41 @@ export const CalendarMultiMonth: CalendarStory = {
}
};


interface CalendarFirstDayOfWeekExampleProps extends CalendarProps<DateValue> {
locale: string
}

export const CalendarFirstDayOfWeekExample: StoryObj<CalendarFirstDayOfWeekExampleProps> = {
render: function Example(args) {
return (
<div>
<I18nProvider locale={args.locale}>
<Calendar style={{width: 220}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<Button slot="previous">&lt;</Button>
<Heading style={{flex: 1, textAlign: 'center'}} />
<Button slot="next">&gt;</Button>
</div>
<CalendarGrid style={{width: '100%'}}>
{date => <CalendarCell date={date} style={({isSelected, isOutsideMonth}) => ({display: isOutsideMonth ? 'none' : '', textAlign: 'center', cursor: 'default', background: isSelected ? 'blue' : ''})} />}
</CalendarGrid>
</Calendar>
</I18nProvider>
</div>
);
},
args: {
locale: 'en-US-u-ca-iso8601-fw-tue'
},
argTypes: {
locale: {
control: 'select',
options: ['en-US-u-ca-iso8601-fw-tue', 'en-US-u-ca-iso8601', 'en-US', 'fr-FR-u-ca-iso8601-fw-tue', 'fr-FR-u-ca-iso8601', 'fr-FR']
}
}
};

export const RangeCalendarExample: RangeCalendarStory = {
render: () => (
<RangeCalendar style={{width: 220}}>
Expand Down