{#- chirp-ui: Form field macros Extended field macros with BEM styling. These complement (and can replace) Chirp's built-in form macros from "chirp/forms". Internal: field_wrapper provides label, hint, error display. Standard fields use it; checkbox/toggle/radio/range/input_group have custom layouts. form() htmx usage: call form("/save", hx={"post": "/save", "target": "#result", "swap": "innerHTML"}) ...fields... end hx={} — preferred for htmx attributes. Individual hx_* kwargs override dict keys. Auto-behaviors: hx-select="unset" + hx-disinherit="hx-select" when htmx is detected; hx-on::after-request reset on success for mutating methods. Mutating htmx forms default to hx-sync="this:drop" plus hx-disabled-elt for submit controls to prevent accidental duplicate in-flight submissions. -#} {% def form(action, method="get", enctype=none, density="", cls="", attrs="", attrs_unsafe="", attrs_map=none, hx=none, hx_get=none, hx_post=none, hx_put=none, hx_patch=none, hx_delete=none, hx_target=none, hx_swap=none, hx_trigger=none, hx_include=none, hx_select=none, hx_select_oob=none, hx_disabled_elt=none, hx_sync=none, hx_ext=none, hx_vals=none, hx_reset_on_success=none) %} {% set _attrs_raw = attrs_unsafe or (attrs | deprecate_param("attrs", "attrs_unsafe or attrs_map")) %} {# @provides _form_density — consumed by: field_wrapper #} {% provide _form_density = density %} {#- hx_reset_on_success: when true, form resets after successful htmx response (2xx). Defaults to true when form has hx-post/put/patch/delete (via params or attrs_map). See https://htmx.org/examples/reset-user-input/ hx_sync: mutating htmx forms default to "this:drop"; pass explicitly or via attrs_map to override, or pass "" to opt out. See https://htmx.org/docs/#synchronization -#} {% set _hx_dict = build_hx_attrs(hx=hx, hx_get=hx_get, hx_post=hx_post, hx_put=hx_put, hx_patch=hx_patch, hx_delete=hx_delete, hx_target=hx_target, hx_swap=hx_swap, hx_trigger=hx_trigger, hx_include=hx_include, hx_ext=hx_ext, hx_vals=hx_vals) %} {% set _has_hx = _hx_dict.get("hx-post") or _hx_dict.get("hx-put") or _hx_dict.get("hx-patch") or _hx_dict.get("hx-delete") or (attrs_map or {}).get("hx-post") or (attrs_map or {}).get("hx-put") or (attrs_map or {}).get("hx-patch") or (attrs_map or {}).get("hx-delete") %} {% set _reset = hx_reset_on_success if hx_reset_on_success is not none else _has_hx %} {% set _sync_from_attrs = (attrs_map or {}).get("hx-sync") %} {% set _disabled_from_attrs = (attrs_map or {}).get("hx-disabled-elt") %} {% set _sync = hx_sync if hx_sync is not none else ("this:drop" if _has_hx and not _sync_from_attrs else none) %} {% set _disabled_elt = hx_disabled_elt if hx_disabled_elt is not none else ("find button, find input[type=submit]" if _has_hx and not _disabled_from_attrs else none) %} {% set _select = hx_select if hx_select is not none else ("unset" if _has_hx else none) %} {% set _disinherit = "hx-select" if _has_hx and hx_select is none else none %}
{% end %} {% end %} {% def fieldset(legend=none, cls="") %} {% end %} {# Shared wrapper: label, slot (control), hint, errors. modifier adds chirpui-field--X. field_id: override wrapper id (default: "field-{name}"). oob: emit hx-swap-oob="true". #} {% def field_wrapper(name, label=none, errors=none, required=false, hint=none, modifier="", field_id=none, oob=false, appearance="", tone="") %} {% set _fid = field_id if field_id else "field-" ~ name %} {% set _eid = "errors-" ~ name %} {% set _has_errors = errors and errors | field_errors(name) %} {% set _appearance = appearance | validate_appearance_block("field", "") %} {% set _tone = tone | validate_tone_block("field", "") %} {# @consumes _form_density from: form — falls back to "" #} {% set _density = consume("_form_density", "") %}Please fix {{ _fields | length }} error{{ "s" if _fields | length != 1 else "" }} below: