{# Form field macros - renders the appropriate input for each field type #} {# Contract: ~/.claude/skills/ux-architect/components/form-field.md (UX-017) #} {# Core branches: pure Tailwind + design-system variables. #} {# Widget branches (combobox, multi_select, tags, picker, range, color, #} {# rich_text, slider, money, file, search-select) retain their own markup #} {# and are out of scope for UX-017 — tracked by UX-009..015. #} {% macro render_field(field, values={}, errors={}) %} {% set value = values.get(field.name, field.default) if values else field.default %} {% set error = errors.get(field.name, "") if errors else "" %} {% set field_id = "field-" ~ field.name %} {% set error_id = "error-" ~ field.name %} {% set hint_id = "hint-" ~ field.name if field.hint is defined and field.hint else "" %} {% set described_by = ([error_id] if error else []) + ([hint_id] if hint_id else []) %} {% set base_input = "w-full h-8 px-3 rounded-[4px] bg-[hsl(var(--background))] border text-[13px] text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-1 focus:ring-[hsl(var(--ring))] transition-[box-shadow,border-color] duration-[120ms] [transition-timing-function:cubic-bezier(0.2,0,0,1)]" %} {% set border_idle = "border-[hsl(var(--border))]" %} {% set border_error = "border-[hsl(var(--destructive))]" %}
{{ field.hint }}
{% endif %} {% include "fragments/search_select.html" %} {% if error %}{{ error }}
{% endif %} {% elif field.ref_entity %} {# Entity-ref auto-wiring (cycle 236 — closes EX-044). When a DSL field is a plain `ref Entity` with no explicit `source:` override, the template compiler populates ref_entity + ref_api from the IR and the form renders a select that hydrates options from the entity's list endpoint. Mirrors the Alpine fetch pattern already used by filter_bar.html for ref/belongs_to column filters. #} {% if field.hint is defined and field.hint %}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %} {# ── Vendored widget overrides (Phase 4) ─────────────────────────── #} {% elif field.widget == "combobox" %} {# UX-009: combobox widget — TomSelect wrapper, pure Tailwind chrome #}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %} {% if errors.get(field.name) %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %}{{ errors[field.name] }}
{% endif %}{{ field.hint }}
{% endif %} {% if field.type == "textarea" %} {% elif field.type == "select" %} {% elif field.type == "date" %} {% elif field.type == "datetime" %} {% elif field.type == "money" %} {# UX-026: money widget — dzMoney Alpine, pure Tailwind chrome #} {# Major-unit display input + hidden minor-unit value carrier #} {% set minor_val = values.get(field.name ~ '_minor', '') if values else '' %} {% if field.extra.get('currency_fixed', true) %} {# Pinned currency — static prefix symbol #}{{ error }}
{% endif %}