Ajoute un sélecteur de champ pour insérer {{champ}} dans le Contenu

Une fois "Lier à un objet de données" réglé (widget Texte/Titre...),
propose maintenant les champs de CET objet en liste déroulante juste sous
le champ "Contenu", avec un bouton "+ Ajouter" qui insère "{{nom_du_champ}}"
à l'emplacement du curseur - plutôt que d'avoir à taper cette syntaxe à
la main sans savoir quels noms de champs existent réellement.

controls_with_values.py pose field_options sur le contrôle "content"
quand _data_definition_id est réglé (réutilise data_binding_options, déjà
calculé pour data_filtre_champ/data_filtre2_champ) ; insertFieldAtCursor()
(screen_edit.html) fait l'insertion via selectionStart/selectionEnd puis
déclenche un événement "input" pour que l'enregistrement automatique se
déclenche normalement, comme une saisie manuelle.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-25 13:18:12 +02:00
co-authored by Claude Sonnet 5
parent baa3da1035
commit 7e80ff952a
2 changed files with 54 additions and 1 deletions
+7
View File
@@ -67,5 +67,12 @@ def controls_with_values(slug, el):
control["options"] = [("", "(sélectionner un objet d'abord)")] + [(f["name"], _field_label(f)) for f in jauge_options["fields"]]
if control["key"] in ("data_filtre_champ", "data_filtre2_champ"):
control["options"] = [("", "(sélectionner un objet d'abord)")] + [(f["name"], _field_label(f)) for f in data_binding_options["fields"]]
if control["key"] == "content" and attributes.get("_data_definition_id"):
# Une fois "Lier à un objet de données" réglé (voir
# DATA_BINDING_CONTROLS), propose les champs de CET objet en
# liste déroulante à côté du texte — plutôt que de devoir taper
# "{{nom_du_champ}}" à la main sans savoir quels noms existent
# (voir insertFieldAtCursor() dans screen_edit.html).
control["field_options"] = [(f["name"], _field_label(f)) for f in data_binding_options["fields"]]
result.append({**control, "value": _control_value(el, control)})
return result
+47 -1
View File
@@ -691,8 +691,31 @@
</div>
<p class="help">Colle une adresse (URL) ci-dessus, ou choisis un fichier sur ton ordinateur — il sera hébergé automatiquement avec ce jeu.</p>
{% endif %}
{% if c.field_options %}
<div class="control fieldRow" style="margin-top:6px;">
<div class="select is-small is-expanded"><select id="fieldPicker-{{ c.key }}">
<option value="">— champ de l'objet lié —</option>
{% for fname, flabel in c.field_options %}
<option value="{{ fname }}">{{ flabel }}</option>
{% endfor %}
</select></div>
<button type="button" class="button is-small" onclick="insertFieldAtCursor('field-{{ c.key }}', 'fieldPicker-{{ c.key }}')">+ Ajouter</button>
</div>
{% endif %}
{% elif c.type == 'textarea' %}
<div class="control"><textarea class="textarea" name="ctrl_{{ c.key }}" rows="4" placeholder="{{ c.placeholder or '' }}">{{ c.value }}</textarea></div>
<div class="control"><textarea class="textarea" name="ctrl_{{ c.key }}" id="field-{{ c.key }}" rows="4" placeholder="{{ c.placeholder or '' }}">{{ c.value }}</textarea></div>
{% if c.field_options %}
<div class="control fieldRow" style="margin-top:6px;">
<div class="select is-small is-expanded"><select id="fieldPicker-{{ c.key }}">
<option value="">— champ de l'objet lié —</option>
{% for fname, flabel in c.field_options %}
<option value="{{ fname }}">{{ flabel }}</option>
{% endfor %}
</select></div>
<button type="button" class="button is-small" onclick="insertFieldAtCursor('field-{{ c.key }}', 'fieldPicker-{{ c.key }}')">+ Ajouter</button>
</div>
<p class="help">Insère "&#123;&#123;champ&#125;&#125;" à l'emplacement du curseur — affichera la valeur de ce champ, sur la ligne trouvée par "Donnée liée" ci-dessous.</p>
{% endif %}
{% elif c.type == 'color' %}
<div class="control"><input class="input" type="color" name="ctrl_{{ c.key }}" value="{{ c.value or c.default }}"></div>
{% elif c.type == 'slider' %}
@@ -1389,6 +1412,29 @@ function debouncedSubmitPropsForm() {
autosaveTimer = setTimeout(submitPropsForm, 500);
}
// Insère "{{champ}}" à l'emplacement du curseur dans le champ "Contenu"
// (voir controls_with_values.py, control["field_options"] posé une fois
// "Lier à un objet de données" réglé) — évite d'avoir à taper la syntaxe
// à la main sans savoir quels noms de champs existent. OPEN/CLOSE en deux
// morceaux : Jinja interpréterait "{{"/"}}" littéralement adjacents dans
// le SOURCE de ce template comme le début d'une expression s'il les
// rencontrait tels quels (même à l'intérieur d'un <script>), voir la même
// astuce pour _FILTER_REF_OPEN/_FILTER_REF_CLOSE plus bas.
function insertFieldAtCursor(fieldId, pickerId) {
var field = document.getElementById(fieldId);
var picker = document.getElementById(pickerId);
if (!field || !picker || !picker.value) return;
var open = '{' + '{', close = '}' + '}';
var text = open + picker.value + close;
var start = field.selectionStart || 0, end = field.selectionEnd || 0;
field.value = field.value.substring(0, start) + text + field.value.substring(end);
var newPos = start + text.length;
field.focus();
field.setSelectionRange(newPos, newPos);
field.dispatchEvent(new Event('input', { bubbles: true }));
picker.value = '';
}
function bindPropsAutosave() {
var form = document.getElementById('propsForm');
autosaveIndicator = document.getElementById('autosaveStatus');