Skip to content

Commit ef8c54a

Browse files
committed
feat: calendar
1 parent 832833e commit ef8c54a

File tree

5 files changed

+505
-22
lines changed

5 files changed

+505
-22
lines changed

App.tsx

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,12 @@
11
import React from 'react';
22
import {SafeAreaView, ScrollView} from 'react-native';
3-
4-
import {Box, ButtonBox, ProgressBar, ProgressCircle} from './src/components';
3+
import {CalendarBox} from './src/atomic/organisms/CalendarBox';
54

65
function App(): React.JSX.Element {
76
const [value, setValue] = React.useState(0);
87
return (
9-
<SafeAreaView>
10-
<ScrollView>
11-
<Box className="bg-secondary-light mb-5">
12-
<ButtonBox title="Progress up to 100" onPress={() => setValue(100)} />
13-
</Box>
14-
<Box className="bg-secondary-light h-10 mb-5">
15-
<ButtonBox title="Progress up to 25" onPress={() => setValue(25)} />
16-
</Box>
17-
<Box className="gap-2">
18-
<ProgressBar value={value} varian="secondary" />
19-
</Box>
20-
<Box>
21-
<ProgressCircle
22-
value={value}
23-
varian="primary"
24-
showLabel
25-
label={`yeu em`}
26-
/>
27-
</Box>
28-
</ScrollView>
8+
<SafeAreaView style={{flex: 1, backgroundColor: 'white'}}>
9+
<CalendarBox />
2910
</SafeAreaView>
3011
);
3112
}

src/atomic/organisms/CalendarBox.tsx

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import React, {useEffect, useRef, useState} from 'react';
2+
import {Box, ButtonBox, TextBox} from '../atoms';
3+
import {FlatList} from 'react-native';
4+
import {getDaysOfMonth, getDaysOfMonths, MONTHS} from '../../utils/date.util';
5+
6+
interface Props {
7+
width?: number;
8+
height?: number;
9+
}
10+
interface Result {
11+
year: number;
12+
month: number;
13+
days: number[];
14+
daysBefore: number[];
15+
daysAfter: [];
16+
}
17+
18+
export const CalendarBox = ({width = 0, height}: Props) => {
19+
const refMonth = useRef<FlatList>(null);
20+
const [currentIndex, setCurrentIndex] = useState(0);
21+
const [offset, setOffset] = useState({
22+
width,
23+
height,
24+
});
25+
const [currentYear, setCurrentYear] = useState(2024);
26+
const [months, setMonths] = useState<Result[]>([]);
27+
const timerUpdate = useRef<NodeJS.Timeout>();
28+
29+
useEffect(() => {
30+
const initMonths = () => {
31+
const now = new Date();
32+
const month = now.getMonth() + 1;
33+
const year = now.getFullYear();
34+
const currentMonth = getDaysOfMonth(month, year);
35+
const nextDayMonth =
36+
month + 1 > 12
37+
? getDaysOfMonth(1, year + 1)
38+
: getDaysOfMonth(month + 1, year);
39+
const preMonth =
40+
month - 1 < 0
41+
? getDaysOfMonth(12, year - 1)
42+
: getDaysOfMonth(month - 1, year);
43+
setMonths([preMonth, currentMonth, nextDayMonth]);
44+
};
45+
initMonths();
46+
setTimeout(() => {
47+
scrollToIndex(1);
48+
});
49+
}, []);
50+
console.log(months.map(item => item.month));
51+
52+
const onNextMonth = () => {
53+
let nextIndex = currentIndex + 1;
54+
if (nextIndex > 11) {
55+
setCurrentYear(currentYear + 1);
56+
nextIndex = 1;
57+
}
58+
scrollToIndex(nextIndex);
59+
};
60+
61+
const scrollToIndex = (index: number) => {
62+
if (refMonth.current) {
63+
refMonth.current.scrollToIndex({
64+
index,
65+
});
66+
}
67+
};
68+
69+
const renderWeek = () => {
70+
return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map(item => (
71+
<TextBox className="w-1/7 text-center" key={`week-${item}`}>
72+
{item}
73+
</TextBox>
74+
));
75+
};
76+
77+
const renderDayOther = (data: number[]) => {
78+
return data.map((_, index) => (
79+
<ButtonBox
80+
disabled
81+
onPress={onNextMonth}
82+
className="w-1/7 border border-gray-400 items-center justify-center"
83+
key={`before-${index + 1}`}
84+
title={`${31 - index}`}
85+
classNameText="text-gray-400"
86+
/>
87+
));
88+
};
89+
90+
const renderDay = (days: number[]) => {
91+
return days.map((_, index) => (
92+
<ButtonBox
93+
className="w-1/7 border border-gray-400 items-center justify-center"
94+
key={`day-${index + 1}`}
95+
title={(index + 1).toString()}
96+
/>
97+
));
98+
};
99+
100+
return (
101+
<Box
102+
className="w-full"
103+
onLayout={({nativeEvent}) => {
104+
if (!offset.width) {
105+
setOffset({
106+
width: Math.round(nativeEvent.layout.width + 0.5),
107+
height: nativeEvent.layout.height,
108+
});
109+
}
110+
}}>
111+
{months.length > 0 && (
112+
<>
113+
<Box className="row self-center w-24">
114+
<TextBox className="w-24 text-center">
115+
{`Month ${months[currentIndex].month} ${months[currentIndex].year}`}
116+
</TextBox>
117+
</Box>
118+
<Box className="row-center">{renderWeek()}</Box>
119+
<FlatList
120+
ref={refMonth}
121+
horizontal
122+
pagingEnabled
123+
data={months}
124+
scrollEventThrottle={12}
125+
onEndReachedThreshold={50}
126+
onScroll={({nativeEvent}) => {
127+
timerUpdate.current && clearTimeout(timerUpdate.current);
128+
const pageIndex = Math.round(
129+
nativeEvent.contentOffset.x / offset.width,
130+
);
131+
if (pageIndex !== currentIndex) {
132+
timerUpdate.current = setTimeout(() => {
133+
setCurrentIndex(pageIndex);
134+
const newMonth = getDaysOfMonths(months, pageIndex);
135+
if (newMonth.length > 0) {
136+
setMonths(newMonth);
137+
}
138+
}, 125);
139+
}
140+
}}
141+
renderItem={({item}) => (
142+
<Box className={`w-[${offset.width}]`}>
143+
<Box className="row flex-wrap">
144+
{renderDayOther(item.daysBefore)}
145+
{renderDay(item.days)}
146+
{renderDayOther(item.daysAfter)}
147+
</Box>
148+
</Box>
149+
)}
150+
getItemLayout={(_, index) => ({
151+
length: MONTHS.length,
152+
offset: offset.width * index,
153+
index,
154+
})}
155+
keyExtractor={(item, index) => `${item.month}-${item.year}`}
156+
/>
157+
</>
158+
)}
159+
</Box>
160+
);
161+
};

0 commit comments

Comments
 (0)