Date align #23

Merged
LucidKobold merged 29 commits from date-align into main 2021-12-29 20:09:30 -05:00
18 changed files with 419 additions and 236 deletions

View File

@@ -1,4 +1,16 @@
{ {
"rules": {
"comma-dangle": [
"error",
{
"arrays": "never",
"objects": "never",
"imports": "never",
"exports": "never",
"functions": "never"
}
]
},
"extends": [ "extends": [
"next", "next",
"next/core-web-vitals", "next/core-web-vitals",

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"trailingComma": "none",
"tabWidth": 2,
"bracketSameLine": false
}

View File

@@ -3,8 +3,8 @@ import { useRouter } from "next/router";
import { HStack, IconButton } from "@chakra-ui/react"; import { HStack, IconButton } from "@chakra-ui/react";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
import { sub, add, format } from "date-fns"; import { sub, add, format } from "date-fns";
import { CalenderContext } from "../../contexts/CalenderContext";
import DatePicker from "./DatePicker"; import DatePicker from "./DatePicker";
import { CalenderContext } from "../../contexts/CalenderContext";
const CalenderNav = (): JSX.Element => { const CalenderNav = (): JSX.Element => {
const { selectedDate } = useContext(CalenderContext); const { selectedDate } = useContext(CalenderContext);
@@ -14,7 +14,7 @@ const CalenderNav = (): JSX.Element => {
const handleNavButtons = (direction: "next" | "prev") => { const handleNavButtons = (direction: "next" | "prev") => {
if (direction === "next") { if (direction === "next") {
const newMonth = add(selectedDate, { const newMonth = add(selectedDate, {
months: 1, months: 1
}); });
const year = format(newMonth, "y"); const year = format(newMonth, "y");
@@ -23,7 +23,7 @@ const CalenderNav = (): JSX.Element => {
router.push(`/calendar/${year}/${month}`); router.push(`/calendar/${year}/${month}`);
} else if (direction === "prev") { } else if (direction === "prev") {
const newMonth = sub(selectedDate, { const newMonth = sub(selectedDate, {
months: 1, months: 1
}); });
const year = format(newMonth, "y"); const year = format(newMonth, "y");

View File

@@ -14,7 +14,7 @@ import {
PopoverContent, PopoverContent,
PopoverHeader, PopoverHeader,
PopoverTrigger, PopoverTrigger,
VStack, VStack
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { import {
Formik, Formik,
@@ -22,11 +22,10 @@ import {
FormikProps, FormikProps,
Form, Form,
Field, Field,
FieldProps, FieldProps
} from "formik"; } from "formik";
import { format } from "date-fns";
import { CalenderContext } from "../../contexts/CalenderContext";
import FormValidateEmoji from "./FormValidateEmoji"; import FormValidateEmoji from "./FormValidateEmoji";
import { CalenderContext } from "../../contexts/CalenderContext";
interface UpdateCalendarProps { interface UpdateCalendarProps {
year: number; year: number;
@@ -35,9 +34,7 @@ interface UpdateCalendarProps {
} }
const DatePicker = (): JSX.Element => { const DatePicker = (): JSX.Element => {
const { selectedDate } = useContext(CalenderContext); const { title } = useContext(CalenderContext);
const currentMonth = format(selectedDate, "LLLL uuuu");
const router = useRouter(); const router = useRouter();
@@ -57,7 +54,7 @@ const DatePicker = (): JSX.Element => {
const date: UpdateCalendarProps = { const date: UpdateCalendarProps = {
year: parseInt(dateArr[0]), year: parseInt(dateArr[0]),
month: parseInt(dateArr[1]), month: parseInt(dateArr[1]),
day: parseInt(dateArr[2]), day: parseInt(dateArr[2])
}; };
if (!/^(19|20)\d{2}$/.test(`${date.year}`)) { if (!/^(19|20)\d{2}$/.test(`${date.year}`)) {
@@ -97,7 +94,7 @@ const DatePicker = (): JSX.Element => {
const date: UpdateCalendarProps = { const date: UpdateCalendarProps = {
year: parseInt(dateArr[0]), year: parseInt(dateArr[0]),
month: parseInt(dateArr[1]), month: parseInt(dateArr[1]),
day: parseInt(dateArr[2]), day: parseInt(dateArr[2])
}; };
return resolve(router.push(`/calendar/${date.year}/${date.month}`)); return resolve(router.push(`/calendar/${date.year}/${date.month}`));
@@ -116,15 +113,15 @@ const DatePicker = (): JSX.Element => {
bg: "gray.900", bg: "gray.900",
borderColor: "white", borderColor: "white",
_placeholder: { _placeholder: {
color: "white", color: "white"
}, },
_focus: { _focus: {
bg: "#000", bg: "#000",
color: "#FFF", color: "#FFF",
borderColor: "#63b3ed", borderColor: "#63b3ed",
boxShadow: "0 0 0 1px #63b3ed", boxShadow: "0 0 0 1px #63b3ed",
zIndex: "1", zIndex: "1"
}, }
}; };
const initRef = useRef(); const initRef = useRef();
@@ -134,7 +131,7 @@ const DatePicker = (): JSX.Element => {
<PopoverTrigger> <PopoverTrigger>
<Button border="none" variant="outline"> <Button border="none" variant="outline">
<Heading w="100%" h="auto"> <Heading w="100%" h="auto">
{currentMonth} {title}
</Heading> </Heading>
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
@@ -148,7 +145,7 @@ const DatePicker = (): JSX.Element => {
<PopoverBody textAlign="center"> <PopoverBody textAlign="center">
<Formik <Formik
initialValues={{ initialValues={{
date: "", date: ""
}} }}
onSubmit={(data, actions) => { onSubmit={(data, actions) => {
handleSubmit(data) handleSubmit(data)
@@ -156,8 +153,8 @@ const DatePicker = (): JSX.Element => {
actions.setSubmitting(false); actions.setSubmitting(false);
actions.resetForm({ actions.resetForm({
values: { values: {
date: "", date: ""
}, }
}); });
}) })
.catch(() => { .catch(() => {
@@ -165,11 +162,15 @@ const DatePicker = (): JSX.Element => {
}); });
}} }}
> >
{(formProps: FormikProps<{ date: string }>) => ( {(
formProps: FormikProps<{
date: string;
}>
) => (
<Form <Form
style={{ style={{
width: "100%", width: "100%",
height: "auto", height: "auto"
}} }}
> >
<VStack <VStack
@@ -223,8 +224,8 @@ const DatePicker = (): JSX.Element => {
boxShadow: "0 0 0 1px #00c17c", boxShadow: "0 0 0 1px #00c17c",
_hover: { _hover: {
borderColor: "brand.valid", borderColor: "brand.valid",
boxShadow: "0 0 0 1px #00c17c", boxShadow: "0 0 0 1px #00c17c"
}, }
} }
: "")} : "")}
/> />

View File

@@ -5,7 +5,7 @@ interface FormValidateEmojiProps {
} }
const FormValidateEmoji: FC<FormValidateEmojiProps> = ({ const FormValidateEmoji: FC<FormValidateEmojiProps> = ({
type, type
}: FormValidateEmojiProps) => { }: FormValidateEmojiProps) => {
interface Validations { interface Validations {
[key: string]: JSX.Element; [key: string]: JSX.Element;
@@ -26,7 +26,7 @@ const FormValidateEmoji: FC<FormValidateEmojiProps> = ({
<span role="img" aria-label="Check"> <span role="img" aria-label="Check">
</span> </span>
), )
}; };
return validations[`${type}`]; return validations[`${type}`];

View File

@@ -2,6 +2,9 @@ import React, { useContext, useEffect } from "react";
import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react"; import { Box, HStack, SimpleGrid, Text, VStack } from "@chakra-ui/react";
import CalenderNav from "./CalenderNav"; import CalenderNav from "./CalenderNav";
import { CalenderContext } from "../../contexts/CalenderContext"; import { CalenderContext } from "../../contexts/CalenderContext";
import { getDate, sub, add, getYear, getMonth } from "date-fns";
import { useRouter } from "next/router";
// TODO: import types
interface UpdateCalendarProps { interface UpdateCalendarProps {
year: number; year: number;
@@ -10,28 +13,32 @@ interface UpdateCalendarProps {
} }
const Calender = (newDate?: UpdateCalendarProps): JSX.Element => { const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
const { daysOfMonth, daysOfWeek, setDate } = useContext(CalenderContext); const { selectedDate, layout, updateDate } = useContext(CalenderContext);
const router = useRouter();
useEffect(() => { useEffect(() => {
if (newDate) { if (newDate) {
const { year, month, day } = newDate; const { year, month, day } = newDate;
if (year > 0 && month > 0 && day > 0) { if (year > 0 && month > 0 && day > 0) {
setDate(newDate); updateDate(newDate);
} else { } else {
console.warn("Invalid date format: ", newDate); console.warn("Invalid date format: ", newDate);
} }
} }
}, [daysOfMonth, daysOfWeek, newDate, setDate]); }, [newDate, updateDate]);
// Simulated user settings context // Simulated user settings context
const userSettings = { const userSettings = {
theme: "default", theme: "default",
startOfWeek: "Sunday", startOfWeek: "Sunday"
}; };
const currMonth = layout[`${userSettings.startOfWeek.toLowerCase()}`];
const { month, weekdays } = currMonth;
return ( return (
<VStack h="100vh" w="100%"> <VStack h="91vh" w="100%">
<CalenderNav /> <CalenderNav />
<HStack <HStack
px={6} px={6}
@@ -42,7 +49,7 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
alignContent="center" alignContent="center"
alignItems="center" alignItems="center"
> >
{daysOfWeek.startOfWeek[userSettings.startOfWeek].map((weekDay) => { {weekdays.map((weekDay) => {
return ( return (
<Box <Box
d="flex" d="flex"
@@ -71,20 +78,52 @@ const Calender = (newDate?: UpdateCalendarProps): JSX.Element => {
// alignContent="center" // alignContent="center"
alignItems="center" alignItems="center"
> >
{daysOfMonth.map((monthDay) => { {Object.keys(month).map((week) => {
return ( const thisWeek = month[week];
<Box
bg="transparent" return thisWeek.map((day) => {
border="2px solid #0068ff" const { date, isOverflow, overflowDirection } = day;
w="100%"
h="100%" return (
key={monthDay} <Box
> bg="transparent"
<Text w="100%" h="100%"> color={isOverflow ? "gray.600" : "whiteAlpha"}
{`Day ${monthDay}`} border={isOverflow ? "2px solid #181d8f" : "2px solid #0068ff"}
</Text> w="100%"
</Box> h="100%"
); key={date}
{...(isOverflow && {
_hover: {
cursor: "pointer"
}
})}
{...(isOverflow && {
onClick: () => {
if (overflowDirection === "next") {
console.log(overflowDirection);
const newMonth = add(selectedDate, { months: 1 });
const year = getYear(newMonth);
const month = getMonth(newMonth) + 1;
router.push(`/calendar/${year}/${month}`);
} else if (overflowDirection === "prev") {
const newMonth = sub(selectedDate, { months: 1 });
const year = getYear(newMonth);
const month = getMonth(newMonth) + 1;
router.push(`/calendar/${year}/${month}`);
}
}
})}
>
<Text w="100%" h="100%">
{`Day ${getDate(date)}`}
</Text>
</Box>
);
});
})} })}
</SimpleGrid> </SimpleGrid>
</VStack> </VStack>

View File

@@ -1,8 +1,19 @@
import React, { createContext, useState, ReactNode, useEffect } from "react"; import React, { createContext, useState, ReactNode } from "react";
import { endOfMonth, getDate, sub, compareAsc } from "date-fns"; import {
format,
startOfMonth,
endOfMonth,
getDate,
add,
sub,
set,
isAfter,
isBefore,
compareAsc
} from "date-fns";
// TODO: import types // TODO: import types
type days = type Days =
| "Sunday" | "Sunday"
| "Monday" | "Monday"
| "Tuesday" | "Tuesday"
@@ -10,11 +21,47 @@ type days =
| "Thursday" | "Thursday"
| "Friday" | "Friday"
| "Saturday"; | "Saturday";
interface DaysOfWeek {
startOfWeek: { type DaysOfWeek = Days[];
Sunday: days[];
Monday: days[]; interface WeekDays {
sunday: DaysOfWeek;
monday: DaysOfWeek;
}
interface MonthDay {
isOverflow: boolean;
overflowDirection: "prev" | "next" | null;
date: Date;
}
interface Month {
week1: MonthDay[];
week2: MonthDay[];
week3: MonthDay[];
week4: MonthDay[];
week5: MonthDay[];
week6: MonthDay[];
}
interface MonthInfo {
date: Date;
title: string;
}
interface MonthLayout {
sunday: {
weekdays: DaysOfWeek;
month: Month;
}; };
monday: {
weekdays: DaysOfWeek;
month: Month;
};
}
interface MonthContext extends MonthInfo {
layout: MonthLayout;
} }
interface UpdateCalendarProps { interface UpdateCalendarProps {
@@ -23,158 +70,217 @@ interface UpdateCalendarProps {
day: number; day: number;
} }
interface Month {
week1: Date[];
week2: Date[];
week3: Date[];
week4: Date[];
week5: Date[];
week6: Date[];
}
interface Calendar {
startOfWeek: {
Sunday: Month;
Monday: Month;
};
}
// Will replace all states and be used in redis as a form of memoization.
interface MonthInfo {
date: Date;
layout: Calendar;
startWeekday: string;
endWeekday: string;
days: number;
}
interface CurrentMonth {
prev: MonthInfo;
curr: MonthInfo;
next: MonthInfo;
}
interface CalenderMemoize {
String: CurrentMonth;
}
interface CalenderContextState { interface CalenderContextState {
selectedDate: Date; selectedDate: Date;
daysOfMonth: [number]; title: string;
daysOfWeek: DaysOfWeek; layout: MonthLayout;
setDate: (date: UpdateCalendarProps) => boolean; updateDate: (input: UpdateCalendarProps) => void;
} }
const CalenderContext = createContext({} as CalenderContextState); const CalenderContext = createContext({} as CalenderContextState);
const CalenderContextProvider = ({ const CalenderContextProvider = ({
children, children
}: { }: {
children: ReactNode; children: ReactNode;
}): JSX.Element => { }): JSX.Element => {
const indexToDay = { const weekDays: WeekDays = {
startOfWeek: { sunday: [
Sunday: { "Sunday",
0: "Sunday", "Monday",
1: "Monday", "Tuesday",
2: "Tuesday", "Wednesday",
3: "Wednesday", "Thursday",
4: "Thursday", "Friday",
5: "Friday", "Saturday"
6: "Saturday", ],
}, monday: [
Monday: { "Monday",
0: "Monday", "Tuesday",
1: "Tuesday", "Wednesday",
2: "Wednesday", "Thursday",
3: "Thursday", "Friday",
4: "Friday", "Saturday",
5: "Saturday", "Sunday"
6: "Sunday", ]
},
},
}; };
// Selected month & year //TODO Add a function that will populate the "MONTH" layout for the context. It should take in the start of the week (Sunday, Monday) and output the appropriate layout based on that preference.
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
// Update this to have the day of week and the last numerical day.
const [endOfSelectedMonth, SetEndOfSelectedDMonth] = useState<number>(
getDate(endOfMonth(selectedDate))
);
// Update this to have the day of week and the last numerical day.
const [endOfPrevMonth, setEndOfPrevMonth] = useState<number>(
getDate(endOfMonth(sub(selectedDate, { months: 1 })))
);
// Add start of selected month and start of next month, including day of week and numerical day.
// TODO: Remove this state and all referenced to it once the date alignment algorithm is complete. /**
const [daysOfMonth, setDaysOfMonth] = useState<[number]>([1]); * Using date-fns, this function checks if currDate is within the month of selectedDate or not.
* @param {Date} selectedDate The current month.
* @param {Date} currDate The date to be compared to the selected month.
* @returns True if currDate is outside of the month of selectedDate, false if otherwise.
*/
const isOverflow = (
selectedDate: Date,
currDate: Date
): {
isOverflow: boolean;
overflowDirection: "prev" | "next" | null;
} => {
let flag = false;
let direction: "next" | "prev" | null = null;
// TODO: Repalce this with the new date alignment algorithm. const start = startOfMonth(selectedDate);
// Update or populate the days of the month. const end = endOfMonth(selectedDate);
const populateDays = (): void => {
let newDaysOfMonth: [number] = [...daysOfMonth];
if (newDaysOfMonth.length > 1) { if (isBefore(currDate, start)) {
newDaysOfMonth = [1]; flag = true;
direction = "prev";
} }
for (let i = 1; i < endOfSelectedMonth; i++) { if (isAfter(currDate, end)) {
newDaysOfMonth.push(i + 1); flag = true;
direction = "next";
} }
setDaysOfMonth(newDaysOfMonth); return { isOverflow: flag, overflowDirection: direction };
}; };
// TODO: Update new referenced once they are added. /**
// Update selected month sates when the selected month is updated. * A function that will return a month layout when given a date. It produces
* an object with 6 weeks that include overflow from the previous and next month
* with all dates aligned with the day of the week.
* @param selectedDate The date of the month to generate a month layout for.
*/
const populateMonth = (selectedDate: Date): MonthLayout => {
const endLastMonth = getDate(endOfMonth(sub(selectedDate, { months: 1 })));
const startOfSelectedMonth = format(startOfMonth(selectedDate), "iii");
// Update days of month. const ISOToIndex = {
useEffect(() => { sunday: {
if (daysOfMonth === null) { Sun: 0,
populateDays(); Mon: 1,
} else { Tue: 2,
if (daysOfMonth[daysOfMonth.length - 1] !== endOfSelectedMonth) { Wed: 3,
populateDays(); Thu: 4,
Fri: 5,
Sat: 6
},
monday: {
Mon: -1,
Tue: 0,
Wed: 1,
Thu: 2,
Fri: 3,
Sat: 4,
Sun: 5
} }
} };
}, [selectedDate, endOfSelectedMonth]);
// Update end of month. const sundays = {
useEffect(() => { week1: new Array(7).fill(null),
if (endOfSelectedMonth !== getDate(endOfMonth(selectedDate))) { week2: new Array(7).fill(null),
SetEndOfSelectedDMonth(getDate(endOfMonth(selectedDate))); week3: new Array(7).fill(null),
} week4: new Array(7).fill(null),
}, [selectedDate]); week5: new Array(7).fill(null),
week6: new Array(7).fill(null)
};
// Calender Layout const sunStartDay =
const daysOfWeek: DaysOfWeek = { endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1);
startOfWeek: {
Sunday: [ let sunCurrDate = set(sub(selectedDate, { months: 1 }), {
"Sunday", date: sunStartDay
"Monday", });
"Tuesday",
"Wednesday", for (const week in sundays) {
"Thursday", const thisWeek = sundays[week];
"Friday",
"Saturday", thisWeek.forEach((e, i) => {
], const overflowInfo = isOverflow(selectedDate, sunCurrDate);
Monday: [
"Monday", const day: MonthDay = {
"Tuesday", ...overflowInfo,
"Wednesday", date: sunCurrDate
"Thursday", };
"Friday", sunCurrDate = add(sunCurrDate, {
"Saturday", days: 1
"Sunday", });
],
}, sundays[week][i] = day;
});
}
const mondays = {
week1: new Array(7).fill(null),
week2: new Array(7).fill(null),
week3: new Array(7).fill(null),
week4: new Array(7).fill(null),
week5: new Array(7).fill(null),
week6: new Array(7).fill(null)
};
const monStartDay = endLastMonth - ISOToIndex.monday[startOfSelectedMonth];
let monCurrDate = set(sub(selectedDate, { months: 1 }), {
date: monStartDay
});
for (const week in mondays) {
const thisWeek = mondays[week];
thisWeek.forEach((e, i) => {
const overflowInfo = isOverflow(selectedDate, sunCurrDate);
const day: MonthDay = {
...overflowInfo,
date: monCurrDate
};
monCurrDate = add(monCurrDate, {
days: 1
});
mondays[week][i] = day;
});
}
const output = {
sunday: {
weekdays: weekDays.sunday,
month: sundays
},
monday: {
weekdays: weekDays.monday,
month: mondays
}
};
return output;
}; };
//TODO: Create an object of arrays that will align with the days on the week. Make two sets for each start of the week setting. const [selectedDate, setSelectedDate] = useState<Date>(new Date());
const [selectedDateInfo, setSelectedMonthInfo] = useState<MonthContext>({
date: selectedDate,
title: format(selectedDate, "LLLL uuuu"),
layout: populateMonth(selectedDate)
});
// Navigation //TODO Update the MonthInfo to use the new month population function on first render.
const setDate = (input: UpdateCalendarProps): boolean => {
//TODO Add a function that will update the MonthInfo state when the selected month changes. This should use the populate month function that will be made above.
/**
* Updates the selectedDateInfo state when given a date.
* @param {Date} newDate The date to set the selectedDateInfo state to.
*/
const updateDateInfo = (newDate: Date) => {
const output = { ...selectedDateInfo };
output.date = newDate;
output.title = format(newDate, "LLLL uuuu");
output.layout = populateMonth(newDate);
setSelectedMonthInfo(output);
};
//TODO: Add a new navigation function that will take in either a direction (next, prev) or a date to go directly to. That will update the selected month and trigger the use effects below.
/**
* Updated the selectedDate state when given the appropriate object.
* @param {UpdateCalendarProps} input An object with year, month,
* and day keys that the selectedDate state will be updated to.
*/
const updateDate = (input: UpdateCalendarProps) => {
const { year, month: inputMonth, day } = input; const { year, month: inputMonth, day } = input;
if (!year || !inputMonth || day < 0 || day > 31) { if (!year || !inputMonth || day < 0 || day > 31) {
@@ -185,15 +291,16 @@ const CalenderContextProvider = ({
if (compareAsc(customDate, selectedDate) !== 0) { if (compareAsc(customDate, selectedDate) !== 0) {
setSelectedDate(customDate); setSelectedDate(customDate);
updateDateInfo(customDate);
} }
} }
}; };
const calenderContextValues = { const calenderContextValues = {
selectedDate, selectedDate,
daysOfMonth, title: selectedDateInfo.title,
daysOfWeek, layout: selectedDateInfo.layout,
setDate, updateDate
}; };
return ( return (

View File

@@ -2,7 +2,7 @@
"private": true, "private": true,
"name": "lucid-creations-media-potty-chart", "name": "lucid-creations-media-potty-chart",
"homepage": "https://lucidcreations.media/introducing-code-name-potty-chart/", "homepage": "https://lucidcreations.media/introducing-code-name-potty-chart/",
"version": "v0.0.5.2-pre-alpha", "version": "v0.0.6.0-pre-alpha",
"author": { "author": {
"name": "Lucid Creations Media", "name": "Lucid Creations Media",
"url": "https://lucidcreations.media", "url": "https://lucidcreations.media",

View File

@@ -13,8 +13,8 @@ class Document extends NextDocument {
<Html> <Html>
<Head> <Head>
<meta name="theme-color" content="#3138dc" /> <meta name="theme-color" content="#3138dc" />
<link rel="icon" href="/images/favicon.svg" sizes="32x32 192x192" /> <link rel="icon" href="/images/logo.svg" sizes="32x32 192x192" />
<link rel="apple-touch-icon" href="/images/favicon.svg" /> <link rel="apple-touch-icon" href="/images/logo.svg" />
<meta property="og:title" content="LCM Potty Chart" /> <meta property="og:title" content="LCM Potty Chart" />
<meta name="og:description" content={description} /> <meta name="og:description" content={description} />
<meta property="og:type" content="Progress Tracking" /> <meta property="og:type" content="Progress Tracking" />

View File

@@ -22,14 +22,14 @@ const DateRoute: React.FC<unknown> = () => {
return { return {
year: 0, year: 0,
month: 0, month: 0,
day: 0, day: 0
}; };
} }
const date = { const date = {
year: 0, year: 0,
month: 0, month: 0,
day: 0, day: 0
}; };
if (/^(19|20)\d{2}$/.test(`${dateArr[0]}`)) { if (/^(19|20)\d{2}$/.test(`${dateArr[0]}`)) {
@@ -58,7 +58,9 @@ const DateRoute: React.FC<unknown> = () => {
const parsedSlug = slug.map((e) => { const parsedSlug = slug.map((e) => {
return parseInt(e); return parseInt(e);
}); });
setDate({ ...validateDateInput(parsedSlug) }); setDate({
...validateDateInput(parsedSlug)
});
} }
}, [slug]); }, [slug]);

View File

@@ -14,8 +14,9 @@ const IndexPage = (): JSX.Element => {
const date = useRef<UpdateCalendarProps>({ const date = useRef<UpdateCalendarProps>({
year: parseInt(format(new Date(), "y")), year: parseInt(format(new Date(), "y")),
month: parseInt(format(new Date(), "M")), month: parseInt(format(new Date(), "M")),
day: parseInt(format(new Date(), "d")), day: parseInt(format(new Date(), "d"))
}); });
return ( return (
<Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh"> <Box textAlign="center" w="100%" h="auto" pt="50px" pb="10vh">
<CalenderContextProvider> <CalenderContextProvider>

1
public/images/logo.svg Normal file
View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.01 157.2"><defs><style>.cls-1{fill:#61a3d7;}</style></defs><path class="cls-1" d="M90.37,123A3.86,3.86,0,0,0,85,124.63q-7.68,13.26-15.31,26.55a11.53,11.53,0,0,0-.75,2,4.09,4.09,0,0,0,3,3.86,3.78,3.78,0,0,0,4.39-1.93q7.71-13.32,15.38-26.65A3.86,3.86,0,0,0,90.37,123Z"/><path class="cls-1" d="M116.26,123a3.84,3.84,0,0,0-5.34,1.59q-6.39,11-12.73,22.06a14.1,14.1,0,0,0-.8,2.11,4.26,4.26,0,0,0,2.93,3.76,3.86,3.86,0,0,0,4.54-1.89q6.4-11.07,12.79-22.16A3.84,3.84,0,0,0,116.26,123Z"/><path class="cls-1" d="M63.53,122.65a3.79,3.79,0,0,0-4.44,1.84q-6.48,11.17-12.89,22.37a4.49,4.49,0,0,0-.51,2.42,3.71,3.71,0,0,0,2.94,3.3,3.76,3.76,0,0,0,4.25-1.81q6.52-11.21,13-22.46a10,10,0,0,0,.62-1.82A4.18,4.18,0,0,0,63.53,122.65Z"/><path class="cls-1" d="M142.6,84.35C139,74,131.3,68.43,120.54,66.76c-.23-1.85-.35-3.69-.69-5.49A34.64,34.64,0,0,0,111.21,44l-5.49,5.48a27.13,27.13,0,0,1,7,20.22c-.19,3,1.76,4.82,4.68,4.78,7.72-.11,13.62,3.21,16.94,10.2a18,18,0,0,1-16.26,25.91c-10.16,0-20.32,0-30.48,0h-30a23,23,0,0,1-10.51-2.5l-5.68,5.68a30.08,30.08,0,0,0,16.13,4.63q30.14,0,60.28,0a29.86,29.86,0,0,0,4.81-.43C138.1,115.3,147.78,99.14,142.6,84.35Z"/><path class="cls-1" d="M139.83,3.83,136.54.54a1.82,1.82,0,0,0-2.59,0L.54,134a1.82,1.82,0,0,0,0,2.59l3.29,3.29a1.83,1.83,0,0,0,1.3.54,1.81,1.81,0,0,0,1.29-.54L139.83,6.42A1.82,1.82,0,0,0,139.83,3.83Z"/><path class="cls-1" d="M36.62,72.45A2.22,2.22,0,0,1,37.29,74c-.7,4.86-1.44,9.72-2.18,14.57L48.59,75.09h0l25.79-25.8h0L84,39.65a1.94,1.94,0,0,1-1.46-1.18C79.06,31.52,75.49,24.6,72,17.64c-1.34-2.68-3.35-4.34-6.35-4.35s-5,1.64-6.37,4.31c-3.5,7-7.08,14-10.59,20.94a1.84,1.84,0,0,1-1.52,1.14c-5.64.9-11.27,1.86-16.91,2.79-2.16.35-4.33.67-6.48,1a6.68,6.68,0,0,0-5.62,4.79A6.77,6.77,0,0,0,20,55.45Q28.33,63.94,36.62,72.45Z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,10 +1,10 @@
import { extendTheme, ThemeConfig } from "@chakra-ui/react"; import { extendTheme, ThemeConfig } from "@chakra-ui/react";
import { createBreakpoints } from "@chakra-ui/theme-tools"; // import { createBreakpoints } from "@chakra-ui/theme-tools";
import buttons from "./components/buttonStyles"; import buttons from "./components/buttonStyles";
const config: ThemeConfig = { const config: ThemeConfig = {
initialColorMode: "dark", initialColorMode: "dark",
useSystemColorMode: false, useSystemColorMode: false
}; };
// const breakpoints = createBreakpoints({ // const breakpoints = createBreakpoints({
@@ -29,19 +29,19 @@ const AppTheme = extendTheme({
footer: "#0097a7", footer: "#0097a7",
footerText: "black", footerText: "black",
content: "#2d3748", content: "#2d3748",
patreon: "#FF424D", patreon: "#FF424D"
}, }
}, },
styles: { styles: {
global: { global: {
body: { body: {
bg: "gray.900", bg: "gray.900"
}, }
}, }
}, },
components: { components: {
Button: buttons, Button: buttons
}, }
// breakpoints, // breakpoints,
}); });

View File

@@ -3,7 +3,7 @@ import {
darken, darken,
mode, mode,
StyleFunctionProps, StyleFunctionProps,
whiten, whiten
} from "@chakra-ui/theme-tools"; } from "@chakra-ui/theme-tools";
import { Dict } from "@chakra-ui/utils"; import { Dict } from "@chakra-ui/utils";
@@ -22,8 +22,8 @@ const buttonStyles = {
bg: mode( bg: mode(
whiten("brand.primary", 20), whiten("brand.primary", 20),
darken("brand.primary", 20) darken("brand.primary", 20)
)(props), )(props)
}, }
}), }),
contactSecondary: (props: Dict<never> | StyleFunctionProps) => ({ contactSecondary: (props: Dict<never> | StyleFunctionProps) => ({
bg: "brand.primary", bg: "brand.primary",
@@ -33,8 +33,8 @@ const buttonStyles = {
bg: mode( bg: mode(
whiten("brand.primary", 20), whiten("brand.primary", 20),
darken("brand.primary", 20) darken("brand.primary", 20)
)(props), )(props)
}, }
}), }),
project: (props: Dict<never> | StyleFunctionProps) => ({ project: (props: Dict<never> | StyleFunctionProps) => ({
bg: "transparent", bg: "transparent",
@@ -50,8 +50,8 @@ const buttonStyles = {
darken("brand.secondary", 20) darken("brand.secondary", 20)
)(props), )(props),
boxShadow: boxShadow:
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px", "rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px"
}, }
}), }),
nav: (props: Dict<never> | StyleFunctionProps) => ({ nav: (props: Dict<never> | StyleFunctionProps) => ({
bg: "transparent", bg: "transparent",
@@ -61,16 +61,16 @@ const buttonStyles = {
bg: mode( bg: mode(
whiten("brand.secondary", 20), whiten("brand.secondary", 20),
darken("brand.secondary", 20) darken("brand.secondary", 20)
)(props), )(props)
}, }
}), }),
stickyNav: (/* props: Dict<never> | StyleFunctionProps */) => ({ stickyNav: (/* props: Dict<never> | StyleFunctionProps */) => ({
bg: "transparent", bg: "transparent",
fontSize: "md", fontSize: "md",
px: "2", px: "2",
_hover: { _hover: {
textDecoration: "underline", textDecoration: "underline"
}, }
}), }),
credits: (props: Dict<never> | StyleFunctionProps) => ({ credits: (props: Dict<never> | StyleFunctionProps) => ({
bg: "brand.main", bg: "brand.main",
@@ -78,8 +78,8 @@ const buttonStyles = {
p: 3, p: 3,
color: "whiteAlpha", color: "whiteAlpha",
_hover: { _hover: {
bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props), bg: mode(whiten("brand.main", 20), darken("brand.main", 20))(props)
}, }
}), }),
backToTop: (props: Dict<never> | StyleFunctionProps) => ({ backToTop: (props: Dict<never> | StyleFunctionProps) => ({
bg: "rgba(23, 25, 35, 0.5)", bg: "rgba(23, 25, 35, 0.5)",
@@ -98,8 +98,8 @@ const buttonStyles = {
boxShadow: boxShadow:
"rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px", "rgba(0, 104, 255, 0.5) 0px 0px 15px, rgba(0, 104, 255, 0.3) 0px 0px 3px 1px",
color: "whiteAlpha.900", color: "whiteAlpha.900",
border: "1px solid rgba(0, 134, 255, 1)", border: "1px solid rgba(0, 134, 255, 1)"
}, }
}), }),
collapse: (props: Dict<never> | StyleFunctionProps) => ({ collapse: (props: Dict<never> | StyleFunctionProps) => ({
bg: "transparent", bg: "transparent",
@@ -114,8 +114,8 @@ const buttonStyles = {
darken("brand.secondary", 20) darken("brand.secondary", 20)
)(props), )(props),
color: "whiteAlpha.900", color: "whiteAlpha.900",
textDecoration: "none", textDecoration: "none"
}, }
}), }),
submit: (props: Dict<never> | StyleFunctionProps) => ({ submit: (props: Dict<never> | StyleFunctionProps) => ({
fontSize: "lg", fontSize: "lg",
@@ -132,9 +132,9 @@ const buttonStyles = {
)(props), )(props),
boxShadow: boxShadow:
"rgba(252, 129, 129, .95) 0px 0px 15px, rgba(252, 129, 129, 0.75) 0px 0px 3px 1px", "rgba(252, 129, 129, .95) 0px 0px 15px, rgba(252, 129, 129, 0.75) 0px 0px 3px 1px",
border: "1px solid #FC8181", border: "1px solid #FC8181"
}, }
}, }
}), }),
mobileNav: (props: Dict<never> | StyleFunctionProps) => ({ mobileNav: (props: Dict<never> | StyleFunctionProps) => ({
// bg: "transparent", // bg: "transparent",
@@ -148,14 +148,14 @@ const buttonStyles = {
darken("brand.secondary", 20) darken("brand.secondary", 20)
)(props), )(props),
boxShadow: boxShadow:
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px", "rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px"
}, },
_expanded: { _expanded: {
bg: "brand.primary", bg: "brand.primary",
boxShadow: boxShadow:
"rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px", "rgba(0, 134, 255, 0.5) 0px 0px 15px, rgba(0, 134, 255, 0.3) 0px 0px 3px 1px",
border: "1px solid #0068ff", border: "1px solid #0068ff"
}, }
}), }),
patreon: (props: Dict<never> | StyleFunctionProps) => ({ patreon: (props: Dict<never> | StyleFunctionProps) => ({
bg: "brand.patreon", bg: "brand.patreon",
@@ -166,12 +166,12 @@ const buttonStyles = {
bg: mode( bg: mode(
whiten("brand.patreon", 20), whiten("brand.patreon", 20),
darken("brand.patreon", 20) darken("brand.patreon", 20)
)(props), )(props)
}, }
}), })
}, },
// default values for `size` and `variant` // default values for `size` and `variant`
defaultProps: {}, defaultProps: {}
}; };
export default buttonStyles; export default buttonStyles;

View File

@@ -7,14 +7,18 @@ interface BackToTopButtonProps {
} }
const BackToTopButton: FC<BackToTopButtonProps> = ({ const BackToTopButton: FC<BackToTopButtonProps> = ({
show, show
}: BackToTopButtonProps) => { }: BackToTopButtonProps) => {
return ( return (
<Flex <Flex
d={show ? "flex" : "none"} d={show ? "flex" : "none"}
pos="fixed" pos="fixed"
top="85vh" top="85vh"
right={{ base: "1.25rem", sm: "2rem", md: "3rem" }} right={{
base: "1.25rem",
sm: "2rem",
md: "3rem"
}}
> >
<Link href="/#top"> <Link href="/#top">
<Button variant="backToTop"> <Button variant="backToTop">

View File

@@ -7,7 +7,7 @@ import {
HStack, HStack,
// Image, // Image,
Button, Button,
BoxProps, BoxProps
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
// import BackToTopButton from "./BackToTopButton"; // import BackToTopButton from "./BackToTopButton";

View File

@@ -1,19 +1,21 @@
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import Image from "next/image";
import { import {
Heading, Heading,
HStack, HStack,
Box, Box,
IconButton, IconButton,
Menu, Menu,
MenuButton, MenuButton
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { Icon } from "@iconify/react"; import { Icon } from "@iconify/react";
import DesktopNav from "./DesktopNav"; import DesktopNav from "./DesktopNav";
import MobileNav from "./MobileNav"; import MobileNav from "./MobileNav";
import appLogo from "../../public/images/logo.svg";
const Header = (): JSX.Element => { const Header = (): JSX.Element => {
const appName = "LCM Potty Chart"; const appName = "LCM Potty Chart";
const appVersion = "v0.0.5.2-pre-alpha"; const appVersion = "v0.0.6.0-pre-alpha";
// Add transparency while not at the top of the page. // Add transparency while not at the top of the page.
const [transparentNavbar, setTransparentNavbar] = useState<boolean>(false); const [transparentNavbar, setTransparentNavbar] = useState<boolean>(false);
@@ -54,7 +56,7 @@ const Header = (): JSX.Element => {
const iconType = { const iconType = {
default: <Icon icon="bx:bx-menu-alt-right" />, default: <Icon icon="bx:bx-menu-alt-right" />,
hover: <Icon icon="bx:bx-menu" />, hover: <Icon icon="bx:bx-menu" />,
open: <Icon icon="bx:bx-x" />, open: <Icon icon="bx:bx-x" />
}; };
if (open) { if (open) {
@@ -91,14 +93,17 @@ const Header = (): JSX.Element => {
bg: "brand.main", bg: "brand.main",
boxShadow: open boxShadow: open
? "none" ? "none"
: "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px", : "rgba(0, 134, 255, 0.9) 0px 0px 15px, rgba(0, 134, 255, 0.7) 0px 0px 3px 1px"
}} }}
h={open ? "125px" : "auto"} h={open ? "125px" : "auto"}
> >
{/* Logo | Site Name */} {/* Logo | Site Name */}
<HStack <HStack
width="100%" width="100%"
justifyContent={{ base: "flex-start", sm: "center" }} justifyContent={{
base: "flex-start",
sm: "center"
}}
alignItems="center" alignItems="center"
height={12} height={12}
top={0} top={0}
@@ -107,9 +112,11 @@ const Header = (): JSX.Element => {
d={{ base: "flex", lg: "none" }} d={{ base: "flex", lg: "none" }}
spacing="5px" spacing="5px"
_hover={{ _hover={{
cursor: "default", cursor: "default"
}} }}
> >
<Image height="30px" width="30px" src={appLogo} alt="App Logo" />
<Heading as="h1" size="md"> <Heading as="h1" size="md">
{appName} {appName}
</Heading> </Heading>
@@ -140,9 +147,10 @@ const Header = (): JSX.Element => {
height="auto" height="auto"
spacing="5px" spacing="5px"
_hover={{ _hover={{
cursor: "default", cursor: "default"
}} }}
> >
<Image height="30px" width="30px" src={appLogo} alt="App Logo" />
<Heading as="h1" size="md"> <Heading as="h1" size="md">
{appName} {appName}
</Heading> </Heading>
@@ -161,7 +169,10 @@ const Header = (): JSX.Element => {
onClick={() => setOpen(!open)} onClick={() => setOpen(!open)}
onMouseEnter={() => setHover(true)} onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)} onMouseLeave={() => setHover(false)}
d={{ base: "inline-flex", lg: "none" }} d={{
base: "inline-flex",
lg: "none"
}}
variant="mobileNav" variant="mobileNav"
bg={transparentNavbar ? "transparent" : "rgba(255, 255, 255, .15)"} bg={transparentNavbar ? "transparent" : "rgba(255, 255, 255, .15)"}
type="button" type="button"

View File

@@ -4,7 +4,7 @@ import {
Link, Link,
MenuDivider, MenuDivider,
MenuItem, MenuItem,
MenuList, MenuList
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import navItems, { NavItem } from "./navItems"; import navItems, { NavItem } from "./navItems";
@@ -33,10 +33,10 @@ const MobileNav: FC<MobileNavProps> = ({ updateOpen }: MobileNavProps) => {
h="auto" h="auto"
p={0} p={0}
_hover={{ _hover={{
backgroundColor: "none", backgroundColor: "none"
}} }}
_focus={{ _focus={{
backgroundColor: "none", backgroundColor: "none"
}} }}
> >
<Link onClick={() => updateOpen(false)} href={navItem[1]}> <Link onClick={() => updateOpen(false)} href={navItem[1]}>