Corrige "Ouvrir"/"Modifier" inertes dans l'onglet Blocs de logique

Deux bugs distincts, signalés par capture d'écran (bouton "Ouvrir" sans
effet, ligne d'édition affichée en permanence au lieu d'être cachée) :

1. onclick="openLogicBlockPanel({{ b.id }}, {{ b.name|tojson }})" cassait
   l'attribut HTML : tojson produit des guillemets DOUBLES (valides en
   JSON), qui terminaient prématurément l'attribut onclick="..." lui-même
   entre guillemets doubles — le gestionnaire de clic généré était donc
   tronqué et invalide, provoquant une erreur JS non interceptée qui
   arrêtait aussi tout le script restant dans la même balise <script>
   (dont l'IIFE qui devait poser window.openLogicBlockPanel). Corrigé en
   ne passant que l'id dans l'attribut et en retrouvant le nom du bloc
   côté client depuis FLOW_BLOCKS (déjà chargé) — plus aucune chaîne
   utilisateur à échapper dans un attribut HTML.

2. <tr class="hidden" id="blockEditRow..."> ne se cachait jamais : le
   CSS ne définissait .hidden que scopé (.floatPanel.hidden,
   .columnFilterMenu.hidden), jamais en règle générique — ajoutée dans
   styles/forge-custom.css.

Vérifié : 215 tests passent, syntaxe JS validée sur un scénario avec un
vrai bloc existant (reproduisant exactement la situation signalée),
onclick généré inspecté directement dans le HTML rendu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-30 11:48:25 +02:00
co-authored by Claude Sonnet 5
parent 1b7706b357
commit a445b72a6e
3 changed files with 15 additions and 7 deletions
+5
View File
@@ -404,6 +404,11 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
box-shadow:0 12px 40px rgba(0,0,0,.55); box-shadow:0 12px 40px rgba(0,0,0,.55);
} }
.floatPanel.hidden{ display:none; } .floatPanel.hidden{ display:none; }
/* Utilitaire générique (jusqu'ici .hidden n'existait que scopé à
.floatPanel/.columnFilterMenu — ex. la ligne d'édition d'un bloc de
logique dans screen_edit.html, un <tr>, avait besoin d'un .hidden qui
marche seul, sans classe compagne). */
.hidden{ display:none; }
/* Coin de redimensionnement (voir wireFloatPanelResize() dans /* Coin de redimensionnement (voir wireFloatPanelResize() dans
game_dashboard.html) — un panneau devient redimensionnable simplement en game_dashboard.html) — un panneau devient redimensionnable simplement en
lui ajoutant ce coin, JS mis à part rien d'autre à faire côté CSS. */ lui ajoutant ce coin, JS mis à part rien d'autre à faire côté CSS. */
+5
View File
@@ -212,6 +212,11 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d
box-shadow:0 12px 40px rgba(0,0,0,.55); box-shadow:0 12px 40px rgba(0,0,0,.55);
} }
.floatPanel.hidden{ display:none; } .floatPanel.hidden{ display:none; }
/* Utilitaire générique (jusqu'ici .hidden n'existait que scopé à
.floatPanel/.columnFilterMenu — ex. la ligne d'édition d'un bloc de
logique dans screen_edit.html, un <tr>, avait besoin d'un .hidden qui
marche seul, sans classe compagne). */
.hidden{ display:none; }
/* Coin de redimensionnement (voir wireFloatPanelResize() dans /* Coin de redimensionnement (voir wireFloatPanelResize() dans
game_dashboard.html) — un panneau devient redimensionnable simplement en game_dashboard.html) — un panneau devient redimensionnable simplement en
lui ajoutant ce coin, JS mis à part rien d'autre à faire côté CSS. */ lui ajoutant ce coin, JS mis à part rien d'autre à faire côté CSS. */
+5 -7
View File
@@ -216,7 +216,7 @@
<td>{% if b.element_labels %}{{ b.element_labels|join(', ') }}{% else %}<span class="hint">—</span>{% endif %}</td> <td>{% if b.element_labels %}{{ b.element_labels|join(', ') }}{% else %}<span class="hint">—</span>{% endif %}</td>
<td>{{ b.node_count }}</td> <td>{{ b.node_count }}</td>
<td class="fieldsTableActions"> <td class="fieldsTableActions">
<button type="button" class="button is-small is-primary" onclick="openLogicBlockPanel({{ b.id }}, {{ b.name|tojson }})" title="Ouvrir le graphe de ce bloc">🧩 Ouvrir</button> <button type="button" class="button is-small is-primary" onclick="openLogicBlockPanel({{ b.id }})" title="Ouvrir le graphe de ce bloc">🧩 Ouvrir</button>
<button type="button" class="button is-small" onclick="toggleBlockEditRow({{ b.id }})" title="Modifier le nom/la description">✏️</button> <button type="button" class="button is-small" onclick="toggleBlockEditRow({{ b.id }})" title="Modifier le nom/la description">✏️</button>
<form method="post" action="{{ url_for('flow_block_delete', slug=game.slug, screen_id=screen.id, block_id=b.id) }}" <form method="post" action="{{ url_for('flow_block_delete', slug=game.slug, screen_id=screen.id, block_id=b.id) }}"
onsubmit="return confirm('Supprimer le bloc « {{ b.name }} » ? Cela supprime aussi ses {{ b.node_count }} nœud(s) et leurs connexions.');" style="display:inline;"> onsubmit="return confirm('Supprimer le bloc « {{ b.name }} » ? Cela supprime aussi ses {{ b.node_count }} nœud(s) et leurs connexions.');" style="display:inline;">
@@ -2963,10 +2963,11 @@ function makeFloatPanelFullscreenable(panel, toggleBtn) {
makeFloatPanelDraggable(panel, document.getElementById('logicBlockPanelHeader')); makeFloatPanelDraggable(panel, document.getElementById('logicBlockPanelHeader'));
makeFloatPanelResizable(panel, document.getElementById('logicBlockPanelResizeHandle')); makeFloatPanelResizable(panel, document.getElementById('logicBlockPanelResizeHandle'));
makeFloatPanelFullscreenable(panel, panel.querySelector('.floatPanelFullscreenBtn')); makeFloatPanelFullscreenable(panel, panel.querySelector('.floatPanelFullscreenBtn'));
window.openLogicBlockPanel = function (blockId, blockName) { window.openLogicBlockPanel = function (blockId) {
CURRENT_BLOCK_ID = blockId; CURRENT_BLOCK_ID = blockId;
closeNodeForm(); closeNodeForm();
document.getElementById('logicBlockPanelTitle').textContent = blockName || '?'; var block = FLOW_BLOCKS.find(function (b) { return b.id === blockId; });
document.getElementById('logicBlockPanelTitle').textContent = (block && block.name) || '?';
panel.classList.remove('hidden'); panel.classList.remove('hidden');
panel._fsToggle.enter(); panel._fsToggle.enter();
renderFlow(); renderFlow();
@@ -2988,10 +2989,7 @@ function makeFloatPanelFullscreenable(panel, toggleBtn) {
var requestedTab = {{ (request.args.get('tab') or '')|tojson }}; var requestedTab = {{ (request.args.get('tab') or '')|tojson }};
if (requestedTab) switchBuilderTab(requestedTab); if (requestedTab) switchBuilderTab(requestedTab);
var requestedBlock = {{ request.args.get('block', type=int)|tojson }}; var requestedBlock = {{ request.args.get('block', type=int)|tojson }};
if (requestedBlock) { if (requestedBlock) openLogicBlockPanel(requestedBlock);
var block = FLOW_BLOCKS.find(function (b) { return b.id === requestedBlock; });
if (block) openLogicBlockPanel(block.id, block.name);
}
})(); })();
// ---------- Timeline d'animation : piste par élément, clips Animate.css ou // ---------- Timeline d'animation : piste par élément, clips Animate.css ou