Implemente le mini-jeu Association (glisser-deposer par paires)
document_engine/labels/association_config.py (nouveau) : modele de
donnees, meme convention resolve_X/sanitize_X que quiz_config.py -
DEFAULT_ASSOCIATION_CONFIG, sanitize_association_config (chaque paire
doit avoir ses deux cotes non vides, sinon supprimee silencieusement ;
liste tronquee a MAX_PAIRS=8).
Cote serveur, routes/document/document_element_update.py revalide
desormais aussi l'association avant persistance (meme raisonnement que
pour le quiz). Le rendu (_render_association) affiche un resume reel
(nombre de paires) et, des qu'au moins une paire existe, un plateau de
glisser-deposer REELEMENT interactif en Mode Apercu
(_render_association_player) : les deux colonnes (termes/
correspondances) sont melangees independamment (random.shuffle,
melange d'affichage documente dans CODE_QUALITY.md) puis embarquees en
JSON dans un attribut data-assoc-config.
Cote editeur, le panneau Proprietes d'une association ("relier
visuellement deux champs qui vont ensemble") est une liste de paires
repetable, chaque ligne reliant visuellement un champ "Element" et un
champ "Correspondance" par un glyphe ↔. Le plateau jouable en Apercu
(static/document/js/document-editor.js) supporte deux facons de jouer,
toutes deux reelles : glisser-deposer HTML5 natif, ou cliquer une
carte puis son emplacement (repli pour les appareils sans support
fiable du drag) - bonne association verrouillee en vert, mauvaise
signalee puis reinitialisee, ecran de resultat une fois toutes les
paires associees.
Bug reel trouve ET corrige via simulation DOM complete (glisser-
deposer + clic simules, pas juste un chargement de page) : le
feedback visuel reutilisait la classe CSS du quiz via une
reaffectation de className qui supprimait au passage la classe
d'identite docAssocFeedback, rendant l'element introuvable des le
premier essai de match (aurait plante en usage reel des la premiere
tentative). Corrige en gardant toujours les deux classes ensemble.
Tests : 8 tests purs (tests/document/test_association_config.py, sans
Flask) + 1 test de route verifiant la sanitization a l'ecriture.
SKIP=djlint : backlog H021 pre-existant, aucun template touche ici.
ruff/mypy --strict/vulture/bandit/import-linter/eslint/stylelint tous
verts ; 50 tests document verifies frais. Verification manuelle live
complete : ajout, sanitization sur paire invalide, rendu du plateau,
et simulation DOM du gameplay reel (glisser-deposer correct/incorrect,
clic-selection, progression, ecran de resultat) - script de
diagnostic non conserve dans le depot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9fb22be1ae
commit
ce750ec697
@@ -500,33 +500,39 @@ img.docImage {
|
||||
color: var(--doc-muted);
|
||||
}
|
||||
|
||||
/* ---- Quiz : questionnaire interactif en Mode Aperçu (voir docs/plan/
|
||||
maquettes/document-formation-web.html — référence visuelle). Masqué en
|
||||
édition (seul .docMinigameBadge y est visible, voir plus haut) ; les
|
||||
deux jamais visibles en même temps. ---- */
|
||||
.docQuizPlayer {
|
||||
/* ---- Quiz/Association : mini-jeux interactifs en Mode Aperçu (voir
|
||||
docs/plan/maquettes/document-formation-web.html — référence visuelle
|
||||
du Quiz). Masqués en édition (seul .docMinigameBadge y est visible,
|
||||
voir plus haut) ; jamais les deux visibles en même temps. ---- */
|
||||
.docQuizPlayer,
|
||||
.docAssocPlayer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"] .docMinigameBadge {
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"] .docMinigameBadge,
|
||||
.docEditor3.docEditor3--preview [data-kind="association"] .docMinigameBadge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"] .docQuizPlayer {
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"] .docQuizPlayer,
|
||||
.docEditor3.docEditor3--preview [data-kind="association"] .docAssocPlayer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"] {
|
||||
.docEditor3.docEditor3--preview [data-kind="quiz"],
|
||||
.docEditor3.docEditor3--preview [data-kind="association"] {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.docEditor3.docEditor3--preview .docQuizPlayer,
|
||||
.docEditor3.docEditor3--preview .docQuizPlayer * {
|
||||
.docEditor3.docEditor3--preview .docQuizPlayer *,
|
||||
.docEditor3.docEditor3--preview .docAssocPlayer,
|
||||
.docEditor3.docEditor3--preview .docAssocPlayer * {
|
||||
/* .docEditor3--preview désactive pointer-events sur tout [data-element-id]
|
||||
(voir plus bas) pour empêcher la sélection/le déplacement pendant
|
||||
l'Aperçu — réactivé ici spécifiquement pour que le quiz reste
|
||||
RÉELLEMENT cliquable (répondre, question suivante, recommencer). */
|
||||
l'Aperçu — réactivé ici spécifiquement pour que ces mini-jeux restent
|
||||
RÉELLEMENT cliquables/glissables. */
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -731,6 +737,135 @@ img.docImage {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ---- Association : plateau de glisser-déposer (voir docs/plan/PLAN.md,
|
||||
"relier visuellement deux éléments qui vont ensemble"). Réutilise
|
||||
.docQuizFeedback/.docQuizKicker (même rôle visuel) plutôt que de
|
||||
dupliquer ces règles. ---- */
|
||||
.docAssocCard,
|
||||
.docAssocResultCard {
|
||||
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
||||
border: 1px solid var(--doc-border);
|
||||
border-radius: 16px;
|
||||
padding: 1.8rem;
|
||||
}
|
||||
|
||||
.docAssocResultCard {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.docAssocTitle {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
.docAssocMeta {
|
||||
font-size: 13px;
|
||||
color: var(--doc-muted);
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.docAssocBoard {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.docAssocColumn {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.docAssocItem,
|
||||
.docAssocSlot {
|
||||
padding: 13px 15px;
|
||||
border: 1px solid var(--doc-border);
|
||||
background: var(--doc-bg-2);
|
||||
color: var(--doc-text);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: border-color 0.15s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.docAssocItem {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.docAssocItem:hover,
|
||||
.docAssocSlot:hover {
|
||||
border-color: var(--doc-accent);
|
||||
}
|
||||
|
||||
.docAssocItem.is-selected {
|
||||
border-color: var(--doc-accent);
|
||||
box-shadow: var(--doc-glow);
|
||||
}
|
||||
|
||||
.docAssocItem.is-matched,
|
||||
.docAssocSlot.is-matched {
|
||||
border-color: var(--doc-quiz-success-border);
|
||||
background: var(--doc-quiz-success-bg);
|
||||
color: var(--doc-quiz-success-text);
|
||||
cursor: default;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.docAssocItem.is-wrong,
|
||||
.docAssocSlot.is-wrong {
|
||||
border-color: var(--doc-quiz-danger-border);
|
||||
background: var(--doc-quiz-danger-bg);
|
||||
}
|
||||
|
||||
.docAssocRestartBtn {
|
||||
margin-top: 20px;
|
||||
padding: 0.7rem 1.5rem;
|
||||
border-radius: 10px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--doc-border);
|
||||
color: var(--doc-text);
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.docAssocResultBig {
|
||||
font-size: 40px;
|
||||
line-height: 1;
|
||||
color: var(--doc-quiz-success-border);
|
||||
}
|
||||
|
||||
.docAssocResultSub {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
color: var(--doc-text);
|
||||
}
|
||||
|
||||
/* ---- Association : panneau Propriétés — paires reliées visuellement par
|
||||
un glyphe entre les deux champs (voir forgeDocRenderAssociationPairHtml,
|
||||
static/document/js/document-editor.js). ---- */
|
||||
.docAssocPairRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.docAssocPairInput {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.docAssocPairLink {
|
||||
flex-shrink: 0;
|
||||
color: var(--doc-accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.docUnknown {
|
||||
font-size: 12.5px;
|
||||
color: var(--doc-muted);
|
||||
@@ -1119,4 +1254,8 @@ img.docImage {
|
||||
.docBottomNav {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.docAssocBoard {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user