Nouveau module partagé document_engine/rendering/box_style.py (padding/margin/background_color/border_radius/border par côté/height/ min-height/max-height/min-width/box-shadow/opacity/align_self) — réutilisable tel quel par tous les kinds suivants du tableau d'audit. Titre/Paragraphe gagnent : barré, police de caractère, taille de police, hauteur de ligne, espacement des lettres, majuscules/ minuscules/capitales, ombre du texte, et tous les attributs de boîte partagés ci-dessus. Interface entièrement à base de curseurs/cases à cocher/listes déroulantes/sélecteurs de couleur natifs — plus aucun champ de texte libre pour une valeur CSS (retour utilisateur). Deux bugs transversaux corrigés au passage (concernent tout l'éditeur) : - Le panneau Propriétés n'était jamais reconstruit après un clic sur un bouton (gras/alignement/segments...) — il fallait recharger la page pour voir l'état réel. Corrigé dans forgeDocUpdateAttributes, point d'entrée unique de toute mise à jour d'attribut. - Les cases à cocher et curseurs héritaient à tort le style d'un champ de texte (padding/bordure/fond/largeur 100%) via la règle générique .docField input. Ajoute docs/plan/AUDIT_MISE_EN_FORME.md : suivi de l'audit élément par élément (Titre/Paragraphe traité, Image ensuite). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
110 lines
4.4 KiB
Python
110 lines
4.4 KiB
Python
"""Attributs de mise en forme de "boîte" PARTAGÉS par plusieurs kinds de
|
|
contenu (padding/margin/background_color/border_radius/border/align_self)
|
|
— un seul et même jeu d'attributs et une seule fonction de rendu pour ne
|
|
jamais dupliquer cette logique entre `_render_text`/`_render_image`/
|
|
`_render_button`/etc. (voir retour utilisateur du 26/09/2026 : audit
|
|
complet des réglages manquants, à ajouter élément par élément en
|
|
réutilisant CE module à chaque fois plutôt que de le réécrire)."""
|
|
|
|
import html as html_lib
|
|
from typing import Any
|
|
|
|
BORDER_SIDES = ("top", "right", "bottom", "left")
|
|
|
|
_DEFAULT_BORDER_SIDE = {"style": "none", "width": "1px", "color": "var(--doc-border)"}
|
|
|
|
|
|
def default_border() -> dict[str, dict[str, str]]:
|
|
"""Nouveau dict à chaque appel (jamais un littéral partagé/muté par
|
|
référence entre plusieurs éléments, même précaution que
|
|
DEFAULT_QUIZ_CONFIG côté labels)."""
|
|
return {side: dict(_DEFAULT_BORDER_SIDE) for side in BORDER_SIDES}
|
|
|
|
|
|
BOX_DEFAULTS = {
|
|
"padding": "",
|
|
"margin": "",
|
|
"background_color": "",
|
|
"border_radius": "",
|
|
"align_self": "stretch",
|
|
"height": "",
|
|
"min_height": "",
|
|
"max_height": "",
|
|
"min_width": "",
|
|
"box_shadow": "",
|
|
"opacity": "",
|
|
}
|
|
|
|
|
|
def render_box_style(a: dict[str, Any]) -> str:
|
|
"""Construit les déclarations CSS inline communes à plusieurs kinds à
|
|
partir des attributs `padding`/`margin`/`background_color`/
|
|
`border_radius`/`height`/`min_height`/`max_height`/`min_width`/
|
|
`box_shadow`/`opacity`/`border`/`align_self` de `a` — chaîne vide pour
|
|
tout attribut absent ou à sa valeur par défaut (aucun style ajouté,
|
|
comportement historique inchangé). `border` est un dict à 4 clés
|
|
(`BORDER_SIDES`), chacune `{"style", "width", "color"}` — un côté à
|
|
`style="none"` (ou absent) ne produit aucune déclaration pour ce
|
|
côté, jamais un `border-top:none` explicite.
|
|
- **Retour** : les déclarations CSS (`"propriete:valeur; ..."`),
|
|
jamais vide ni `None`.
|
|
- **Exceptions** : aucune."""
|
|
parts: list[str] = []
|
|
|
|
padding = str(a.get("padding", "")).strip()
|
|
if padding:
|
|
parts.append(f"padding:{html_lib.escape(padding)};")
|
|
|
|
margin = str(a.get("margin", "")).strip()
|
|
if margin:
|
|
parts.append(f"margin:{html_lib.escape(margin)};")
|
|
|
|
background_color = str(a.get("background_color", "")).strip()
|
|
if background_color:
|
|
parts.append(f"background-color:{html_lib.escape(background_color)};")
|
|
|
|
border_radius = str(a.get("border_radius", "")).strip()
|
|
if border_radius:
|
|
parts.append(f"border-radius:{html_lib.escape(border_radius)};")
|
|
|
|
height = str(a.get("height", "")).strip()
|
|
if height:
|
|
parts.append(f"height:{html_lib.escape(height)};")
|
|
min_height = str(a.get("min_height", "")).strip()
|
|
if min_height:
|
|
parts.append(f"min-height:{html_lib.escape(min_height)};")
|
|
max_height = str(a.get("max_height", "")).strip()
|
|
if max_height:
|
|
parts.append(f"max-height:{html_lib.escape(max_height)};")
|
|
min_width = str(a.get("min_width", "")).strip()
|
|
if min_width:
|
|
parts.append(f"min-width:{html_lib.escape(min_width)};")
|
|
|
|
box_shadow = str(a.get("box_shadow", "")).strip()
|
|
if box_shadow:
|
|
parts.append(f"box-shadow:{html_lib.escape(box_shadow)};")
|
|
|
|
opacity = str(a.get("opacity", "")).strip()
|
|
if opacity:
|
|
parts.append(f"opacity:{html_lib.escape(opacity)};")
|
|
|
|
border = a.get("border") or {}
|
|
for side in BORDER_SIDES:
|
|
side_border = border.get(side) or {}
|
|
style = str(side_border.get("style", "none"))
|
|
if style and style != "none":
|
|
width = html_lib.escape(str(side_border.get("width", "1px")))
|
|
color = html_lib.escape(str(side_border.get("color", "var(--doc-border)")))
|
|
parts.append(f"border-{side}:{width} {html_lib.escape(style)} {color};")
|
|
|
|
# align-self ne fait quoi que ce soit d'utile QUE si l'élément a par
|
|
# ailleurs une taille bornée (max_width/width) — voir la note dans
|
|
# element_kind_labels.md — mais reste toujours sûr à poser seul
|
|
# ("stretch" est déjà le comportement par défaut d'un enfant flex en
|
|
# colonne, donc jamais ajouté explicitement pour ne rien changer).
|
|
align_self = str(a.get("align_self", "stretch"))
|
|
if align_self and align_self != "stretch":
|
|
parts.append(f"align-self:{html_lib.escape(align_self)};")
|
|
|
|
return " ".join(parts)
|