react-native-paper-dates
Version:
Performant Date Picker for React Native Paper
350 lines (349 loc) • 12.5 kB
JavaScript
"use strict";
import { StyleSheet, View } from 'react-native';
import { Icon, Text, TouchableRipple, useTheme } from 'react-native-paper';
import Day, { EmptyDay } from './Day';
import { addMonths, areDatesOnSameDay, daySize, estimatedMonthHeight, getFirstDayOfMonth, getGridCount, getRealIndex, getStartAtIndex, getTotalMonths, createGridCounts, getDaysInMonth, isDateBetween, showWeekDay, useRangeChecker } from './dateUtils';
import { getCalendarHeaderHeight } from './CalendarHeader';
import { dayNamesHeight } from './DayNames';
import { useTextColorOnPrimary } from '../shared/utils';
import { memo, useMemo } from 'react';
import { sharedStyles } from '../shared/styles';
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
function Month(props) {
const {
locale,
mode,
index,
onPressYear,
selectingYear,
onPressDate,
primaryColor,
selectColor,
roundness,
validRange,
disableWeekDays,
scrollMode,
startDate,
endDate,
date,
dates,
startWeekOnMonday,
startYear,
endYear
} = props;
const isHorizontal = scrollMode === 'horizontal';
const theme = useTheme();
const textColorOnPrimary = useTextColorOnPrimary();
const realIndex = getRealIndex(index, startYear, endYear);
const {
isDisabled,
isWithinValidRange
} = useRangeChecker(validRange);
const {
monthName,
month,
year
} = useMemo(() => {
const md = addMonths(new Date(), realIndex);
const y = md.getFullYear();
const m = md.getMonth();
const formatter = new Intl.DateTimeFormat(locale, {
month: 'long'
});
return {
monthName: formatter.format(md),
month: m,
year: y
};
}, [realIndex, locale]);
const grid = useMemo(() => {
const today = new Date();
const daysInMonth = getDaysInMonth({
year,
month
});
const dayOfWeek = getFirstDayOfMonth({
year,
month,
startWeekOnMonday
});
const emptyDays = dayOfWeek;
return monthGrid(index, startWeekOnMonday, startYear, endYear).map(({
days,
weekGrid
}) => {
return {
weekIndex: weekGrid,
generatedDays: days.map((_, dayIndex) => {
const isFirstWeek = weekGrid === 0;
const realDayIndex = emptyDays - dayIndex;
const beforeWeekDay = isFirstWeek && realDayIndex > 0;
const dayOfMonth = weekGrid * 7 + dayIndex - emptyDays + 1;
const afterWeekDay = dayOfMonth > daysInMonth;
const day = new Date(year, month, dayOfMonth);
const isToday = areDatesOnSameDay(day, today);
let inRange = false;
let disabled = isDisabled(day);
let selected = false;
let leftCrop = dayOfMonth === 1;
let rightCrop = dayOfMonth === daysInMonth;
const isFirstDayOfMonth = dayOfMonth === 1;
const isLastDayOfMonth = dayOfMonth === daysInMonth;
if (mode === 'range') {
const selectedStartDay = areDatesOnSameDay(day, startDate);
const selectedEndDay = areDatesOnSameDay(day, endDate);
selected = selectedStartDay || selectedEndDay;
inRange = isDateBetween(day, {
startDate,
endDate
});
if (selectedStartDay) {
leftCrop = true;
}
if (selectedEndDay) {
rightCrop = true;
}
if (dayIndex === 0 && !selectedStartDay) {
leftCrop = false;
}
if (dayIndex === 6 && !selectedEndDay) {
rightCrop = false;
}
if (isFirstDayOfMonth && selectedEndDay || isLastDayOfMonth && selectedStartDay) {
inRange = false;
}
} else if (mode === 'multiple') {
const safeDates = dates || [];
selected = safeDates.some(d => areDatesOnSameDay(day, d));
const yesterday = new Date(year, month, dayOfMonth - 1);
const tomorrow = new Date(year, month, dayOfMonth + 1);
const yesterdaySelected = safeDates.some(d => areDatesOnSameDay(d, yesterday));
const tomorrowSelected = safeDates.some(d => areDatesOnSameDay(d, tomorrow));
if (selected) {
if (tomorrowSelected && yesterdaySelected) {
inRange = true;
}
if (tomorrowSelected && !yesterdaySelected) {
inRange = true;
leftCrop = true;
}
if (yesterdaySelected && !tomorrowSelected) {
inRange = true;
rightCrop = true;
}
if (isFirstDayOfMonth && !tomorrowSelected) {
inRange = false;
}
if (isLastDayOfMonth && !yesterdaySelected) {
inRange = false;
}
if (inRange && !leftCrop && !rightCrop) {
selected = false;
}
}
} else if (mode === 'single') {
selected = areDatesOnSameDay(day, date);
}
const isWithinOptionalValidRange = isWithinValidRange(day);
if (inRange && !disabled) {
disabled = false;
}
if (!isWithinOptionalValidRange) {
disabled = true;
}
return {
beforeWeekDay,
afterWeekDay,
year,
month,
dayOfMonth,
dayIndex,
mode,
selected,
inRange,
leftCrop,
rightCrop,
isToday,
disabled
};
})
};
});
}, [year, month, index, isDisabled, mode, isWithinValidRange, startDate, endDate, dates, date, startWeekOnMonday, startYear, endYear]);
let textFont = theme?.isV3 ? theme.fonts.titleSmall : theme.fonts.medium;
const iconColor = theme.isV3 ? theme.colors.onSurfaceVariant : theme.colors.onSurface;
const iconSourceV3 = selectingYear ? 'menu-up' : 'menu-down';
const iconSourceV2 = selectingYear ? 'chevron-up' : 'chevron-down';
const iconSource = theme.isV3 ? iconSourceV3 : iconSourceV2;
return /*#__PURE__*/_jsxs(View, {
style: {
height: getMonthHeight(scrollMode, index, startWeekOnMonday, startYear, endYear)
},
children: [/*#__PURE__*/_jsx(View, {
style: [styles.monthHeader, isHorizontal ? {
marginTop: monthHeaderSingleMarginTop,
marginBottom: monthHeaderSingleMarginBottom
} : null],
children: /*#__PURE__*/_jsx(TouchableRipple, {
disabled: !isHorizontal,
onPress: isHorizontal ? () => onPressYear(year) : undefined,
accessibilityRole: "button",
accessibilityLabel: `${monthName} ${year}`,
style: [styles.yearButton, {
borderRadius: roundness
}],
children: /*#__PURE__*/_jsxs(View, {
style: [styles.yearButtonInner, {
borderRadius: roundness
}],
children: [/*#__PURE__*/_jsxs(Text, {
maxFontSizeMultiplier: 1.5,
style: [styles.monthLabel, {
...textFont,
color: theme.isV3 ? theme.colors.onSurfaceVariant : theme.colors.onSurface
}],
selectable: false,
children: [monthName, " ", year]
}), /*#__PURE__*/_jsx(View, {
style: [styles.iconWrapper, isHorizontal ? sharedStyles.opacity1 : sharedStyles.opacity0],
children: /*#__PURE__*/_jsx(Icon, {
size: 24,
color: iconColor,
source: iconSource
})
})]
})
})
}), grid.map(({
weekIndex,
generatedDays
}) => /*#__PURE__*/_jsx(View, {
style: styles.week,
children: generatedDays.filter(gd => showWeekDay(gd.dayIndex, disableWeekDays)).map(gd => gd.beforeWeekDay || gd.afterWeekDay ? /*#__PURE__*/_jsx(EmptyDay, {}, gd.dayIndex) : /*#__PURE__*/_jsx(Day, {
theme: theme,
day: gd.dayOfMonth,
month: gd.month,
year: gd.year,
selected: gd.selected,
inRange: gd.inRange,
leftCrop: gd.leftCrop,
rightCrop: gd.rightCrop,
onPressDate: onPressDate,
isToday: gd.isToday,
selectColor: selectColor,
primaryColor: primaryColor,
disabled: gd.disabled,
textColorOnPrimary: textColorOnPrimary
}, gd.dayIndex))
}, weekIndex))]
});
}
export const weekMargin = 6;
export const weekSize = daySize + weekMargin;
export const montHeaderHeight = 56;
export const monthHeaderSingleMarginTop = 4;
export const monthHeaderSingleMarginBottom = 8 + 44 + 12;
export const monthHeaderSingleHeight = monthHeaderSingleMarginTop + monthHeaderSingleMarginBottom;
const styles = StyleSheet.create({
iconWrapper: {
padding: 8
},
monthHeader: {
height: montHeaderHeight,
justifyContent: 'center',
overflow: 'hidden'
},
monthLabel: {
fontSize: 14,
opacity: 0.7
},
week: {
flexDirection: 'row',
marginBottom: weekMargin,
height: daySize
},
yearButton: {
alignSelf: 'flex-start',
marginLeft: 6
},
yearButtonInner: {
paddingLeft: 16,
flexDirection: 'row',
alignItems: 'center'
}
});
const monthGrid = (index, startWeekOnMonday, startYear, endYear) => {
return Array(getGridCount(index, startWeekOnMonday, startYear, endYear)).fill(null).map((_, weekGrid) => {
const days = Array(7).fill(null);
return {
weekGrid,
days
};
});
};
function getIndexCount(index, startYear, endYear) {
const dynamicStartAtIndex = getStartAtIndex(startYear, endYear);
if (index > dynamicStartAtIndex) {
return index - dynamicStartAtIndex;
}
return -(dynamicStartAtIndex - index);
}
function weeksOffset(index, startWeekOnMonday, startYear, endYear) {
const dynamicStartAtIndex = getStartAtIndex(startYear, endYear);
const dynamicGridCounts = createGridCounts(getTotalMonths(startYear, endYear));
if (index === dynamicStartAtIndex) {
return 0;
}
let off = 0;
if (index > dynamicStartAtIndex) {
for (let i = 0; i < index - dynamicStartAtIndex; i++) {
const cIndex = dynamicStartAtIndex + i;
off += dynamicGridCounts[cIndex] || getGridCount(cIndex, startWeekOnMonday, startYear, endYear);
}
} else {
for (let i = 0; i < dynamicStartAtIndex - index; i++) {
const cIndex = dynamicStartAtIndex - i - 1;
off -= dynamicGridCounts[cIndex] || getGridCount(cIndex, startWeekOnMonday, startYear, endYear);
}
}
return off;
}
export function getIndexFromHorizontalOffset(offset, width, startYear, endYear) {
const dynamicStartAtIndex = getStartAtIndex(startYear, endYear);
return dynamicStartAtIndex + Math.floor(offset / width);
}
export function getIndexFromVerticalOffset(offset, startWeekOnMonday, startYear, endYear) {
const dynamicStartAtIndex = getStartAtIndex(startYear, endYear);
const dynamicBeginOffset = estimatedMonthHeight * dynamicStartAtIndex;
let estimatedIndex = dynamicStartAtIndex + Math.ceil(offset / estimatedMonthHeight);
const realOffset = getVerticalMonthsOffset(estimatedIndex, startWeekOnMonday, startYear, endYear);
const difference = (realOffset - dynamicBeginOffset - offset) / estimatedMonthHeight;
if (difference >= 1 || difference <= -1) {
estimatedIndex -= Math.floor(difference);
}
return estimatedIndex;
}
export function getHorizontalMonthOffset(index, width) {
if (index < 0) {
return 0;
}
return width * index;
}
export function getVerticalMonthsOffset(index, startWeekOnMonday, startYear, endYear) {
const count = getIndexCount(index, startYear, endYear);
const ob = weeksOffset(index, startWeekOnMonday, startYear, endYear);
const monthsHeight = weekSize * ob;
const c = monthsHeight + count * (dayNamesHeight + montHeaderHeight);
const dynamicBeginOffset = estimatedMonthHeight * getStartAtIndex(startYear, endYear);
return (c || 0) + dynamicBeginOffset;
}
export function getMonthHeight(scrollMode, index, startWeekOnMonday, startYear, endYear) {
const calendarHeight = getCalendarHeaderHeight(scrollMode);
const gc = getGridCount(index, startWeekOnMonday, startYear, endYear);
const currentMonthHeight = weekSize * gc;
const extraHeight = scrollMode === 'horizontal' ? monthHeaderSingleHeight : montHeaderHeight;
const c = calendarHeight + currentMonthHeight + extraHeight;
return c || 0;
}
export default /*#__PURE__*/memo(Month);
//# sourceMappingURL=Month.js.map