Skip to content

Commit dd5c91b

Browse files
fix: year range selector based current date
1 parent b2daead commit dd5c91b

File tree

7 files changed

+149
-103
lines changed

7 files changed

+149
-103
lines changed

src/CalendarContext.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ export interface CalendarContextType extends CalendarState {
2424

2525
const CalendarContext = createContext<CalendarContextType>({
2626
calendarView: CalendarViews.day,
27-
selectedDate: Date.now(),
28-
currentDate: Date.now(),
27+
selectedDate: new Date(),
28+
currentDate: new Date(),
29+
currentYear: new Date().getFullYear(),
2930
mode: 'datetime',
3031
locale: 'en',
3132
minimumDate: null,

src/DateTimePicker.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useReducer } from 'react';
2-
import { getNow, getFormated, getDate } from './utils';
2+
import { getFormated, getDate, getDateYear } from './utils';
33
import CalendarContext from './CalendarContext';
44
import { CalendarViews, CalendarActionKind } from './enums';
55
import type {
@@ -31,7 +31,7 @@ interface PropTypes extends CalendarTheme, HeaderProps {
3131
}
3232

3333
const DateTimePicker = ({
34-
value = getNow(),
34+
value = new Date(),
3535
mode = 'datetime',
3636
locale = 'en',
3737
minimumDate = null,
@@ -97,6 +97,11 @@ const DateTimePicker = ({
9797
...prevState,
9898
currentDate: action.payload,
9999
};
100+
case CalendarActionKind.CHANGE_CURRENT_YEAR:
101+
return {
102+
...prevState,
103+
currentYear: action.payload,
104+
};
100105
case CalendarActionKind.CHANGE_SELECTED_DATE:
101106
return {
102107
...prevState,
@@ -106,8 +111,9 @@ const DateTimePicker = ({
106111
},
107112
{
108113
calendarView: mode === 'time' ? CalendarViews.time : CalendarViews.day,
109-
selectedDate: value ? getFormated(value) : getNow(),
110-
currentDate: value ? getFormated(value) : getNow(),
114+
selectedDate: value ? getFormated(value) : new Date(),
115+
currentDate: value ? getFormated(value) : new Date(),
116+
currentYear: value ? getDateYear(value) : new Date().getFullYear(),
111117
}
112118
);
113119

@@ -120,6 +126,10 @@ const DateTimePicker = ({
120126
type: CalendarActionKind.CHANGE_CURRENT_DATE,
121127
payload: value,
122128
});
129+
dispatch({
130+
type: CalendarActionKind.CHANGE_CURRENT_YEAR,
131+
payload: getDateYear(value),
132+
});
123133
}, [value]);
124134

125135
useEffect(() => {
@@ -173,10 +183,9 @@ const DateTimePicker = ({
173183
});
174184
},
175185
onChangeYear: (year: number) => {
176-
const newDate = getDate(state.currentDate).add(year, 'year');
177186
dispatch({
178-
type: CalendarActionKind.CHANGE_CURRENT_DATE,
179-
payload: getFormated(newDate),
187+
type: CalendarActionKind.CHANGE_CURRENT_YEAR,
188+
payload: year,
180189
});
181190
},
182191
};

src/components/Header.tsx

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React from 'react';
1+
import React, { useCallback } from 'react';
22
import { View, Text, Pressable, StyleSheet, Image } from 'react-native';
33
import { useCalendarContext } from '../CalendarContext';
44
import dayjs from 'dayjs';
55
import { CalendarViews } from '../enums';
66
import type { HeaderProps } from '../types';
7+
import { getDateYear, getYearRange, YEAR_PAGE_SIZE } from '../utils';
78

89
const arrow_left = require('../assets/images/arrow_left.png');
910
const arrow_right = require('../assets/images/arrow_right.png');
@@ -12,6 +13,7 @@ const Header = ({ buttonPrevIcon, buttonNextIcon }: HeaderProps) => {
1213
const {
1314
currentDate,
1415
selectedDate,
16+
currentYear,
1517
onChangeMonth,
1618
onChangeYear,
1719
calendarView,
@@ -28,8 +30,9 @@ const Header = ({ buttonPrevIcon, buttonNextIcon }: HeaderProps) => {
2830
calendarView === CalendarViews.day
2931
? onChangeMonth(-1)
3032
: calendarView === CalendarViews.month
31-
? onChangeYear(-1)
32-
: calendarView === CalendarViews.year && onChangeYear(-12)
33+
? onChangeYear(currentYear - 1)
34+
: calendarView === CalendarViews.year &&
35+
onChangeYear(currentYear - YEAR_PAGE_SIZE)
3336
}
3437
testID="btn-prev"
3538
accessibilityRole="button"
@@ -58,8 +61,9 @@ const Header = ({ buttonPrevIcon, buttonNextIcon }: HeaderProps) => {
5861
calendarView === CalendarViews.day
5962
? onChangeMonth(1)
6063
: calendarView === CalendarViews.month
61-
? onChangeYear(1)
62-
: calendarView === CalendarViews.year && onChangeYear(12)
64+
? onChangeYear(currentYear + 1)
65+
: calendarView === CalendarViews.year &&
66+
onChangeYear(currentYear + YEAR_PAGE_SIZE)
6367
}
6468
testID="btn-next"
6569
accessibilityRole="button"
@@ -81,48 +85,66 @@ const Header = ({ buttonPrevIcon, buttonNextIcon }: HeaderProps) => {
8185
</Pressable>
8286
);
8387

88+
const yearSelector = useCallback(() => {
89+
const years = getYearRange(currentYear);
90+
return (
91+
<Pressable
92+
onPress={() => {
93+
setCalendarView(
94+
calendarView === CalendarViews.year
95+
? CalendarViews.day
96+
: CalendarViews.year
97+
);
98+
onChangeYear(getDateYear(currentDate));
99+
}}
100+
testID="btn-year"
101+
accessibilityRole="button"
102+
>
103+
<View style={[styles.textContainer, theme?.headerTextContainerStyle]}>
104+
<Text style={[styles.text, theme?.headerTextStyle]}>
105+
{calendarView === CalendarViews.year
106+
? `${years.at(0)} - ${years.at(-1)}`
107+
: dayjs(currentDate).format('YYYY')}
108+
</Text>
109+
</View>
110+
</Pressable>
111+
);
112+
}, [
113+
calendarView,
114+
currentDate,
115+
currentYear,
116+
setCalendarView,
117+
onChangeYear,
118+
theme,
119+
]);
120+
121+
const monthSelector = (
122+
<Pressable
123+
onPress={() =>
124+
setCalendarView(
125+
calendarView === CalendarViews.month
126+
? CalendarViews.day
127+
: CalendarViews.month
128+
)
129+
}
130+
testID="btn-month"
131+
accessibilityRole="button"
132+
>
133+
<View style={[styles.textContainer, theme?.headerTextContainerStyle]}>
134+
<Text style={[styles.text, theme?.headerTextStyle]}>
135+
{dayjs(currentDate).locale(locale).format('MMMM')}
136+
</Text>
137+
</View>
138+
</Pressable>
139+
);
140+
84141
const renderSelectors = (
85142
<>
86143
<View style={styles.selectorContainer}>
87-
<Pressable
88-
onPress={() =>
89-
setCalendarView(
90-
calendarView === CalendarViews.month
91-
? CalendarViews.day
92-
: CalendarViews.month
93-
)
94-
}
95-
testID="btn-month"
96-
accessibilityRole="button"
97-
>
98-
<View style={[styles.textContainer, theme?.headerTextContainerStyle]}>
99-
<Text style={[styles.text, theme?.headerTextStyle]}>
100-
{dayjs(currentDate).locale(locale).format('MMMM')}
101-
</Text>
102-
</View>
103-
</Pressable>
104-
105-
<Pressable
106-
onPress={() =>
107-
setCalendarView(
108-
calendarView === CalendarViews.year
109-
? CalendarViews.day
110-
: CalendarViews.year
111-
)
112-
}
113-
testID="btn-year"
114-
accessibilityRole="button"
115-
>
116-
<View style={[styles.textContainer, theme?.headerTextContainerStyle]}>
117-
<Text style={[styles.text, theme?.headerTextStyle]}>
118-
{calendarView === CalendarViews.year
119-
? dayjs(selectedDate).format('YYYY')
120-
: dayjs(currentDate).format('YYYY')}
121-
</Text>
122-
</View>
123-
</Pressable>
144+
{calendarView !== CalendarViews.year ? monthSelector : null}
145+
{yearSelector()}
124146
</View>
125-
{mode === 'datetime' ? (
147+
{mode === 'datetime' && calendarView !== CalendarViews.year ? (
126148
<Pressable
127149
onPress={() =>
128150
setCalendarView(

src/components/YearSelector.tsx

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,69 @@
11
import React, { useCallback } from 'react';
2-
import { Text, View, Pressable, StyleSheet } from 'react-native';
2+
import {
3+
Text,
4+
View,
5+
Pressable,
6+
StyleSheet,
7+
TextStyle,
8+
ViewStyle,
9+
} from 'react-native';
310
import { useCalendarContext } from '../CalendarContext';
4-
import { getDateYear } from '../utils';
11+
import { getDateYear, getYearRange } from '../utils';
512

613
const YearSelector = () => {
7-
const { currentDate, selectedDate, onSelectYear, theme } =
14+
const { currentDate, currentYear, selectedDate, onSelectYear, theme } =
815
useCalendarContext();
9-
const currentYear = getDateYear(currentDate);
1016
const selectedYear = getDateYear(selectedDate);
11-
const colArray = [1, 2, 3, 4];
1217

13-
let year = 12 * Math.ceil(currentYear / 12) - 12;
14-
if (year < 0) year = 0;
15-
16-
const generateColumns = useCallback(() => {
17-
const rowArray = [1, 2, 3];
18-
const column = rowArray.map(() => {
19-
const cellYear = year++;
20-
const activeItemStyle =
21-
cellYear === selectedYear
18+
const generateCells = useCallback(() => {
19+
const years = getYearRange(currentYear);
20+
const activeYear = getDateYear(currentDate);
21+
const column = years.map((year) => {
22+
const activeItemStyle: ViewStyle =
23+
year === selectedYear
2224
? {
2325
borderColor: theme?.selectedItemColor || '#0047FF',
2426
backgroundColor: theme?.selectedItemColor || '#0047FF',
2527
}
26-
: null;
28+
: year === activeYear
29+
? {
30+
borderColor: theme?.selectedItemColor || '#0047FF',
31+
}
32+
: {};
2733

28-
const textStyle =
29-
cellYear === selectedYear
34+
const textStyle: TextStyle =
35+
year === selectedYear
3036
? { color: '#fff', ...theme?.selectedTextStyle }
31-
: theme?.calendarTextStyle;
37+
: year === activeYear
38+
? {
39+
color: theme?.selectedItemColor || '#0047FF',
40+
fontWeight: 'bold',
41+
}
42+
: { ...theme?.calendarTextStyle };
3243

3344
return (
3445
<Pressable
35-
key={cellYear}
36-
onPress={() => onSelectYear(cellYear)}
46+
key={year}
47+
onPress={() => onSelectYear(year)}
3748
style={styles.yearCell}
3849
accessibilityRole="button"
3950
>
4051
<View
4152
style={[styles.year, theme?.yearContainerStyle, activeItemStyle]}
4253
>
43-
<Text key={cellYear} style={textStyle}>
44-
{cellYear}
54+
<Text key={year} style={textStyle}>
55+
{year}
4556
</Text>
4657
</View>
4758
</Pressable>
4859
);
4960
});
5061
return column;
51-
}, [onSelectYear, selectedYear, year, theme]);
62+
}, [onSelectYear, selectedYear, currentYear, currentDate, theme]);
5263

5364
return (
5465
<View style={styles.container} testID="year-selector">
55-
{colArray.map((index) => (
56-
<View key={index} style={styles.yearsRow}>
57-
{generateColumns()}
58-
</View>
59-
))}
66+
<View style={styles.years}>{generateCells()}</View>
6067
</View>
6168
);
6269
};
@@ -72,8 +79,9 @@ const styles = StyleSheet.create({
7279
yearCell: {
7380
width: '33.3%',
7481
},
75-
yearsRow: {
82+
years: {
7683
flexDirection: 'row',
84+
flexWrap: 'wrap',
7785
width: '100%',
7886
},
7987
year: {

src/enums.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export enum CalendarViews {
88
export enum CalendarActionKind {
99
SET_CALENDAR_VIEW = 'SET_CALENDAR_VIEW',
1010
CHANGE_CURRENT_DATE = 'CHANGE_CURRENT_DATE',
11+
CHANGE_CURRENT_YEAR = 'CHANGE_CURRENT_YEAR',
1112
CHANGE_SELECTED_DATE = 'CHANGE_SELECTED_DATE',
1213
}
1314

src/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export type HeaderButtonPositions = 'around' | 'right' | 'left';
1212
export type CalendarState = {
1313
calendarView: CalendarViews;
1414
selectedDate: DateType;
15-
currentDate: DateType;
15+
currentDate: DateType; // used for latest state of calendar based on Month and Year
16+
currentYear: number; // used for pagination in YearSelector
1617
};
1718

1819
export type CalendarAction = {

0 commit comments

Comments
 (0)