diff --git a/screens/rendering/render_jauge.py b/screens/rendering/render_jauge.py index f2efc3d4..4fd09b85 100644 --- a/screens/rendering/render_jauge.py +++ b/screens/rendering/render_jauge.py @@ -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'{percent}%' ) diff --git a/screens/rendering/render_onglets.py b/screens/rendering/render_onglets.py index 633b933e..feb1fbaa 100644 --- a/screens/rendering/render_onglets.py +++ b/screens/rendering/render_onglets.py @@ -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 + #
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}
' ) + tabs_class = html_lib.escape(("tabs " + tabs_extra_classes).strip()) return ( f'
' - f'
' + f'
' f'
{"".join(panels)}
' f"
" ) diff --git a/screens/rendering/render_select.py b/screens/rendering/render_select.py index cd3835c1..ac0e9505 100644 --- a/screens/rendering/render_select.py +++ b/screens/rendering/render_select.py @@ -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 + # 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'' for o in options) select_html = f'' - return f'
{select_html}
' + select_wrap_class = html_lib.escape(("select is-fullwidth " + bulma_classes).strip()) + return f'
{select_html}
' diff --git a/screens/widgets/bulma_variants.py b/screens/widgets/bulma_variants.py new file mode 100644 index 00000000..bc678597 --- /dev/null +++ b/screens/widgets/bulma_variants.py @@ -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"), +] diff --git a/screens/widgets/control_groups/bulma_controls.py b/screens/widgets/control_groups/bulma_controls.py index fe93bca7..365a8ea0 100644 --- a/screens/widgets/control_groups/bulma_controls.py +++ b/screens/widgets/control_groups/bulma_controls.py @@ -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), ] diff --git a/screens/widgets/default_style_for_widget.py b/screens/widgets/default_style_for_widget.py index 885b824f..2b2d04a5 100644 --- a/screens/widgets/default_style_for_widget.py +++ b/screens/widgets/default_style_for_widget.py @@ -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 diff --git a/screens/widgets/registry.py b/screens/widgets/registry.py index 998be7d3..354cbf6d 100644 --- a/screens/widgets/registry.py +++ b/screens/widgets/registry.py @@ -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, }, }