From f4a7a7340e66ba7ac0a96555ff219a3e9d418a63 Mon Sep 17 00:00:00 2001 From: william Date: Wed, 26 Aug 2026 12:51:57 +0200 Subject: [PATCH] =?UTF-8?q?Cible=20aussi=20les=20=C3=A9l=C3=A9ments=20imbr?= =?UTF-8?q?iqu=C3=A9s=20dans=20refreshRuntimeData()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Même défaut que celui corrigé dans applyOpenRowBindings() (commit précédent), mais dans le second endroit qui régénère l'écran après un changement de donnée : refreshRuntimeData() ne pouvait régénérer que les éléments de PREMIER NIVEAU (seuls eux ont un data-el-id sur leur wrapper .playElement). Un Répéteur niché dans un conteneur — comme celui de cet écran — n'est jamais du premier niveau : c'est donc son ANCÊTRE de premier niveau qui portait le marqueur "repeaterItem" à l'intérieur et se faisait régénérer en entier à sa place, potentiellement l'écran complet (jauges, onglets compris) si l'écran n'a qu'un seul gros conteneur racine. C'était la cause réelle du "rechargement" toujours visible après le précédent correctif : celui-ci ne portait que sur applyOpenRowBindings(), pas sur cette 2e régénération déclenchée par "Modifier une donnée"/"Modifier une variable". Fix : même principe que le commit précédent — cible chaque élément marqué (repeaterItem/jaugeBar/visibilityGated) directement via son data-element-id, à n'importe quel niveau d'imbrication, en ne gardant que les plus "hauts" parmi les éléments marqués pour ne jamais régénérer un même nœud deux fois. Co-Authored-By: Claude Sonnet 5 --- templates/play.html | 55 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/templates/play.html b/templates/play.html index 8b65705f..69369ca4 100644 --- a/templates/play.html +++ b/templates/play.html @@ -463,18 +463,49 @@ // tout autre élément cliquable) reste silencieusement inerte pour // le reste de la partie, sans le moindre message d'erreur visible. try { - screenData.elements.filter(function(e){ return !e.parent_id; }).forEach(function(el) { - if (!el.rendered_html) return; - // "repeaterItem" (liste de données), "jaugeBar" (2.1 — jauge liée - // à une donnée) et "visibilityGated" (condition de visibilité — - // voir render_element_html.py) sont les types d'éléments dont le - // contenu/l'affichage dépend de la donnée elle-même plutôt que - // d'une action "Modifier un élément" posée à la main dans le - // graphe — eux seuls doivent être régénérés après un changement - // de donnée ou de variable. - if (el.rendered_html.indexOf('repeaterItem') === -1 && el.rendered_html.indexOf('jaugeBar') === -1 && el.rendered_html.indexOf('visibilityGated') === -1) return; - const wrapper = screenDiv.querySelector('.playElement[data-el-id="' + el.id + '"] .playElementInner'); - if (wrapper) wrapper.innerHTML = el.rendered_html; + // "repeaterItem" (liste de données), "jaugeBar" (2.1 — jauge liée + // à une donnée) et "visibilityGated" (condition de visibilité — + // voir render_element_html.py) sont les types d'éléments dont le + // contenu/l'affichage dépend de la donnée elle-même plutôt que + // d'une action "Modifier un élément" posée à la main dans le + // graphe — eux seuls doivent être régénérés après un changement + // de donnée ou de variable. + // + // On cible ces éléments à N'IMPORTE QUEL niveau d'imbrication + // (pas seulement le premier niveau de l'écran), via leur + // data-element-id — disponible sur chaque élément rendu, pas + // seulement les éléments de premier niveau. Avant ce correctif, + // un Répéteur niché dans un conteneur (comme ici) n'était JAMAIS + // du premier niveau, donc c'est son ANCÊTRE de premier niveau + // qui portait le marqueur (le Répéteur imbriqué à l'intérieur) + // et se retrouvait régénéré en entier à sa place — potentiellement + // l'écran entier (jauges, onglets... compris) s'il n'y avait + // qu'un seul gros conteneur racine. Comme pour + // applyOpenRowBindings(), on ne garde que les éléments marqués + // les plus "hauts" dans l'arbre, pour ne jamais régénérer un même + // nœud deux fois. + const byId = {}; + screenData.elements.forEach(function(e){ byId[e.id] = e; }); + function hasMarker(e) { + return !!(e.rendered_html && (e.rendered_html.indexOf('repeaterItem') !== -1 || e.rendered_html.indexOf('jaugeBar') !== -1 || e.rendered_html.indexOf('visibilityGated') !== -1)); + } + function hasMarkedAncestor(e) { + let p = e.parent_id ? byId[e.parent_id] : null; + while (p) { + if (hasMarker(p)) return true; + p = p.parent_id ? byId[p.parent_id] : null; + } + return false; + } + screenData.elements.filter(function(e) { + return hasMarker(e) && !hasMarkedAncestor(e); + }).forEach(function(el) { + const target = screenDiv.querySelector('[data-element-id="' + el.id + '"]'); + if (!target) return; + const container = document.createElement('div'); + container.innerHTML = el.rendered_html; + const newNode = container.firstElementChild; + if (newNode) target.replaceWith(newNode); }); applyOpenRowBindings(); } catch (err) {