Skip to content

Commit 326fc33

Browse files
Merge pull request #251 from sinabdollahi/feat/add-reset-functionallity-and-year-month-styles
feat: add allowRangeReset prop to Range datepicker and add month and year selector Components.
2 parents 8bc148f + fd2d3ce commit 326fc33

6 files changed

Lines changed: 10046 additions & 13670 deletions

File tree

src/components/header/month-button.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const MonthButton = () => {
1515
classNames,
1616
disableMonthPicker,
1717
monthCaptionFormat,
18+
components,
1819
} = useCalendarContext();
1920

2021
const currentMonthText = dayjs(currentDate)
@@ -24,12 +25,25 @@ const MonthButton = () => {
2425
)
2526
.format(monthCaptionFormat === 'full' ? 'MMMM' : 'MMM');
2627

28+
const handlePress = () =>
29+
setCalendarView(calendarView === 'month' ? 'day' : 'month');
30+
31+
if (components?.MonthSelector) {
32+
return (
33+
<>
34+
{components.MonthSelector({
35+
text: currentMonthText,
36+
isOpen: calendarView === 'month',
37+
onPress: disableMonthPicker ? () => {} : handlePress,
38+
})}
39+
</>
40+
);
41+
}
42+
2743
return (
2844
<Pressable
2945
disabled={disableMonthPicker}
30-
onPress={() =>
31-
setCalendarView(calendarView === 'month' ? 'day' : 'month')
32-
}
46+
onPress={handlePress}
3347
testID="btn-month"
3448
accessibilityRole="button"
3549
accessibilityLabel={currentMonthText}

src/components/header/year-button.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,40 @@ const YearButton = () => {
1616
disableYearPicker,
1717
calendar = 'gregory',
1818
numerals = 'latn',
19+
components,
1920
} = useCalendarContext();
2021

2122
const years = getYearRange(currentYear);
23+
24+
const yearText =
25+
calendarView === 'year'
26+
? `${formatNumber(years[0] || 0, numerals)} - ${formatNumber(years[years.length - 1] || 0, numerals)}`
27+
: formatNumber(
28+
parseInt(dayjs(currentDate).calendar(calendar).format('YYYY')),
29+
numerals
30+
);
31+
32+
const handlePress = () => {
33+
setCalendarView(calendarView === 'year' ? 'day' : 'year');
34+
onChangeYear(getDateYear(currentDate));
35+
};
36+
37+
if (components?.YearSelector) {
38+
return (
39+
<>
40+
{components.YearSelector({
41+
text: yearText,
42+
isOpen: calendarView === 'year',
43+
onPress: disableYearPicker ? () => {} : handlePress,
44+
})}
45+
</>
46+
);
47+
}
48+
2249
return (
2350
<Pressable
2451
disabled={disableYearPicker}
25-
onPress={() => {
26-
setCalendarView(calendarView === 'year' ? 'day' : 'year');
27-
onChangeYear(getDateYear(currentDate));
28-
}}
52+
onPress={handlePress}
2953
testID="btn-year"
3054
accessibilityRole="button"
3155
accessibilityLabel={dayjs(currentDate).calendar(calendar).format('YYYY')}
@@ -38,12 +62,7 @@ const YearButton = () => {
3862
style={styles?.year_selector_label}
3963
className={classNames?.year_selector_label}
4064
>
41-
{calendarView === 'year'
42-
? `${formatNumber(years[0] || 0, numerals)} - ${formatNumber(years[years.length - 1] || 0, numerals)}`
43-
: formatNumber(
44-
parseInt(dayjs(currentDate).calendar(calendar).format('YYYY')),
45-
numerals
46-
)}
65+
{yearText}
4766
</Text>
4867
</View>
4968
</Pressable>

src/datetime-picker.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ export interface DatePickerRangeProps extends DatePickerBaseProps {
6060
startDate?: DateType;
6161
endDate?: DateType;
6262
onChange?: RangeChange;
63+
/**
64+
* When `true`, tapping any date while a complete range (both startDate and
65+
* endDate) is selected will clear the endDate and begin a fresh selection
66+
* with the tapped date as the new startDate, regardless of whether the
67+
* tapped day falls inside the range, on the start, or on the end.
68+
*/
69+
allowRangeReset?: boolean;
6370
}
6471

6572
export interface DatePickerMultipleProps extends DatePickerBaseProps {
@@ -117,6 +124,11 @@ const DateTimePicker = (
117124
use12Hours,
118125
} = props;
119126

127+
const allowRangeReset =
128+
mode === 'range'
129+
? (props as DatePickerRangeProps).allowRangeReset
130+
: undefined;
131+
120132
dayjs.tz.setDefault(timeZone);
121133
dayjs.calendar(calendar);
122134
dayjs.locale(locale);
@@ -385,6 +397,7 @@ const DateTimePicker = (
385397
prevTimezone,
386398
timeZone,
387399
calendar,
400+
onChange,
388401
]);
389402

390403
const setCalendarView = useCallback((view: CalendarViews) => {
@@ -412,6 +425,15 @@ const DateTimePicker = (
412425
let start = removeTime(stateRef.current.startDate, timeZone);
413426
let end = removeTime(stateRef.current.endDate, timeZone);
414427
const selected = removeTime(selectedDate, timeZone);
428+
429+
if (allowRangeReset && start && end) {
430+
(onChange as RangeChange)({
431+
startDate: dayjs(selected).toDate(),
432+
endDate: undefined,
433+
});
434+
return;
435+
}
436+
415437
let isStart: boolean = true;
416438
let isReset: boolean = false;
417439

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export type {
1010
CalendarMonth,
1111
CalendarYear,
1212
CalendarComponents,
13+
CalendarMonthSelectorProps,
14+
CalendarYearSelectorProps,
1315
DatePickerBaseProps,
1416
} from './types';
1517
export { useDefaultClassNames, useDefaultStyles } from './theme';

src/types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,24 @@ export type Styles = Partial<{
116116
| CalenderFlag]: ViewStyle | TextStyle | ImageStyle;
117117
}>;
118118

119+
export type CalendarMonthSelectorProps = {
120+
/** The formatted month name shown in the header (e.g. "January" or "فروردین") */
121+
text: string;
122+
/** Whether the month-picker view is currently open */
123+
isOpen: boolean;
124+
/** Toggles the month-picker view */
125+
onPress: () => void;
126+
};
127+
128+
export type CalendarYearSelectorProps = {
129+
/** The formatted year or year-range shown in the header (e.g. "2025" or "2024 - 2035") */
130+
text: string;
131+
/** Whether the year-picker view is currently open */
132+
isOpen: boolean;
133+
/** Toggles the year-picker view */
134+
onPress: () => void;
135+
};
136+
119137
export type CalendarComponents = Partial<{
120138
/** The component containing the day in the days grid */
121139
Day: (day: CalendarDay) => React.ReactNode;
@@ -129,6 +147,10 @@ export type CalendarComponents = Partial<{
129147
IconPrev: React.ReactNode;
130148
/** The next month button/year icon in the header */
131149
IconNext: React.ReactNode;
150+
/** Fully replaces the month selector button in the header */
151+
MonthSelector: (props: CalendarMonthSelectorProps) => React.ReactNode;
152+
/** Fully replaces the year selector button in the header */
153+
YearSelector: (props: CalendarYearSelectorProps) => React.ReactNode;
132154
}>;
133155

134156
export interface DatePickerBaseProps {

0 commit comments

Comments
 (0)