From 1b24df95469fdedda5a159a88a800294e10a3110 Mon Sep 17 00:00:00 2001 From: william Date: Thu, 27 Aug 2026 14:41:37 +0200 Subject: [PATCH] =?UTF-8?q?=C3=89diteur=20=C3=A0=20champs=20pour=20les=20v?= =?UTF-8?q?ariables=20Objet/Tableau=20(plus=20de=20JSON=20=C3=A0=20taper)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'édition en JSON brut (textarea) n'était pas adaptée à un public non développeur. Remplacée par un panneau "Configurer la valeur" (déplaçable/ redimensionnable/plein écran, comme les autres) avec une ligne par clé (Objet) ou par élément (Tableau) : un champ Clé (Objet seulement), un type (Texte/Nombre/Oui-Non) et une valeur en texte libre — le JSON est reconstruit automatiquement au clic sur "OK", jamais tapé à la main. Un seul panneau partagé (#varValuePanel) pour toutes les variables : à l'ouverture (openVarValuePanel(targetInputId, mode)), reconstruit une ligne par entrée déjà présente dans le JSON actuel de l' ciblé — un objet/tableau imbriqué en valeur est préservé tel quel (JSON.stringify en texte opaque) plutôt que perdu, même si cette valeur précise n'est pas éditable via une ligne (portée volontairement limitée aux valeurs simples : suffisant pour une configuration ou une liste, sans la complexité d'un éditeur JSON récursif). - Barre "+ Créer une variable" : le champ de valeur devient en lecture seule pour Objet/Tableau, avec un bouton "🔧 Configurer" à côté. - Ligne d'une variable existante : la cellule Valeur montre un aperçu en lecture seule + le même bouton "🔧 Configurer" (au lieu du textarea JSON du commit précédent). Co-Authored-By: Claude Sonnet 5 --- static/style.css | 6 ++ templates/game_dashboard.html | 169 +++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 3 deletions(-) diff --git a/static/style.css b/static/style.css index e50cca49..d8958ff4 100644 --- a/static/style.css +++ b/static/style.css @@ -552,3 +552,9 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p .columnFilterMenu.hidden{ display:none; } .columnFilterOption{ display:flex; align-items:center; gap:6px; padding:4px 6px; border-radius:4px; font-size:12.5px; white-space:nowrap; } .columnFilterOption:hover{ background:var(--panel2); } + +/* ---------- Panneau "Configurer la valeur" (variable Objet/Tableau) ---------- */ +/* La colonne "Clé" n'a de sens que pour un Objet (paires clé/valeur) — un + Tableau n'est qu'une liste de valeurs, voir openVarValuePanel() dans + game_dashboard.html. */ +#varValuePanel[data-mode="tableau"] .varValueKeyField{ display:none; } diff --git a/templates/game_dashboard.html b/templates/game_dashboard.html index c8b9df5d..43a0f41c 100644 --- a/templates/game_dashboard.html +++ b/templates/game_dashboard.html @@ -146,12 +146,13 @@ {% if variables %} @@ -174,7 +175,10 @@ {% if v.type in ('objet', 'tableau') %} - +
+ + +
{% else %} {% endif %} @@ -196,6 +200,56 @@ {% endif %} + + + + + +

Paramètres du jeu

@@ -651,6 +705,19 @@ function toggleDashCreate(tab) { } } +// Barre "+ Créer une variable" : pour un type Objet/Tableau, la valeur de +// départ se configure via le panneau à champs (voir openVarValuePanel) — +// la saisie libre n'a alors plus de sens, remplacée par un aperçu en +// lecture seule + le bouton "🔧 Configurer". +function onVarTypeSelectChange(sel) { + var valueInput = document.getElementById('newVarValue'); + var configureBtn = document.getElementById('newVarConfigureBtn'); + var structured = sel.value === 'objet' || sel.value === 'tableau'; + valueInput.readOnly = structured; + configureBtn.style.display = structured ? '' : 'none'; + if (structured && !valueInput.value) valueInput.value = sel.value === 'objet' ? '{}' : '[]'; +} + // Onglet initial : ?tab=... (utilisé par les liens "crée-en un" ailleurs // dans l'outil, ex. l'éditeur d'écran, pour arriver directement sur le bon // onglet plutôt que de laisser deviner où cliquer). @@ -942,5 +1009,101 @@ function switchObjectPanelTab(btn, tab) { p.classList.toggle('active', p.dataset.optabPanel === tab); }); } + +// ---------- Panneau "Configurer la valeur" (variable Objet/Tableau) ---------- +// Un seul panneau partagé (voir le commentaire sur son HTML) : à +// l'ouverture, mémorise vers quel (targetId) le JSON reconstruit +// devra être réécrit, construit une ligne par clé/élément déjà présent +// dans le JSON actuel de cet , puis "OK" sérialise les lignes et +// referme. +(function () { + var panel = document.getElementById('varValuePanel'); + var rowsWrap = document.getElementById('varValueRows'); + var rowTemplate = document.getElementById('varValueRowTemplate'); + var title = document.getElementById('varValuePanelTitle'); + var hint = document.getElementById('varValuePanelHint'); + + makeFloatPanelDraggable(panel, document.getElementById('varValuePanelHeader')); + makeFloatPanelResizable(panel, document.getElementById('varValuePanelResizeHandle')); + makeFloatPanelFullscreenable(panel, panel.querySelector('.floatPanelFullscreenBtn')); + + function valueTypeOf(v) { + if (typeof v === 'boolean') return 'booleen'; + if (typeof v === 'number') return 'nombre'; + return 'texte'; // texte, et repli pour un objet/tableau imbriqué (non éditable ici, voir addRowFrom) + } + function valueTextOf(v, type) { + if (type === 'booleen') return v ? 'Oui' : 'Non'; + if (typeof v === 'object' && v !== null) return JSON.stringify(v); // imbriqué : préservé tel quel, en texte opaque + return v == null ? '' : String(v); + } + function coerceRowValue(type, text) { + if (type === 'nombre') { var n = parseFloat(text); return isNaN(n) ? 0 : n; } + if (type === 'booleen') return ['oui', 'vrai', '1', 'true'].indexOf(text.trim().toLowerCase()) !== -1; + return text; + } + + function addRowFrom(key, value) { + var clone = rowTemplate.content.cloneNode(true); + var row = clone.querySelector('.varValueRow'); + var type = valueTypeOf(value); + if (key !== null) row.querySelector('.varValueKey').value = key; + row.querySelector('.varValueType').value = type; + row.querySelector('.varValueValue').value = valueTextOf(value, type); + row.querySelector('.varValueRemoveBtn').addEventListener('click', function () { row.remove(); }); + rowsWrap.appendChild(clone); + } + + window.addVarValueRow = function () { + addRowFrom(panel.dataset.mode === 'tableau' ? null : '', ''); + }; + + window.openVarValuePanel = function (targetInputId, mode) { + var input = document.getElementById(targetInputId); + if (!input) return; + panel.dataset.target = targetInputId; + panel.dataset.mode = mode; + rowsWrap.innerHTML = ''; + var parsed; + try { parsed = JSON.parse(input.value || (mode === 'objet' ? '{}' : '[]')); } catch (e) { parsed = mode === 'objet' ? {} : []; } + if (mode === 'tableau') { + title.textContent = '⋮⋮ Configurer le tableau'; + hint.textContent = 'Une ligne par élément — valeurs simples uniquement (pas de tableau/objet imbriqué).'; + (Array.isArray(parsed) ? parsed : []).forEach(function (v) { addRowFrom(null, v); }); + } else { + title.textContent = '⋮⋮ Configurer l\'objet'; + hint.textContent = 'Une ligne par clé — valeurs simples uniquement (pas de tableau/objet imbriqué).'; + Object.keys(parsed || {}).forEach(function (k) { addRowFrom(k, parsed[k]); }); + } + if (!rowsWrap.children.length) window.addVarValueRow(); + panel.classList.remove('hidden'); + panel._fsToggle.enter(); + }; + + window.closeVarValuePanel = function () { + panel.classList.add('hidden'); + }; + + window.applyVarValuePanel = function () { + var mode = panel.dataset.mode; + var result = mode === 'tableau' ? [] : {}; + rowsWrap.querySelectorAll('.varValueRow').forEach(function (row) { + var type = row.querySelector('.varValueType').value; + var value = coerceRowValue(type, row.querySelector('.varValueValue').value); + if (mode === 'tableau') { + result.push(value); + } else { + var key = row.querySelector('.varValueKey').value.trim(); + if (key) result[key] = value; + } + }); + var input = document.getElementById(panel.dataset.target); + if (input) { + input.value = JSON.stringify(result); + input.dispatchEvent(new Event('input', { bubbles: true })); + } + closeVarValuePanel(); + }; +})(); {% endblock %}