diff --git a/static/style.css b/static/style.css index 79169b6b..f4f7d29f 100644 --- a/static/style.css +++ b/static/style.css @@ -160,10 +160,20 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d pour rester lisible même avec beaucoup d'éléments. */ .elementTree{ display:flex; flex-direction:column; gap:1px; max-height:55vh; overflow-y:auto; margin-bottom:14px; } .treeNode{ display:flex; flex-direction:column; } +.treeRow{ display:flex; align-items:center; gap:2px; } +.treeToggle{ + flex:0 0 auto; width:16px; height:16px; padding:0; border:none; background:none; cursor:pointer; + color:var(--text-dim); font-size:10px; display:flex; align-items:center; justify-content:center; + transition:transform .12s ease; +} +.treeToggle:hover{ color:var(--text); } +.treeToggleSpacer{ flex:0 0 auto; width:16px; height:16px; } +.treeNode.collapsed > .treeRow > .treeToggle{ transform:rotate(-90deg); } +.treeNode.collapsed > .treeChildren{ display:none; } .treeRowLink{ - display:flex; align-items:center; gap:6px; padding:4px 6px; border-radius:5px; + flex:1; display:flex; align-items:center; gap:6px; padding:4px 6px; border-radius:5px; text-decoration:none; color:var(--text); font-size:12.5px; line-height:1.5; - white-space:nowrap; overflow:hidden; + white-space:nowrap; overflow:hidden; min-width:0; } .treeRowLink:hover{ background:var(--panel2); } .treeRowLink.selected{ background:var(--accent); color:#0b0e14; font-weight:600; } diff --git a/templates/screen_edit.html b/templates/screen_edit.html index f5e39591..fcc8a972 100644 --- a/templates/screen_edit.html +++ b/templates/screen_edit.html @@ -30,15 +30,22 @@ {% endif %} {% macro render_tree_node(el) %} -
- - {{ widgets.get(el.widget, {}).get('icon', '❔') }} - {{ el|elabel }} - - {% set kids = children_of.get(el.id, []) %} +{% set kids = children_of.get(el.id, []) %} +
+
+ {% if kids %} + + {% else %} + + {% endif %} + + {{ widgets.get(el.widget, {}).get('icon', '❔') }} + {{ el|elabel }} + +
{% if kids %}
{% for child in kids %} @@ -53,6 +60,17 @@ {% set el = elements|selectattr("id", "equalto", selected_id)|first if selected_id else none %}
+

Éléments de cet écran

+ {% if top_level_elements %} +
+ {% for el in top_level_elements %} + {{ render_tree_node(el) }} + {% endfor %} +
+ {% else %} +

Aucun élément pour l'instant.

+ {% endif %} +
Ajouter un élément sur l'écran
@@ -95,17 +113,6 @@ {% endif %}
- -

Éléments de cet écran

- {% if top_level_elements %} -
- {% for el in top_level_elements %} - {{ render_tree_node(el) }} - {% endfor %} -
- {% else %} -

Aucun élément pour l'instant.

- {% endif %}
@@ -579,6 +586,7 @@ function saveGeometry(id, x, y, width, height) { var SCREEN_EDIT_URL = "{{ url_for('screen_edit', slug=game.slug, screen_id=screen.id) }}"; var CURRENT_SELECTED_ID = {{ selected_id or 'null' }}; var GAME_SLUG = "{{ game.slug }}"; +var SCREEN_ID = {{ screen.id }}; // ---------- Menu contextuel (clic droit) de l'arborescence des éléments ---------- // @@ -626,6 +634,42 @@ if (!window.__forgeContextMenuBound) { document.addEventListener('scroll', hideElementContextMenu, true); } +// ---------- Arborescence : replier/déplier un conteneur ---------- +// +// Le triangle (.treeToggle) replie/déplie SANS toucher à la sélection : le +// nom de l'élément reste un lien à part (.treeRowLink) qui, lui, sélectionne +// toujours l'élément et affiche ses réglages — cliquer sur un conteneur +// replié donne donc bien accès à ses propriétés, comme demandé. L'état +// replié/déplié est mémorisé par élément dans localStorage pour survivre à +// un changement de sélection ou un rechargement de l'éditeur. +function treeCollapseKey(nodeId) { + return 'forgeTreeCollapsed:' + GAME_SLUG + ':' + SCREEN_ID + ':' + nodeId; +} + +function restoreTreeCollapsedState() { + document.querySelectorAll('.elementTree .treeNode').forEach(function (node) { + var id = node.dataset.elementId; + var collapsed = false; + try { collapsed = localStorage.getItem(treeCollapseKey(id)) === '1'; } catch (e) {} + node.classList.toggle('collapsed', collapsed); + }); +} + +if (!window.__forgeTreeToggleBound) { + window.__forgeTreeToggleBound = true; + document.addEventListener('click', function (e) { + var btn = e.target.closest('.treeToggle'); + if (!btn) return; + e.preventDefault(); + e.stopPropagation(); + var node = btn.closest('.treeNode'); + var collapsed = node.classList.toggle('collapsed'); + try { localStorage.setItem(treeCollapseKey(node.dataset.elementId), collapsed ? '1' : '0'); } catch (err) {} + }); +} + +restoreTreeCollapsedState(); + // ---------- Navigation dans l'éditeur sans AUCUN rechargement de page ---------- // // Se déplacer dans le canevas (glisser, redimensionner, cliquer un autre @@ -676,7 +720,7 @@ function refreshCanvasOnly() { // focus/curseur pendant qu'on modifie un réglage. var newTree = doc.querySelector('.builderPanel .elementTree'); var curTree = document.querySelector('.builderPanel .elementTree'); - if (newTree && curTree) curTree.innerHTML = newTree.innerHTML; + if (newTree && curTree) { curTree.innerHTML = newTree.innerHTML; restoreTreeCollapsedState(); } }); } @@ -880,6 +924,7 @@ function initBuilderPanel() { bindPropsAutosave(); bindJaugeDefinitionSelect(); + restoreTreeCollapsedState(); } // Jauge : quand on change l'objet de données à suivre SANS quitter le