Skip to content

Commit 4d57bf8

Browse files
committed
tweaks
1 parent 35e5996 commit 4d57bf8

File tree

8 files changed

+236
-139
lines changed

8 files changed

+236
-139
lines changed

src/common/utils/colorUtils.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { getLuminance } from "polished";
2+
3+
export function isLight(color: string): boolean {
4+
return getLuminance(color) >= 0.5;
5+
}

src/common/utils/dateUtils.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
export function getMonthName(
2+
monthIndex: number,
3+
locale = "en-US",
4+
format: "long" | "short" = "long",
5+
) {
6+
return new Intl.DateTimeFormat(locale, { month: format }).format(
7+
new Date(2021, monthIndex, 1),
8+
);
9+
}
10+
11+
export function getDayName(
12+
dayIndex: number,
13+
locale = "en-US",
14+
format: "long" | "short" = "short",
15+
) {
16+
return new Intl.DateTimeFormat(locale, { weekday: format }).format(
17+
new Date(2021, 0, dayIndex + 3),
18+
);
19+
}
20+
21+
export function isToday(
22+
year: number,
23+
month: number,
24+
day: number,
25+
today: Date = new Date(),
26+
) {
27+
return (
28+
year === today.getFullYear() &&
29+
month === today.getMonth() &&
30+
day === today.getDate()
31+
);
32+
}

src/programs/Calendar/Calendar.tsx

Lines changed: 24 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,30 @@
1-
import React, {
2-
forwardRef,
3-
useEffect,
4-
useImperativeHandle,
5-
useRef,
6-
useState,
7-
} from "react";
1+
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
82
import useToggle from "../../hooks/useToggle";
93
import {
104
StyledCalendarDay,
115
StyledCalendarDays,
126
StyledCalendarDaysFrame,
137
StyledCalendarLayout,
14-
StyledCalendarNavigation,
15-
StyledCalendarNavigationSection,
16-
StyledNavigationButton,
178
} from "./styles";
189
import useSystemSettings from "../../stores/systemSettingsStore";
1910
import { BorderedAppContentHandles } from "../../components/BorderedApp/BorderedApp";
11+
import CalendarNavigation from "./CalendarNavigation/CalendarNavigation";
12+
import { useCalendar } from "./hooks/useCalendar";
2013

2114
type CalendarHandles = BorderedAppContentHandles<HTMLDivElement>;
15+
2216
interface CalendarProps {}
2317

