diff --git a/static/js/scenes/collision-rules-editor.js b/static/js/scenes/collision-rules-editor.js index 9e1be956..c173c069 100644 --- a/static/js/scenes/collision-rules-editor.js +++ b/static/js/scenes/collision-rules-editor.js @@ -37,22 +37,28 @@ function collisionRuleActionSummary(action) { return COLLISION_ACTION_LABELS[action.type] || action.type; } -// Une règle = une chaîne de "maillons" (déclencheur -> action -> sous-action -// éventuelle) — chacun sa propre petite carte, visuellement emboîtées. +// Une règle = une chaîne de "maillons" en forme de pièces qui s'emboîtent +// (déclencheur -> action -> sous-action éventuelle), pas une liste plate — +// chaque maillon porte un data-kind (couleur en CSS) et un délai d'entrée +// en cascade (voir @keyframes collisionLinkIn, static/style.css). function collisionRuleChainHtml(rule, ruleIndex, objectId) { - var links = [collisionRuleTriggerSummary(rule), collisionRuleActionSummary(rule.action)]; + var links = [ + { kind: 'trigger', text: collisionRuleTriggerSummary(rule) }, + { kind: rule.action.type, text: collisionRuleActionSummary(rule.action) }, + ]; var action = rule.action; while (action.type === 'interagir' && action.sub_action) { - links.push(collisionRuleActionSummary(action.sub_action)); + links.push({ kind: action.sub_action.type, text: collisionRuleActionSummary(action.sub_action) }); action = action.sub_action; } - var linksHtml = links.map(function (text) { - return '' + text.replace(/'; - }).join('→'); + var linksHtml = links.map(function (link, i) { + return '' + + link.text.replace(/'; + }).join(''); return ( '
' + '' + - '' + '
' ); @@ -69,7 +75,7 @@ function collisionRuleCardHtml(obj) { '

' + collisionRuleObjectLabel(obj).replace(/' + '' + '
' + rulesHtml + '
' + - '' + + '' + '' ); } @@ -81,7 +87,11 @@ function renderCollisionRuleCards() { container.innerHTML = '

Aucun objet éligible pour l\'instant — un décor, un ennemi ou un PNJ (jamais le joueur lui-même).

'; return; } - container.innerHTML = COLLISION_RULE_OBJECTS.map(collisionRuleCardHtml).join(''); + // Chaque carte entre en cascade (voir @keyframes collisionCardIn, + // static/style.css) plutôt que toutes en même temps. + container.innerHTML = COLLISION_RULE_OBJECTS.map(function (obj, i) { + return collisionRuleCardHtml(obj).replace('
' + - '' + - '
' + - '' + - '' + - 'px' + - '
' + + '' + + '' + '
'; } @@ -164,8 +187,11 @@ function collisionWizardChooseTrigger(trigger) { // quelle action exécuter. function renderCollisionWizardActionStep(nested) { var types = nested ? COLLISION_ACTION_TYPES.filter(function (t) { return t !== 'interagir'; }) : COLLISION_ACTION_TYPES; - var buttons = types.map(function (type) { - return ''; + var buttons = types.map(function (type, i) { + var split = collisionSplitEmojiLabel(COLLISION_ACTION_LABELS[type]); + return ''; }).join(''); collisionWizardBodyEl().innerHTML = '

' + (nested ? 'Une fois la touche pressée...' : 'Alors...') + '

' + @@ -223,8 +249,10 @@ function collisionWizardChooseAction(type, nested) { collisionWizardBodyEl().innerHTML = '

Aucun événement personnalisé pour l\'instant — crée-en un dans l\'onglet "📣 Événements", puis reviens ici.

'; return; } - var buttons = eventIds.map(function (id) { - return ''; + var buttons = eventIds.map(function (id, i) { + return ''; }).join(''); collisionWizardBodyEl().innerHTML = '

Quel événement ?

