From d8ab71ddeb041769a28e3b04c9cbe8853e9ea7d6 Mon Sep 17 00:00:00 2001 From: william Date: Sun, 23 Aug 2026 20:25:05 +0200 Subject: [PATCH] Highlight the selected element on the canvas even when it's nested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A top-level element already got a selection border via its .canvasElement wrapper, but a child posed inside a container/répéteur/ groupe de champs has no such separate frame, so selecting it from the tree gave no visual feedback on the canvas at all. applySelectionHighlight() now targets the element's own tag directly via data-element-id (present on every rendered element, nested or not) and outlines it, re-run after every canvas refresh path (initial load, full pjax navigation, partial panel refresh, and the autosave-only canvas refresh). Co-Authored-By: Claude Sonnet 5 --- static/style.css | 7 +++++++ templates/screen_edit.html | 22 ++++++++++++++++++++++ tests/test_screens_and_elements.py | 16 ++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/static/style.css b/static/style.css index f4f7d29f..3e297371 100644 --- a/static/style.css +++ b/static/style.css @@ -143,6 +143,13 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d .canvasElement:hover{ border-color:rgba(91,140,255,.5); } .canvasElement.selected{ border:2px solid var(--accent); } .canvasElementInner{ width:100%; height:100%; pointer-events:none; } + +/* Surbrillance de l'élément sélectionné dans l'arborescence, posée en JS sur + sa propre balise (voir applySelectionHighlight) — seul moyen de la voir + sur le canevas quand cet élément est imbriqué dans un conteneur/répéteur/ + groupe de champs, qui n'a pas de cadre ".canvasElement" séparé comme un + élément posé directement sur l'écran. */ +.elementHighlighted{ outline:2px solid var(--accent); outline-offset:-1px; } .resizeHandle{ position:absolute; right:0; bottom:0; width:14px; height:14px; cursor:nwse-resize; background:var(--accent); border-radius:3px 0 6px 0; opacity:.85; diff --git a/templates/screen_edit.html b/templates/screen_edit.html index fcc8a972..38b61775 100644 --- a/templates/screen_edit.html +++ b/templates/screen_edit.html @@ -655,6 +655,26 @@ function restoreTreeCollapsedState() { }); } +// ---------- Surbrillance sur le canevas de l'élément sélectionné ---------- +// +// Un élément posé DIRECTEMENT sur l'écran a déjà son cadre de sélection via +// la classe "canvasElement.selected" (posée côté serveur). Un élément +// imbriqué dans un conteneur/répéteur/groupe de champs n'a pas ce cadre +// séparé — sa propre balise porte "data-element-id" (voir visible_attrs.py) +// mais rien ne la mettait en valeur, donc sélectionner un enfant dans +// l'arborescence ne se voyait pas du tout sur le canevas. Ce contour +// générique cible directement cette balise, quelle que soit sa profondeur. +function applySelectionHighlight(id) { + var canvas = document.getElementById('canvas'); + if (!canvas) return; + canvas.querySelectorAll('.elementHighlighted').forEach(function (n) { + n.classList.remove('elementHighlighted'); + }); + if (!id) return; + var node = canvas.querySelector('[data-element-id="' + id + '"]'); + if (node) node.classList.add('elementHighlighted'); +} + if (!window.__forgeTreeToggleBound) { window.__forgeTreeToggleBound = true; document.addEventListener('click', function (e) { @@ -714,6 +734,7 @@ function refreshCanvasOnly() { var curCanvas = document.getElementById("canvas"); if (newCanvas && curCanvas) { curCanvas.innerHTML = newCanvas.innerHTML; + applySelectionHighlight(CURRENT_SELECTED_ID); } // Aperçu du contenu dans l'arborescence de gauche (ex: texte tronqué) — // sans toucher au panneau de droite, pour ne jamais faire perdre le @@ -925,6 +946,7 @@ function initBuilderPanel() { bindPropsAutosave(); bindJaugeDefinitionSelect(); restoreTreeCollapsedState(); + applySelectionHighlight(CURRENT_SELECTED_ID); } // Jauge : quand on change l'objet de données à suivre SANS quitter le diff --git a/tests/test_screens_and_elements.py b/tests/test_screens_and_elements.py index b8fad436..ccf5d142 100644 --- a/tests/test_screens_and_elements.py +++ b/tests/test_screens_and_elements.py @@ -46,6 +46,22 @@ def test_add_element_and_save_properties(client, game): assert "Bienvenue" in html +def test_nested_child_carries_data_element_id_for_canvas_highlight(client, game): + """Régression : le contour de sélection JS de l'éditeur (voir + applySelectionHighlight dans screen_edit.html) cible la balise de + l'élément via son attribut "data-element-id" — le seul repère possible + pour un enfant imbriqué, qui n'a pas de cadre ".canvasElement" séparé + comme un élément posé directement sur l'écran (voir visible_attrs.py).""" + screen_id = _create_screen(client, game) + resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "conteneur"}) + container_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) + resp = client.post(f"/game/{game}/elements/{container_id}/children/add", data={"widget": "titre"}) + child_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) + + html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() + assert f'data-element-id="{child_id}"' in html + + def test_container_child_flex_row_no_collision(client, game): """Régression : un conteneur en disposition 'ligne' ne doit plus forcer chacun de ses enfants à 100% de largeur (voir _style_string /