Animated almost functional scoreboard
This commit is contained in:
parent
28b5b22bdd
commit
e36bb1adb2
6 changed files with 13179 additions and 47 deletions
13090
package-lock.json
generated
13090
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,9 +13,13 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-spring": "^9.6.1",
|
||||||
"typescript": "^4.9.4",
|
"typescript": "^4.9.4",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
|
"dev-dependencies": {
|
||||||
|
"prettier": "^2.8.3"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
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="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.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
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.
|
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`.
|
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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|
60
src/App.css
60
src/App.css
|
@ -1,38 +1,40 @@
|
||||||
.App {
|
body {
|
||||||
text-align: center;
|
font-family: 'Fredoka One', cursive;
|
||||||
|
color: #161a19;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-logo {
|
.score-board {
|
||||||
height: 40vmin;
|
font-size: 75px;
|
||||||
pointer-events: none;
|
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;
|
||||||
}
|
}
|
||||||
|
.row {
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
display: flex;
|
||||||
.App-logo {
|
font-size: 50px;
|
||||||
animation: App-logo-spin infinite 20s linear;
|
width: auto;
|
||||||
}
|
gap: 15px;
|
||||||
}
|
padding: 10px 0;
|
||||||
|
margin: 15px 30px;
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
}
|
||||||
min-height: 100vh;
|
|
||||||
|
.cell {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: calc(10px + 2vmin);
|
border: solid #161a19 3px;
|
||||||
color: white;
|
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 {
|
.cell.checked {
|
||||||
color: #61dafb;
|
background-color: unset;
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
65
src/App.tsx
65
src/App.tsx
|
@ -1,24 +1,57 @@
|
||||||
import React from 'react';
|
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';
|
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() {
|
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">
|
<div className="App">
|
||||||
<header className="App-header">
|
<div className="score-board">
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
Points: <ScoreCounter stats={stats}/>
|
||||||
<p>
|
</div>
|
||||||
Edit <code>src/App.tsx</code> and save to reload.
|
<div className="row attempt" onClick={() => setStats({...stats, attempts: attempts + 1 })}>
|
||||||
</p>
|
{COLUMNS.map(i => <div className={`attempt cell ${i <= attempts ? "checked" : ""}`}>{i <= attempts ? "🚽" : ""}</div>)}
|
||||||
<a
|
</div>
|
||||||
className="App-link"
|
<div className="row scheduled-poops" onClick={() => setStats({...stats, scheduledPoops: scheduledPoops + 1 })}>
|
||||||
href="https://reactjs.org"
|
{COLUMNS.map(i => <div className={`scheduled cell ${i <= scheduledPoops ? "checked" : ""}`}>{i <= scheduledPoops ? "💩" : ""}</div>)}
|
||||||
target="_blank"
|
</div>
|
||||||
rel="noopener noreferrer"
|
<div className="row self-poops" onClick={() => setStats({...stats, selfPoops: selfPoops + 1 })}>
|
||||||
>
|
{COLUMNS.map(i => <div className={`self cell ${i <= selfPoops ? "checked" : ""}`}>{i <= selfPoops ? "💩" : ""}</div>)}
|
||||||
Learn React
|
</div>
|
||||||
</a>
|
<div className="row clean-undies" onClick={() => setStats({...stats, cleanUndies: cleanUndies + 1 })}>
|
||||||
</header>
|
{COLUMNS.map(i => <div className={`clean-undies cell ${i <= cleanUndies ? "checked" : ""}`}>{i <= cleanUndies ? "🩲" : ""}</div>)}
|
||||||
|
</div>
|
||||||
|
<div className="streak-counter"></div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
|
|
Loading…
Reference in a new issue