first commit
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
reactHooks.configs.flat.recommended,
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.browser,
|
||||||
|
parserOptions: { ecmaFeatures: { jsx: true } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<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=Karla:ital,wght@0,200..800;1,200..800&display=swap"
|
||||||
|
rel="stylesheet">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="stylesheet" href="src/index.css">
|
||||||
|
<title>Tenzies</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script src="src/index.jsx" type="module"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
Generated
+1024
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "Scrim-s06j04g",
|
||||||
|
"description": "https://scrimba.com/learn-react-c0e/~02si",
|
||||||
|
"scripts": {
|
||||||
|
"start": "vite",
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^5.1.11",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-confetti": "6.1.0",
|
||||||
|
"react-dom": "18.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^6.0.2",
|
||||||
|
"vite": "^8.0.14"
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
import { useState, useRef, useEffect } from "react"
|
||||||
|
import Die from "./Die"
|
||||||
|
import { nanoid } from "nanoid"
|
||||||
|
import Confetti from "react-confetti"
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [dice, setDice] = useState(() => generateAllNewDice())
|
||||||
|
const buttonRef = useRef(null)
|
||||||
|
|
||||||
|
const gameWon = dice.every(die => die.isHeld) &&
|
||||||
|
dice.every(die => die.value === dice[0].value)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (gameWon) {
|
||||||
|
buttonRef.current.focus()
|
||||||
|
}
|
||||||
|
}, [gameWon])
|
||||||
|
|
||||||
|
function generateAllNewDice() {
|
||||||
|
return new Array(10)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => ({
|
||||||
|
value: Math.ceil(Math.random() * 6),
|
||||||
|
// value: 5,
|
||||||
|
isHeld: false,
|
||||||
|
id: nanoid()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function rollDice() {
|
||||||
|
if (!gameWon) {
|
||||||
|
setDice(oldDice => oldDice.map(die =>
|
||||||
|
die.isHeld ?
|
||||||
|
die :
|
||||||
|
{ ...die, value: Math.ceil(Math.random() * 6) }
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
setDice(generateAllNewDice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hold(id) {
|
||||||
|
setDice(oldDice => oldDice.map(die =>
|
||||||
|
die.id === id ?
|
||||||
|
{ ...die, isHeld: !die.isHeld } :
|
||||||
|
die
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
const diceElements = dice.map(dieObj => (
|
||||||
|
<Die
|
||||||
|
key={dieObj.id}
|
||||||
|
value={dieObj.value}
|
||||||
|
isHeld={dieObj.isHeld}
|
||||||
|
hold={() => hold(dieObj.id)}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
{gameWon && <Confetti />}
|
||||||
|
<div aria-live="polite" className="sr-only">
|
||||||
|
{gameWon && <p>Congratulations! You won! Press "New Game" to start again.</p>}
|
||||||
|
</div>
|
||||||
|
<h1 className="title">Tenzies</h1>
|
||||||
|
<p className="instructions">Roll until all dice are the same. Click each die to freeze it at its current value between rolls.</p>
|
||||||
|
<div className="dice-container">
|
||||||
|
{diceElements}
|
||||||
|
</div>
|
||||||
|
<button ref={buttonRef} className="roll-dice" onClick={rollDice}>
|
||||||
|
{gameWon ? "New Game" : "Roll"}
|
||||||
|
</button>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
export default function Die(props) {
|
||||||
|
const styles = {
|
||||||
|
backgroundColor: props.isHeld ? "#59E391" : "white"
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
style={styles}
|
||||||
|
onClick={props.hold}
|
||||||
|
aria-pressed={props.isHeld}
|
||||||
|
aria-label={`Die with value ${props.value},
|
||||||
|
${props.isHeld ? "held" : "not held"}`}
|
||||||
|
>{props.value}</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Karla, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #0B2434;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#root {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 400px;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 40px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dice-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template: auto auto / repeat(5, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: Karla, sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dice-container button {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
background-color: white;
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.roll-dice {
|
||||||
|
height: 50px;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: auto;
|
||||||
|
padding: 6px 21px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #5035FF;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import ReactDOM from "react-dom/client"
|
||||||
|
import App from "./App"
|
||||||
|
|
||||||
|
ReactDOM
|
||||||
|
.createRoot(document.getElementById("root"))
|
||||||
|
.render(<App />)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import {defineConfig} from "vite"
|
||||||
|
import react from "@vitejs/plugin-react"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
react()
|
||||||
|
]
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user