' + buttons + '
'; } diff --git a/static/style.css b/static/style.css index ecc17843..930f00bd 100644 --- a/static/style.css +++ b/static/style.css @@ -676,28 +676,80 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d métaphore "pièces qui s'emboîtent" demandée, sans un vrai graphe à tracer à la souris (l'assistant "+ Action" construit la chaîne pas à pas, voir openCollisionRuleModal). */ -.collisionRulesCards{ display:flex; flex-direction:column; gap:14px; margin-top:12px; } +@keyframes collisionCardIn{ from{ opacity:0; transform:translateY(10px) scale(.97); } to{ opacity:1; transform:none; } } +@keyframes collisionLinkIn{ from{ opacity:0; transform:translateX(-10px) scale(.9); } to{ opacity:1; transform:none; } } +@keyframes collisionChoiceIn{ from{ opacity:0; transform:translateY(8px) scale(.92); } to{ opacity:1; transform:none; } } + +.collisionRulesCards{ display:grid; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); gap:18px; margin-top:14px; } .collisionRuleCard{ - background:var(--panel); border:1px solid var(--border); border-radius:10px; padding:14px; + position:relative; background:linear-gradient(165deg, var(--panel), var(--panel2)); + border:1px solid var(--border); border-radius:16px; padding:16px; + box-shadow:0 6px 18px rgba(0,0,0,.18); animation:collisionCardIn .35s ease backwards; + transition:transform .18s ease, box-shadow .18s ease; } -.collisionRuleCardHeader{ display:flex; align-items:center; gap:10px; margin-bottom:8px; } +.collisionRuleCard:hover{ transform:translateY(-3px); box-shadow:0 10px 26px rgba(0,0,0,.28); } +.collisionRuleCardHeader{ display:flex; align-items:center; gap:10px; margin-bottom:12px; } .collisionRuleCardThumb{ - width:36px; height:36px; border-radius:6px; overflow:hidden; background:var(--panel2); + width:40px; height:40px; border-radius:10px; overflow:hidden; background:var(--panel2); display:flex; align-items:center; justify-content:center; flex-shrink:0; + border:1px solid var(--border); box-shadow:inset 0 0 0 2px rgba(255,255,255,.03); } .collisionRuleCardThumb img{ max-width:100%; max-height:100%; object-fit:contain; } -.collisionRuleCardTitle{ margin:0; font-size:14px; } -.collisionRuleCardRules{ display:flex; flex-direction:column; gap:8px; margin-bottom:10px; } +.collisionRuleCardTitle{ margin:0; font-size:15px; font-weight:700; } +.collisionRuleCardRules{ display:flex; flex-direction:column; gap:10px; margin-bottom:14px; } + +/* Une règle = une "chaîne" de maillons en forme de pièces qui s'emboîtent + (déclencheur -> action -> sous-action éventuelle), pas une simple liste + de pastilles à plat — chaque maillon chevauche le précédent via une + marge négative + une pointe en clip-path, coloré selon son type. */ .collisionRuleChain{ - display:flex; align-items:center; justify-content:space-between; gap:8px; - background:var(--panel2); border:1px solid var(--border); border-radius:8px; padding:6px 8px; + display:flex; align-items:center; gap:10px; background:rgba(0,0,0,.15); + border:1px solid var(--border); border-radius:12px; padding:8px 10px; } -.collisionRuleChainLinks{ display:flex; align-items:center; flex-wrap:wrap; gap:6px; } +.collisionRuleChainLinks{ display:flex; align-items:center; flex-wrap:wrap; row-gap:6px; } .collisionRuleLink{ - background:var(--accent); color:#fff; border-radius:12px; padding:3px 10px; font-size:12px; white-space:nowrap; + --link-color:var(--accent); + position:relative; display:inline-flex; align-items:center; gap:4px; + background:linear-gradient(135deg, var(--link-color), color-mix(in srgb, var(--link-color) 70%, #000 30%)); + color:#fff; padding:7px 16px 7px 20px; font-size:12.5px; font-weight:600; white-space:nowrap; + clip-path:polygon(0% 0%, 90% 0%, 100% 50%, 90% 100%, 0% 100%, 10% 50%); + margin-left:-12px; z-index:1; animation:collisionLinkIn .3s ease backwards; + transition:transform .15s ease, filter .15s ease; } -.collisionRuleArrow{ color:var(--forge-text-muted, #8b95a7); font-size:12px; } -.collisionWizardChoices{ display:flex; flex-direction:column; } +.collisionRuleLink:first-child{ margin-left:0; clip-path:polygon(0% 0%, 90% 0%, 100% 50%, 90% 100%, 0% 100%); padding-left:14px; border-radius:8px 0 0 8px; } +.collisionRuleLink:hover{ transform:translateY(-2px) scale(1.04); filter:brightness(1.12); z-index:2; } +.collisionRuleLink[data-kind="trigger"]{ --link-color:#f2994a; } +.collisionRuleLink[data-kind="quete"]{ --link-color:#9b59b6; } +.collisionRuleLink[data-kind="attaque"]{ --link-color:#e0524d; } +.collisionRuleLink[data-kind="evenement"]{ --link-color:#17a2b8; } +.collisionRuleLink[data-kind="interagir"]{ --link-color:#d4a72c; } +.collisionRuleChainDelete{ + margin-left:auto; flex-shrink:0; width:26px; height:26px; border-radius:50%; border:none; + background:rgba(224,82,77,.15); color:#e0524d; display:flex; align-items:center; justify-content:center; + cursor:pointer; transition:background .15s ease, transform .15s ease; font-size:13px; +} +.collisionRuleChainDelete:hover{ background:#e0524d; color:#fff; transform:scale(1.08); } + +.collisionRuleAddBtn{ + display:inline-flex; align-items:center; gap:6px; background:linear-gradient(135deg, var(--accent), color-mix(in srgb, var(--accent) 70%, #000 30%)); + color:#fff; border:none; border-radius:999px; padding:8px 16px; font-size:13px; font-weight:700; + cursor:pointer; box-shadow:0 4px 12px rgba(0,0,0,.25); transition:transform .15s ease, box-shadow .15s ease; +} +.collisionRuleAddBtn:hover{ transform:translateY(-2px) scale(1.03); box-shadow:0 6px 18px rgba(0,0,0,.35); } +.collisionRuleAddBtn:active{ transform:translateY(0) scale(.98); } + +/* Assistant "+ Action" — une grille de cartes-choix animées en cascade + plutôt qu'une pile de boutons pleine largeur. */ +.collisionWizardChoices{ display:grid; grid-template-columns:repeat(auto-fill, minmax(190px, 1fr)); gap:10px; } +.collisionWizardChoice{ + display:flex; flex-direction:column; align-items:flex-start; gap:4px; text-align:left; + background:var(--panel2); border:1px solid var(--border); border-radius:12px; padding:12px 14px; + cursor:pointer; font-size:13.5px; font-weight:600; color:inherit; + animation:collisionChoiceIn .25s ease backwards; transition:transform .15s ease, border-color .15s ease, box-shadow .15s ease; +} +.collisionWizardChoice:hover{ transform:translateY(-3px) scale(1.02); border-color:var(--accent); box-shadow:0 8px 18px rgba(0,0,0,.25); } +.collisionWizardChoice .emoji{ font-size:22px; } + .collisionInteractBubble{ display:none; position:absolute; left:50%; bottom:100%; transform:translateX(-50%); margin-bottom:6px; background:#111; color:#fff; border-radius:10px; padding:4px 10px; font-size:12px; white-space:nowrap; pointer-events:none; z-index:20; } .listRow.selected{ border-color:var(--accent); background:var(--panel2); }