diff --git a/public/index.html b/public/index.html index aa069f2..1c01e35 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - React App + CS Tournament Bingo diff --git a/public/manifest.json b/public/manifest.json index 080d6c7..6bb3093 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "CS Tournament Bingo", + "name": "CS Copenhagen Major Bingo", "icons": [ { "src": "favicon.ico", diff --git a/src/App.css b/src/App.css index 74b5e05..716ce02 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,70 @@ -.App { - text-align: center; +@font-face { + font-family: "cs_regular"; + src: url(./cs_regular.ttf) format("ttf"); + font-weight: normal; + font-style: normal; } - -.App-logo { - height: 40vmin; - pointer-events: none; +h1, span { + user-select: none; } - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; +.container { + padding: 5px; display: flex; flex-direction: column; align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); + margin: auto; +} +.bingo-board { + width: 100%; + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 3px; +} +.bingo-board > div { + width: 100%; +} +.bingo-board > div > div { + background: #E48720; + display: grid; + border-radius: .5rem; + aspect-ratio: 1; +} +.bingo-board div span { + font-size: 3.5rem; + place-self: center; color: white; } - -.App-link { - color: #61dafb; +.legend { + display: grid; + grid-template-columns: auto 1fr; + column-gap: 1rem; +} +.legend .header span { + font-weight: bold; +} +div.highlighted { + animation: fadeinout 1s linear; +} +div > div.begone { + animation: disappear 1s forwards; } -@keyframes App-logo-spin { - from { - transform: rotate(0deg); +@keyframes fadeinout { + 0% { + background-color: white; } - to { - transform: rotate(360deg); + 50% { + background-color: yellow; + } + 100% { + background-color: white; + } +} +@keyframes disappear { + 0% { + transform: scale(1) rotate(0deg); + } + 100% { + transform: scale(0) rotate(720deg); } } diff --git a/src/App.tsx b/src/App.tsx index a53698a..c990485 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,24 +1,106 @@ -import React from 'react'; -import logo from './logo.svg'; +import React, { Fragment, useEffect, useState } from 'react'; import './App.css'; -function App() { +import {prompts} from './prompts'; + +const alphabet = "ABCDEFGHIKLMNOPQRSTUVWXYZ"; +const lskey_prompts = "cs_bingo_prompts"; +const lskey_ticked = "cs_bingo_ticked"; + +const shuffle = (array: T[]): T[] => { + return array + .map((item) => ({ key: Math.random(), value: item })) + .sort((a, b) => b.key - a.key) + .map((item) => item.value); + } + +const App: React.FC<{}> = () => { + const [resetCounter, setResetCounter] = useState(0); + const [lines, setLines] = useState<[string,string][]>([]); + const saveLines = (lines: [string,string][]) => { + localStorage.setItem(lskey_prompts, JSON.stringify(lines)); + }; + const saveTicks = (lines: boolean[]) => { + localStorage.setItem(lskey_ticked, JSON.stringify(lines)); + }; + const [highlighted, setHighlighted] = useState(null); + const highlight = (index: number) => { + setHighlighted(index); + setTimeout(() => setHighlighted(null), 800); + document.getElementById("letter-" + alphabet[index])?.scrollIntoView(); + }; + useEffect(() => { + console.log("no lines"); + if (lines.length !== 0) return; + const loadLines = localStorage.getItem(lskey_prompts); + const loadTicks = localStorage.getItem(lskey_ticked); + if (loadLines === null || loadTicks === null) { + const newPrompts = shuffle(prompts).slice(0, 25) + const newTicks = new Array(25).fill(false); + setLines(newPrompts); + setTicked(newTicks); + saveLines(newPrompts); + saveTicks(newTicks); + } else { + setLines(JSON.parse(loadLines)); + setTicked(JSON.parse(loadTicks)); + } + }, [lines]) + + const [ticked, setTicked] = useState(new Array(25).fill(false) ); + const toggleField = (index: number) => { + console.log("ticking", index); + setTicked((old) => { + const newTicks = old.map((e, i) => i === index ? !e : e); + saveTicks(newTicks); + return newTicks; + }); + }; + const incCounter = () => { + setResetCounter((old) => { + if (old === 15) { + const newPrompts = shuffle(prompts).slice(0, 25) + const newTicked = new Array(25).fill(false) ; + setLines(newPrompts); + setTicked(newTicked); + saveLines(newPrompts); + saveTicks(newTicked) + } + return old + 1; + }); + } + return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
+
+

CS Bingo

+
+ {Array.from(alphabet).map((e, index) => +
+
highlight(index)} onContextMenu={(e) => {e.preventDefault(); toggleField(index);}}> + {e} +
+
+ )} +
+
+
+
+ Letter +
+
+ Goal +
+ {lines.map(([category, line], index)=> + +
+ {alphabet[index]} +
+
+ {line} +
+
+ )} +
); } diff --git a/src/cs_regular.ttf b/src/cs_regular.ttf new file mode 100644 index 0000000..b036253 Binary files /dev/null and b/src/cs_regular.ttf differ diff --git a/src/prompts.ts b/src/prompts.ts new file mode 100644 index 0000000..5dde8d3 --- /dev/null +++ b/src/prompts.ts @@ -0,0 +1,40 @@ +type Categories = "kills" | "rounds" | "fails" | "epic" | "meta" | "unexpected"; +export const prompts: [Categories, string][] = [ + ["kills", "Zeus Kill"], + ["kills", "Knife Kill"], + ["kills", "Team Kill"], + ["kills", "HE Kill"], + ["kills", "Molotov Kill"], + ["rounds", "9 rounds in a row"], + ["rounds", "10 rounds in one half"], + ["fails", "Ninja Defuse"], + ["fails", "Eco win vs Full Buy"], + ["fails", "Trigger discipline moment"], + ["fails", "Knife try"], + ["fails", "Underestimated bomb radius"], + ["fails", "T dies after time"], + ["fails", "4:3 Moment"], + ["fails", "CT forgot defuser"], + ["fails", "CS2 is bug free"], + ["fails", "Death while throwing util"], + ["fails", "#Chicken Moment"], + ["epic", "Ace"], + ["epic", "IGL > 15 kills"], + ["epic", "30 bomb"], + ["epic", "AWP Collat"], + ["epic", "Clutch 1v3"], + ["epic", "Clutch 2v5"], + ["epic", "Get down Mr. President!"], + ["meta", "Inferno picked"], + ["meta", "Double OT"], + ["meta", "Rush B Blyat"], + ["meta", "5 CTs save"], + ["unexpected", "Ladder kill"], + ["unexpected", "Kill while flashed"], + ["unexpected", "AWP noscope"], + ["unexpected", "Tech Timeout"], + ["unexpected", "Player hiding in smoke"], + ["unexpected", "150 damage with 1 HE"], +] + +export default {};