first commit

This commit is contained in:
2026-05-26 16:15:44 +02:00
commit b7add683d5
11 changed files with 1303 additions and 0 deletions
+24
View File
@@ -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?
+1
View File
@@ -0,0 +1 @@
Projet issu de la formation React de Scrimba : Jeu de Yahtzee.
+21
View File
@@ -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
View File
@@ -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>
+1024
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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>
)
}
+91
View File
@@ -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;
}
+6
View File
@@ -0,0 +1,6 @@
import ReactDOM from "react-dom/client"
import App from "./App"
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />)
+8
View File
@@ -0,0 +1,8 @@
import {defineConfig} from "vite"
import react from "@vitejs/plugin-react"
export default defineConfig({
plugins: [
react()
]
})