diff --git a/document_engine/labels/element_kind_labels.py b/document_engine/labels/element_kind_labels.py
index e42de5f8..2b4db007 100644
--- a/document_engine/labels/element_kind_labels.py
+++ b/document_engine/labels/element_kind_labels.py
@@ -11,7 +11,7 @@ from .mots_config import DEFAULT_MOTS_CONFIG
from .quiz_config import DEFAULT_QUIZ_CONFIG
from .scenario_config import DEFAULT_SCENARIO_CONFIG
-CONTENT_KINDS = ("titre", "paragraphe", "image", "bouton")
+CONTENT_KINDS = ("titre", "paragraphe", "image", "bouton", "liste_puces", "liste_numerotee")
MINIGAME_KINDS = ("quiz", "association", "memory", "mots", "scenario", "zones")
# "row" n'apparaît jamais dans la bibliothèque (créé implicitement par le
@@ -28,6 +28,8 @@ ELEMENT_KIND_LABELS: dict[str, str] = {
"paragraphe": "Paragraphe",
"image": "Image",
"bouton": "Bouton",
+ "liste_puces": "Liste à puces",
+ "liste_numerotee": "Liste numérotée",
"quiz": "Quiz",
"association": "Association",
"memory": "Memory",
@@ -57,6 +59,12 @@ def element_default_attributes(kind: str) -> dict[str, Any]:
return {"src": "", "alt": ""}
if kind == "bouton":
return {"label": "Bouton", "target": ""}
+ if kind in ("liste_puces", "liste_numerotee"):
+ # Une seule et même structure d'attributs pour les deux kinds —
+ # "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"]}
if kind == "row":
return {"gap": 16, "align": "stretch", "justify": "flex-start"}
if kind == "quiz":
diff --git a/document_engine/labels/labels.md b/document_engine/labels/labels.md
index 829e0362..2ccd7bbe 100644
--- a/document_engine/labels/labels.md
+++ b/document_engine/labels/labels.md
@@ -5,8 +5,13 @@ groupée par catégorie (panneau gauche de l'éditeur), libellés d'affichage, e
attributs par défaut posés à la création de chaque type.
## `CONTENT_KINDS: tuple[str, ...]`
-`("titre", "paragraphe", "image", "bouton")` — éléments du flux, peuvent
-être top-niveau ou enfants d'une rangée.
+`("titre", "paragraphe", "image", "bouton", "liste_puces",
+"liste_numerotee")` — éléments du flux, peuvent être top-niveau ou
+enfants d'une rangée. `"liste_puces"`/`"liste_numerotee"` partagent la
+même structure d'attributs (`items`) ; c'est le `kind` lui-même qui
+décide `
` ou `` au rendu (voir
+`document_engine/rendering/render_document_element.py::_render_list`),
+jamais un attribut `ordered` redondant.
## `MINIGAME_KINDS: tuple[str, ...]`
`("quiz", "association", "memory", "mots", "scenario", "zones")` —
@@ -30,7 +35,8 @@ Attributs posés à la création d'un élément de ce type (voir
`document_engine/elements/add_document_element.py`).
- **Retour** : un dict d'attributs par défaut, dépendant du `kind` :
texte (`content/style` + `bold/italic/underline/align/color`),
- image (`src/alt`), bouton (`label/target`), rangée (`gap/align/justify`),
+ image (`src/alt`), bouton (`label/target`), liste à puces/numérotée
+ (`items`, une liste de chaînes), rangée (`gap/align/justify`),
quiz (`DEFAULT_QUIZ_CONFIG`, voir `quiz_config.py`), association
(`DEFAULT_ASSOCIATION_CONFIG`, voir `association_config.py`), memory
(`DEFAULT_MEMORY_CONFIG`, voir `memory_config.py`), autre mini-jeu
diff --git a/document_engine/rendering/render_document_element.py b/document_engine/rendering/render_document_element.py
index 37776a33..c21b7349 100644
--- a/document_engine/rendering/render_document_element.py
+++ b/document_engine/rendering/render_document_element.py
@@ -74,6 +74,20 @@ def _render_image(el: dict[str, Any], _children_by_parent: dict[int | None, list
return f''
+def _render_list(el: dict[str, Any], _children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str:
+ """Liste à puces (
) ou numérotée () — le kind lui-même décide
+ la balise, pas un attribut "ordered" séparé (voir
+ element_kind_labels.element_default_attributes). Une "items" vide
+ rend une liste vide plutôt qu'un placeholder : contrairement à une
+ 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"
+ items_html = "".join(f"
{html_lib.escape(str(item))}
" for item in items)
+ return f'<{tag} class="docList" data-element-id="{el["id"]}" data-kind="{el["kind"]}">{items_html}{tag}>'
+
+
def _render_button(el: dict[str, Any], _children_by_parent: dict[int | None, list[dict[str, Any]]]) -> str:
a = el["attributes"]
label = html_lib.escape(str(a.get("label", "Bouton")))
@@ -498,6 +512,8 @@ _RENDERERS = {
"paragraphe": _render_text,
"image": _render_image,
"bouton": _render_button,
+ "liste_puces": _render_list,
+ "liste_numerotee": _render_list,
"quiz": _render_quiz,
"association": _render_association,
"memory": _render_memory,
diff --git a/document_engine/rendering/rendering.md b/document_engine/rendering/rendering.md
index db231a6a..05dad6fc 100644
--- a/document_engine/rendering/rendering.md
+++ b/document_engine/rendering/rendering.md
@@ -32,6 +32,11 @@ regroupement à chaque appel.
taille/graisse/interligne) et `bold`/`italic`/`underline`/`align`/`color`.
- **Image** : ``, ou un bloc placeholder si `src` est vide.
- **Bouton** : `