Add modal
This commit is contained in:
parent
23880322c4
commit
0918fe65fa
5 changed files with 113 additions and 52 deletions
|
@ -1,5 +1,6 @@
|
||||||
import MainMenu from "./mainMenu";
|
import MainMenu from "./mainMenu";
|
||||||
import Game from "./game";
|
import Game from "./game";
|
||||||
|
import Modal from "./modal";
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
return (
|
return (
|
||||||
|
|
125
src/game.jsx
125
src/game.jsx
|
@ -1,4 +1,5 @@
|
||||||
import { useImmer } from "use-immer";
|
import { useImmer } from "use-immer";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
import Cross from "../assets/icon-x.svg?react";
|
import Cross from "../assets/icon-x.svg?react";
|
||||||
import CrossOutline from "../assets/icon-x-outline.svg?react";
|
import CrossOutline from "../assets/icon-x-outline.svg?react";
|
||||||
|
@ -7,10 +8,13 @@ import OvalOutline from "../assets/icon-o-outline.svg?react";
|
||||||
import logo from "../assets/logo.svg";
|
import logo from "../assets/logo.svg";
|
||||||
import restart from "../assets/icon-restart.svg";
|
import restart from "../assets/icon-restart.svg";
|
||||||
|
|
||||||
|
import Modal from "./modal";
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [grid, updateGrid] = useImmer(Array(9).fill(""));
|
const [grid, updateGrid] = useImmer(Array(9).fill(""));
|
||||||
const turn = grid.filter((s) => s == "").length % 2 != 0 ? "X" : "O";
|
const [modal, setModal] = useState(false);
|
||||||
|
|
||||||
|
const turn = grid.filter((s) => s == "").length % 2 != 0 ? "X" : "O";
|
||||||
const TurnOutline = turn == "X" ? CrossOutline : OvalOutline;
|
const TurnOutline = turn == "X" ? CrossOutline : OvalOutline;
|
||||||
const TurnIndicator = turn == "X" ? Cross : Oval;
|
const TurnIndicator = turn == "X" ? Cross : Oval;
|
||||||
|
|
||||||
|
@ -32,58 +36,85 @@ export default () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-4">
|
<>
|
||||||
<header className="flex items-center justify-between px-1">
|
<div className="flex flex-col space-y-4">
|
||||||
<div className="flex-1">
|
<header className="flex items-center justify-between px-1">
|
||||||
<img src={logo} alt="logo" />
|
<div className="flex-1">
|
||||||
</div>
|
<img src={logo} alt="logo" />
|
||||||
<div className="h-14 w-36 rounded-xl bg-navy-400 px-8 py-4 inner-shadow-1-navy-900">
|
|
||||||
<div className="flex items-center justify-between text-silver-700">
|
|
||||||
<TurnIndicator className="size-5" />
|
|
||||||
<p className="text-h-xs uppercase">Turn</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="h-14 w-36 rounded-xl bg-navy-400 px-8 py-4 inner-shadow-1-navy-900">
|
||||||
<div className="flex-1">
|
<div className="flex items-center justify-between text-silver-700">
|
||||||
<button
|
<TurnIndicator className="size-5" />
|
||||||
onClick={() => updateGrid(() => Array(9).fill(""))}
|
<p className="text-h-xs uppercase">Turn</p>
|
||||||
className="ml-auto flex size-14 items-center justify-center rounded-xl bg-silver-700 inner-shadow-1-silver-900 hover:bg-silver-400"
|
</div>
|
||||||
>
|
</div>
|
||||||
<img src={restart} alt="restart" />
|
<div className="flex-1">
|
||||||
</button>
|
<button
|
||||||
</div>
|
onClick={() => setModal(true)}
|
||||||
</header>
|
className="ml-auto flex size-14 items-center justify-center rounded-xl bg-silver-700 inner-shadow-1-silver-900 hover:bg-silver-400"
|
||||||
|
>
|
||||||
|
<img src={restart} alt="restart" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<main className="grid grid-cols-3 grid-rows-3 gap-5">
|
<main className="grid grid-cols-3 grid-rows-3 gap-5">
|
||||||
{grid.map((symbol, index) => (
|
{grid.map((symbol, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
disabled={symbol != ""}
|
||||||
|
className="group flex size-36 items-center justify-center rounded-2xl bg-navy-400 inner-shadow-2-navy-900"
|
||||||
|
onClick={() => {
|
||||||
|
updateGrid((grid) => {
|
||||||
|
grid[index] = turn;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderSymbol(symbol)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer className="flex items-center justify-between text-navy-700">
|
||||||
|
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-blue-700">
|
||||||
|
<p className="text-base uppercase">X (you)</p>
|
||||||
|
<p className="text-h-m uppercase">14</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-silver-700">
|
||||||
|
<p className="text-base uppercase">Ties</p>
|
||||||
|
<p className="text-h-m uppercase">32</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-yellow-700">
|
||||||
|
<p className="text-base uppercase">O (cpu)</p>
|
||||||
|
<p className="text-h-m uppercase">11</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
isOpen={modal}
|
||||||
|
className="space-y-8"
|
||||||
|
onClose={() => {
|
||||||
|
setModal(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2 className="text-h-l uppercase text-silver-700">Restart game?</h2>
|
||||||
|
<form
|
||||||
|
className="flex items-center justify-around text-navy-700"
|
||||||
|
method="dialog"
|
||||||
|
>
|
||||||
|
<button className="rounded-xl bg-silver-700 px-5 py-4 inner-shadow-1-silver-900 hover:bg-silver-400">
|
||||||
|
<p className="text-h-xs uppercase">No, cancel</p>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
key={index}
|
|
||||||
disabled={symbol != ""}
|
|
||||||
className="group flex size-36 items-center justify-center rounded-2xl bg-navy-400 inner-shadow-2-navy-900"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updateGrid((grid) => {
|
updateGrid(() => Array(9).fill(""));
|
||||||
grid[index] = turn;
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
|
className="rounded-xl bg-yellow-700 px-5 py-4 inner-shadow-1-yellow-900 hover:bg-yellow-400"
|
||||||
>
|
>
|
||||||
{renderSymbol(symbol)}
|
<p className="text-h-xs uppercase">Yes, restart</p>
|
||||||
</button>
|
</button>
|
||||||
))}
|
</form>
|
||||||
</main>
|
</Modal>
|
||||||
|
</>
|
||||||
<footer className="flex items-center justify-between text-navy-700">
|
|
||||||
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-blue-700">
|
|
||||||
<p className="text-base uppercase">X (you)</p>
|
|
||||||
<p className="text-h-m uppercase">14</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-silver-700">
|
|
||||||
<p className="text-base uppercase">Ties</p>
|
|
||||||
<p className="text-h-m uppercase">32</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex h-20 w-36 flex-col items-center justify-center rounded-2xl bg-yellow-700">
|
|
||||||
<p className="text-base uppercase">O (cpu)</p>
|
|
||||||
<p className="text-h-m uppercase">11</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||||
import logo from "../assets/logo.svg";
|
import logo from "../assets/logo.svg";
|
||||||
import Cross from "../assets/icon-x.svg?react";
|
import Cross from "../assets/icon-x.svg?react";
|
||||||
import Oval from "../assets/icon-o.svg?react";
|
import Oval from "../assets/icon-o.svg?react";
|
||||||
import { useStore, setPlayerOneSymbol } from "./store.jsx";
|
import { useStore, setPlayerOneSymbol } from "./store";
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const playerOneSymbol = useStore((state) => state.playerOneSymbol);
|
const playerOneSymbol = useStore((state) => state.playerOneSymbol);
|
||||||
|
@ -11,7 +11,7 @@ export default () => {
|
||||||
return (
|
return (
|
||||||
<div className="m-6 flex w-full max-w-lg flex-col items-center space-y-10">
|
<div className="m-6 flex w-full max-w-lg flex-col items-center space-y-10">
|
||||||
<img src={logo} alt="logo" />
|
<img src={logo} alt="logo" />
|
||||||
<div className="inner-shadow-2-navy-900 flex w-full flex-col items-center justify-center rounded-2xl bg-navy-400 p-6">
|
<div className="flex w-full flex-col items-center justify-center rounded-2xl bg-navy-400 p-6 inner-shadow-2-navy-900">
|
||||||
<h2 className="mb-6 text-h-xs uppercase text-silver-700">
|
<h2 className="mb-6 text-h-xs uppercase text-silver-700">
|
||||||
Pick player 1's mark
|
Pick player 1's mark
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -43,10 +43,10 @@ export default () => {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full space-y-5">
|
<div className="w-full space-y-5">
|
||||||
<button className="inner-shadow-2-yellow-900 flex w-full items-center justify-center rounded-2xl bg-yellow-700 p-4 hover:bg-yellow-400">
|
<button className="flex w-full items-center justify-center rounded-2xl bg-yellow-700 p-4 inner-shadow-2-yellow-900 hover:bg-yellow-400">
|
||||||
<p className="text-h-s uppercase text-navy-700">New game (vs cpu)</p>
|
<p className="text-h-s uppercase text-navy-700">New game (vs cpu)</p>
|
||||||
</button>
|
</button>
|
||||||
<button className="inner-shadow-2-blue-900 flex w-full items-center justify-center rounded-2xl bg-blue-700 p-4 hover:bg-blue-400">
|
<button className="flex w-full items-center justify-center rounded-2xl bg-blue-700 p-4 inner-shadow-2-blue-900 hover:bg-blue-400">
|
||||||
<p className="text-h-s uppercase text-navy-700">
|
<p className="text-h-s uppercase text-navy-700">
|
||||||
New game (vs player)
|
New game (vs player)
|
||||||
</p>
|
</p>
|
||||||
|
|
28
src/modal.jsx
Normal file
28
src/modal.jsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export default ({ isOpen, children, className, onClose }) => {
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
ref.current.showModal();
|
||||||
|
|
||||||
|
if (onClose) {
|
||||||
|
ref.current.addEventListener("close", onClose);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ref.current.close();
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<dialog
|
||||||
|
className="backdrop:bg-black/50 h-64 w-screen max-w-[100vw] bg-navy-700"
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<div className={className}>{children}</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
);
|
||||||
|
};
|
|
@ -46,6 +46,7 @@ export default {
|
||||||
"yellow-900": "#CC8B13",
|
"yellow-900": "#CC8B13",
|
||||||
"yellow-700": "#F2B137",
|
"yellow-700": "#F2B137",
|
||||||
"yellow-400": "#FFC860",
|
"yellow-400": "#FFC860",
|
||||||
|
black: "#000",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Outfit", "sans-serif"],
|
sans: ["Outfit", "sans-serif"],
|
||||||
|
@ -80,7 +81,7 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"h-l": [
|
"h-l": [
|
||||||
"24px",
|
"40px",
|
||||||
{
|
{
|
||||||
letterSpacing: "2.5px",
|
letterSpacing: "2.5px",
|
||||||
fontWeight: "700",
|
fontWeight: "700",
|
||||||
|
|
Reference in a new issue