2418
const Calendar = forwardRef<CalendarHandles, CalendarProps>((_props, ref) => {
2519
const sidebarToggle = useToggle();
2620
const calendarRef = useRef<HTMLDivElement>(null);
21+
const calendar = useCalendar();
2722
const [mainColor, accentColor, fontColor] = useSystemSettings((s) => [
2823
s.mainColor,
2924
s.accentColor,
3025
s.fontColor,
3126
]);
3227

33-
const now = new Date();
34-
const [year, setYear] = useState(now.getFullYear());
35-
const [month, setMonth] = useState(now.getMonth());
36-
const prevYear = () => setYear((x) => x - 1);
37-
const nextYear = () => setYear((x) => x + 1);
38-
const wrapMonth = (m: number) => ((m % 12) + 12) % 12;
39-
const nextMonth = () => setMonth((x) => wrapMonth(x + 1));
40-
const prevMonth = () => setMonth((x) => wrapMonth(x - 1));
41-
4228
useImperativeHandle(ref, () => ({
4329
onParentKeyDown() {},
4430
element: calendarRef.current,
@@ -56,7 +42,6 @@ const Calendar = forwardRef<CalendarHandles, CalendarProps>((_props, ref) => {
5642
const entry = entries[0];
5743
if (!entry) return;
5844

59-
// Most reliable in modern browsers
6045
const width = entry.contentRect.width;
6146

6247
const wide = width >= 600;
@@ -73,105 +58,36 @@ const Calendar = forwardRef<CalendarHandles, CalendarProps>((_props, ref) => {
7358
};
7459
});
7560

76-
const dayName = (
77-
dayIndex: number,
78-
locale = "en-US",
79-
format: "long" | "short" = "short",
80-
) =>
81-
new Intl.DateTimeFormat(locale, { weekday: format }).format(
82-
new Date(2021, 0, dayIndex + 3),
83-
);
84-
const monthName = (
85-
monthIndex: number,
86-
locale = "en-US",
87-
format: "long" | "short" = "long",
88-
) =>
89-
new Intl.DateTimeFormat(locale, { month: format }).format(
90-
new Date(2021, monthIndex, 1),
91-
);
92-
93-
const buildGrid = () => {
94-
const daysInMonth = new Date(year, month + 1, 0).getDate();
95-
const firstDayOfMonth = new Date(year, month, 0).getDay(); // 0 - 6
96-
const calendarCellCount = 7 * 6; // 7 cols (1 per day), 6 rows
97-
98-
const elements: React.ReactNode[] = [];
99-
100-
for (let i = 0; i < calendarCellCount; i++) {
101-
const dayNumber = i - firstDayOfMonth + 1;
102-
const cellDayName = dayName(i + 1);
103-
104-
if (dayNumber < 1 || dayNumber > daysInMonth) {
105-
// Empty cell
106-
elements.push(
107-
<StyledCalendarDay
108-
backgroundColor={mainColor}
109-
color={fontColor}
110-
key={i}
111-
currentMonth={false}
112-
>
113-
{cellDayName}
114-
</StyledCalendarDay>,
115-
);
116-
} else {
117-
// Valid day
118-
elements.push(
119-
<StyledCalendarDay
120-
backgroundColor={mainColor}
121-
color={fontColor}
122-
key={i}
123-
currentMonth
124-
>
125-
<span>{cellDayName}</span>
126-
<span>{dayNumber}</span>
127-
</StyledCalendarDay>,
128-
);
129-
}
130-
}
131-
return elements;
132-
};
133-
13461
return (
13562
<StyledCalendarLayout
13663
ref={calendarRef}
13764
sidebarOpen={sidebarToggle.state}
13865
className="calendar"
13966
>
140-
<StyledCalendarNavigation className="calendar__nav">
141-
<StyledCalendarNavigationSection>
142-
<StyledNavigationButton
143-
className="calendar__nav-button"
144-
onClick={prevMonth}
145-
>{`<`}</StyledNavigationButton>
146-
<StyledNavigationButton className="calendar__nav-button">
147-
{monthName(month)}
148-
</StyledNavigationButton>
149-
<StyledNavigationButton
150-
className="calendar__nav-button"
151-
onClick={nextMonth}
152-
>{`>`}</StyledNavigationButton>
153-
</StyledCalendarNavigationSection>
154-
155-
<StyledCalendarNavigationSection>
156-
<StyledNavigationButton
157-
className="calendar__nav-button"
158-
onClick={prevYear}
159-
>{`<`}</StyledNavigationButton>
160-
<StyledNavigationButton className="calendar__nav-button">
161-
{year}
162-
</StyledNavigationButton>
163-
<StyledNavigationButton
164-
className="calendar__nav-button"
165-
onClick={nextYear}
166-
>{`>`}</StyledNavigationButton>
167-
</StyledCalendarNavigationSection>
168-
</StyledCalendarNavigation>
67+
<CalendarNavigation
68+
month={calendar.month}
69+
year={calendar.year}
70+
onClickNextMonth={calendar.nextMonth}
71+
onClickNextYear={calendar.nextYear}
72+
onClickPrevMonth={calendar.prevMonth}
73+
onClickPrevYear={calendar.prevYear}
74+
/>
16975
<StyledCalendarDaysFrame frameColor={accentColor}>
17076
<StyledCalendarDays
17177
borderColor={accentColor}
17278
className="calendar__days"
17379
>
174-
{buildGrid()}
80+
{calendar.days.map(({ date, day, isToday, isThisMonth }) => (
81+
<StyledCalendarDay
82+
backgroundColor={mainColor}
83+
color={fontColor}
84+
currentMonth={isThisMonth}
85+
>
86+
<span>{day}</span>
87+
<span>{date}</span>
88+
{isToday && <span>TODAY</span>}
89+
</StyledCalendarDay>
90+
))}
17591
</StyledCalendarDays>
17692
</StyledCalendarDaysFrame>
17793
</StyledCalendarLayout>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { getMonthName } from "../../../common/utils/dateUtils";
2+
import {
3+
StyledCalendarNavigation,
4+
StyledCalendarNavigationSection,
5+
StyledNavigationButton,
6+
} from "./styles";
7+
8+
interface StyledNavigationProps {
9+
month: number;
10+
year: number;
11+
onClickPrevMonth(): void;
12+
onClickNextMonth(): void;
13+
onClickPrevYear(): void;
14+
onClickNextYear(): void;
15+
}
16+
export default function CalendarNavigation({
17+
month,
18+
year,
19+
onClickNextMonth,
20+
onClickNextYear,
21+
onClickPrevMonth,
22+
onClickPrevYear,
23+
}: StyledNavigationProps) {
24+
return (
25+
<StyledCalendarNavigation className="calendar__nav">
26+
<StyledCalendarNavigationSection className="calendar__nav-section">
27+
<StyledNavigationButton
28+
className="calendar__nav-button"
29+
onClick={onClickPrevMonth}
30+
>{`<`}</StyledNavigationButton>
31+
<StyledNavigationButton className="calendar__nav-button">
32+
{getMonthName(month)}
33+
</StyledNavigationButton>
34+
<StyledNavigationButton
35+
className="calendar__nav-button"
36+
onClick={onClickNextMonth}
37+
>{`>`}</StyledNavigationButton>
38+
</StyledCalendarNavigationSection>
39+
40+
<StyledCalendarNavigationSection className="calendar__nav-section">
41+
<StyledNavigationButton
42+
className="calendar__nav-button"
43+
onClick={onClickPrevYear}
44+
>{`<`}</StyledNavigationButton>
45+
<StyledNavigationButton className="calendar__nav-button">
46+
{year}
47+
</StyledNavigationButton>
48+
<StyledNavigationButton
49+
className="calendar__nav-button"
50+
onClick={onClickNextYear}
51+
>{`>`}</StyledNavigationButton>
52+
</StyledCalendarNavigationSection>
53+
</StyledCalendarNavigation>
54+
);
55+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import styled from "@emotion/styled";
2+
3+
interface StyledCalendarNavigationSectionProps {}
4+
5+
interface StyledCalendarNavigationProps {}
6+
export const StyledCalendarNavigation = styled.div<StyledCalendarNavigationProps>`
7+
width: 100%;
8+
display: flex;
9+
`;
10+
11+
interface StyledNavigationButtonProps {}
12+
export const StyledNavigationButton = styled.button<StyledNavigationButtonProps>``;
13+
14+
export const StyledCalendarNavigationSection = styled.div<StyledCalendarNavigationSectionProps>`
15+
flex: 1;
16+
display: flex;
17+
justify-content: center;
18+
`;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import { getDayName, isToday } from "../../../common/utils/dateUtils";
3+
4+
interface CalendarDay {
5+
date: number;
6+
day: string;
7+
isToday: boolean;
8+
isThisMonth: boolean;
9+
}
10+
export function useCalendar() {
11+
const now = new Date();
12+
const [year, setYear] = useState(now.getFullYear());
13+
const [month, setMonth] = useState(now.getMonth());
14+
const prevYear = () => setYear((x) => x - 1);
15+
const nextYear = () => setYear((x) => x + 1);
16+
const wrapMonth = (m: number) => ((m % 12) + 12) % 12;
17+
const nextMonth = () => setMonth((x) => wrapMonth(x + 1));
18+
const prevMonth = () => setMonth((x) => wrapMonth(x - 1));
19+
20+
const getCalendarDays = useCallback(() => {
21+
const daysInMonth = new Date(year, month + 1, 0).getDate();
22+
const firstDayOfMonth = new Date(year, month, 0).getDay(); // 0 - 6
23+
const calendarCellCount = 7 * 6; // 7 cols (1 per day), 6 rows
24+
const date = new Date();
25+
26+
const days: CalendarDay[] = [];
27+
28+
for (let i = 0; i < calendarCellCount; i++) {
29+
let dayNumber = i - firstDayOfMonth + 1;
30+
const cellDayName = getDayName(i + 1);
31+
const today = isToday(year, month, dayNumber, date);
32+
33+
if (dayNumber < 1 || dayNumber > daysInMonth) {
34+
dayNumber = new Date(year, month, dayNumber).getDate();
35+
// Empty cell
36+
days.push({
37+
date: dayNumber,
38+
day: cellDayName,
39+
isToday: today,
40+
isThisMonth: false,
41+
});
42+
} else {
43+
// Valid day
44+
days.push({
45+
date: dayNumber,
46+
day: cellDayName,
47+
isToday: today,
48+
isThisMonth: true,
49+
});
50+
}
51+
}
52+
return days;
53+
}, [month, year]);
54+
55+
const [days, setDays] = useState<Array<CalendarDay>>(getCalendarDays);
56+
57+
useEffect(() => setDays(getCalendarDays), [year, month, getCalendarDays]);
58+
59+
return {
60+
year,
61+
month,
62+
prevMonth,
63+
nextMonth,
64+
prevYear,
65+
nextYear,
66+
days,
67+
};
68+
}

0 commit comments

Comments
 (0)