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) => {
const thisWeek = month[week];
return thisWeek.map((day) => {
const { date, isOverflow, overflowDirection } = day;
return ( return (
<Box <Box
bg="transparent" bg="transparent"
border="2px solid #0068ff" color={isOverflow ? "gray.600" : "whiteAlpha"}
border={isOverflow ? "2px solid #181d8f" : "2px solid #0068ff"}
w="100%" w="100%"
h="100%" h="100%"
key={monthDay} 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%"> <Text w="100%" h="100%">
{`Day ${monthDay}`} {`Day ${getDate(date)}`}
</Text> </Text>
</Box> </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", ]
}, };
},
//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.
/**
* 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;
const start = startOfMonth(selectedDate);
const end = endOfMonth(selectedDate);
if (isBefore(currDate, start)) {
flag = true;
direction = "prev";
}
if (isAfter(currDate, end)) {
flag = true;
direction = "next";
}
return { isOverflow: flag, overflowDirection: direction };
};
/**
* 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");
const ISOToIndex = {
sunday: {
Sun: 0,
Mon: 1,
Tue: 2,
Wed: 3,
Thu: 4,
Fri: 5,
Sat: 6
},
monday: {
Mon: -1,
Tue: 0,
Wed: 1,
Thu: 2,
Fri: 3,
Sat: 4,
Sun: 5
}
};
const sundays = {
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 sunStartDay =
endLastMonth - (ISOToIndex.sunday[startOfSelectedMonth] - 1);
let sunCurrDate = set(sub(selectedDate, { months: 1 }), {
date: sunStartDay
});
for (const week in sundays) {
const thisWeek = sundays[week];
thisWeek.forEach((e, i) => {
const overflowInfo = isOverflow(selectedDate, sunCurrDate);
const day: MonthDay = {
...overflowInfo,
date: sunCurrDate
};
sunCurrDate = add(sunCurrDate, {
days: 1
});
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;
}; };
// Selected month & year
const [selectedDate, setSelectedDate] = useState<Date>(new Date()); const [selectedDate, setSelectedDate] = useState<Date>(new Date());
// Update this to have the day of week and the last numerical day. const [selectedDateInfo, setSelectedMonthInfo] = useState<MonthContext>({
const [endOfSelectedMonth, SetEndOfSelectedDMonth] = useState<number>( date: selectedDate,
getDate(endOfMonth(selectedDate)) title: format(selectedDate, "LLLL uuuu"),
); layout: populateMonth(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. //TODO Update the MonthInfo to use the new month population function on first render.
const [daysOfMonth, setDaysOfMonth] = useState<[number]>([1]);
// TODO: Repalce this with the new date alignment algorithm. //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.
// Update or populate the days of the month. /**
const populateDays = (): void => { * Updates the selectedDateInfo state when given a date.
let newDaysOfMonth: [number] = [...daysOfMonth]; * @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);
if (newDaysOfMonth.length > 1) { setSelectedMonthInfo(output);
newDaysOfMonth = [1];
}
for (let i = 1; i < endOfSelectedMonth; i++) {
newDaysOfMonth.push(i + 1);
}
setDaysOfMonth(newDaysOfMonth);
}; };
// TODO: Update new referenced once they are added. //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.
// Update selected month sates when the selected month is updated. /**
* Updated the selectedDate state when given the appropriate object.
// Update days of month. * @param {UpdateCalendarProps} input An object with year, month,
useEffect(() => { * and day keys that the selectedDate state will be updated to.
if (daysOfMonth === null) { */
populateDays(); const updateDate = (input: UpdateCalendarProps) => {
} else {
if (daysOfMonth[daysOfMonth.length - 1] !== endOfSelectedMonth) {
populateDays();
}
}
}, [selectedDate, endOfSelectedMonth]);
// Update end of month.
useEffect(() => {
if (endOfSelectedMonth !== getDate(endOfMonth(selectedDate))) {
SetEndOfSelectedDMonth(getDate(endOfMonth(selectedDate)));
}
}, [selectedDate]);
// Calender Layout
const daysOfWeek: DaysOfWeek = {
startOfWeek: {
Sunday: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
Monday: [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
],
},
};
//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.
// Navigation
const setDate = (input: UpdateCalendarProps): boolean => {
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]}>