import db from .filter_repeater_rows import _resolve_filter_value, _compare, _field_column def is_element_visible(slug, attrs, ctx=None): """Condition de visibilité (panneau de propriétés, tout widget — voir VISIBILITY_CONDITION_CONTROLS) : masque un élément selon soit une VARIABLE GLOBALE (une seule valeur pour tout le jeu, voir db/global_vars/), soit le champ d'un objet de données. Absente de réglage (_visibilite_cond_mode vide) => toujours visible, comportement inchangé pour tous les éléments déjà créés. Réutilise _compare/_resolve_filter_value/_field_column de filter_repeater_rows.py pour rester cohérent avec les mêmes opérateurs et la même syntaxe "{{Objet.champ}}" pour une valeur de comparaison dynamique. `ctx` porte les {{champ}} de la ligne en cours (voir render_repeater.py) quand cet élément est descendant d'un Répéteur de données — en mode "objet", si le champ réglé s'y trouve déjà, sa valeur est utilisée telle quelle plutôt que d'aller chercher la ligne la plus récente de l'objet en base : sans ça, TOUS les exemplaires d'un même modèle de ligne (ex. une "Mail card" répétée pour chaque e-mail) évalueraient la même ligne (la plus récente de l'objet) au lieu de chacun la sienne, et n'afficheraient donc jamais rien de cohérent. Hors Répéteur (mode "objet" sur un élément posé directement sur l'écran), le champ n'est pas dans ctx et on retombe sur la convention "1 seule ligne = état de partie" (déjà utilisée par la Jauge/Donnée liée) : la ligne la plus récente de l'objet choisi.""" mode = attrs.get("_visibilite_cond_mode") if mode not in ("variable", "objet"): return True operator = attrs.get("_visibilite_cond_operateur") or "egal" expected = _resolve_filter_value(slug, attrs.get("_visibilite_cond_valeur") or "") if mode == "variable": var_name = attrs.get("_visibilite_cond_variable") if not var_name: return True variable = db.get_global_variable(slug, var_name) if not variable: return True # variable supprimée depuis : ne bloque jamais l'affichage return _compare(variable.get("value"), operator, expected, variable.get("type") or "texte") definition_id = attrs.get("_visibilite_cond_definition_id") field_name = attrs.get("_visibilite_cond_champ") if not definition_id or not field_name: return True definition = db.get_definition(slug, int(definition_id)) if not definition: return True field_type = next((f["type"] for f in definition["fields"] if f["name"] == field_name), "texte") if ctx and field_name in ctx: return _compare(ctx.get(field_name), operator, expected, field_type) rows = db.list_rows(slug, definition) if not rows: return True # objet sans donnée pour l'instant : ne bloque pas row = rows[0] # convention "état de partie" : une seule ligne, voir data_definition_options.py return _compare(row.get(_field_column(definition, field_name)), operator, expected, field_type)