Merge pull request #44 from LucidKobold/modals

Modals
This commit is contained in:
Lucid Kobold
2022-04-03 01:53:08 -05:00
committed by GitHub
8 changed files with 360 additions and 171 deletions

View File

@@ -12,7 +12,7 @@ import {
import router from "next/router";
import React, { Fragment, useState } from "react";
import { StickersContextProvider } from "../../contexts/StickerContext";
import AddSticker from "./modals/AddSticker";
import AddUpdateSticker from "./modals/AddUpdateSticker";
import DemoStickers from "./stickers/DemoStickers";
interface DayProps {
@@ -58,11 +58,19 @@ const Day = ({
}
};
// This handles the modal for this date.
// This handles the modal for the day.
const [isOpen, setIsOpen] = useState<boolean>(false);
// The current sticker to be displayed on the current date.
// * This is temporary. There should be no need for this once persistent storage is used. This is being used as a workaround to a bug.
const [stickerState, setStickerState] = useState<StickerVal>(sticker);
// The step the modal is at.
const [step, setStep] = useState<number>(0);
// The current selected sticker. (To be added or updated)
const [selectedSticker, setSelectedSticker] = useState<StickerVal>(null);
/**
* TODO: Add logic to remove the onClick within overflow dates.
* Do not give dates for the next month an onClick.
@@ -114,7 +122,11 @@ const Day = ({
border="1px solid #0068ff"
w="100%"
h="100%"
onClick={() => setIsOpen(true)}
onClick={() => {
setStep(0);
setSelectedSticker(null);
setIsOpen(true);
}}
alignContent="center"
justifyContent="flex-start"
pt={2}
@@ -151,11 +163,16 @@ const Day = ({
</Box>
<StickersContextProvider>
{isBefore(date, endOfDay(new Date())) && (
<AddSticker
<AddUpdateSticker
date={date}
isOpen={isOpen}
updateIsOpen={setIsOpen}
updateSticker={setStickerState}
currSticker={stickerState}
step={step}
updateStep={setStep}
selectedSticker={selectedSticker}
updateSelectedSticker={setSelectedSticker}
/>
)}
</StickersContextProvider>

View File

@@ -1,145 +0,0 @@
import {
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
Heading,
HStack,
VStack
} from "@chakra-ui/react";
import React, { Fragment, useState, useContext } from "react";
import { format } from "date-fns";
import DemoStickers from "../stickers/DemoStickers";
import { StickersContext } from "../../../contexts/StickerContext";
interface AddStickerProps {
isOpen: boolean;
updateIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
date: Date;
updateSticker: React.Dispatch<React.SetStateAction<StickerVal>>;
}
/**
* Handles adding and modifying the stickers for the given month.
* @param props the props for this component.
* @param {boolean} props.isOpen tells the component when the modal should be open.
* @param {React.Dispatch<React.SetStateAction<boolean>>} props.updateIsOpen used to close the modal.
* @param {date} props.date the date for which the sticker will be added or modified.
* @param {React.Dispatch<React.SetStateAction<StickerVal>>} updateSticker the react state function to update the sticker.
*/
const AddSticker = ({
isOpen,
updateIsOpen,
date,
updateSticker
}: AddStickerProps): JSX.Element => {
// TODO: Import the stickers array from the calender context.
// TODO: Add a function that will add or update the sticker for the current date.
/**
* TODO: Add logic into the contents of the modal to show messages if the selected date is out of range.
* Show a message when a date in the future is selected.
* Show a message when a date before the current date is selected.
*/
const [selectedSticker, setSelectedSticker] = useState<StickerVal>(null);
const { addEditSticker } = useContext(StickersContext);
const handleClose = () => {
setSelectedSticker(null);
updateIsOpen(false);
};
const handleSubmit = (sticker) => {
const newSticker: Sticker = addEditSticker(date, sticker);
updateSticker(newSticker.sticker);
handleClose();
};
// TODO: Invalidate submit button if the selected sticker is the same as the current sticker.
// TODO: Display the current sticker above the selection screen if a current sticker exists.
// TODO: Invalidate the button for the current sticker and gray it out.
// TODO: Trigger a warning if the date is in the past showing the sticker change.
// ! DO NOT update the sticker state or trigger the edd/edit function until that new warning is accepted.
return (
<Fragment>
<Modal
isCentered
isOpen={isOpen}
onClose={() => handleClose()}
motionPreset="slideInBottom"
scrollBehavior="inside"
size="lg"
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Heading textAlign="center" as="h2" size="md" w="100%" h="auto">
{`Which sticker did you earn for ${format(date, "LLL d, y")}?`}
</Heading>
</ModalHeader>
<ModalCloseButton />
<ModalBody>
<VStack>
<HStack
w="100%"
h="auto"
justifyContent="center"
alignContent="center"
spacing={6}
>
<Button
border={selectedSticker === 1 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === 1 && "gray.800"}
onClick={() => setSelectedSticker(1)}
variant="stickerButton"
>
<DemoStickers stickerVal={1} />
</Button>
<Button
border={selectedSticker === 0 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === 0 && "gray.800"}
onClick={() => setSelectedSticker(0)}
variant="stickerButton"
>
<DemoStickers stickerVal={0} />
</Button>
<Button
border={selectedSticker === -1 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === -1 && "gray.800"}
onClick={() => setSelectedSticker(-1)}
variant="stickerButton"
>
<DemoStickers stickerVal={-1} />
</Button>
</HStack>
</VStack>
</ModalBody>
<ModalFooter>
<Button
variant="primary"
isDisabled={selectedSticker === null}
// mr={3}
onClick={() => handleSubmit(selectedSticker)}
>
{"Submit"}
</Button>
{/* <Button variant="ghost">Secondary Action</Button> */}
</ModalFooter>
</ModalContent>
</Modal>
</Fragment>
);
};
export default AddSticker;

View File

@@ -0,0 +1,245 @@
import {
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Heading,
HStack,
Text,
VStack,
SimpleGrid,
Box
} from "@chakra-ui/react";
import React, { useState, useContext } from "react";
import { format, isSameDay } from "date-fns";
import { Icon } from "@iconify/react";
import { StickersContext } from "../../../contexts/StickerContext";
import StickerSelector from "./StickerSelector";
import DemoStickers from "../stickers/DemoStickers";
interface AddStickerProps {
isOpen: boolean;
updateIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
date: Date;
updateSticker: React.Dispatch<React.SetStateAction<StickerVal>>;
currSticker: StickerVal;
step: number;
updateStep: React.Dispatch<React.SetStateAction<number>>;
selectedSticker: StickerVal;
updateSelectedSticker: React.Dispatch<React.SetStateAction<StickerVal>>;
}
/**
* Handles adding and modifying the stickers for the given month.
* @param {boolean} isOpen Tells the component when the modal should be open.
* @param {React.Dispatch<React.SetStateAction<boolean>>} updateIsOpen Used to close the modal.
* @param {date} date The date for which the sticker will be added or modified.
* @param {React.Dispatch<React.SetStateAction<StickerVal>>} updateSticker The react state function to update the sticker.
* @param {StickerVal} currSticker The current sticker for the date.
* @param {number} step A numerical variable that represents the page the modal should be at.
* @param {React.Dispatch<React.SetStateAction<number>>} updateStep Used to navigate the pages of the modal by updating the step the modal is on.
* @param {React.Dispatch<React.SetStateAction<StickerVal>>} updateSticker The react state function to update the selected sticker that will be added or updated.
*/
const AddUpdateSticker = ({
isOpen,
updateIsOpen,
date,
updateSticker,
currSticker,
step,
updateStep,
selectedSticker,
updateSelectedSticker
}: AddStickerProps): JSX.Element => {
// TODO: Import the stickers array from the calender context.
const { addEditSticker } = useContext(StickersContext);
const [modalVariant] = useState<"currDate" | "notCurrDate">(
isSameDay(date, new Date()) ? "currDate" : "notCurrDate"
);
const handleClose = () => {
updateIsOpen(false);
};
// TODO: Validate that the provided sticker is not the current sticker. Throw an error if the same sticker is attempted.
const handleSubmit = (sticker) => {
const newSticker: Sticker = addEditSticker(date, sticker);
updateSticker(newSticker.sticker);
handleClose();
};
// * Double check that the submit button is disabled if the selected sticker is the same as the current sticker.
const variants = {
currDate: [
{
header: `Which sticker did you earn for ${format(date, "LLL d, y")}?`,
body: (
<StickerSelector
stickerSet="Demo"
currSticker={currSticker}
selectedSticker={selectedSticker}
updateSelectedSticker={updateSelectedSticker}
/>
),
footer: (
<Button
variant="submit"
isDisabled={
selectedSticker === null || selectedSticker === currSticker
}
onClick={() => handleSubmit(selectedSticker)}
>
{"Submit"}
</Button>
)
}
],
notCurrDate: [
{
header: `Which sticker did you want to update for ${format(
date,
"LLL d, y"
)}?`,
body: (
<VStack
w="100%"
h="auto"
justifyContent="space-between"
alignContent="center"
>
<Heading textAlign="center" as="h3" size="md" w="100%" h="auto">
{"Current Sticker"}
</Heading>
<Text fontSize="4rem">
<DemoStickers stickerVal={currSticker} />
</Text>
<Heading textAlign="center" as="h3" size="md" w="100%" h="auto">
{"Select your new sticker"}
</Heading>
<StickerSelector
stickerSet="Demo"
currSticker={currSticker}
selectedSticker={selectedSticker}
updateSelectedSticker={updateSelectedSticker}
/>
</VStack>
),
footer: (
<Button
variant="primary"
// isDisabled={
// selectedSticker === null || selectedSticker === currSticker
// }
onClick={() => updateStep(step + 1)}
>
{"Next"}
</Button>
)
},
{
header: `Are you sure you want to change the sticker for ${format(
date,
"M/d/y"
)}?`,
body: (
<SimpleGrid my="1.5rem" mx="5rem" w="auto" h="100%" columns={3}>
<Heading textAlign="center" as="h3" size="md" w="100%" h="auto">
{"Previous Sticker"}
</Heading>
<Box></Box>
<Heading textAlign="center" as="h3" size="md" w="100%" h="auto">
{"New Sticker"}
</Heading>
<Text textAlign="center" w="100%" fontSize="4rem">
<DemoStickers stickerVal={currSticker} />
</Text>
<Box fontSize="4rem" m="auto">
<Icon fontSize="4rem" icon="bi:arrow-right" />
</Box>
<Text textAlign="center" w="100%" fontSize="4rem">
<DemoStickers stickerVal={selectedSticker} />
</Text>
</SimpleGrid>
),
footer: (
<HStack
w="100%"
h="auto"
justifyContent="space-between"
alignContent="center"
>
<Button variant="primary" onClick={() => updateStep(step - 1)}>
{"Previous"}
</Button>
<HStack w="auto" h="auto" alignContent="center" spacing={6}>
<Button
backgroundColor="transparent"
_hover={{ backgroundColor: "brand.danger" }}
onClick={() => updateIsOpen(!isOpen)}
>
{"Cancel"}
</Button>
<Button
variant="submit"
isDisabled={
selectedSticker === null || selectedSticker === currSticker
}
onClick={() => handleSubmit(selectedSticker)}
>
{"Confirm"}
</Button>
</HStack>
</HStack>
)
}
]
};
return (
<Modal
isCentered
isOpen={isOpen}
onClose={() => handleClose()}
motionPreset="slideInBottom"
scrollBehavior="inside"
size={modalVariant === "currDate" ? "xl" : "2xl"}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
<HStack
w="100%"
h="auto"
justifyContent="space-between"
alignContent="center"
>
<Heading textAlign="center" as="h2" size="md" w="100%" h="auto">
{modalVariant && variants[modalVariant][step].header}
</Heading>
<Button
fontSize="2rem"
px="1"
onClick={() => updateIsOpen(!isOpen)}
>
<Icon icon="bi:x" />
</Button>
</HStack>
</ModalHeader>
<ModalBody>
{modalVariant && variants[modalVariant][step].body}
</ModalBody>
<ModalFooter>
{modalVariant && variants[modalVariant][step].footer}
</ModalFooter>
</ModalContent>
</Modal>
);
};
export default AddUpdateSticker;

View File

@@ -0,0 +1,69 @@
import { HStack, Button } from "@chakra-ui/react";
import React from "react";
import DemoStickers from "../stickers/DemoStickers";
interface StickerSelectorProps {
stickerSet: "Demo";
currSticker: StickerVal;
selectedSticker: StickerVal;
updateSelectedSticker: React.Dispatch<React.SetStateAction<StickerVal>>;
}
/**
* Handles displaying a list of dynamic stickers to be selected.
* @param {string} stickerSet The name of the stickers that should be displayed.
* @param {StickerVal} currSticker The current sticker for the date.
* @param {StickerVal} selectedSticker The selected sticker for the current. date
* @param {React.Dispatch<React.SetStateAction<StickerVal>>} updateSelectedSticker TThe react state function to update the selected sticker that will be added or updated.
*/
const StickerSelector = ({
stickerSet,
currSticker,
selectedSticker,
updateSelectedSticker
}: StickerSelectorProps): JSX.Element => {
const stickers = {
Demo: (
<HStack
w="100%"
h="auto"
justifyContent={"center"}
alignContent={"center"}
spacing={14}
>
<Button
isDisabled={currSticker >= 1}
border={selectedSticker === 1 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === 1 && "gray.800"}
onClick={() => updateSelectedSticker(1)}
variant="stickerButton"
>
<DemoStickers stickerVal={1} />
</Button>
<Button
isDisabled={currSticker === 0}
border={selectedSticker === 0 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === 0 && "gray.800"}
onClick={() => updateSelectedSticker(0)}
variant="stickerButton"
>
<DemoStickers stickerVal={0} />
</Button>
<Button
isDisabled={currSticker <= -1}
border={selectedSticker === -1 ? "1px solid #FFF" : "opx"}
bg={selectedSticker === -1 && "gray.800"}
onClick={() => updateSelectedSticker(-1)}
variant="stickerButton"
>
<DemoStickers stickerVal={-1} />
</Button>
</HStack>
)
};
return stickers[stickerSet];
};
export default StickerSelector;

View File

@@ -2,7 +2,7 @@
"private": true,
"name": "lucid-creations-media-potty-chart",
"homepage": "https://lucidcreations.media/introducing-code-name-potty-chart/",
"version": "v0.0.8.1-alpha",
"version": "v0.0.9.0-alpha",
"author": {
"name": "Lucid Creations Media",
"url": "https://lucidcreations.media",
@@ -25,8 +25,8 @@
"formik": "^2.2.9",
"framer-motion": "^6.2.8",
"next": "12.1.4",
"react": "18.0.0",
"react-dom": "18.0.0",
"react": "<=17.0.2",
"react-dom": "<=17.0.2",
"sharp": "^0.30.3"
},
"devDependencies": {

View File

@@ -2,7 +2,7 @@ import React, { useEffect } from "react";
import { useRouter } from "next/router";
import { Box, Heading } from "@chakra-ui/react";
const DateRoute = () => {
const DateIndex = () => {
const router = useRouter();
useEffect(() => {
@@ -20,4 +20,4 @@ const DateRoute = () => {
);
};
export default DateRoute;
export default DateIndex;

View File

@@ -15,7 +15,7 @@ import appLogo from "../../public/images/logo.svg";
const Header = (): JSX.Element => {
const appName = "LCM Potty Chart";
const appVersion = "v0.0.8.1-alpha";
const appVersion = "v0.0.9.0-alpha";
// Add transparency while not at the top of the page.
const [transparentNavbar, setTransparentNavbar] = useState<boolean>(false);

View File

@@ -3644,8 +3644,8 @@ __metadata:
framer-motion: ^6.2.8
next: 12.1.4
prettier: ^2.6.1
react: 18.0.0
react-dom: 18.0.0
react: <=17.0.2
react-dom: <=17.0.2
sharp: ^0.30.3
typescript: <4.6.0
languageName: unknown
@@ -4365,15 +4365,16 @@ __metadata:
languageName: node
linkType: hard
"react-dom@npm:18.0.0":
version: 18.0.0
resolution: "react-dom@npm:18.0.0"
"react-dom@npm:<=17.0.2":
version: 17.0.2
resolution: "react-dom@npm:17.0.2"
dependencies:
loose-envify: ^1.1.0
scheduler: ^0.21.0
object-assign: ^4.1.1
scheduler: ^0.20.2
peerDependencies:
react: ^18.0.0
checksum: dd0ba9f2f31dd728076c892a95b2f5a8dfe79136431b0289afb46eec39d0ca6b6f0f40a60fd8aa6ef702c98ce7c26100d3d4dbc35c7c9e87429cd04f84cb58bd
react: 17.0.2
checksum: 1c1eaa3bca7c7228d24b70932e3d7c99e70d1d04e13bb0843bbf321582bc25d7961d6b8a6978a58a598af2af496d1cedcfb1bf65f6b0960a0a8161cb8dab743c
languageName: node
linkType: hard
@@ -4466,12 +4467,13 @@ __metadata:
languageName: node
linkType: hard
"react@npm:18.0.0":
version: 18.0.0
resolution: "react@npm:18.0.0"
"react@npm:<=17.0.2":
version: 17.0.2
resolution: "react@npm:17.0.2"
dependencies:
loose-envify: ^1.1.0
checksum: 293020b96536b3c7113ee57ca5c990a3f25649d1751b1c7a3aabd16dff0691fe9f1eed1206616d0906d05933536052037340a0c8d0941ff870b0eb469a2f975b
object-assign: ^4.1.1
checksum: b254cc17ce3011788330f7bbf383ab653c6848902d7936a87b09d835d091e3f295f7e9dd1597c6daac5dc80f90e778c8230218ba8ad599f74adcc11e33b9d61b
languageName: node
linkType: hard
@@ -4653,12 +4655,13 @@ __metadata:
languageName: node
linkType: hard
"scheduler@npm:^0.21.0":
version: 0.21.0
resolution: "scheduler@npm:0.21.0"
"scheduler@npm:^0.20.2":
version: 0.20.2
resolution: "scheduler@npm:0.20.2"
dependencies:
loose-envify: ^1.1.0
checksum: 4f8285076041ed2c81acdd1faa987f1655fdbd30554bc667c1ea64743fc74fb3a04ca7d27454b3d678735df5a230137a3b84756061b43dc5796e80701b66d124
object-assign: ^4.1.1
checksum: c4b35cf967c8f0d3e65753252d0f260271f81a81e427241295c5a7b783abf4ea9e905f22f815ab66676f5313be0a25f47be582254db8f9241b259213e999b8fc
languageName: node
linkType: hard