Animated almost functional scoreboard

This commit is contained in:
John Shaver 2023-01-17 15:37:29 -08:00
parent 28b5b22bdd
commit e36bb1adb2
6 changed files with 13179 additions and 47 deletions

13090
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,9 +13,13 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"react-spring": "^9.6.1",
"typescript": "^4.9.4",
"web-vitals": "^2.1.4"
},
"dev-dependencies": {
"prettier": "^2.8.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",

View file

@ -15,6 +15,9 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fredoka+One&family=Permanent+Marker&display=swap" rel="stylesheet">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
@ -24,7 +27,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>Pax's Potty Chart</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View file

@ -1,38 +1,40 @@
.App {
text-align: center;
body {
font-family: 'Fredoka One', cursive;
color: #161a19;
}
.App-logo {
height: 40vmin;
pointer-events: none;
.score-board {
font-size: 75px;
padding: 30px;
margin: 20px 30px;
border: solid #161a19 3px;
-webkit-box-shadow: 3px 3px 15px 0px rgba(10,11,25,0.16);
box-shadow: 3px 3px 15px 0px rgba(10,11,25,0.16);
border-radius: 5px;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
.row {
display: flex;
font-size: 50px;
width: auto;
gap: 15px;
padding: 10px 0;
margin: 15px 30px;
}
.cell {
width: 80px;
height: 80px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
border: solid #161a19 3px;
background-color: #D1CEC9;
-webkit-box-shadow: 3px 3px 15px 0px rgba(10,11,25,0.16);
box-shadow: 3px 3px 15px 0px rgba(10,11,25,0.16);
border-radius: 5px;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.cell.checked {
background-color: unset;
}

View file

@ -1,24 +1,57 @@
import React from 'react';
import logo from './logo.svg';
import LoginForm from './LoginForm';
import ScoreCounter from './ScoreCounter';
import { useJSONLocalStorage, useLocalStorage } from './hooks/useLocalStorage';
import './App.css';
const COLUMNS = [1,2,3,4,5,6,7,8,9,10];
interface Stats {
attempts: number;
scheduledPoops: number;
selfPoops: number;
cleanUndies: number;
streaks: number[];
currentStreakSince: number;
};
const initialStats:Stats = {
attempts: 1,
scheduledPoops: 1,
selfPoops: 1,
cleanUndies: 1,
streaks: [0,0,0,0],
currentStreakSince: Date.now()
};
function App() {
return (
const [stats, setStats] = useJSONLocalStorage<Stats>("stats", initialStats);
const [name] = useLocalStorage<string>("name", {});
const [password] = useLocalStorage<string>("password", {});
const { attempts, scheduledPoops, selfPoops, cleanUndies, streaks, currentStreakSince } = stats;
return !name || !password ? <LoginForm /> :(
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<div className="score-board">
Points: <ScoreCounter stats={stats}/>
</div>
<div className="row attempt" onClick={() => setStats({...stats, attempts: attempts + 1 })}>
{COLUMNS.map(i => <div className={`attempt cell ${i <= attempts ? "checked" : ""}`}>{i <= attempts ? "🚽" : ""}</div>)}
</div>
<div className="row scheduled-poops" onClick={() => setStats({...stats, scheduledPoops: scheduledPoops + 1 })}>
{COLUMNS.map(i => <div className={`scheduled cell ${i <= scheduledPoops ? "checked" : ""}`}>{i <= scheduledPoops ? "💩" : ""}</div>)}
</div>
<div className="row self-poops" onClick={() => setStats({...stats, selfPoops: selfPoops + 1 })}>
{COLUMNS.map(i => <div className={`self cell ${i <= selfPoops ? "checked" : ""}`}>{i <= selfPoops ? "💩" : ""}</div>)}
</div>
<div className="row clean-undies" onClick={() => setStats({...stats, cleanUndies: cleanUndies + 1 })}>
{COLUMNS.map(i => <div className={`clean-undies cell ${i <= cleanUndies ? "checked" : ""}`}>{i <= cleanUndies ? "🩲" : ""}</div>)}
</div>
<div className="streak-counter"></div>
</div>
);
}

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"lib": [
"dom",
"dom.iterable",