Jusqu'ici, la logique (déclencheurs Au clic/Au survol/Fin du survol) et
les animations posées dans l'éditeur de l'écran-MODÈLE d'un élément de
jeu réutilisable (ex. "mail content") sur SES PROPRES enfants ne
s'exécutaient jamais quand cet élément était simplement posé sur une
autre scène : findTriggerNode() cherchait bien le déclencheur dans tous
les écrans (modèles compris) mais runFlowFrom() n'exécutait ensuite le
graphe que dans l'écran RÉELLEMENT affiché — le nœud trouvé n'existait
pas dans ce graphe-là, donc rien ne se déclenchait, silencieusement.
Même limitation pour les animations, dont la timeline ne lisait que les
clips propres à l'écran affiché.
Logique (templates/play.html) :
- findTriggerNode() renvoie désormais { node, screenId } plutôt que
juste le nœud, pour transmettre l'écran D'ORIGINE du déclencheur (qui
peut être un écran-modèle).
- runFlowFrom(nodeId, flowScreenId) accepte un 2e paramètre optionnel
(par défaut l'écran affiché, comportement inchangé pour tout le
reste) pour exécuter le graphe dans le BON écran.
- bindClicks()/bindHoverTriggers() passent maintenant cet écran
d'origine à runFlowFrom(). runScreenShowTriggers() (déclencheur "À
l'affichage de l'écran") reste volontairement inchangé — hors scope,
ambiguïté sur plusieurs exemplaires d'un même modèle sur un écran.
Animations (screens/payload/full_game_payload.py, templates/play.html) :
- Le payload expose désormais element_types (element_type_id -> id de
son écran-modèle), via screens.list_element_types() déjà existant.
- collectAnimationClips(screenId) rassemble récursivement les clips de
l'écran affiché ET de tout écran-modèle utilisé par un de ses
éléments (garde anti-boucle, dédoublonnage par écran).
- applyAnimationClip() cible désormais TOUS les exemplaires d'un id
d'élément (querySelectorAll, plus querySelector) : un enfant de
modèle garde le même id à chaque exemplaire, y compris pour chaque
ligne d'un Répéteur utilisant ce modèle comme gabarit de ligne.
Limite connue, non corrigée ici (pas la demande) : une action "Modifier
un élément" ciblant un enfant de modèle reste, elle, scopée au premier
exemplaire trouvé dans le DOM (document.querySelector singulier dans
runActionNode/applyElementProperty) — sans impact pour un modèle posé
une seule fois par écran, comme dans le cas d'usage actuel.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
59 lines
2.7 KiB
Python
59 lines
2.7 KiB
Python
import db
|
|
|
|
from ..screens_repo.list_screens import list_screens
|
|
from ..elements.list_elements import list_elements
|
|
from ..flow.list_flow_nodes import list_flow_nodes
|
|
from ..flow.list_flow_edges import list_flow_edges
|
|
from ..animations.list_animation_clips import list_animation_clips
|
|
from ..element_types.list_element_types import list_element_types
|
|
|
|
|
|
def full_game_payload(slug):
|
|
"""Toutes les données nécessaires au runtime JS de la page de jeu jouable
|
|
(/game/<slug>/play) : chaque écran avec ses éléments (HTML déjà généré,
|
|
y compris les Répéteurs de données, lus en direct), le graphe de logique
|
|
(nœuds + fils) de chaque écran, et un instantané des données du jeu
|
|
(avec le type de chaque champ, nécessaire pour évaluer une condition
|
|
correctement) — un seul aller-retour serveur, ensuite tout se joue côté
|
|
client (aucune navigation ne recharge la page, sauf pour appliquer une
|
|
action "modifier une donnée", qui doit passer par le serveur)."""
|
|
screens_ = list_screens(slug)
|
|
payload_screens = []
|
|
flows = {}
|
|
animations = {}
|
|
for s in screens_:
|
|
elements = list_elements(slug, s["id"], enforce_visibility=True)
|
|
payload_screens.append({**s, "elements": elements})
|
|
flows[str(s["id"])] = {
|
|
"nodes": list_flow_nodes(slug, s["id"]),
|
|
"edges": list_flow_edges(slug, s["id"]),
|
|
}
|
|
animations[str(s["id"])] = list_animation_clips(slug, s["id"])
|
|
|
|
definitions = db.list_definitions(slug)
|
|
data = {}
|
|
fields_meta = {}
|
|
for d in definitions:
|
|
full = db.get_definition(slug, d["id"])
|
|
rows = db.list_rows(slug, full)
|
|
cols = {f["name"]: db.slugify(f["name"]).replace("-", "_") for f in full["fields"]}
|
|
data[str(d["id"])] = [
|
|
{**{fname: r.get(col) for fname, col in cols.items()}, "id": r["id"]}
|
|
for r in rows
|
|
]
|
|
fields_meta[str(d["id"])] = [{"name": f["name"], "type": f["type"]} for f in full["fields"]]
|
|
|
|
# element_type_id -> id de son écran-modèle : nécessaire côté client (voir
|
|
# collectAnimationClips() dans play.html) pour savoir, quand un élément de
|
|
# jeu réutilisable (ex. "mail content") est posé sur un écran, quels
|
|
# autres clips d'animation (ceux de SON PROPRE écran-modèle) doivent
|
|
# aussi être joués à l'affichage de cet écran — sans quoi une animation
|
|
# posée directement dans l'éditeur du modèle ne se jouait jamais quand le
|
|
# modèle est utilisé ailleurs.
|
|
element_types = {str(t["id"]): t["template_screen_id"] for t in list_element_types(slug)}
|
|
|
|
return {
|
|
"screens": payload_screens, "flows": flows, "animations": animations,
|
|
"element_types": element_types, "data": data, "fields_meta": fields_meta,
|
|
}
|