Architecture¶
Arborescence¶
RandomDeadlock/
├── index.html # page HTML d'entrée (div#root + /src/main.jsx)
├── vite.config.js # plugins Tailwind + React SWC, serveur de dev
├── eslint.config.js # configuration ESLint « flat »
├── package.json # scripts et dépendances (npm, package-lock.json)
├── public/ # copié tel quel à la racine du site
│ ├── Deadlock_Render_Heroes/ # images en grand des héros
│ ├── deadlockCards/ # cartes miniatures (grille)
│ ├── deadlockTypo/ # noms stylisés
│ └── vite.svg # favicon (celui du template Vite)
└── src/
├── main.jsx # monte <App /> dans #root, en StrictMode
├── App.jsx # toute l'application
├── index.css # @import "tailwindcss" + @plugin "daisyui"
└── assets/react.svg # reste du template, non utilisé
Toute la logique tient dans src/App.jsx, qui contient un seul composant.
Composant App¶
État¶
| Variable | Initialisation | Rôle |
|---|---|---|
heroLists |
useState([...]) |
Liste en dur des 32 héros, sous forme d'objets { name: "..." }. Elle n'a pas de setter, donc elle ne change jamais. |
choosedHero |
useState(null) |
Héros actuellement tiré |
reset |
useState(false) |
Booléen inversé à chaque clic. Il sert uniquement à relancer l'effet de tirage. |
Logique de tirage¶
useEffect(() => {
const randomIndex = Math.floor(Math.random() * heroLists.length)
setChoosedHero(heroLists[randomIndex])
}, [reset])
- L'index est uniforme entre
0etheroLists.length - 1. - Le bouton exécute
setReset(!reset). La dépendance de l'effet change, ce qui provoque un nouveau tirage. - Tant que
choosedHerovautnull(au tout premier rendu), le composant afficheChargement....
flowchart TD
A[Montage de App] --> B["useEffect : index = random × 32"]
B --> C[setChoosedHero]
C --> D[Rendu : grille + image du héros + nom + bouton]
D -->|clic « Choisir à nouveau »| E["setReset(!reset)"]
E --> B
StrictMode en développement
main.jsx enveloppe l'application dans <StrictMode>. En dev, React exécute alors l'effet deux fois au montage, ce qui fait deux tirages d'affilée. Le résultat n'est pas visible à l'écran et ce double appel n'existe pas en production.
Rendu¶
| Zone | Classes principales | Contenu |
|---|---|---|
| Conteneur | flex max-h-screen bg-black text-white |
Disposition en deux colonnes |
| Grille (gauche) | grid grid-cols-2 md:grid-cols-4 w-1/3 overflow-y-auto |
Une carte de 88×119 px par héros, dans l'ordre de heroLists, avec le nom en surimpression (bg-black/50) |
| Héros tiré (droite) | flex-1, image h-screen object-contain |
Image du héros en grand, puis par-dessus, en position absolue : l'image du nom et le bouton daisyUI btn btn-primary |
Assets¶
Les images se trouvent dans public/. Vite les sert à la racine du site (/deadlockCards/...) et les copie telles quelles dans dist/ au moment du build.
| Dossier | Usage | Nom de fichier | Format |
|---|---|---|---|
public/Deadlock_Render_Heroes/ |
Image en grand du héros tiré | {Nom}_Render.png |
PNG de 1112 à 2080 px de large et 1024 à 1440 px de haut, 1 à 2,9 Mo chacun (≈ 72 Mo au total) |
public/deadlockCards/ |
Cartes de la grille | 88px-{Nom}_card.png |
PNG 88×119 px |
public/deadlockTypo/ |
Nom stylisé du héros tiré | 120px-{Nom}_name.png |
PNG de 120 px de large, hauteur variable |
Chaque dossier contient 32 fichiers, un par héros de heroLists.
Convention {Nom}¶
{Nom} correspond au champ name du héros, dont chaque suite d'espaces est remplacée par _ (name.replace(/\s+/g, '_')). Tout le reste est conservé : majuscules, &, accents.
name dans heroLists |
{Nom} |
Fichier de carte |
|---|---|---|
Abrams |
Abrams |
88px-Abrams_card.png |
Grey Talon |
Grey_Talon |
88px-Grey_Talon_card.png |
Mo & Krill |
Mo_&_Krill |
88px-Mo_&_Krill_card.png |
The Doorman |
The_Doorman |
88px-The_Doorman_card.png |
Fichier manquant ou mal nommé
- Pour les cartes et l'image en grand, un handler
onErrormasque l'image (display: none) : aucune erreur n'apparaît, le héros s'affiche simplement sans image. - L'image du nom n'a ni
onErrornialt. Si elle manque, le navigateur affiche une icône d'image cassée. - En production (Linux), les noms de fichiers sont sensibles à la casse.
Ajouter un nouveau héros¶
Exemple avec un héros fictif nommé Nouveau Heros :
-
Préparer les 3 images, nommées selon la convention :
-
Déclarer le héros dans
heroLists(src/App.jsx). Sa position dans le tableau fixe sa place dans la grille : -
Vérifier en local avec
npm run dev(Développement) : la carte doit apparaître dans la grille, et l'image en grand ainsi que le nom doivent s'afficher quand le héros est tiré. - Pousser sur
main: Dokploy redéploie automatiquement (Déploiement).
Caractères à éviter dans name
Le nom sert tel quel dans l'URL de l'image. Évitez #, ? et % : # et ? couperaient l'URL et % serait lu comme le début d'un encodage. Les espaces sont acceptés, puisqu'ils sont convertis en _.
Limites connues¶
- Liste en dur : les héros sont codés dans
App.jsxet doivent rester synchronisés à la main avec les trois dossiers d'images. - Images en grand lourdes : chaque tirage charge un PNG de 1 à 3 Mo. Les convertir en WebP ou AVIF réduirait nettement le poids.
- Tirages consécutifs identiques possibles, car rien n'exclut le héros précédent.
- Dépendances de l'effet :
heroListsest utilisé dans leuseEffectmais absent de son tableau de dépendances. Sans conséquence ici, puisque la liste ne change jamais, mais la règlereact-hooks/exhaustive-depsle signale normalement.