first commit
Build and deploy / build-and-push (push) Successful in 17s
Build and deploy / deploy (push) Successful in 10s

This commit is contained in:
william
2026-08-21 16:23:49 +02:00
committed by william
commit 3f4ebc4527
248 changed files with 10180 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
from ..widgets.layout_capable_widgets import LAYOUT_CAPABLE_WIDGETS
def _style_string(el, extra_base="", parent_flex_direction=None, widget_key=None):
style = dict(el.get("style") or {})
if widget_key in LAYOUT_CAPABLE_WIDGETS and "display" not in style:
# "Empilement libre" (valeur par défaut de "Disposition interne")
# doit quand même permettre l'espacement (gap) entre les enfants —
# or le CSS "gap" n'a aucun effet sur un display bloc classique, il
# lui faut un conteneur flex/grid. Tant qu'aucune disposition n'a
# été choisie explicitement, on bascule donc ce widget sur un flex
# en colonne : visuellement identique à un simple empilement, mais
# l'espacement fonctionne dès la création, sans avoir à changer la
# disposition interne au préalable.
style["display"] = "flex"
style.setdefault("flex-direction", "column")
is_child = bool(el.get("parent_id"))
if is_child:
# Élément physiquement posé à l'intérieur d'un conteneur/répéteur/
# groupe de champs : une hauteur fixée à 100% se calcule contre la
# hauteur du parent et écraserait/déborderait les autres enfants
# (le conteneur coupe alors ce qui dépasse -> l'enfant "disparaît").
# La hauteur s'adapte donc à son contenu, comme dans une vraie page.
# Largeur : dans une disposition en LIGNE (flex-direction row), la
# largeur est l'axe principal — si chaque enfant réclame 100% de
# cette largeur, ils entrent en collision (l'algorithme flex les
# comprime alors tous, jusqu'à les faire quasi disparaître). Dans les
# autres cas (empilement libre, ou disposition en COLONNE où la
# largeur est l'axe transversal), 100% reste le bon défaut.
width_default = "auto" if parent_flex_direction in ("row", "row-reverse") else "100%"
base_style = f"width:{width_default}; height:auto; box-sizing:border-box; margin:0;" + extra_base
else:
base_style = "width:100%; height:100%; box-sizing:border-box; margin:0;" + extra_base
has_size_override = False
def _emit(k, v):
nonlocal has_size_override
if v in (None, ""):
return False
if k == "transform" and not is_child:
# Un élément posé directement sur un écran a déjà ce "transform"
# posé sur son cadre .canvasElementInner / .playElementInner
# (voir filters/element_style_filter.py:_element_transform_style)
# — l'écrire ici aussi (sur la balise intérieure, à l'intérieur
# d'un cadre qui reste fixe) donnait l'impression que l'échelle/
# l'animation restait "coincée à l'intérieur" au lieu de
# s'appliquer à l'élément entier. Un élément posé à l'intérieur
# d'un conteneur/répéteur/groupe de champs n'a pas ce cadre
# séparé (sa balise EST déjà tout l'élément) : pour lui,
# "transform" reste ici.
return False
if k in ("width", "height") and str(v).strip() in ("0px", "0"):
# 0 = réglage "Taille dans le conteneur" laissé à sa valeur par
# défaut (voir SIZE_OVERRIDE_CONTROLS) : ne pas l'écrire, pour
# garder le comportement automatique de base_style ci-dessus.
return False
if k in ("width", "height"):
has_size_override = True
return True
user_style = "; ".join(f"{k}:{v}" for k, v in style.items() if _emit(k, v))
if has_size_override:
# Une taille fixe explicite (voir "Taille dans le conteneur") ne doit
# pas être écrasée par la compression automatique d'une disposition
# flex (flex-shrink vaut 1 par défaut sur tout enfant) — sinon un
# élément à qui on a donné une largeur/hauteur précise peut quand
# même se retrouver comprimé si ses voisins manquent de place.
user_style = (user_style + "; " if user_style else "") + "flex-shrink:0"
return base_style + (" " + user_style + ";" if user_style else "")