Audit complet de mise en forme — Liste à puces/numérotée (4e élément)
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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8f879f6321
commit
4db1269348
@@ -165,7 +165,43 @@ def element_default_attributes(kind: str) -> dict[str, Any]:
|
||||
# "ordonnée ou non" se lit directement sur le kind au moment du
|
||||
# rendu (voir render_document_element._render_list), jamais un
|
||||
# attribut "ordered" redondant à tenir synchronisé avec le kind.
|
||||
return {"items": ["Premier élément", "Deuxième élément"]}
|
||||
#
|
||||
# Audit du 26/09/2026 (réglages manquants — colonne "Liste à
|
||||
# puces/numérotée") : portée actée avec l'utilisateur = style de
|
||||
# la LISTE ENTIÈRE (typo/puces/boîte/bordure/fond), jamais un
|
||||
# style par élément individuel ni de sous-listes (chantier bien
|
||||
# plus lourd, transformerait `items` d'une liste de chaînes en
|
||||
# objets structurés — différé à une demande séparée). Seule
|
||||
# exception : `item_padding`, un padding UNIFORME appliqué à
|
||||
# CHAQUE élément (retour utilisateur explicite : "il faut un
|
||||
# padding de base par élément de liste car y en a pas
|
||||
# aujourd'hui") — une valeur PARTAGÉE par tous les éléments,
|
||||
# jamais réglable individuellement, voir _render_list.
|
||||
# list_style_type : valide uniquement parmi les valeurs propres
|
||||
# au kind (disc/circle/square/none pour puces,
|
||||
# decimal/.../none pour numérotée) — vérifié au rendu, jamais ici.
|
||||
# svg_markup (puce personnalisée) : ignoré au rendu pour
|
||||
# liste_numerotee (une puce imagée n'a pas de sens sur une liste
|
||||
# numérotée, voir _render_list).
|
||||
return {
|
||||
"items": ["Premier élément", "Deuxième élément"],
|
||||
"bold": False,
|
||||
"italic": False,
|
||||
"underline": False,
|
||||
"font_family": "",
|
||||
"font_size": "",
|
||||
"line_height": "",
|
||||
"text_color": "",
|
||||
"list_style_type": "",
|
||||
"list_style_position": "outside",
|
||||
"marker_color": "",
|
||||
"marker_size": "",
|
||||
"svg_markup": "",
|
||||
"item_padding": "6px",
|
||||
"item_spacing": "",
|
||||
**BOX_DEFAULTS,
|
||||
"border": default_border(),
|
||||
}
|
||||
if kind == "badge":
|
||||
# Même esprit que titre/paragraphe (bold/align/color... déjà des
|
||||
# attributs par élément, pas des choix figés par le moteur) :
|
||||
|
||||
@@ -47,10 +47,14 @@ Attributs posés à la création d'un élément de ce type (voir
|
||||
- **Retour** : un dict d'attributs par défaut, dépendant du `kind` (les
|
||||
"attributs de boîte partagés" mentionnés ci-dessous — `padding/margin/
|
||||
background_color/border_radius/width/max_width/height/min_height/
|
||||
max_height/min_width/box_shadow/opacity/align_self/border` — sont
|
||||
toujours les mêmes, voir `rendering/box_style.py` : tous vides,
|
||||
`False` ou `"none"`/`"stretch"` par défaut = comportement historique
|
||||
inchangé pour le kind qui les gagne) :
|
||||
max_height/min_width/box_shadow/opacity/align_self/content_align/
|
||||
border` — sont toujours les mêmes, voir `rendering/box_style.py` :
|
||||
tous vides, `False` ou `"none"`/`"stretch"`/`"top"` par défaut =
|
||||
comportement historique inchangé pour le kind qui les gagne ;
|
||||
`content_align` — alignement vertical du CONTENU dans son bloc, retour
|
||||
utilisateur du 26/09/2026 — n'est cependant appliqué au rendu QUE par
|
||||
les kinds dont le conteneur est en colonne (texte, liste, figure d'une
|
||||
image légendée), jamais par le Bouton, voir `rendering.md`) :
|
||||
texte (`content/style` + `bold/italic/underline/strikethrough/align/
|
||||
color/font_family/font_size/line_height/letter_spacing/
|
||||
text_transform/text_shadow` + les attributs de boîte partagés),
|
||||
@@ -70,7 +74,18 @@ Attributs posés à la création d'un élément de ce type (voir
|
||||
(typo, jamais gérés par box_style.py) + `svg_markup/icon_position
|
||||
("before"/"after")/icon_size` (icône optionnelle, voir rendering.md)
|
||||
+ les attributs de boîte partagés), liste à
|
||||
puces/numérotée (`items`, une liste de chaînes), badge
|
||||
puces/numérotée (`items`, une liste de chaînes — style sur la LISTE
|
||||
ENTIÈRE uniquement, jamais par élément individuel ni de sous-listes,
|
||||
portée actée avec l'utilisateur le 26/09/2026 : `bold/italic/
|
||||
underline/font_family/font_size/line_height/text_color` (typo) +
|
||||
`list_style_type` (valide selon le kind, voir rendering.md)/
|
||||
`list_style_position`/`marker_color`/`marker_size`/`svg_markup`
|
||||
(puce personnalisée, liste à puces uniquement) + `item_padding`
|
||||
(`"6px"` par défaut, PAS vide — retour utilisateur explicite : "il
|
||||
faut un padding de base par élément de liste car y en a pas
|
||||
aujourd'hui", UNIFORME sur tous les éléments, jamais réglable
|
||||
individuellement) + `item_spacing` + les attributs de boîte
|
||||
partagés), badge
|
||||
(`content/svg_markup/width/border_radius/bold/uppercase` — tous vides
|
||||
ou `False` par défaut = comportement historique inchangé, voir
|
||||
rendering.md), carte (`label/title/description`, contenu pur sans
|
||||
|
||||
@@ -35,8 +35,11 @@ BOX_DEFAULTS = {
|
||||
"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
|
||||
@@ -104,3 +107,27 @@ def render_box_style(a: dict[str, Any]) -> str:
|
||||
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 ""
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import html as html_lib
|
||||
import json
|
||||
import random
|
||||
import urllib.parse
|
||||
from typing import Any
|
||||
|
||||
from .box_style import render_box_style
|
||||
from .box_style import render_box_style, render_content_align
|
||||
from .sanitize_svg_markup import sanitize_svg_markup
|
||||
|
||||
|
||||
@@ -102,6 +103,9 @@ def _render_text(el: dict[str, Any], _children_by_parent: dict[int | None, list[
|
||||
box_style = render_box_style(a)
|
||||
if box_style:
|
||||
style += f" {box_style}"
|
||||
content_align = render_content_align(a)
|
||||
if content_align:
|
||||
style += f" {content_align}"
|
||||
|
||||
return f'<div class="docText" data-element-id="{el["id"]}" data-kind="{el["kind"]}" style="{style}">{content}</div>'
|
||||
|
||||
@@ -199,7 +203,15 @@ def _render_image(el: dict[str, Any], _children_by_parent: dict[int | None, list
|
||||
media = f'<div class="docImageLightboxTrigger"{wrapper_style_attr}>{media}</div>'
|
||||
|
||||
if caption:
|
||||
figure_style_attr = f' style="{box_style}"' if box_style else ""
|
||||
# render_content_align (retour utilisateur du 26/09/2026 :
|
||||
# "je peux augmenter la hauteur d'un conteneur mais pas
|
||||
# l'alignement vertical à l'intérieur") n'a de sens ici QUE pour
|
||||
# la figure (conteneur flex-colonne à plusieurs enfants réels —
|
||||
# image + légende) : jamais sur l'<img> seul ni sur les
|
||||
# enveloppes lien/plein écran, qui ne sont pas des conteneurs
|
||||
# flex-colonne à plusieurs enfants.
|
||||
figure_style = " ".join(p for p in (box_style, render_content_align(a)) if p)
|
||||
figure_style_attr = f' style="{figure_style}"' if figure_style else ""
|
||||
media = (
|
||||
f'<figure class="docImageFigure"{figure_style_attr}>{media}'
|
||||
f'<figcaption class="docImageCaption">{html_lib.escape(caption)}</figcaption></figure>'
|
||||
@@ -208,6 +220,101 @@ def _render_image(el: dict[str, Any], _children_by_parent: dict[int | None, list
|
||||
return media
|
||||
|
||||
|
||||
_LIST_STYLE_TYPES = {
|
||||
"liste_puces": ("disc", "circle", "square", "none"),
|
||||
"liste_numerotee": (
|
||||
"decimal",
|
||||
"decimal-leading-zero",
|
||||
"lower-roman",
|
||||
"upper-roman",
|
||||
"lower-alpha",
|
||||
"upper-alpha",
|
||||
"none",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _list_text_style(a: dict[str, Any]) -> list[str]:
|
||||
"""Typographie de la liste ENTIÈRE (jamais par élément individuel,
|
||||
voir element_kind_labels.py — portée actée avec l'utilisateur)."""
|
||||
parts = []
|
||||
if a.get("bold"):
|
||||
parts.append("font-weight:700;")
|
||||
if a.get("italic"):
|
||||
parts.append("font-style:italic;")
|
||||
if a.get("underline"):
|
||||
parts.append("text-decoration:underline;")
|
||||
font_family = str(a.get("font_family", "")).strip()
|
||||
if font_family:
|
||||
parts.append(f"font-family:{html_lib.escape(font_family)};")
|
||||
font_size = str(a.get("font_size", "")).strip()
|
||||
if font_size:
|
||||
parts.append(f"font-size:{html_lib.escape(font_size)};")
|
||||
line_height = str(a.get("line_height", "")).strip()
|
||||
if line_height:
|
||||
parts.append(f"line-height:{html_lib.escape(line_height)};")
|
||||
text_color = str(a.get("text_color", "")).strip()
|
||||
if text_color:
|
||||
parts.append(f"color:{html_lib.escape(text_color)};")
|
||||
return parts
|
||||
|
||||
|
||||
def _list_marker_style(a: dict[str, Any], kind: str) -> list[str]:
|
||||
"""Puces/numéros — `marker_color`/`marker_size` passent par des
|
||||
PROPRIÉTÉS PERSONNALISÉES CSS (héritées jusqu'au pseudo-élément
|
||||
`::marker` de chaque <li>, voir .docList li::marker dans
|
||||
document-editor.css) : un style inline posé sur le <ul>/<ol> ne peut
|
||||
pas cibler directement le `::marker` de ses enfants autrement."""
|
||||
parts = []
|
||||
list_style_type = str(a.get("list_style_type", ""))
|
||||
if list_style_type in _LIST_STYLE_TYPES.get(kind, ()):
|
||||
parts.append(f"list-style-type:{list_style_type};")
|
||||
position_inside = str(a.get("list_style_position", "outside")) == "inside"
|
||||
if position_inside:
|
||||
parts.append("list-style-position:inside;")
|
||||
# Retour utilisateur du 26/09/2026 : "si j'enlève les puces ou que les
|
||||
# puces se mettent à l'intérieur, il reste un espace devant la liste,
|
||||
# cet espace doit être supprimé" — le padding-left:1.4em par défaut
|
||||
# (document-editor.css, .docList) réserve la place d'une puce
|
||||
# EXTÉRIEURE ; il n'a plus lieu d'être dès que la puce n'est plus là
|
||||
# ("none") ou qu'elle rejoint le flux du texte ("inside").
|
||||
if list_style_type == "none" or position_inside:
|
||||
parts.append("padding-left:0;")
|
||||
marker_color = str(a.get("marker_color", "")).strip()
|
||||
if marker_color:
|
||||
parts.append(f"--doc-marker-color:{html_lib.escape(marker_color)};")
|
||||
marker_size = str(a.get("marker_size", "")).strip()
|
||||
if marker_size:
|
||||
parts.append(f"--doc-marker-size:{html_lib.escape(marker_size)};")
|
||||
# Puce personnalisée (image SVG) : liste à puces UNIQUEMENT, une
|
||||
# puce imagée n'a pas de sens sur une liste numérotée. list-style-image
|
||||
# prime visuellement sur list-style-type dès qu'il est posé (aucun
|
||||
# conflit à gérer entre les deux).
|
||||
svg_markup = str(a.get("svg_markup", "")).strip() if kind == "liste_puces" else ""
|
||||
if svg_markup:
|
||||
sanitized = sanitize_svg_markup(svg_markup)
|
||||
encoded = urllib.parse.quote(sanitized)
|
||||
parts.append(f'list-style-image:url("data:image/svg+xml,{encoded}");')
|
||||
return parts
|
||||
|
||||
|
||||
def _list_item_style(a: dict[str, Any]) -> list[str]:
|
||||
"""`item_padding`/`item_spacing` s'appliquent à CHAQUE <li>, jamais au
|
||||
conteneur <ul>/<ol> lui-même — même mécanisme de propriété
|
||||
personnalisée CSS héritée que `_list_marker_style` ci-dessus (voir
|
||||
.docList li dans document-editor.css). Une valeur UNIFORME partagée
|
||||
par tous les éléments (retour utilisateur du 26/09/2026), jamais
|
||||
réglable par élément individuel."""
|
||||
parts = []
|
||||
item_padding = str(a.get("item_padding", "")).strip()
|
||||
if item_padding:
|
||||
parts.append(f"--doc-item-padding:{html_lib.escape(item_padding)};")
|
||||
item_spacing = str(a.get("item_spacing", "")).strip()
|
||||
if item_spacing:
|
||||
parts.append(f"--doc-item-spacing:{html_lib.escape(item_spacing)};")
|
||||
return parts
|
||||
|
||||
|
||||
def _render_list(el: dict[str, Any], _children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str:
|
||||
"""Liste à puces (<ul>) ou numérotée (<ol>) — le kind lui-même décide
|
||||
la balise, pas un attribut "ordered" séparé (voir
|
||||
@@ -216,10 +323,22 @@ def _render_list(el: dict[str, Any], _children_by_parent: dict[int | None, list[
|
||||
image sans fichier, une liste sans élément n'a rien d'anormal à
|
||||
afficher (le créateur vient peut-être de tout supprimer avant d'en
|
||||
retaper un)."""
|
||||
items = el["attributes"].get("items", [])
|
||||
tag = "ol" if el["kind"] == "liste_numerotee" else "ul"
|
||||
a = el["attributes"]
|
||||
kind = el["kind"]
|
||||
items = a.get("items", [])
|
||||
tag = "ol" if kind == "liste_numerotee" else "ul"
|
||||
items_html = "".join(f"<li>{html_lib.escape(str(item))}</li>" for item in items)
|
||||
return f'<{tag} class="docList" data-element-id="{el["id"]}" data-kind="{el["kind"]}">{items_html}</{tag}>'
|
||||
|
||||
style = " ".join(
|
||||
_list_text_style(a)
|
||||
+ _list_marker_style(a, kind)
|
||||
+ _list_item_style(a)
|
||||
+ [render_box_style(a), render_content_align(a)]
|
||||
)
|
||||
style = style.strip()
|
||||
style_attr = f' style="{style}"' if style else ""
|
||||
|
||||
return f'<{tag} class="docList" data-element-id="{el["id"]}" data-kind="{kind}"{style_attr}>{items_html}</{tag}>'
|
||||
|
||||
|
||||
_BUTTON_TEXT_TRANSFORMS = ("uppercase", "lowercase", "capitalize")
|
||||
|
||||
@@ -113,6 +113,41 @@ regroupement à chaque appel.
|
||||
partagée par les deux) — un `<li>` par entrée de `attributes["items"]`.
|
||||
Une liste vide rend `<ul>`/`<ol>` sans enfant plutôt qu'un placeholder :
|
||||
contrairement à une image sans fichier, ce n'est pas un état anormal.
|
||||
Style inline (audit du 26/09/2026, portée actée avec l'utilisateur :
|
||||
la LISTE ENTIÈRE uniquement, jamais par élément individuel ni de
|
||||
sous-listes) : `bold`/`italic`/`underline`/`font_family`/`font_size`/
|
||||
`line_height`/`text_color` (typo, `_list_text_style`) +
|
||||
`list_style_type` (validé selon le `kind` — `disc`/`circle`/`square`/
|
||||
`none` pour `liste_puces`, `decimal`/`decimal-leading-zero`/
|
||||
`lower-roman`/`upper-roman`/`lower-alpha`/`upper-alpha`/`none` pour
|
||||
`liste_numerotee`, toute autre valeur ignorée)/`list_style_position`
|
||||
(`"outside"` par défaut) + les attributs de boîte partagés
|
||||
(`render_box_style`, voir `box_style.py`). `marker_color`/
|
||||
`marker_size`/`item_padding`/`item_spacing` (`_list_marker_style`/
|
||||
`_list_item_style`) passent par des PROPRIÉTÉS PERSONNALISÉES CSS
|
||||
(`--doc-marker-color`/`--doc-marker-size`/`--doc-item-padding`/
|
||||
`--doc-item-spacing`) : un style inline sur le `<ul>`/`<ol>` ne peut
|
||||
pas cibler directement le `::marker` ou le padding de ses `<li>`
|
||||
enfants autrement — ces propriétés sont posées sur le conteneur et
|
||||
consommées par `static/document/document-editor.css`
|
||||
(`.docList li`/`.docList li::marker`), qui hérite jusque-là.
|
||||
`svg_markup` (liste à puces UNIQUEMENT, ignoré pour `liste_numerotee`)
|
||||
: puce personnalisée — nettoyé par `sanitize_svg_markup` puis encodé
|
||||
en URI de données (`urllib.parse.quote`) pour `list-style-image`, qui
|
||||
prime visuellement sur `list_style_type` dès qu'il est posé.
|
||||
`render_content_align(a)` (voir `box_style.py`) également ajouté :
|
||||
`.docList` est `display:flex; flex-direction:column;` (un `<li>` garde
|
||||
son `display:list-item` propre — puce/numéro visibles — même une fois
|
||||
flex-item, ce sont deux notions indépendantes en CSS).
|
||||
**Bug réel corrigé** (retour utilisateur du 26/09/2026 : "si j'enlève
|
||||
les puces ou que les puces se mettent à l'intérieur, il reste un
|
||||
espace devant la liste, cet espace doit être supprimé") :
|
||||
`padding-left:0;` est ajouté automatiquement dès que
|
||||
`list_style_type="none"` ou `list_style_position="inside"` — le
|
||||
`padding-left:1.4em` par défaut (`.docList`, réservé pour une puce
|
||||
EXTÉRIEURE) n'a alors plus lieu d'être ; un `padding` uniforme réglé
|
||||
explicitement par ailleurs (attributs de boîte partagés) reste
|
||||
prioritaire (déclaré après, dans le même style inline).
|
||||
- **Quiz** : toujours une carte résumant la config réelle (nombre de
|
||||
questions, total des points via `quiz_total_points`, minuteur si
|
||||
activé) — sanitizée (`sanitize_quiz_config`) avant lecture, jamais un
|
||||
@@ -246,9 +281,9 @@ Un dict à 4 clés (`BORDER_SIDES`), chacune `{"style": "none", "width":
|
||||
`{"padding": "", "margin": "", "background_color": "", "border_radius":
|
||||
"", "align_self": "stretch", "width": "", "max_width": "", "height": "",
|
||||
"min_height": "", "max_height": "", "min_width": "", "box_shadow": "",
|
||||
"opacity": ""}` — `border` n'y figure PAS (voir `default_border()`, à
|
||||
ajouter séparément par chaque appelant pour éviter le partage par
|
||||
référence).
|
||||
"opacity": "", "content_align": "top"}` — `border` n'y figure PAS (voir
|
||||
`default_border()`, à ajouter séparément par chaque appelant pour éviter
|
||||
le partage par référence).
|
||||
|
||||
### `render_box_style(a: dict[str, Any]) -> str`
|
||||
Construit les déclarations CSS inline pour chaque attribut listé dans
|
||||
@@ -269,6 +304,26 @@ colonne).
|
||||
vide si rien à ajouter.
|
||||
- **Exceptions** : aucune.
|
||||
|
||||
### `render_content_align(a: dict[str, Any]) -> str`
|
||||
Alignement vertical du CONTENU à l'intérieur de son propre bloc (retour
|
||||
utilisateur du 26/09/2026 : "je peux augmenter la hauteur d'un
|
||||
conteneur mais pas l'alignement vertical à l'intérieur") — `content_align`
|
||||
(`"top"` par défaut, `"center"` ou `"bottom"`) mappé vers
|
||||
`justify-content` (`""` pour `"top"`, comportement historique inchangé).
|
||||
Jamais fusionné dans `render_box_style` : contrairement à `align_self`
|
||||
(position du BLOC dans SON parent, valable pour tout consommateur),
|
||||
l'alignement du CONTENU dépend de l'axe interne du conteneur —
|
||||
`justify-content` convient à un conteneur en colonne (`_render_text`/
|
||||
`_render_list`, dont les classes CSS `.docText`/`.docList` sont
|
||||
`display:flex; flex-direction:column;`, et la figure d'une image
|
||||
légendée, déjà flex-colonne), mais serait FAUX pour le Bouton (rangée
|
||||
icône+texte : l'axe vertical y est déjà géré par `align-items`, voir
|
||||
`static/document/document-editor.css`, `.docButton`) — chaque renderer
|
||||
qui veut ce comportement l'appelle donc explicitement lui-même.
|
||||
- **Retour** : `""` si absent/`"top"`/valeur inconnue, sinon
|
||||
`"justify-content:...;"`.
|
||||
- **Exceptions** : aucune.
|
||||
|
||||
## `sanitize_svg_markup.py` — nettoyage du contenu SVG inline d'une image
|
||||
|
||||
### `sanitize_svg_markup(markup: str) -> str`
|
||||
|
||||
Reference in New Issue
Block a user