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 /