Implémente toutes les options manquantes identifiées pour les listes :
typographie complète, style/position/couleur/taille de puce (validés
selon le kind), puce personnalisée en SVG pour les listes à puces,
padding uniforme par élément (nouveau, il n'y en avait aucun), espacement
entre éléments réglable, et tous les attributs de boîte partagés sur la
liste entière. Bordure/fond/padding par élément individuel et sous-listes
imbriquées volontairement différés (portée actée avec l'utilisateur
avant implémentation : transformeraient le stockage des éléments en
objets structurés, chantier bien plus lourd).
Trois ajouts transversaux bénéficiant à plusieurs éléments : sections
"Contenu"/"Conteneur" dans tous les panneaux de propriétés, alignement
vertical du contenu dans son bloc (Titre/Paragraphe/Liste/Image
légendée), et une option pour retirer un thème appliqué ("Aucun modèle"
dans la modale, avec une nouvelle fonction db.remove_document_theme).
Quatre bugs réels trouvés et corrigés en chaîne pendant la validation
avec le thème "Sécurité incendie" : un badge de thème s'affichait
au-dessus du texte au lieu d'à côté ; le correctif a d'abord fait
disparaître les puces/numéros natifs de TOUTES les listes (bug plus
grave que celui corrigé) ; puis un marqueur natif redondant apparaissait
à côté du badge du thème ; puis une règle CSS site-large de spécificité
supérieure empêchait silencieusement ce dernier correctif. Chaque étape
vérifiée par navigateur automatisé sur un support jetable.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
134 lines
5.6 KiB
Python
134 lines
5.6 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",
|
|
"width": "",
|
|
"max_width": "",
|
|
"height": "",
|
|
"min_height": "",
|
|
"max_height": "",
|
|
"min_width": "",
|
|
"box_shadow": "",
|
|
"opacity": "",
|
|
"content_align": "top",
|
|
}
|
|
|
|
_CONTENT_ALIGN_TO_JUSTIFY_CONTENT = {"center": "center", "bottom": "flex-end"}
|
|
|
|
|
|
# (clé d'attribut, propriété CSS) — chaque paire suit exactement le même
|
|
# patron (lire/nettoyer/ajouter si non vide) ; une simple table de
|
|
# correspondance ici évite un enchaînement de blocs `if` identiques
|
|
# (complexité cognitive réduite, voir _render_simple_properties).
|
|
_SIMPLE_PROPERTIES = (
|
|
("padding", "padding"),
|
|
("margin", "margin"),
|
|
("background_color", "background-color"),
|
|
("border_radius", "border-radius"),
|
|
("width", "width"),
|
|
("max_width", "max-width"),
|
|
("height", "height"),
|
|
("min_height", "min-height"),
|
|
("max_height", "max-height"),
|
|
("min_width", "min-width"),
|
|
("box_shadow", "box-shadow"),
|
|
("opacity", "opacity"),
|
|
)
|
|
|
|
|
|
def _render_simple_properties(a: dict[str, Any]) -> list[str]:
|
|
parts = []
|
|
for attr_key, css_prop in _SIMPLE_PROPERTIES:
|
|
value = str(a.get(attr_key, "")).strip()
|
|
if value:
|
|
parts.append(f"{css_prop}:{html_lib.escape(value)};")
|
|
return parts
|
|
|
|
|
|
def _render_border(a: dict[str, Any]) -> list[str]:
|
|
"""Un côté à `style="none"` (ou absent) ne produit aucune déclaration
|
|
pour ce côté, jamais un `border-top:none` explicite."""
|
|
parts = []
|
|
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};")
|
|
return parts
|
|
|
|
|
|
def render_box_style(a: dict[str, Any]) -> str:
|
|
"""Construit les déclarations CSS inline communes à plusieurs kinds à
|
|
partir des attributs listés dans `_SIMPLE_PROPERTIES` + `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"}`.
|
|
- **Retour** : les déclarations CSS (`"propriete:valeur; ..."`),
|
|
jamais vide ni `None`.
|
|
- **Exceptions** : aucune."""
|
|
parts = _render_simple_properties(a) + _render_border(a)
|
|
|
|
# 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)
|
|
|
|
|
|
def render_content_align(a: dict[str, Any]) -> str:
|
|
"""Alignement vertical du CONTENU à l'intérieur de son propre bloc —
|
|
utile UNIQUEMENT une fois qu'une hauteur fixe/minimale dépasse la
|
|
hauteur naturelle du contenu (retour utilisateur du 26/09/2026 : "je
|
|
peux augmenter la hauteur d'un conteneur mais pas l'alignement
|
|
vertical à l'intérieur"). Jamais fusionné dans `render_box_style` :
|
|
contrairement à `align_self` (position du BLOC dans SON parent, la
|
|
même logique convient à tout consommateur), l'alignement du CONTENU
|
|
dépend de l'axe interne du conteneur — correct en `justify-content`
|
|
pour un conteneur en colonne (texte, liste), mais un bouton
|
|
(rangée : icône + texte) gère déjà cet axe autrement (`align-items`,
|
|
voir static/document/document-editor.css, .docButton) : chaque
|
|
renderer qui veut ce comportement l'appelle donc explicitement lui-
|
|
même (voir _render_text/_render_list/_render_image), jamais
|
|
automatiquement pour tous les kinds.
|
|
- **Retour** : `""` si `content_align` est absent ou `"top"` (défaut,
|
|
comportement historique inchangé), sinon la déclaration
|
|
`justify-content:...;`.
|
|
- **Exceptions** : aucune."""
|
|
content_align = str(a.get("content_align", "top"))
|
|
justify_content = _CONTENT_ALIGN_TO_JUSTIFY_CONTENT.get(content_align)
|
|
return f"justify-content:{justify_content};" if justify_content else ""
|