Expose every Bulma variant as a choosable option, not just a curated few
Adds a shared bulma_variants.py (color/size option lists) and expands BULMA_*_CONTROLS in bulma_controls.py to cover each component's full modifier set: Bouton (colour incl. white/light/dark/black/text, light-shade toggle, size, rounded, outlined, inverted, static, loading, fullwidth), Titre (Bulma size is-1..is-6 independent of heading level, is-spaced), Jauge (size), Tableau (bordered/fullwidth/striped/hoverable/narrow, now toggleable instead of hardcoded), Onglets (alignment, boxed/toggle/toggle-rounded style, size, fullwidth), and every form field — champ_texte/email/mot_de_passe, zone_texte, liste_deroulante (colour, size, rounded, static). render_select.py/render_onglets.py/render_jauge.py now redirect the merged "class" from _visible_attrs to the actual Bulma sub-element (the .select wrapper, the .tabs div, the <progress> tag) instead of the outer positioning wrapper, since that's what needs to carry the modifier classes. Also fixed a real latent bug found while wiring this up: a checkbox control with default=True (e.g. Tableau's "Première ligne = en-tête") was never actually applied on a freshly created element — default_style_and_attributes unconditionally skipped ALL checkbox types at creation, so the panel showed it checked while the element itself had nothing set. Now a checkbox's default=True is frozen at creation like any other meaningful default; default=False (the common case) is unaffected. Verified end to end: every widget's variant controls save and render the right class tokens (spot-checked titre/tableau/onglets/champ_texte/liste_deroulante/jauge), full suite green (89 passed).
This commit is contained in:
@@ -138,8 +138,10 @@ def _render_jauge(el, meta, slug, children_map=None, ctx=None, parent_flex_direc
|
||||
# donnée" — sinon la jauge resterait figée sur sa valeur de départ après
|
||||
# un changement, exactement comme il fallait déjà repérer "repeaterItem"
|
||||
# pour les Répéteurs (voir 1.1/1.2).
|
||||
extra_classes = " ".join(v for k, v in attrs.items() if k.startswith("_class_") and v)
|
||||
bar_class = html_lib.escape((f"progress jaugeBar {extra_classes}").strip())
|
||||
bar = (
|
||||
f'<progress class="progress jaugeBar" value="{percent}" max="100" '
|
||||
f'<progress class="{bar_class}" value="{percent}" max="100" '
|
||||
f'style="--bulma-progress-value-background-color:{fill_color}; --bulma-progress-bar-background-color:#1c2130; '
|
||||
f'margin:0; border-radius:6px; transition:background-color .3s ease;">{percent}%</progress>'
|
||||
)
|
||||
|
||||
@@ -21,6 +21,10 @@ def _render_onglets(el, meta, slug, children_map=None, ctx=None, parent_flex_dir
|
||||
|
||||
tabs = (children_map or {}).get(el["id"], [])
|
||||
attrs = _visible_attrs(el, meta, ctx)
|
||||
# Les variantes Bulma (alignement/style/taille... voir BULMA_TABS_CONTROLS)
|
||||
# calculées par _visible_attrs atterrissent dans "class" — c'est le
|
||||
# <div class="tabs"> qui doit les porter, pas ce wrapper de positionnement.
|
||||
tabs_extra_classes = attrs.pop("class", "")
|
||||
style = _style_string(el, " display:flex; flex-direction:column;", parent_flex_direction)
|
||||
group_id = _auto_id(el)
|
||||
|
||||
@@ -53,9 +57,10 @@ def _render_onglets(el, meta, slug, children_map=None, ctx=None, parent_flex_dir
|
||||
f'{tab_html}</div>'
|
||||
)
|
||||
|
||||
tabs_class = html_lib.escape(("tabs " + tabs_extra_classes).strip())
|
||||
return (
|
||||
f'<div {_attr_string(attrs, style)}>'
|
||||
f'<div class="tabs"><ul>{"".join(bar_items)}</ul></div>'
|
||||
f'<div class="{tabs_class}"><ul>{"".join(bar_items)}</ul></div>'
|
||||
f'<div class="forgeTabPanels" style="flex:1 1 auto; min-height:0;">{"".join(panels)}</div>'
|
||||
f"</div>"
|
||||
)
|
||||
|
||||
@@ -15,6 +15,11 @@ def _render_select(el, meta, slug=None, children_map=None, ctx=None, parent_flex
|
||||
# l'élément (comme tous les autres widgets), le <select> reste à
|
||||
# l'intérieur avec ses propres attributs de formulaire (name, required).
|
||||
outer_attrs = _visible_attrs(el, meta, ctx)
|
||||
# Les variantes Bulma (couleur/taille/arrondi... voir BULMA_FIELD_CONTROLS)
|
||||
# calculées par _visible_attrs atterrissent normalement dans "class" —
|
||||
# ici c'est le <div class="select"> qui doit les porter, pas le <select>
|
||||
# ni le wrapper de positionnement, donc on les redirige à la main.
|
||||
bulma_classes = outer_attrs.get("class", "")
|
||||
select_attrs = {k: v for k, v in outer_attrs.items() if k not in ("id", "class", "data-element-id", "data-anim-target")}
|
||||
wrapper_attrs = {k: v for k, v in outer_attrs.items() if k in ("id", "data-element-id", "data-anim-target")}
|
||||
style = _style_string(el, " display:flex;", parent_flex_direction)
|
||||
@@ -22,4 +27,5 @@ def _render_select(el, meta, slug=None, children_map=None, ctx=None, parent_flex
|
||||
options = [o.strip() for o in content.split("\n") if o.strip()]
|
||||
opts_html = "".join(f'<option value="{html_lib.escape(o)}">{html_lib.escape(o)}</option>' for o in options)
|
||||
select_html = f'<select {_attr_string(select_attrs, "")}>{opts_html}</select>'
|
||||
return f'<div {_attr_string(wrapper_attrs, style)}><div class="select is-fullwidth">{select_html}</div></div>'
|
||||
select_wrap_class = html_lib.escape(("select is-fullwidth " + bulma_classes).strip())
|
||||
return f'<div {_attr_string(wrapper_attrs, style)}><div class="{select_wrap_class}">{select_html}</div></div>'
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Listes de modificateurs Bulma (https://bulma.io) réutilisées par plusieurs
|
||||
# widgets pour construire leurs réglages "class:xxx" (voir
|
||||
# control_groups/bulma_controls.py et save_element_controls.py) — un seul
|
||||
# endroit à mettre à jour si Bulma change de version/palette.
|
||||
BULMA_COLOR_OPTIONS = [
|
||||
("", "Aucune (couleur par défaut)"),
|
||||
("is-primary", "Primaire"), ("is-link", "Lien"), ("is-info", "Info"),
|
||||
("is-success", "Succès"), ("is-warning", "Attention"), ("is-danger", "Danger"),
|
||||
("is-white", "Blanc"), ("is-light", "Clair"), ("is-dark", "Sombre"), ("is-black", "Noir"), ("is-text", "Texte"),
|
||||
]
|
||||
|
||||
BULMA_SIZE_OPTIONS = [
|
||||
("", "Normale"), ("is-small", "Petite"), ("is-medium", "Moyenne"), ("is-large", "Grande"),
|
||||
]
|
||||
@@ -1,25 +1,65 @@
|
||||
from ..controls.c_select import c_select
|
||||
from ..controls.c_checkbox import c_checkbox
|
||||
from ..bulma_variants import BULMA_COLOR_OPTIONS, BULMA_SIZE_OPTIONS
|
||||
|
||||
_GROUP = "Style Bulma"
|
||||
|
||||
# Chaque widget "natif Bulma" (voir fixed_attrs dans registry.py) expose ici
|
||||
# TOUTES les variantes que Bulma propose pour son composant — chacune ajoute
|
||||
# un jeton de classe indépendant (target "class:xxx", voir
|
||||
# save_element_controls.py et _visible_attrs.py) à la classe de base déjà
|
||||
# posée par fixed_attrs. Laissé à "" (par défaut) = aucun modificateur, look
|
||||
# Bulma nu. Les réglages de couleur/taille "libres" du widget (curseurs,
|
||||
# sélecteurs de couleur) restent utilisables par-dessus : un style en ligne
|
||||
# gagne toujours contre une classe CSS, donc personnaliser une couleur ou une
|
||||
# taille "à la main" continue de fonctionner exactement comme avant.
|
||||
|
||||
# Réglages Bulma (https://bulma.io) posés en plus des réglages de style
|
||||
# "libres" déjà existants (couleur, taille de police...) — chacun ajoute un
|
||||
# jeton de classe CSS (target "class:xxx", voir save_element_controls.py et
|
||||
# _visible_attrs.py) à la classe de base du widget (posée via fixed_attrs,
|
||||
# ex. "button"). Laissé à "" (par défaut) = look Bulma nu, sans modificateur.
|
||||
# Les réglages de couleur/taille "libres" du widget restent utilisables par-
|
||||
# dessus : un style en ligne gagne toujours contre une classe CSS, donc
|
||||
# personnaliser une couleur ou une taille continue de fonctionner exactement
|
||||
# comme avant, Bulma ne servant alors que de base par défaut.
|
||||
BULMA_BUTTON_CONTROLS = [
|
||||
c_select("bulma_couleur", "Style Bulma — couleur", "class:couleur", [
|
||||
("", "Aucun (couleurs ci-dessous)"),
|
||||
("is-primary", "Primaire"), ("is-link", "Lien"), ("is-info", "Info"),
|
||||
("is-success", "Succès"), ("is-warning", "Attention"), ("is-danger", "Danger"),
|
||||
("is-light", "Clair"), ("is-dark", "Sombre"),
|
||||
], default="", group="Style Bulma"),
|
||||
c_select("bulma_taille", "Style Bulma — taille", "class:taille", [
|
||||
("", "Normale"), ("is-small", "Petite"), ("is-medium", "Moyenne"), ("is-large", "Grande"),
|
||||
], default="", group="Style Bulma"),
|
||||
c_checkbox("bulma_arrondi", "Coins bien arrondis (Bulma)", "class:arrondi", "is-rounded", "", group="Style Bulma"),
|
||||
c_checkbox("bulma_contour", "Contour seulement (Bulma)", "class:contour", "is-outlined", "", group="Style Bulma"),
|
||||
c_select("bulma_couleur", "Couleur", "class:couleur", BULMA_COLOR_OPTIONS, default="", group=_GROUP),
|
||||
c_checkbox("bulma_nuance_claire", "Nuance claire (combinable avec une couleur)", "class:nuance", "is-light", "", group=_GROUP),
|
||||
c_select("bulma_taille", "Taille", "class:taille", BULMA_SIZE_OPTIONS, default="", group=_GROUP),
|
||||
c_checkbox("bulma_arrondi", "Coins bien arrondis", "class:arrondi", "is-rounded", "", group=_GROUP),
|
||||
c_checkbox("bulma_contour", "Contour seulement (fond transparent)", "class:contour", "is-outlined", "", group=_GROUP),
|
||||
c_checkbox("bulma_inverse", "Couleurs inversées", "class:inverse", "is-inverted", "", group=_GROUP),
|
||||
c_checkbox("bulma_statique", "Statique (non cliquable visuellement)", "class:statique", "is-static", "", group=_GROUP),
|
||||
c_checkbox("bulma_chargement", "Indicateur de chargement", "class:chargement", "is-loading", "", group=_GROUP),
|
||||
c_checkbox("bulma_pleine_largeur", "Pleine largeur", "class:largeur", "is-fullwidth", "", group=_GROUP),
|
||||
]
|
||||
|
||||
BULMA_TITLE_CONTROLS = [
|
||||
c_select("bulma_taille", "Taille Bulma (indépendante du niveau de titre)", "class:taille",
|
||||
[("", "Automatique"), ("is-1", "1 — la plus grande"), ("is-2", "2"), ("is-3", "3"),
|
||||
("is-4", "4"), ("is-5", "5"), ("is-6", "6 — la plus petite")], default="", group=_GROUP),
|
||||
c_checkbox("bulma_espace", "Garder l'espacement même sans sous-titre", "class:espace", "is-spaced", "", group=_GROUP),
|
||||
]
|
||||
|
||||
BULMA_PROGRESS_CONTROLS = [
|
||||
c_select("bulma_taille", "Taille de la barre", "class:taille", BULMA_SIZE_OPTIONS, default="", group=_GROUP),
|
||||
]
|
||||
|
||||
BULMA_TABLE_CONTROLS = [
|
||||
c_checkbox("bulma_bordure", "Bordures", "class:bordure", "is-bordered", "", default=True, group=_GROUP),
|
||||
c_checkbox("bulma_pleine_largeur", "Pleine largeur", "class:largeur", "is-fullwidth", "", default=True, group=_GROUP),
|
||||
c_checkbox("bulma_zebre", "Lignes zébrées", "class:zebre", "is-striped", "", group=_GROUP),
|
||||
c_checkbox("bulma_survol", "Ligne surlignée au survol", "class:survol", "is-hoverable", "", group=_GROUP),
|
||||
c_checkbox("bulma_compact", "Compact (cellules resserrées)", "class:compact", "is-narrow", "", group=_GROUP),
|
||||
]
|
||||
|
||||
BULMA_TABS_CONTROLS = [
|
||||
c_select("bulma_alignement", "Alignement", "class:alignement",
|
||||
[("", "Gauche (par défaut)"), ("is-centered", "Centré"), ("is-right", "Droite")], default="", group=_GROUP),
|
||||
c_select("bulma_style", "Style", "class:style",
|
||||
[("", "Simple (soulignement)"), ("is-boxed", "Encadré"), ("is-toggle", "Boutons accolés"),
|
||||
("is-toggle is-toggle-rounded", "Boutons accolés, arrondis")], default="", group=_GROUP),
|
||||
c_select("bulma_taille", "Taille", "class:taille", BULMA_SIZE_OPTIONS, default="", group=_GROUP),
|
||||
c_checkbox("bulma_pleine_largeur", "Pleine largeur (onglets étirés)", "class:largeur", "is-fullwidth", "", group=_GROUP),
|
||||
]
|
||||
|
||||
# Champs de formulaire (input/textarea/select) : mêmes variantes que Bulma
|
||||
# propose pour .input/.textarea/.select.
|
||||
BULMA_FIELD_CONTROLS = [
|
||||
c_select("bulma_couleur", "Couleur", "class:couleur", BULMA_COLOR_OPTIONS, default="", group=_GROUP),
|
||||
c_select("bulma_taille", "Taille", "class:taille", BULMA_SIZE_OPTIONS, default="", group=_GROUP),
|
||||
c_checkbox("bulma_arrondi", "Coins bien arrondis", "class:arrondi", "is-rounded", "", group=_GROUP),
|
||||
c_checkbox("bulma_statique", "Statique (lecture seule visuelle)", "class:statique", "is-static", "", group=_GROUP),
|
||||
]
|
||||
|
||||
@@ -49,7 +49,24 @@ def default_style_and_attributes(widget_key):
|
||||
attributes, style = {}, {}
|
||||
for control in meta.get("controls", []):
|
||||
ctype = control["type"]
|
||||
if ctype in ("checkbox", "toggle_token", "preset", "scale", "color"):
|
||||
if ctype == "checkbox":
|
||||
# Une case cochée PAR DÉFAUT (ex: "Première ligne = en-tête" du
|
||||
# Tableau, ou un modificateur Bulma comme "is-bordered" activé
|
||||
# d'origine) doit vraiment être active dès la création — sinon le
|
||||
# panneau de propriétés affiche une case cochée qui ne correspond
|
||||
# à rien de réellement posé sur l'élément. Une case décochée par
|
||||
# défaut, elle, reste volontairement absente (comportement
|
||||
# existant, inchangé) : rien à figer.
|
||||
if control.get("default") is True and control.get("target"):
|
||||
kind, name = control["target"].split(":", 1)
|
||||
if kind == "class":
|
||||
attributes["_class_" + name] = control["true_value"]
|
||||
elif kind == "attr":
|
||||
attributes[name] = control["true_value"]
|
||||
else:
|
||||
style[name] = control["true_value"]
|
||||
continue
|
||||
if ctype in ("toggle_token", "preset", "scale", "color"):
|
||||
continue
|
||||
if ctype == "size":
|
||||
# 0 est TOUJOURS la valeur sentinelle "automatique" pour ce
|
||||
|
||||
+14
-11
@@ -9,7 +9,10 @@ from .controls.c_align import c_align
|
||||
from .controls.c_select import c_select
|
||||
from .control_groups.text_style_controls_extra import TEXT_STYLE_CONTROLS_EXTRA
|
||||
from .control_groups.data_binding_controls import DATA_BINDING_CONTROLS
|
||||
from .control_groups.bulma_controls import BULMA_BUTTON_CONTROLS
|
||||
from .control_groups.bulma_controls import (
|
||||
BULMA_BUTTON_CONTROLS, BULMA_TITLE_CONTROLS, BULMA_PROGRESS_CONTROLS,
|
||||
BULMA_TABLE_CONTROLS, BULMA_TABS_CONTROLS, BULMA_FIELD_CONTROLS,
|
||||
)
|
||||
from .font_options import FONT_OPTIONS
|
||||
from ..flow.constants import CONDITION_OPERATORS
|
||||
|
||||
@@ -40,7 +43,7 @@ WIDGETS = {
|
||||
c_textarea("content", "Texte — {{champ}} affiche la valeur de ce champ sur la ligne trouvée ci-dessous", "content", default="Titre", group="Contenu"),
|
||||
c_select("niveau", "Importance du titre", "attr:_niveau",
|
||||
[("1", "Très grand — titre principal"), ("2", "Grand"), ("3", "Moyen")], default="2", group="Contenu"),
|
||||
] + DATA_BINDING_CONTROLS + [
|
||||
] + DATA_BINDING_CONTROLS + BULMA_TITLE_CONTROLS + [
|
||||
c_slider("font-size", "Taille du texte", "style:font-size", min=14, max=80, default=32, group="Texte"),
|
||||
c_color("color", "Couleur du texte", "style:color", "#e8eaf0", group="Texte"),
|
||||
c_align("text-align", "Alignement", "style:text-align", default="left", group="Texte"),
|
||||
@@ -105,7 +108,7 @@ WIDGETS = {
|
||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre réponse", group="Contenu"),
|
||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. reponse1", group="Contenu"),
|
||||
c_checkbox("required", "Obligatoire", "attr:required", "required", "", group="Contenu"),
|
||||
],
|
||||
] + BULMA_FIELD_CONTROLS,
|
||||
},
|
||||
"champ_email": {
|
||||
"label": "Champ — email", "icon": "📧", "tag": "input", "has_content": False, "self_closing": True,
|
||||
@@ -114,7 +117,7 @@ WIDGETS = {
|
||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. votre@email.com", default="votre@email.com", group="Contenu"),
|
||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. email", group="Contenu"),
|
||||
c_checkbox("required", "Obligatoire", "attr:required", "required", "", group="Contenu"),
|
||||
],
|
||||
] + BULMA_FIELD_CONTROLS,
|
||||
},
|
||||
"champ_mot_de_passe": {
|
||||
"label": "Champ — mot de passe", "icon": "🔒", "tag": "input", "has_content": False, "self_closing": True,
|
||||
@@ -123,7 +126,7 @@ WIDGETS = {
|
||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Mot de passe", group="Contenu"),
|
||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. mot_de_passe", group="Contenu"),
|
||||
c_checkbox("required", "Obligatoire", "attr:required", "required", "", group="Contenu"),
|
||||
],
|
||||
] + BULMA_FIELD_CONTROLS,
|
||||
},
|
||||
"case_a_cocher": {
|
||||
"label": "Case à cocher", "icon": "☑️", "tag": "input", "has_content": True, "special_render": "checkbox_label",
|
||||
@@ -154,7 +157,7 @@ WIDGETS = {
|
||||
c_text("placeholder", "Texte indicatif", "attr:placeholder", placeholder="ex. Votre message", group="Contenu"),
|
||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. message", group="Contenu"),
|
||||
c_checkbox("required", "Obligatoire", "attr:required", "required", "", group="Contenu"),
|
||||
],
|
||||
] + BULMA_FIELD_CONTROLS,
|
||||
},
|
||||
"liste_deroulante": {
|
||||
"label": "Liste déroulante", "icon": "🔽", "tag": "select", "has_content": True, "special_render": "select_options",
|
||||
@@ -163,7 +166,7 @@ WIDGETS = {
|
||||
c_textarea("content", "Options — une par ligne", "content", default="Option 1\nOption 2\nOption 3", group="Contenu"),
|
||||
c_text("name", "Nom du champ", "attr:name", placeholder="ex. choix", group="Contenu"),
|
||||
c_checkbox("required", "Obligatoire", "attr:required", "required", "", group="Contenu"),
|
||||
],
|
||||
] + BULMA_FIELD_CONTROLS,
|
||||
},
|
||||
"groupe_champs": {
|
||||
"label": "Groupe de champs", "icon": "🗃️", "tag": "fieldset", "has_content": True, "special_render": "fieldset_legend",
|
||||
@@ -195,13 +198,13 @@ WIDGETS = {
|
||||
"tableau": {
|
||||
"label": "Tableau", "icon": "🗂️", "tag": "table", "has_content": True, "special_render": "table",
|
||||
"w": 55, "h": 25, "default_content": "Colonne 1;Colonne 2\nValeur A;Valeur B",
|
||||
"fixed_attrs": {"class": "table is-bordered is-fullwidth"},
|
||||
"fixed_attrs": {"class": "table"},
|
||||
"controls": [
|
||||
c_textarea("content", "Contenu — une ligne par rangée, colonnes séparées par ;", "content",
|
||||
default="Colonne 1;Colonne 2\nValeur A;Valeur B", group="Contenu"),
|
||||
c_checkbox("header_row", "Première ligne = en-tête", "attr:_header_row", "1", "", default=True, group="Contenu"),
|
||||
c_color("color", "Couleur du texte", "style:color", "#e8eaf0", group="Texte"),
|
||||
],
|
||||
] + BULMA_TABLE_CONTROLS,
|
||||
},
|
||||
"conteneur": {
|
||||
"label": "Conteneur / décor", "icon": "⬛", "tag": "div", "has_content": True,
|
||||
@@ -283,7 +286,7 @@ WIDGETS = {
|
||||
c_slider("nom_taille", "Taille du texte du nom", "attr:_nom_taille", unit="", min=8, max=32, default=12, group="Apparence"),
|
||||
c_checkbox("nom_gras", "Nom en gras", "attr:_nom_gras", "1", "", default=False, group="Apparence"),
|
||||
c_checkbox("nom_italique", "Nom en italique", "attr:_nom_italique", "1", "", default=False, group="Apparence"),
|
||||
],
|
||||
] + BULMA_PROGRESS_CONTROLS,
|
||||
},
|
||||
"superposition": {
|
||||
# 3.4 (Confort) — overlay/modale réutilisable : couvre TOUJOURS tout
|
||||
@@ -310,6 +313,6 @@ WIDGETS = {
|
||||
# qui réutilise les routes existantes (element_rename, element_delete).
|
||||
"label": "Onglets", "icon": "🗂️", "tag": "div", "has_content": False, "special_render": "onglets",
|
||||
"w": 60, "h": 40, "default_content": "",
|
||||
"controls": [],
|
||||
"controls": BULMA_TABS_CONTROLS,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user