Éditeur à champs pour les variables Objet/Tableau (plus de JSON à taper)
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'<input> 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
aaf9954446
commit
1b24df9546
@@ -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; }
|
||||
|
||||
@@ -146,12 +146,13 @@
|
||||
</div>
|
||||
<form method="post" action="{{ url_for('create_global_var', slug=game.slug) }}" class="dashCreateBar" id="dashCreate-variables" style="display:none;">
|
||||
<input type="text" name="name" placeholder="Nom (ex. porte_ouverte)" required autocomplete="off">
|
||||
<select name="type">
|
||||
<select id="newVarType" name="type" onchange="onVarTypeSelectChange(this)">
|
||||
{% for key, val in variable_types.items() %}
|
||||
<option value="{{ key }}">{{ val.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<textarea name="default_value" placeholder="Valeur de départ (JSON pour Objet/Tableau, ex. {} ou [])" rows="1"></textarea>
|
||||
<input type="text" id="newVarValue" name="default_value" placeholder="Valeur de départ">
|
||||
<button type="button" id="newVarConfigureBtn" class="button is-small" style="display:none;" onclick="openVarValuePanel('newVarValue', document.getElementById('newVarType').value)">🔧 Configurer la valeur</button>
|
||||
<button type="submit" class="primary">Créer</button>
|
||||
</form>
|
||||
{% if variables %}
|
||||
@@ -174,7 +175,10 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if v.type in ('objet', 'tableau') %}
|
||||
<textarea form="varEditForm{{ v.id }}" name="value" rows="3" aria-label="Valeur de « {{ v.name }} »">{{ v.value or '' }}</textarea>
|
||||
<div class="fieldRow" style="align-items:center;">
|
||||
<input type="text" form="varEditForm{{ v.id }}" name="value" id="varValue-{{ v.id }}" value="{{ v.value or '' }}" readonly aria-label="Valeur de « {{ v.name }} »">
|
||||
<button type="button" class="button is-small" onclick="openVarValuePanel('varValue-{{ v.id }}', '{{ v.type }}')">🔧 Configurer</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<input type="text" form="varEditForm{{ v.id }}" name="value" value="{{ v.value or '' }}" aria-label="Valeur de « {{ v.name }} »">
|
||||
{% endif %}
|
||||
@@ -196,6 +200,56 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- ---------- Panneau "Configurer la valeur" (variable Objet/Tableau) ---------- -->
|
||||
<!-- Un seul panneau partagé (pas un par variable) : une seule valeur à la
|
||||
fois peut être en cours d'édition. openVarValuePanel(targetInputId,
|
||||
mode) mémorise vers quel <input> écrire le JSON reconstruit (celui de
|
||||
la barre de création, ou celui, en lecture seule, de la ligne d'une
|
||||
variable existante) — pas de JSON à taper à la main : une ligne par
|
||||
clé (Objet) ou par élément (Tableau), avec un type (Texte/Nombre/
|
||||
Oui-Non) qui pilote comment la valeur est encodée en JSON. Ne gère
|
||||
que des valeurs SIMPLES (pas d'objet/tableau imbriqué) — largement
|
||||
suffisant pour une configuration ou une liste, et ça reste lisible
|
||||
pour qui ne connaît pas JSON. -->
|
||||
<div class="floatPanel floatPanel--center hidden" id="varValuePanel">
|
||||
<div class="floatPanelHeader" id="varValuePanelHeader">
|
||||
<span class="floatPanelTitle" id="varValuePanelTitle">⋮⋮ Configurer la valeur</span>
|
||||
<button type="button" class="button is-small floatPanelFullscreenBtn" title="Plein écran">⛶</button>
|
||||
<button type="button" class="button is-small" onclick="closeVarValuePanel()">✕</button>
|
||||
</div>
|
||||
<div class="floatPanelBody">
|
||||
<p class="hint mb-2" id="varValuePanelHint"></p>
|
||||
<button type="button" class="button is-outlined is-fullwidth mb-2" onclick="addVarValueRow()">+ Ajouter</button>
|
||||
<div id="varValueRows"></div>
|
||||
<div class="actionsRow"><button type="button" class="primary" onclick="applyVarValuePanel()">OK — utiliser cette valeur</button></div>
|
||||
</div>
|
||||
<div class="floatPanelResizeHandle" id="varValuePanelResizeHandle"></div>
|
||||
</div>
|
||||
|
||||
<template id="varValueRowTemplate">
|
||||
<div class="fieldRowCard box varValueRow">
|
||||
<div class="fieldRow">
|
||||
<div class="formField varValueKeyField">
|
||||
<label>Clé</label>
|
||||
<input type="text" class="varValueKey" placeholder="ex. nom, degats...">
|
||||
</div>
|
||||
<div class="formField">
|
||||
<label>Type</label>
|
||||
<select class="varValueType">
|
||||
<option value="texte">Texte</option>
|
||||
<option value="nombre">Nombre</option>
|
||||
<option value="booleen">Oui / Non</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="formField" style="flex:2;">
|
||||
<label>Valeur</label>
|
||||
<input type="text" class="varValueValue" placeholder="ex. Alice, 12, Oui...">
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="button is-small is-danger varValueRemoveBtn">🗑️ Retirer</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="dangerZone dashDangerZone">
|
||||
<h3>Paramètres du jeu</h3>
|
||||
<form method="post" action="{{ url_for('game_rename', slug=game.slug) }}">
|
||||
@@ -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 <input> (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 <input>, 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();
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user