Dev #1
+12
-2
@@ -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; }
|
||||
|
||||
+66
-21
@@ -30,15 +30,22 @@
|
||||
{% endif %}
|
||||
|
||||
{% macro render_tree_node(el) %}
|
||||
<div class="treeNode">
|
||||
<a class="treeRowLink {{ 'selected' if el.id == selected_id else '' }}"
|
||||
href="{{ url_for('screen_edit', slug=game.slug, screen_id=screen.id, selected=el.id) }}"
|
||||
data-element-id="{{ el.id }}"
|
||||
oncontextmenu="return showElementContextMenu(event, {{ el.id }});">
|
||||
<span class="treeIcon">{{ widgets.get(el.widget, {}).get('icon', '❔') }}</span>
|
||||
<span class="treeLabel">{{ el|elabel }}</span>
|
||||
</a>
|
||||
{% set kids = children_of.get(el.id, []) %}
|
||||
{% set kids = children_of.get(el.id, []) %}
|
||||
<div class="treeNode" data-element-id="{{ el.id }}">
|
||||
<div class="treeRow">
|
||||
{% if kids %}
|
||||
<button type="button" class="treeToggle" data-node-id="{{ el.id }}" aria-label="Réduire / déplier">▾</button>
|
||||
{% else %}
|
||||
<span class="treeToggleSpacer"></span>
|
||||
{% endif %}
|
||||
<a class="treeRowLink {{ 'selected' if el.id == selected_id else '' }}"
|
||||
href="{{ url_for('screen_edit', slug=game.slug, screen_id=screen.id, selected=el.id) }}"
|
||||
data-element-id="{{ el.id }}"
|
||||
oncontextmenu="return showElementContextMenu(event, {{ el.id }});">
|
||||
<span class="treeIcon">{{ widgets.get(el.widget, {}).get('icon', '❔') }}</span>
|
||||
<span class="treeLabel">{{ el|elabel }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% if kids %}
|
||||
<div class="treeChildren">
|
||||
{% for child in kids %}
|
||||
@@ -53,6 +60,17 @@
|
||||
{% set el = elements|selectattr("id", "equalto", selected_id)|first if selected_id else none %}
|
||||
<!-- Panneau gauche : ajouter un élément (icônes, sans jargon technique) -->
|
||||
<div class="builderPanel">
|
||||
<h2>Éléments de cet écran</h2>
|
||||
{% if top_level_elements %}
|
||||
<div class="elementTree">
|
||||
{% for el in top_level_elements %}
|
||||
{{ render_tree_node(el) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="hint">Aucun élément pour l'instant.</p>
|
||||
{% endif %}
|
||||
|
||||
<details class="propGroup">
|
||||
<summary>Ajouter un élément sur l'écran</summary>
|
||||
<div class="propGroupBody">
|
||||
@@ -95,17 +113,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<h2>Éléments de cet écran</h2>
|
||||
{% if top_level_elements %}
|
||||
<div class="elementTree">
|
||||
{% for el in top_level_elements %}
|
||||
{{ render_tree_node(el) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="hint">Aucun élément pour l'instant.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Menu contextuel (clic droit sur un élément de l'arborescence) -->
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user