Skip to content

Commit a8896c1

Browse files
committed
WIP calendar
1 parent bf68f3a commit a8896c1

15 files changed

Lines changed: 333 additions & 18 deletions

File tree

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
25

package-lock.json

Lines changed: 13 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@types/uuid": "^9.0.4",
1717
"base64-js": "^1.5.1",
1818
"pako": "^2.1.0",
19+
"polished": "^4.3.1",
1920
"react": "^18.2.0",
2021
"react-dom": "^18.2.0",
2122
"sass": "^1.67.0",

src/components/BorderedApp/BorderedApp.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
StyledWindowButtonsWrapper,
2121
StyledWindowMenus,
2222
StyledWindowMenusWrapper,
23+
StyledContentInner,
2324
} from "./styles";
2425

2526
interface BorderedAppProps extends BaseProps {
@@ -126,7 +127,11 @@ function BorderedApp({
126127
</StyledWindowButtons>
127128
</StyledWindowButtonsWrapper>
128129
</StyledTitleBar>
129-
<StyledContent>{children}</StyledContent>
130+
<StyledContent className="bordered-app__content">
131+
<StyledContentInner className="bordered-app__content-inner">
132+
{children}
133+
</StyledContentInner>
134+
</StyledContent>
130135
<StyledCorner location="sw" ref={resizeHandleSW} />
131136
<StyledEdge location="s" ref={resizeHandleS} />
132137
<StyledCorner location="se" ref={resizeHandleSE} />

src/components/BorderedApp/styles.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ export const StyledBorderedApp = styled.div<StyledBorderedAppProps>`
1919
"title-bar"
2020
"content";
2121
border-radius: 10px;
22-
box-shadow: 0px -2px 10px 1px rgb(0, 0, 0, 0.5);
22+
box-shadow:
23+
0px -2px 10px 1px rgb(0, 0, 0, 0.4),
24+
0 -0.5px 1px #8d8d8d inset;
2325
position: fixed;
2426
width: ${(props) => props.initialDimensions.width}px;
2527
height: ${(props) => props.initialDimensions.height}px;
@@ -141,10 +143,20 @@ export const StyledWindowButton = styled.button<{
141143

142144
export const StyledContent = styled.div`
143145
grid-area: content;
144-
padding: 5px;
145-
padding-top: 0;
146146
box-sizing: border-box;
147147
width: 100%;
148148
height: 100%;
149149
min-height: 0;
150+
padding: 5px;
151+
padding-top: 0;
152+
`;
153+
154+
export const StyledContentInner = styled.div`
155+
width: 100%;
156+
height: 100%;
157+
box-shadow: 0px 0px 4px rgb(0, 0, 0, 0.5) inset;
158+
overflow: hidden;
159+
border-radius: 10px;
160+
box-sizing: border-box;
161+
padding: 5px;
150162
`;

src/components/BottomBar/BottomBar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import CalculatorLauncher from "../../programs/Calculator/CalculatorLauncher";
2+
import CalendarLauncher from "../../programs/Calendar/CalendarLauncher/CalendarLauncher";
23
import FileBrowserLauncher from "../../programs/FileBrowser/FileBrowserLauncher";
34
import SettingsLauncher from "../../programs/Settings/SettingsLauncher";
45
import TextEditorLauncher from "../../programs/TextEditor/TextEditorLauncher";
@@ -21,6 +22,7 @@ function BottomBar({}: BottomBarProps) {
2122
<WebBrowserLauncher />
2223
<FileBrowserLauncher />
2324
<SettingsLauncher />
25+
<CalendarLauncher />
2426
</StyledContents>
2527
</StyledBottomBar>
2628
</StyledContainer>

src/hooks/useToggle.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useEffect, useState } from "react";
2+
3+
interface UseToggleParams {
4+
initialValue?: boolean;
5+
onChanged?(value: boolean): void;
6+
}
7+
8+
function useToggle({ initialValue = false, onChanged }: UseToggleParams = {}) {
9+
const [state, setState] = useState(initialValue);
10+
11+
function toggle() {
12+
setState((prev) => !prev);
13+
}
14+
15+
useEffect(() => {
16+
onChanged?.(state);
17+
}, [onChanged, state]);
18+
19+
return {
20+
state,
21+
setState,
22+
toggle,
23+
};
24+
}
25+
26+
export default useToggle;

src/programs/Calculator/styles.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export const StyledCalc = styled.div`
99
"input"
1010
"output"
1111
"buttons";
12-
box-shadow: 0px 0px 4px rgb(0, 0, 0, 0.5) inset;
1312
padding: 10px;
1413
border-radius: 10px;
1514
box-sizing: border-box;

src/programs/Calendar/Calendar.tsx

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import React, { useEffect, useRef, useState } from "react";
2+
import useToggle from "../../hooks/useToggle";
3+
import {
4+
StyledCalendarDay,
5+
StyledCalendarDays,
6+
StyledCalendarDaysFrame,
7+
StyledCalendarLayout,
8+
StyledCalendarNavigation,
9+
StyledCalendarNavigationSection,
10+
StyledNavigationButton,
11+
} from "./styles";
12+
import useSystemSettings from "../../stores/systemSettingsStore";
13+
14+
interface CalendarProps {}
15+
16+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
17+
function Calendar(_props: CalendarProps) {
18+
const sidebarToggle = useToggle();
19+
const calendarRef = useRef<HTMLDivElement>(null);
20+
const [mainColor, accentColor, fontColor] = useSystemSettings((s) => [
21+
s.mainColor,
22+
s.accentColor,
23+
s.fontColor,
24+
]);
25+
26+
const now = new Date();
27+
const [year, setYear] = useState(now.getFullYear());
28+
const [month, setMonth] = useState(now.getMonth());
29+
const prevYear = () => setYear((x) => x - 1);
30+
const nextYear = () => setYear((x) => x + 1);
31+
const wrapMonth = (m: number) => ((m % 12) + 12) % 12;
32+
const nextMonth = () => setMonth((x) => wrapMonth(x + 1));
33+
const prevMonth = () => setMonth((x) => wrapMonth(x - 1));
34+
35+
useEffect(() => {
36+
console.log("sidebar toggle", sidebarToggle.state);
37+
}, [sidebarToggle.state]);
38+
39+
useEffect(() => {
40+
if (!calendarRef.current) return;
41+
const el = calendarRef.current;
42+
43+
function onResize(entries: ResizeObserverEntry[]) {
44+
const entry = entries[0];
45+
if (!entry) return;
46+
47+
// Most reliable in modern browsers
48+
const width = entry.contentRect.width;
49+
50+
const wide = width >= 600;
51+
sidebarToggle.setState(wide);
52+
}
53+
54+
const observer = new ResizeObserver(onResize);
55+
56+
observer.observe(el);
57+
58+
return () => {
59+
if (el) observer.unobserve(el);
60+
observer.disconnect();
61+
};
62+
});
63+
64+
const dayName = (
65+
dayIndex: number,
66+
locale = "en-US",
67+
format: "long" | "short" = "short",
68+
) =>
69+
new Intl.DateTimeFormat(locale, { weekday: format }).format(
70+
new Date(2021, 0, dayIndex + 3),
71+
);
72+
const monthName = (
73+
monthIndex: number,
74+
locale = "en-US",
75+
format: "long" | "short" = "long",
76+
) =>
77+
new Intl.DateTimeFormat(locale, { month: format }).format(
78+
new Date(2021, monthIndex, 1),
79+
);
80+
81+
const buildGrid = () => {
82+
const daysInMonth = new Date(year, month + 1, 0).getDate();
83+
const firstDayOfMonth = new Date(year, month, 0).getDay(); // 0 - 6
84+
const calendarCellCount = 7 * 6; // 7 cols (1 per day), 6 rows
85+
86+
const elements: React.ReactNode[] = [];
87+
88+
for (let i = 0; i < calendarCellCount; i++) {
89+
const dayNumber = i - firstDayOfMonth + 1;
90+
const cellDayName = dayName(i + 1);
91+
92+
if (dayNumber < 1 || dayNumber > daysInMonth) {
93+
// Empty cell
94+
elements.push(
95+
<StyledCalendarDay
96+
backgroundColor={mainColor}
97+
color={fontColor}
98+
key={i}
99+
currentMonth={false}
100+
>
101+
{cellDayName}
102+
</StyledCalendarDay>,
103+
);
104+
} else {
105+
// Valid day
106+
elements.push(
107+
<StyledCalendarDay
108+
backgroundColor={mainColor}
109+
color={fontColor}
110+
key={i}
111+
currentMonth
112+
>
113+
<span>{cellDayName}</span>
114+
<span>{dayNumber}</span>
115+
</StyledCalendarDay>,
116+
);
117+
}
118+
}
119+
return elements;
120+
};
121+
122+
return (
123+
<StyledCalendarLayout
124+
ref={calendarRef}
125+
sidebarOpen={sidebarToggle.state}
126+
className="calendar"
127+
>
128+
<StyledCalendarNavigation className="calendar__nav">
129+
<StyledCalendarNavigationSection>
130+
<StyledNavigationButton
131+
className="calendar__nav-button"
132+
onClick={prevMonth}
133+
>{`<`}</StyledNavigationButton>
134+
<StyledNavigationButton className="calendar__nav-button">
135+
{monthName(month)}
136+
</StyledNavigationButton>
137+
<StyledNavigationButton
138+
className="calendar__nav-button"
139+
onClick={nextMonth}
140+
>{`>`}</StyledNavigationButton>
141+
</StyledCalendarNavigationSection>
142+
143+
<StyledCalendarNavigationSection>
144+
<StyledNavigationButton
145+
className="calendar__nav-button"
146+
onClick={prevYear}
147+
>{`<`}</StyledNavigationButton>
148+
<StyledNavigationButton className="calendar__nav-button">
149+
{year}
150+
</StyledNavigationButton>
151+
<StyledNavigationButton
152+
className="calendar__nav-button"
153+
onClick={nextYear}
154+
>{`>`}</StyledNavigationButton>
155+
</StyledCalendarNavigationSection>
156+
</StyledCalendarNavigation>
157+
<StyledCalendarDaysFrame frameColor={accentColor}>
158+
<StyledCalendarDays
159+
borderColor={accentColor}
160+
className="calendar__days"
161+
>
162+
{buildGrid()}
163+
</StyledCalendarDays>
164+
</StyledCalendarDaysFrame>
165+
</StyledCalendarLayout>
166+
);
167+
}
168+
169+
export default Calendar;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Launcher from "../../../components/BottomBar/Launcher";
2+
import Calendar from "../Calendar";
3+
4+
interface CalendarLauncherProps {}
5+
6+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
7+
function CalendarLauncher(_props: CalendarLauncherProps) {
8+
return (
9+
<Launcher
10+
windowType={"calendar"}
11+
WindowTitle="Calendar"
12+
initialDimensions={{ height: 500, width: 500 }}
13+
menus={[]}
14+
appContent={<Calendar />}
15+
icon=""
16+
/>
17+
);
18+
}
19+
20+
export default CalendarLauncher;

0 commit comments

Comments
 (0)