diff --git a/docusaurus/docs/date-picker/input-date-picker.md b/docusaurus/docs/date-picker/input-date-picker.md
index 74a1eadd..58d8cf67 100644
--- a/docusaurus/docs/date-picker/input-date-picker.md
+++ b/docusaurus/docs/date-picker/input-date-picker.md
@@ -119,6 +119,10 @@ The start year when the component is rendered. Defaults to `1800`.
`Type: number | undefined`
The end year when the component is rendered. Defaults to `2200`.
+**startWeekOnMonday**
+`Type: boolean | undefined`
+Flag indicating if calendar grid sould show monday as the first column. Defaults to `false`.
+
**inputEnabled**
`Type: boolean | undefined`
Flag indicating if the component should be enabled or not. Behavior similarly to `disabled`. Defaults to `true`.
diff --git a/docusaurus/docs/date-picker/multiple-dates-picker.md b/docusaurus/docs/date-picker/multiple-dates-picker.md
index de00f3b4..571ebe4b 100644
--- a/docusaurus/docs/date-picker/multiple-dates-picker.md
+++ b/docusaurus/docs/date-picker/multiple-dates-picker.md
@@ -131,6 +131,10 @@ The start year when the component is rendered. Defaults to `1800`.
`Type: number | undefined`
The end year when the component is rendered. Defaults to `2200`.
+**startWeekOnMonday**
+`Type: boolean | undefined`
+Flag indicating if calendar grid sould show monday as the first column. Defaults to `false`.
+
**closeIcon**
`Type: string | undefined`
The icon used to close the component. Defaults to `close`. You can pass the name of an icon from [MaterialCommunityIcons](https://materialdesignicons.com/).
diff --git a/docusaurus/docs/date-picker/range-date-picker.md b/docusaurus/docs/date-picker/range-date-picker.md
index bb9ddd63..76e09a78 100644
--- a/docusaurus/docs/date-picker/range-date-picker.md
+++ b/docusaurus/docs/date-picker/range-date-picker.md
@@ -134,6 +134,10 @@ The start year when the component is rendered. Defaults to `1800`.
`Type: number | undefined`
The end year when the component is rendered. Defaults to `2200`.
+**startWeekOnMonday**
+`Type: boolean | undefined`
+Flag indicating if calendar grid sould show monday as the first column. Defaults to `false`.
+
**closeIcon**
`Type: string | undefined`
The icon used to close the component. Defaults to `close`. You can pass the name of an icon from [MaterialCommunityIcons](https://materialdesignicons.com/).
diff --git a/docusaurus/docs/date-picker/single-date-picker.md b/docusaurus/docs/date-picker/single-date-picker.md
index 8d3d573c..b68bc3d9 100644
--- a/docusaurus/docs/date-picker/single-date-picker.md
+++ b/docusaurus/docs/date-picker/single-date-picker.md
@@ -121,6 +121,10 @@ The start year when the component is rendered. Defaults to `1800`.
`Type: number | undefined`
The end year when the component is rendered. Defaults to `2200`.
+**startWeekOnMonday**
+`Type: boolean | undefined`
+Flag indicating if calendar grid sould show monday as the first column. Defaults to `false`.
+
**closeIcon**
`Type: string | undefined`
The icon used to close the component. Defaults to `close`. You can pass the name of an icon from [MaterialCommunityIcons](https://materialdesignicons.com/).
diff --git a/example/src/ReadMeExampleMultiple.tsx b/example/src/ReadMeExampleMultiple.tsx
index b42ae45f..5fd6a627 100644
--- a/example/src/ReadMeExampleMultiple.tsx
+++ b/example/src/ReadMeExampleMultiple.tsx
@@ -44,6 +44,7 @@ export default function ReadMeExampleMultiple() {
// animationType="slide" // optional, default is slide on ios/android and none on web
// startYear={2000} // optional, default is 1800
// endYear={2100} // optional, default is 2200
+ // startWeekOnMonday={true} // optional, default is false
/>
>
)
diff --git a/example/src/ReadMeExampleRange.tsx b/example/src/ReadMeExampleRange.tsx
index 12d24d2e..e00802df 100644
--- a/example/src/ReadMeExampleRange.tsx
+++ b/example/src/ReadMeExampleRange.tsx
@@ -52,6 +52,7 @@ export default function ReadMeExampleRange() {
// animationType="slide" // optional, default is slide on ios/android and none on web
// startYear={2000} // optional, default is 1800
// endYear={2100} // optional, default is 2200
+ // startWeekOnMonday={true} // optional, default is false
/>
>
)
diff --git a/example/src/ReadMeExampleSingle.tsx b/example/src/ReadMeExampleSingle.tsx
index 6e4c2f5f..cd18b4cb 100644
--- a/example/src/ReadMeExampleSingle.tsx
+++ b/example/src/ReadMeExampleSingle.tsx
@@ -43,6 +43,7 @@ export default function ReadMeExampleSingle() {
// animationType="slide" // optional, default is 'slide' on ios/android and 'none' on web
// startYear={2000} // optional, default is 1800
// endYear={2100} // optional, default is 2200
+ // startWeekOnMonday={true} // optional, default is false
//
/>
>
diff --git a/src/Date/Calendar.tsx b/src/Date/Calendar.tsx
index 76dbc538..780e7036 100644
--- a/src/Date/Calendar.tsx
+++ b/src/Date/Calendar.tsx
@@ -33,6 +33,7 @@ export type BaseCalendarProps = {
validRange?: ValidRangeType
startYear?: number
endYear?: number
+ startWeekOnMonday?: boolean
// here they are optional but in final implementation they are required
date?: CalendarDate
@@ -95,6 +96,7 @@ function Calendar(
dates,
validRange,
dateMode,
+ startWeekOnMonday,
} = props
const theme = useTheme()
@@ -177,6 +179,7 @@ function Calendar(
initialIndex={getInitialIndex(firstDate)}
selectedYear={selectedYear}
scrollMode={scrollMode}
+ startWeekOnMonday={startWeekOnMonday || false}
renderItem={({ index }) => (
)}
renderHeader={({ onPrev, onNext }) => (
@@ -205,6 +209,7 @@ function Calendar(
onNext={onNext}
scrollMode={scrollMode}
disableWeekDays={disableWeekDays}
+ startWeekOnMonday={startWeekOnMonday || false}
/>
)}
/>
diff --git a/src/Date/CalendarHeader.tsx b/src/Date/CalendarHeader.tsx
index d95206b2..1b845200 100644
--- a/src/Date/CalendarHeader.tsx
+++ b/src/Date/CalendarHeader.tsx
@@ -27,12 +27,14 @@ function CalendarHeader({
onNext,
disableWeekDays,
locale,
+ startWeekOnMonday,
}: {
locale: undefined | string
scrollMode: 'horizontal' | 'vertical'
onPrev: () => any
onNext: () => any
disableWeekDays?: DisableWeekDaysType
+ startWeekOnMonday: boolean
}) {
const theme = useTheme()
const isHorizontal = scrollMode === 'horizontal'
@@ -67,7 +69,11 @@ function CalendarHeader({
) : null}
-
+
)
}
diff --git a/src/Date/DatePickerInput.shared.tsx b/src/Date/DatePickerInput.shared.tsx
index 44b71df0..bb0f896f 100644
--- a/src/Date/DatePickerInput.shared.tsx
+++ b/src/Date/DatePickerInput.shared.tsx
@@ -27,6 +27,7 @@ export type DatePickerInputProps = {
disableStatusBarPadding?: boolean
animationType?: 'slide' | 'fade' | 'none'
presentationStyle?: 'pageSheet' | 'overFullScreen'
+ startWeekOnMonday?: boolean
} & Omit<
React.ComponentProps,
'value' | 'onChange' | 'onChangeText' | 'inputMode'
diff --git a/src/Date/DatePickerInput.tsx b/src/Date/DatePickerInput.tsx
index fc5608c7..2decd08f 100644
--- a/src/Date/DatePickerInput.tsx
+++ b/src/Date/DatePickerInput.tsx
@@ -60,6 +60,7 @@ function DatePickerInput(
endYear,
inputEnabled,
disableStatusBarPadding,
+ startWeekOnMonday,
}) =>
withModal ? (
) : null
}
diff --git a/src/Date/DatePickerInputWithoutModal.tsx b/src/Date/DatePickerInputWithoutModal.tsx
index d86d6ef8..47d0be2c 100644
--- a/src/Date/DatePickerInputWithoutModal.tsx
+++ b/src/Date/DatePickerInputWithoutModal.tsx
@@ -29,6 +29,7 @@ function DatePickerInputWithoutModal(
onChangeText,
inputEnabled,
disableStatusBarPadding,
+ startWeekOnMonday,
...rest
}: DatePickerInputProps & {
modal?: (params: {
@@ -43,6 +44,7 @@ function DatePickerInputWithoutModal(
endYear: DatePickerInputProps['endYear']
inputEnabled: DatePickerInputProps['inputEnabled']
disableStatusBarPadding: DatePickerInputProps['disableStatusBarPadding']
+ startWeekOnMonday?: DatePickerInputProps['startWeekOnMonday']
}) => any
inputButton?: React.ReactNode
},
@@ -118,6 +120,7 @@ function DatePickerInputWithoutModal(
endYear,
inputEnabled,
disableStatusBarPadding,
+ startWeekOnMonday,
})}
>
)
diff --git a/src/Date/DatePickerModalContent.tsx b/src/Date/DatePickerModalContent.tsx
index 08b2877f..98f91fef 100644
--- a/src/Date/DatePickerModalContent.tsx
+++ b/src/Date/DatePickerModalContent.tsx
@@ -93,6 +93,7 @@ export function DatePickerModalContent(
startYear,
endYear,
statusBarOnTopOfBackdrop,
+ startWeekOnMonday,
} = props
const anyProps = props as any
@@ -199,6 +200,7 @@ export function DatePickerModalContent(
dateMode={dateMode}
startYear={startYear}
endYear={endYear}
+ startWeekOnMonday={startWeekOnMonday}
/>
}
calendarEdit={
diff --git a/src/Date/DayNames.tsx b/src/Date/DayNames.tsx
index 898969c0..6584cd9c 100644
--- a/src/Date/DayNames.tsx
+++ b/src/Date/DayNames.tsx
@@ -6,31 +6,35 @@ import { DisableWeekDaysType, showWeekDay } from './dateUtils'
export const dayNamesHeight = 44
-// TODO: wait for a better Intl api ;-)
-const weekdays = [
- new Date(2020, 7, 2),
- new Date(2020, 7, 3),
- new Date(2020, 7, 4),
- new Date(2020, 7, 5),
- new Date(2020, 7, 6),
- new Date(2020, 7, 7),
- new Date(2020, 7, 8),
-]
-
function DayNames({
disableWeekDays,
locale,
+ startWeekOnMonday,
}: {
locale: undefined | string
disableWeekDays?: DisableWeekDaysType
+ startWeekOnMonday: boolean
}) {
const theme = useTheme()
const shortDayNames = React.useMemo(() => {
+ // TODO: wait for a better Intl api ;-)
+ const weekdays = [
+ new Date(2020, 7, 2),
+ new Date(2020, 7, 3),
+ new Date(2020, 7, 4),
+ new Date(2020, 7, 5),
+ new Date(2020, 7, 6),
+ new Date(2020, 7, 7),
+ new Date(2020, 7, 8),
+ ]
+ if (startWeekOnMonday) {
+ weekdays.push(weekdays.shift() as Date)
+ }
const formatter = new Intl.DateTimeFormat(locale, {
weekday: 'narrow',
})
return weekdays.map((date) => formatter.format(date))
- }, [locale])
+ }, [locale, startWeekOnMonday])
return (
{
+ return monthGrid(index, startWeekOnMonday).map(({ days, weekGrid }) => {
return {
weekIndex: weekGrid,
generatedDays: days.map((_, dayIndex) => {
@@ -248,6 +250,7 @@ function Month(props: MonthSingleProps | MonthRangeProps | MonthMultiProps) {
endDate,
dates,
date,
+ startWeekOnMonday,
])
let textFont = theme?.isV3
@@ -263,7 +266,12 @@ function Month(props: MonthSingleProps | MonthRangeProps | MonthMultiProps) {
const iconSource = theme.isV3 ? iconSourceV3 : iconSourceV2
return (
-
+
{
- return Array(getGridCount(index))
+const monthGrid = (index: number, startWeekOnMonday: boolean) => {
+ return Array(getGridCount(index, startWeekOnMonday))
.fill(null)
.map((_, weekGrid) => {
const days = Array(7).fill(null)
@@ -405,7 +413,7 @@ function getIndexCount(index: number): number {
return -(startAtIndex - index)
}
-function weeksOffset(index: number): number {
+function weeksOffset(index: number, startWeekOnMonday: boolean): number {
if (index === startAtIndex) {
return 0
}
@@ -413,12 +421,12 @@ function weeksOffset(index: number): number {
if (index > startAtIndex) {
for (let i = 0; i < index - startAtIndex; i++) {
const cIndex = startAtIndex + i
- off += gridCounts[cIndex] || getGridCount(cIndex)
+ off += gridCounts[cIndex] || getGridCount(cIndex, startWeekOnMonday)
}
} else {
for (let i = 0; i < startAtIndex - index; i++) {
const cIndex = startAtIndex - i - 1
- off -= gridCounts[cIndex] || getGridCount(cIndex)
+ off -= gridCounts[cIndex] || getGridCount(cIndex, startWeekOnMonday)
}
}
return off
@@ -431,10 +439,13 @@ export function getIndexFromHorizontalOffset(
return startAtIndex + Math.floor(offset / width)
}
-export function getIndexFromVerticalOffset(offset: number): number {
+export function getIndexFromVerticalOffset(
+ offset: number,
+ startWeekOnMonday: boolean
+): number {
let estimatedIndex = startAtIndex + Math.ceil(offset / estimatedMonthHeight)
- const realOffset = getVerticalMonthsOffset(estimatedIndex)
+ const realOffset = getVerticalMonthsOffset(estimatedIndex, startWeekOnMonday)
const difference = (realOffset - beginOffset - offset) / estimatedMonthHeight
if (difference >= 1 || difference <= -1) {
estimatedIndex -= Math.floor(difference)
@@ -449,9 +460,12 @@ export function getHorizontalMonthOffset(index: number, width: number) {
return width * index
}
-export function getVerticalMonthsOffset(index: number) {
+export function getVerticalMonthsOffset(
+ index: number,
+ startWeekOnMonday: boolean
+) {
const count = getIndexCount(index)
- const ob = weeksOffset(index)
+ const ob = weeksOffset(index, startWeekOnMonday)
const monthsHeight = weekSize * ob
const c = monthsHeight + count * (dayNamesHeight + montHeaderHeight)
@@ -460,10 +474,11 @@ export function getVerticalMonthsOffset(index: number) {
export function getMonthHeight(
scrollMode: 'horizontal' | 'vertical',
- index: number
+ index: number,
+ startWeekOnMonday: boolean
): number {
const calendarHeight = getCalendarHeaderHeight(scrollMode)
- const gc = getGridCount(index)
+ const gc = getGridCount(index, startWeekOnMonday)
const currentMonthHeight = weekSize * gc
const extraHeight =
diff --git a/src/Date/Swiper.native.tsx b/src/Date/Swiper.native.tsx
index f80d2ccf..28278536 100644
--- a/src/Date/Swiper.native.tsx
+++ b/src/Date/Swiper.native.tsx
@@ -56,6 +56,7 @@ function SwiperInner({
initialIndex,
width,
height,
+ startWeekOnMonday,
}: SwiperProps & { width: number; height: number }) {
const idx = React.useRef(initialIndex)
const isHorizontal = scrollMode === 'horizontal'
@@ -75,7 +76,7 @@ function SwiperInner({
}
const offset = isHorizontal
? getHorizontalMonthOffset(index, width)
- : getVerticalMonthsOffset(index) - montHeaderHeight
+ : getVerticalMonthsOffset(index, startWeekOnMonday) - montHeaderHeight
if (isHorizontal) {
parentRef.current.scrollTo({
@@ -91,7 +92,7 @@ function SwiperInner({
})
}
},
- [parentRef, isHorizontal, width, height]
+ [parentRef, isHorizontal, width, height, startWeekOnMonday]
)
const onPrev = React.useCallback(() => {
@@ -112,7 +113,10 @@ function SwiperInner({
const viewSize = e.nativeEvent.layoutMeasurement
const newIndex = isHorizontal
? Math.floor(contentOffset.x / viewSize.width)
- : getIndexFromVerticalOffset(contentOffset.y - beginOffset)
+ : getIndexFromVerticalOffset(
+ contentOffset.y - beginOffset,
+ startWeekOnMonday
+ )
if (newIndex === 0) {
return
@@ -123,7 +127,7 @@ function SwiperInner({
setVisibleIndexes(getVisibleArray(newIndex, { isHorizontal, height }))
}
},
- [idx, height, isHorizontal]
+ [idx, height, isHorizontal, startWeekOnMonday]
)
const renderProps = {
@@ -179,7 +183,10 @@ function SwiperInner({
style={{
top: isHorizontal
? 0
- : getVerticalMonthsOffset(visibleIndexes[vi]),
+ : getVerticalMonthsOffset(
+ visibleIndexes[vi],
+ startWeekOnMonday
+ ),
left: isHorizontal
? getHorizontalMonthOffset(visibleIndexes[vi], width)
: 0,
@@ -189,7 +196,11 @@ function SwiperInner({
width: isHorizontal ? width : undefined,
height: isHorizontal
? undefined
- : getMonthHeight(scrollMode, visibleIndexes[vi]),
+ : getMonthHeight(
+ scrollMode,
+ visibleIndexes[vi],
+ startWeekOnMonday
+ ),
}}
>
{renderItem({
diff --git a/src/Date/Swiper.tsx b/src/Date/Swiper.tsx
index 33f3ccd2..2cfe90de 100644
--- a/src/Date/Swiper.tsx
+++ b/src/Date/Swiper.tsx
@@ -20,6 +20,7 @@ function Swiper({
renderFooter,
selectedYear,
initialIndex,
+ startWeekOnMonday,
}: SwiperProps) {
const isHorizontal = scrollMode === 'horizontal'
const [index, setIndex] = React.useState(initialIndex)
@@ -66,6 +67,7 @@ function Swiper({
initialIndex={initialIndex}
estimatedHeight={estimatedMonthHeight}
renderItem={renderItem}
+ startWeekOnMonday={startWeekOnMonday}
/>
)}
@@ -83,12 +85,14 @@ function VerticalScroller({
initialIndex,
estimatedHeight,
renderItem,
+ startWeekOnMonday,
}: {
renderItem: (renderProps: RenderProps) => any
width: number
height: number
initialIndex: number
estimatedHeight: number
+ startWeekOnMonday: boolean
}) {
const idx = React.useRef(initialIndex)
const [visibleIndexes, setVisibleIndexes] = React.useState(
@@ -101,7 +105,8 @@ function VerticalScroller({
if (!element) {
return
}
- const top = getVerticalMonthsOffset(idx.current) - montHeaderHeight
+ const top =
+ getVerticalMonthsOffset(idx.current, startWeekOnMonday) - montHeaderHeight
element.scrollTo({
top,
@@ -118,14 +123,14 @@ function VerticalScroller({
}
const offset = top - beginOffset
- const index = getIndexFromVerticalOffset(offset)
+ const index = getIndexFromVerticalOffset(offset, startWeekOnMonday)
if (idx.current !== index) {
idx.current = index
setVisibleIndexesThrottled(visibleArray(index))
}
},
- [setVisibleIndexesThrottled]
+ [setVisibleIndexesThrottled, startWeekOnMonday]
)
return (
@@ -153,12 +158,17 @@ function VerticalScroller({
style={{
willChange: 'transform',
transform: `translateY(${getVerticalMonthsOffset(
- visibleIndexes[vi]
+ visibleIndexes[vi],
+ startWeekOnMonday
)}px)`,
left: 0,
right: 0,
position: 'absolute',
- height: getMonthHeight('vertical', visibleIndexes[vi]),
+ height: getMonthHeight(
+ 'vertical',
+ visibleIndexes[vi],
+ startWeekOnMonday
+ ),
}}
>
{renderItem({
diff --git a/src/Date/SwiperUtils.ts b/src/Date/SwiperUtils.ts
index 8a98be84..c6f6fd2a 100644
--- a/src/Date/SwiperUtils.ts
+++ b/src/Date/SwiperUtils.ts
@@ -21,6 +21,7 @@ export type SwiperProps = {
renderHeader?: (renderProps: RenderProps) => any
renderFooter?: (renderProps: RenderProps) => any
selectedYear: number | undefined
+ startWeekOnMonday: boolean
}
export function useYearChange(
diff --git a/src/Date/dateUtils.tsx b/src/Date/dateUtils.tsx
index 26131a3a..c4a59ce1 100644
--- a/src/Date/dateUtils.tsx
+++ b/src/Date/dateUtils.tsx
@@ -58,11 +58,19 @@ export function getDaysInMonth({
export function getFirstDayOfMonth({
year,
month,
+ startWeekOnMonday,
}: {
year: number
month: number
+ startWeekOnMonday: boolean
}): number {
- return new Date(year, month, 1).getDay()
+ let dayOfWeek = new Date(year, month, 1).getDay()
+ if (startWeekOnMonday) {
+ // Map Sunday (0) to 6, Monday (1) to 0, etc.
+ dayOfWeek = (dayOfWeek + 6) % 7
+ }
+
+ return dayOfWeek
}
export function useRangeChecker(validRange: ValidRangeType | undefined) {
@@ -167,22 +175,22 @@ export const totalMonths = startAtIndex * 2
export const beginOffset = estimatedMonthHeight * startAtIndex
export const gridCounts = new Array(totalMonths)
-export function getGridCount(index: number) {
+export function getGridCount(index: number, startWeekOnMonday: boolean) {
const cHeight = gridCounts[index]
if (cHeight) {
return cHeight
}
const monthDate = addMonths(new Date(), getRealIndex(index))
- const h = getGridCountForDate(monthDate)
+ const h = getGridCountForDate(monthDate, startWeekOnMonday)
gridCounts[index] = h
return h
}
-export function getGridCountForDate(date: Date) {
+export function getGridCountForDate(date: Date, startWeekOnMonday: boolean) {
const year = date.getFullYear()
const month = date.getMonth()
const daysInMonth = getDaysInMonth({ year, month })
- const dayOfWeek = getFirstDayOfMonth({ year, month })
+ const dayOfWeek = getFirstDayOfMonth({ year, month, startWeekOnMonday })
return Math.ceil((daysInMonth + dayOfWeek) / 7)
}
diff --git a/src/__tests__/Date/CalendarHeader.test.tsx b/src/__tests__/Date/CalendarHeader.test.tsx
index 8fa1b4cb..e9601585 100644
--- a/src/__tests__/Date/CalendarHeader.test.tsx
+++ b/src/__tests__/Date/CalendarHeader.test.tsx
@@ -11,6 +11,7 @@ it('renders CalendarHeader', () => {
onPrev={() => null}
onNext={() => null}
scrollMode={'vertical'}
+ startWeekOnMonday={false}
/>
)
.toJSON()
diff --git a/src/__tests__/Date/DayNames.test.tsx b/src/__tests__/Date/DayNames.test.tsx
index 1a16ed1f..c59e620c 100644
--- a/src/__tests__/Date/DayNames.test.tsx
+++ b/src/__tests__/Date/DayNames.test.tsx
@@ -4,7 +4,9 @@ import renderer from 'react-test-renderer'
import DayNames from '../../Date/DayNames'
it('renders DayNames', () => {
- const tree = renderer.create().toJSON()
+ const tree = renderer
+ .create()
+ .toJSON()
expect(tree).toMatchSnapshot()
})
diff --git a/src/__tests__/Date/dateUtils.test.tsx b/src/__tests__/Date/dateUtils.test.tsx
index 3de4975e..165f27ea 100644
--- a/src/__tests__/Date/dateUtils.test.tsx
+++ b/src/__tests__/Date/dateUtils.test.tsx
@@ -73,10 +73,16 @@ describe('timeUtils', () => {
it('should return correct gridCount for October 2021', () => {
expect(
- getGridCountForDate(addMonths(new Date(2018, 10 - 1, 1), 12 * 3))
+ getGridCountForDate(addMonths(new Date(2018, 10 - 1, 1), 12 * 3), false)
).toBe(6)
})
+ it('should return correct gridCount for October 2021 with weeks starting on monday', () => {
+ expect(
+ getGridCountForDate(addMonths(new Date(2018, 10 - 1, 1), 12 * 3), true)
+ ).toBe(5)
+ })
+
it('should get correct difference in month between dates', () => {
expect(differenceInMonths(new Date(2022, 1, 1), new Date(2022, 2, 1))).toBe(
1