From 8da5715e5b9a9076e0d17b13a789fb4eb759b950 Mon Sep 17 00:00:00 2001 From: william Date: Mon, 24 Aug 2026 13:30:17 +0200 Subject: [PATCH] Make the screen editor's properties panel use native Bulma elements, not just Bulma-styled buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The element header/rename/id row, tabs list, position & size grid, and — most importantly — the generic control-rendering loop (used by every widget's properties panel: text, color, slider, checkbox, align, select...) now emit real field/control/label/select/checkbox/buttons-has-addons Bulma markup instead of the old custom controlRow/sliderRow/alignGroup/posGrid CSS. Removed the now-redundant custom CSS those classes used to carry. Verified structurally safe: bindPropsAutosave/submitPropsForm already used querySelectorAll/FormData (structure-agnostic), so wrapping inputs in field/control divs doesn't affect autosave; the one sibling-dependent bit (slider oninput reading nextElementSibling) was kept adjacent inside its wrapper. Confirmed end-to-end with a live save round-trip through the actual route (Bulma modifier + free-style values both persist correctly) and every widget type's panel still renders 200 OK. The flow-graph node editor (bottom panel) is not converted yet — part of its markup is built dynamically in JS (renderFlowConditionClauses), so reskinning it means updating the JS templates in lockstep with the HTML, which is more work/risk than this pass; noting it as the next piece. --- static/style.css | 20 ++-- templates/screen_edit.html | 191 +++++++++++++++++++++++-------------- 2 files changed, 123 insertions(+), 88 deletions(-) diff --git a/static/style.css b/static/style.css index ce06ad1e..9bd2ad87 100644 --- a/static/style.css +++ b/static/style.css @@ -284,23 +284,15 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d .containerContentGroup{ border-color:#3d6b3d; background:#152015; } .containerContentGroup > summary{ color:#8fd98f; } -.posGrid{ display:grid; grid-template-columns:1fr 1fr; gap:8px 12px; margin-bottom:14px; } -.posGrid label{ font-size:11px; color:var(--text-dim); display:flex; flex-direction:column; gap:4px; } -.posGrid input{ background:var(--panel2); border:1px solid var(--border); border-radius:6px; color:var(--text); padding:6px 8px; } +/* .posGrid/.controlRow/.sizeField/.sliderRow/.alignGroup : Bulma fournit + déjà la mise en page (columns/field/control/buttons has-addons, voir + screen_edit.html) — ce qui reste ici n'est que ce que Bulma ne fournit + pas nativement : l'espacement propre à un curseur (slider) et son + pastille de valeur affichée à côté. */ .posGrid input:disabled{ opacity:0.4; cursor:not-allowed; } -.sizeField{ display:flex; gap:4px; } -.sizeField input{ flex:1; min-width:0; } -.sizeField select{ width:auto; flex:0 0 auto; padding:6px 4px; } - -.controlRow{ margin-bottom:12px; } -.controlRow > label{ display:block; font-size:12px; color:var(--text-dim); margin-bottom:4px; } .sliderRow{ display:flex; align-items:center; gap:10px; } .sliderRow input[type=range]{ flex:1; } -.sliderValue{ font-size:12px; color:var(--text-dim); min-width:44px; text-align:right; } -.alignGroup{ display:flex; gap:6px; } -.alignOption{ flex:1; text-align:center; padding:8px 0; border:1px solid var(--border); border-radius:6px; cursor:pointer; } -.alignOption input{ display:none; } -.alignOption.active{ border-color:var(--accent); background:rgba(91,140,255,.15); } +.sliderValue{ min-width:44px; text-align:right; } input[type=color]{ width:52px; height:34px; padding:2px; } .propsPanel .sectionDivider, .builderPanelRight .sectionDivider{ border:none; border-top:1px solid var(--border); margin:14px 0; } diff --git a/templates/screen_edit.html b/templates/screen_edit.html index 9bb705f6..260bbc6c 100644 --- a/templates/screen_edit.html +++ b/templates/screen_edit.html @@ -151,19 +151,19 @@ {% if el %} {% set w = widgets.get(el.widget, {}) %}
-

{{ w.get('icon','❔') }} {{ el|elabel }}{% if el.name %} — {{ w.get('label', el.widget) }}{% endif %}

+

{{ w.get('icon','❔') }} {{ el|elabel }}{% if el.name %} — {{ w.get('label', el.widget) }}{% endif %}

- +
-
- - + +
+
-
- - +
+ +
{% if el.widget in container_widgets %} @@ -191,27 +191,27 @@ 🗂️ Onglets{{ ' (' ~ tabs|length ~ ')' if tabs else '' }}
{% if tabs %} -
- {% for tab in tabs %} -
-
- - -
-
- 📥 Contenu de l'onglet + {% for tab in tabs %} +
+
+
+
+
+
+ +
- +
- {% endfor %}
+ {% endfor %} {% else %} -

Aucun onglet pour l'instant.

+

Aucun onglet pour l'instant.

{% endif %}
- +
@@ -225,35 +225,63 @@
Position & taille
-
- - +
+
+
+
+
+
+
+
+
+
+
{% if el.parent_id %} -
@@ -271,50 +299,65 @@ {% set ns.last_group = c.group %} {% set ns.started = True %} {% endif %} -
- - {% if c.type in ('text', 'url') %} - - {% if c.allow_upload %} -
- - +
+ {% if c.type in ('checkbox', 'toggle_token') %} +
+
-

Colle une adresse (URL) ci-dessus, ou choisis un fichier sur ton ordinateur — il sera hébergé automatiquement avec ce jeu.

+ {% else %} + + {% if c.type in ('text', 'url') %} +
+ +
+ {% if c.allow_upload %} +
+ + +
+

Colle une adresse (URL) ci-dessus, ou choisis un fichier sur ton ordinateur — il sera hébergé automatiquement avec ce jeu.

{% endif %} {% elif c.type == 'textarea' %} - +
{% elif c.type == 'color' %} - +
{% elif c.type == 'slider' %} -
+
- {{ c.value }}{{ c.unit }} + {{ c.value }}{{ c.unit }}
{% elif c.type == 'scale' %} -
+
- x{{ c.value }} + x{{ c.value }}
- {% elif c.type in ('checkbox', 'toggle_token') %} - {% elif c.type == 'align' %} -
- {% for val, icon in [('left','⬅️'),('center','⬛'),('right','➡️'),('justify','☰')] %} - - {% endfor %} +
+
+ {% for val, icon in [('left','⬅️'),('center','⬛'),('right','➡️'),('justify','☰')] %} + + {% endfor %} +
{% elif c.type in ('select', 'preset') %} - +
+
+ +
+
+ {% endif %} {% endif %}
{% endfor %} @@ -1178,8 +1221,8 @@ function onDataDefinitionChange(sel) { document.querySelectorAll('.targetDefinitionSelect').forEach(onDataDefinitionChange); function syncAlign(radio) { - radio.closest('.alignGroup').querySelectorAll('.alignOption').forEach(function(o){ o.classList.remove('active'); }); - radio.closest('.alignOption').classList.add('active'); + radio.closest('.alignGroup').querySelectorAll('.alignOption').forEach(function(o){ o.classList.remove('active', 'is-primary'); }); + radio.closest('.alignOption').classList.add('active', 'is-primary'); } // Choix du format d'aperçu (Portrait/Paysage/Carré) : mémorisé côté serveur