Dev #2
+53
-16
@@ -339,30 +339,63 @@
|
||||
const screenData = screensData.find(s => s.id === window.currentScreenId);
|
||||
const screenDiv = document.querySelector('.playScreen[data-screen-id="' + window.currentScreenId + '"]');
|
||||
if (!row || !screenData || !screenDiv) return;
|
||||
screenData.elements.filter(function(e){ return !e.parent_id; }).forEach(function(el) {
|
||||
const wrapper = screenDiv.querySelector('.playElement[data-el-id="' + el.id + '"] .playElementInner');
|
||||
if (!wrapper || !el.rendered_html || el.rendered_html.indexOf(PLACEHOLDER_OPEN) === -1) return;
|
||||
|
||||
// Avant de regénérer ce sous-arbre depuis le HTML D'ORIGINE (nécessaire
|
||||
// pour ne pas laisser les valeurs d'un mail précédent affichées, voir
|
||||
// plus haut), sauvegarde l'état visuel TRANSITOIRE posé par une action
|
||||
// Cible directement, à N'IMPORTE QUEL niveau d'imbrication, le ou les
|
||||
// éléments qui portent vraiment un {{champ}} non résolu — et non plus
|
||||
// seulement les éléments de PREMIER NIVEAU de l'écran. Avant ce
|
||||
// correctif, un panneau de détail niché à 2-3 niveaux (ex. "mail
|
||||
// content" posé dans un conteneur, lui-même dans un autre) forçait à
|
||||
// regénérer TOUT son ancêtre de premier niveau — potentiellement
|
||||
// l'écran entier (jauges, onglets, Répéteur compris) s'il n'y avait
|
||||
// qu'un seul gros conteneur racine — d'où l'impression trompeuse d'un
|
||||
// rechargement de page (flash à vide) à chaque clic sur une ligne.
|
||||
//
|
||||
// Parmi les éléments qui ont un {{champ}} non résolu, on ne garde que
|
||||
// les plus "hauts" dans l'arbre : si un ancêtre ET un de ses
|
||||
// descendants sont tous deux candidats, seul l'ancêtre est régénéré
|
||||
// (son rendered_html inclut déjà ce descendant) — sans ce filtre, le
|
||||
// descendant serait régénéré deux fois, la 2e fois à partir du DOM déjà
|
||||
// à jour de la 1re passe.
|
||||
const byId = {};
|
||||
screenData.elements.forEach(function(e){ byId[e.id] = e; });
|
||||
function hasUnresolvedPlaceholder(e) {
|
||||
return !!(e.rendered_html && e.rendered_html.indexOf(PLACEHOLDER_OPEN) !== -1);
|
||||
}
|
||||
function hasCandidateAncestor(e) {
|
||||
let p = e.parent_id ? byId[e.parent_id] : null;
|
||||
while (p) {
|
||||
if (hasUnresolvedPlaceholder(p)) return true;
|
||||
p = p.parent_id ? byId[p.parent_id] : null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const targets = screenData.elements.filter(function(e) {
|
||||
return hasUnresolvedPlaceholder(e) && !hasCandidateAncestor(e);
|
||||
});
|
||||
|
||||
targets.forEach(function(el) {
|
||||
const target = screenDiv.querySelector('[data-element-id="' + el.id + '"]');
|
||||
if (!target) return;
|
||||
|
||||
// Avant de regénérer ce nœud depuis le HTML D'ORIGINE (nécessaire pour
|
||||
// ne pas laisser les valeurs d'un mail précédent affichées, voir plus
|
||||
// haut), sauvegarde l'état visuel TRANSITOIRE posé par une action
|
||||
// "Modifier un élément" (jamais enregistré en base — voir
|
||||
// applyElementProperty) sur cet élément ou l'un de ses descendants,
|
||||
// pour le réappliquer après. Sans ça, un panneau rendu visible par
|
||||
// cette action (ex. "mail content") redevenait invisible à CHAQUE
|
||||
// applyElementProperty) sur ce nœud ou l'un de ses descendants, pour
|
||||
// le réappliquer après. Sans ça, un panneau rendu visible par cette
|
||||
// action (ex. "mail content") redevenait invisible à CHAQUE
|
||||
// rafraîchissement de données, y compris pour un changement de donnée
|
||||
// sans rapport avec lui — puisque wrapper.innerHTML = html ci-dessous
|
||||
// recrée entièrement ses nœuds à partir de leur état PAR DÉFAUT.
|
||||
// sans rapport avec lui — puisque le remplacement ci-dessous recrée
|
||||
// entièrement ses nœuds à partir de leur état PAR DÉFAUT.
|
||||
const savedState = {};
|
||||
wrapper.querySelectorAll('[data-element-id]').forEach(function(node) {
|
||||
[target].concat(Array.from(target.querySelectorAll('[data-element-id]'))).forEach(function(node) {
|
||||
// clickBound/hoverBound/hoverTriggerBound (voir bindClicks/
|
||||
// bindHoverTexts/bindHoverTriggers) sont des indicateurs INTERNES du
|
||||
// moteur, jamais un état posé par une action "Modifier un élément" —
|
||||
// les restaurer sur le nœud tout neuf créé ci-dessous lui ferait
|
||||
// croire à tort qu'un écouteur de clic/survol lui est déjà rattaché
|
||||
// (alors qu'aucun ne l'est réellement), ce qui bloquait
|
||||
// silencieusement TOUS les clics suivants sur cet élément (ex. les
|
||||
// lignes d'un Répéteur voisin, une fois régénérées avec lui).
|
||||
// silencieusement TOUS les clics suivants sur cet élément.
|
||||
const dataset = Object.assign({}, node.dataset);
|
||||
delete dataset.clickBound;
|
||||
delete dataset.hoverBound;
|
||||
@@ -382,9 +415,13 @@
|
||||
tmp.textContent = value;
|
||||
html = html.split(PLACEHOLDER_OPEN + fieldName + PLACEHOLDER_CLOSE).join(tmp.innerHTML);
|
||||
});
|
||||
wrapper.innerHTML = html;
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
const newNode = container.firstElementChild;
|
||||
if (!newNode) return;
|
||||
target.replaceWith(newNode);
|
||||
|
||||
wrapper.querySelectorAll('[data-element-id]').forEach(function(node) {
|
||||
[newNode].concat(Array.from(newNode.querySelectorAll('[data-element-id]'))).forEach(function(node) {
|
||||
const saved = savedState[node.dataset.elementId];
|
||||
if (!saved) return;
|
||||
node.setAttribute('style', saved.style);
|
||||
|
||||
Reference in New Issue
Block a user