ready for publish
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import type { GameStatus, UseScoreType } from "../../src/types";
|
||||
import { useIsMobile } from "./useIsMobile";
|
||||
import { MobileGameOverlay } from "./MobileGameOverlay";
|
||||
|
||||
type GameOverlayProps = {
|
||||
gameStatus: GameStatus;
|
||||
scoreManager: UseScoreType;
|
||||
};
|
||||
|
||||
export const GameOverlay = ({ gameStatus, scoreManager }: GameOverlayProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
if (isMobile) return <MobileGameOverlay gameStatus={gameStatus} scoreManager={scoreManager} />
|
||||
|
||||
if (gameStatus === "started") return null;
|
||||
|
||||
return (
|
||||
<GameOverlayContainer>
|
||||
{gameStatus === "waiting" && <GameWaitingContent />}
|
||||
{gameStatus === "game-over" && <GameOverContent />}
|
||||
{gameStatus === "game-won" && <GameWonContent scoreManager={scoreManager} />}
|
||||
</GameOverlayContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameWaitingContent = () => {
|
||||
return (
|
||||
<div
|
||||
className="text-[#c8d3f5] font-mono text-center select-none"
|
||||
style={{
|
||||
textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)",
|
||||
fontSize: "calc(2rem * var(--sudoku-size-multiplier))"
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col" style={{ gap: "calc(2.5rem * var(--sudoku-size-multiplier))" }}>
|
||||
<p style={{ fontSize: "calc(3rem * var(--sudoku-size-multiplier))" }}>Press <strong className="text-[#82aaff]">New Puzzle</strong> to start</p>
|
||||
<div style={{ fontSize: "calc(1.2rem * var(--sudoku-size-multiplier))" }}>
|
||||
<p style={{ marginBottom: "calc(1rem * var(--sudoku-size-multiplier))" }}><strong className="text-[#ffc777]">Navigation Mode</strong> (default):</p>
|
||||
<p><strong className="text-[#82aaff]">h</strong>,<strong className="text-[#82aaff]">j</strong>,<strong className="text-[#82aaff]">k</strong>,<strong className="text-[#82aaff]">l</strong> to move around</p>
|
||||
<p style={{ marginTop: "calc(0.5rem * var(--sudoku-size-multiplier))" }}><strong className="text-[#82aaff]">R</strong> or <strong className="text-[#82aaff]">C</strong> to enter edit mode</p>
|
||||
|
||||
<p style={{ marginTop: "calc(1.5rem * var(--sudoku-size-multiplier))", marginBottom: "calc(1rem * var(--sudoku-size-multiplier))" }}><strong className="text-[#ffc777]">Edit Mode</strong>:</p>
|
||||
<p>Type <strong className="text-[#82aaff]">1-9</strong> to fill cell</p>
|
||||
<p style={{ marginTop: "calc(0.5rem * var(--sudoku-size-multiplier))" }}><strong className="text-[#82aaff]">Esc</strong> or <strong className="text-[#82aaff]">Enter</strong> to exit edit mode</p>
|
||||
|
||||
<p className="text-[#828bb8]" style={{ marginTop: "calc(1.5rem * var(--sudoku-size-multiplier))", fontSize: "calc(0.8rem * var(--sudoku-size-multiplier))" }}>
|
||||
Try vim motions: <strong className="text-[#82aaff]">$</strong>, <strong className="text-[#82aaff]">^</strong>, <strong className="text-[#82aaff]">0</strong>, counted moves
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameWonContent = ({ scoreManager }: { scoreManager: UseScoreType }) => {
|
||||
const finalScore = scoreManager.finalScore ?? 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="text-[#c8d3f5] grid font-mono uppercase text-center select-none"
|
||||
style={{
|
||||
textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)",
|
||||
fontSize: "calc(2rem * var(--sudoku-size-multiplier))",
|
||||
gap: "calc(1rem * var(--sudoku-size-multiplier))"
|
||||
}}
|
||||
>
|
||||
<p className="text-[#c3e88d]">🎉 Puzzle Solved!</p>
|
||||
<p>Final Score:</p>
|
||||
<p className="text-[#82aaff]" style={{ fontSize: "calc(2.5rem * var(--sudoku-size-multiplier))" }}>{finalScore.toLocaleString()}</p>
|
||||
<p className="text-[#828bb8] normal-case" style={{ fontSize: "calc(0.65rem * var(--sudoku-size-multiplier))" }}>
|
||||
<span className="text-[#c099ff]">max</span>
|
||||
<span className="text-[#c8d3f5]">(</span>
|
||||
<span className="text-[#ff966c]">0</span>
|
||||
<span className="text-[#c8d3f5]">, </span>
|
||||
<span className="text-[#ff966c]">100000</span>
|
||||
<span className="text-[#c8d3f5]"> - </span>
|
||||
<span className="text-[#82aaff]">timeInSeconds</span>
|
||||
<span className="text-[#c8d3f5]"> * </span>
|
||||
<span className="text-[#ff966c]">10</span>
|
||||
<span className="text-[#c8d3f5]">)</span>
|
||||
</p>
|
||||
<p className="text-[#828bb8]">Click <strong className="text-[#ffc777] normal-case">New Puzzle</strong> to play again</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameOverContent = () => {
|
||||
return (
|
||||
<div
|
||||
className="text-[#ff757f] font-mono uppercase text-center select-none"
|
||||
style={{ textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)" }}
|
||||
>
|
||||
<p style={{ fontSize: "calc(5rem * var(--sudoku-size-multiplier))" }}>Game Over</p>
|
||||
<p className="text-[#828bb8]" style={{ fontSize: "calc(2rem * var(--sudoku-size-multiplier))" }}>Press <strong className="text-[#ffc777] normal-case">i</strong> to play again</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameOverlayContainer = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="
|
||||
absolute inset-0
|
||||
flex justify-center items-center
|
||||
z-40
|
||||
pointer-events-none
|
||||
"
|
||||
style={{
|
||||
backgroundColor: 'rgba(27, 29, 43, 0.75)',
|
||||
backdropFilter: 'blur(1px)'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { GameStatus, UseScoreType } from "../../src/types";
|
||||
|
||||
type MobileGameOverlayProps = {
|
||||
gameStatus: GameStatus;
|
||||
scoreManager: UseScoreType;
|
||||
};
|
||||
|
||||
export const MobileGameOverlay = ({ gameStatus, scoreManager }: MobileGameOverlayProps) => {
|
||||
if (gameStatus === "started") return null;
|
||||
|
||||
return (
|
||||
<GameOverlayContainer>
|
||||
{gameStatus === "waiting" && <GameWaitingContent />}
|
||||
{gameStatus === "game-over" && <GameOverContent />}
|
||||
{gameStatus === "game-won" && <GameWonContent scoreManager={scoreManager} />}
|
||||
</GameOverlayContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameWaitingContent = () => {
|
||||
return (
|
||||
<div
|
||||
className="text-[#c8d3f5] font-mono text-center select-none"
|
||||
style={{
|
||||
textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)",
|
||||
fontSize: "calc(1rem * var(--sudoku-size-multiplier))"
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col" style={{ gap: "calc(0.75rem * var(--sudoku-size-multiplier))" }}>
|
||||
<p style={{ fontSize: "calc(1.5rem * var(--sudoku-size-multiplier))" }}>Tap New Puzzle to start</p>
|
||||
<p>VIM motions available <br /> via on-screen keyboard</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameWonContent = ({ scoreManager }: { scoreManager: UseScoreType }) => {
|
||||
const finalScore = scoreManager.finalScore ?? 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="text-[#c8d3f5] grid font-mono uppercase text-center select-none"
|
||||
style={{
|
||||
textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)",
|
||||
fontSize: "calc(1rem * var(--sudoku-size-multiplier))",
|
||||
gap: "calc(1rem * var(--sudoku-size-multiplier))"
|
||||
}}
|
||||
>
|
||||
<p className="text-[#c3e88d]">🎉 Solved!</p>
|
||||
<p>Final Score:</p>
|
||||
<p className="text-[#82aaff]" style={{ fontSize: "calc(2.5rem * var(--sudoku-size-multiplier))" }}>{finalScore.toLocaleString()}</p>
|
||||
<p className="text-[#828bb8] normal-case" style={{ fontSize: "calc(0.65rem * var(--sudoku-size-multiplier))" }}>
|
||||
<span className="text-[#c099ff]">max</span>
|
||||
<span className="text-[#c8d3f5]">(</span>
|
||||
<span className="text-[#ff966c]">0</span>
|
||||
<span className="text-[#c8d3f5]">, </span>
|
||||
<span className="text-[#ff966c]">100000</span>
|
||||
<span className="text-[#c8d3f5]"> - </span>
|
||||
<span className="text-[#82aaff]">time</span>
|
||||
<span className="text-[#c8d3f5]"> * </span>
|
||||
<span className="text-[#ff966c]">10</span>
|
||||
<span className="text-[#c8d3f5]">)</span>
|
||||
</p>
|
||||
<p className="text-[#828bb8]">Tap to play again</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameOverContent = () => {
|
||||
return (
|
||||
<div
|
||||
className="text-[#ff757f] font-mono uppercase text-center select-none"
|
||||
style={{
|
||||
textShadow: "2px 2px 4px rgba(27, 29, 43, 0.9)",
|
||||
fontSize: "calc(5rem * var(--sudoku-size-multiplier))"
|
||||
}}
|
||||
>
|
||||
Game Over
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GameOverlayContainer = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className="
|
||||
absolute inset-0
|
||||
flex justify-center items-center
|
||||
z-40
|
||||
pointer-events-none
|
||||
"
|
||||
style={{
|
||||
backgroundColor: 'rgba(27, 29, 43, 0.75)',
|
||||
backdropFilter: 'blur(1px)'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--sudoku-size-multiplier: 3;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
export default {
|
||||
content: [
|
||||
'./src/**/*.{js,ts,jsx,tsx}',
|
||||
'../src/**/*.{js,ts,jsx,tsx}',
|
||||
],
|
||||
} satisfies Config
|
||||
Reference in New Issue
Block a user