Skip to content

Commit

Permalink
fix: checkbox state gets lost when toggling between ingredientsinstru…
Browse files Browse the repository at this point in the history
…ctions (#231)

* update paddings

* add useState for checkboxes

* fix styling for section title

* no toggle from sm screens
  • Loading branch information
sirisayshello authored Nov 18, 2024
1 parent 18d740f commit 687a945
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 62 deletions.
4 changes: 2 additions & 2 deletions src/app/dashboard/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ export default async function RecipePage({
</Stack>

<ScreenAwakeToggle />
<Box component="section" mb={"md"} pb={"md"}>
<Box component="section" pb={"xl"}>
<IngredientsAndInstructionsToggle recipe={convertedRecipe} />
</Box>

<Group mb="md">
<Group pb={"xl"}>
{recipe.tags?.map((tagRelation, index) => (
<Pill key={index} size="md">
{tagRelation.tag.name}
Expand Down
125 changes: 85 additions & 40 deletions src/components/IngredientsAndInstructionsToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use client";
import { Button, Group, List, Stack } from "@mantine/core";
import { Button, Flex, Group, Paper, Stack, Text } from "@mantine/core";
import { useState } from "react";
import RenderedInstructions from "./RenderedInstructions";
import RenderedIngredients from "./RenderedIngredients";

type IngAndInstToggleProps = {
recipe: UserRecipe;
Expand All @@ -12,45 +13,89 @@ export const IngredientsAndInstructionsToggle = ({
}: IngAndInstToggleProps) => {
const [view, setView] = useState("ingredients");

const [checkboxStates, setCheckboxStates] = useState({
instructions: Array(recipe.instructions.length).fill(false),
ingredients: Array(recipe.ingredients.length).fill(false),
});

const handleCheckboxChange = (
type: "instructions" | "ingredients",
index: number,
checked: boolean
) => {
setCheckboxStates((prev) => ({
...prev,
[type]: prev[type].map((state, idx) => (idx === index ? checked : state)),
}));
};

return (
<Stack mb={"md"}>
<Group justify="space-between" grow mb="md">
<Button
variant={view === "ingredients" ? "filled" : "light"}
size="md"
onClick={() => setView("ingredients")}
>
Ingredients
</Button>

<Button
variant={view === "instructions" ? "filled" : "light"}
size="md"
onClick={() => setView("instructions")}
>
Instructions
</Button>
</Group>

{view === "ingredients" && (
<List listStyleType="none">
{recipe.ingredients.map((ingredient, index) => {
return (
<List.Item
styles={{
itemWrapper: {
display: "inline",
},
}}
key={index}
>
{ingredient}
</List.Item>
);
})}
</List>
)}
{view === "instructions" && <RenderedInstructions recipe={recipe} />}
</Stack>
<>
<Flex gap={"sm"} visibleFrom="sm">
<Paper miw={"30%"} withBorder radius={"xs"} p="md" shadow="xs">
<Text fw={700} size="xl" mb={"md"}>
Ingredients
</Text>
<RenderedIngredients
recipe={recipe}
checkboxStates={checkboxStates.ingredients}
onCheckboxChange={(index, checked) =>
handleCheckboxChange("ingredients", index, checked)
}
/>
</Paper>
<Paper withBorder radius={"xs"} p="md" shadow="xs">
<Text fw={700} size="xl" mb={"md"}>
Instructions
</Text>
<RenderedInstructions
recipe={recipe}
checkboxStates={checkboxStates.instructions}
onCheckboxChange={(index, checked) =>
handleCheckboxChange("instructions", index, checked)
}
/>
</Paper>
</Flex>

<Stack hiddenFrom="sm">
<Group justify="space-between" grow mb="md">
<Button
variant={view === "ingredients" ? "filled" : "light"}
size="md"
onClick={() => setView("ingredients")}
>
Ingredients
</Button>

<Button
variant={view === "instructions" ? "filled" : "light"}
size="md"
onClick={() => setView("instructions")}
>
Instructions
</Button>
</Group>

{view === "ingredients" && (
<RenderedIngredients
recipe={recipe}
checkboxStates={checkboxStates.ingredients}
onCheckboxChange={(index, checked) =>
handleCheckboxChange("ingredients", index, checked)
}
/>
)}
{view === "instructions" && (
<RenderedInstructions
recipe={recipe}
checkboxStates={checkboxStates.instructions}
onCheckboxChange={(index, checked) =>
handleCheckboxChange("instructions", index, checked)
}
/>
)}
</Stack>
</>
);
};
47 changes: 47 additions & 0 deletions src/components/RenderedIngredients.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Checkbox, List } from "@mantine/core";

type RenderedIngredientsProps = {
recipe: UserRecipe;
checkboxStates: boolean[];
onCheckboxChange: (index: number, checked: boolean) => void;
};

export default function RenderedIngredients({
recipe,
checkboxStates,
onCheckboxChange,
}: RenderedIngredientsProps) {
return (
<List listStyleType="none" spacing="xs">
{recipe.ingredients.map((ingredient, index) => {
return (
<List.Item
styles={{
itemWrapper: {
display: "inline",
},
}}
key={index}
>
<Checkbox
size="md"
checked={checkboxStates[index]}
onChange={(event) =>
onCheckboxChange(index, event.currentTarget.checked)
}
label={
<span
style={{
opacity: checkboxStates[index] ? 0.5 : 1,
}}
>
{ingredient}
</span>
}
/>
</List.Item>
);
})}
</List>
);
}
56 changes: 36 additions & 20 deletions src/components/RenderedInstructions.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Checkbox, List, Title } from "@mantine/core";
import { useRef } from "react";
import { Box, Checkbox, List, Text } from "@mantine/core";

type RenderProps = {
recipe: UserRecipe;
checkboxStates: boolean[];
onCheckboxChange: (index: number, checked: boolean) => void;
};

export default function RenderedInstructions({ recipe }: RenderProps) {
const labelRefs = useRef<(HTMLSpanElement | null)[]>([]);

export default function RenderedInstructions({
recipe,
checkboxStates,
onCheckboxChange,
}: RenderProps) {
if (typeof recipe.instructions[0] === "string") {
return (
<List listStyleType="none" spacing="xs">
Expand All @@ -22,18 +25,14 @@ export default function RenderedInstructions({ recipe }: RenderProps) {
>
<Checkbox
size="md"
onChange={(event) => {
if (labelRefs.current[index]) {
labelRefs.current[index].style.opacity = event.currentTarget
.checked
? "0.4"
: "1";
}
}}
checked={checkboxStates[index]}
onChange={(event) =>
onCheckboxChange(index, event.currentTarget.checked)
}
label={
<span
ref={(el) => {
labelRefs.current[index] = el;
style={{
opacity: checkboxStates[index] ? 0.5 : 1,
}}
>
{`${index + 1}. ${instruction}`}
Expand All @@ -50,9 +49,11 @@ export default function RenderedInstructions({ recipe }: RenderProps) {
{recipe.instructions.map((section, sectionIndex) => {
if (typeof section === "object" && "name" in section) {
return (
<div key={sectionIndex}>
<Title order={3}>{section.name}</Title>
<List type="ordered" spacing="xs">
<Box key={sectionIndex} pb={"sm"}>
<Text pl={"md"} ml={"lg"} fw={"bold"}>
{section.name}
</Text>
<List listStyleType="none" spacing="xs">
{section.text.map((instruction: string, index: number) => (
<List.Item
styles={{
Expand All @@ -62,11 +63,26 @@ export default function RenderedInstructions({ recipe }: RenderProps) {
}}
key={index}
>
{instruction}
<Checkbox
size="md"
checked={checkboxStates[index]}
onChange={(event) =>
onCheckboxChange(index, event.currentTarget.checked)
}
label={
<span
style={{
opacity: checkboxStates[index] ? 0.5 : 1,
}}
>
{`${index + 1}. ${instruction}`}
</span>
}
/>
</List.Item>
))}
</List>
</div>
</Box>
);
}
})}
Expand Down

0 comments on commit 687a945

Please sign in to comment.