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