{# macros/form_fields.html — Complete widget macro library for form rendering. Usage: {% from "macros/form_fields.html" import render_field %} {{ render_field(field_ctx, model_name) }} field_ctx is a FieldRenderContext with: .meta — FieldMeta (name, label, required, readonly, help_text, placeholder, extra) .widget_macro — str macro name .widget_context — dict from widget.render_context() .errors — list[str] #} {# ─── Dispatcher ────────────────────────────────────────────────────────── #} {% macro render_field(field_ctx, model_name) %} {% set m = field_ctx.widget_macro %} {% if m == "text_input" %}{{ _text_input(field_ctx, model_name) }} {% elif m == "textarea" %}{{ _textarea(field_ctx, model_name) }} {% elif m == "number_input" %}{{ _number_input(field_ctx, model_name) }} {% elif m == "toggle" %}{{ _toggle(field_ctx) }} {% elif m == "select" %}{{ _select(field_ctx, model_name) }} {% elif m == "date_picker" %}{{ _date_picker(field_ctx, model_name) }} {% elif m == "datetime_picker" %}{{ _datetime_picker(field_ctx, model_name) }} {% elif m == "time_picker" %}{{ _time_picker(field_ctx, model_name) }} {% elif m == "json_editor" %}{{ _json_editor(field_ctx) }} {% elif m == "password_input" %}{{ _password_input(field_ctx, model_name) }} {% elif m == "readonly" %}{{ _readonly(field_ctx) }} {% elif m == "hidden" %}{{ _hidden(field_ctx) }} {% elif m == "email_input" %}{{ _email_input(field_ctx, model_name) }} {% elif m == "url_input" %}{{ _url_input(field_ctx, model_name) }} {% elif m == "phone_input" %}{{ _phone_input(field_ctx, model_name) }} {% elif m == "color_picker" %}{{ _color_picker(field_ctx) }} {% elif m == "slug" %}{{ _slug(field_ctx, model_name) }} {% elif m == "image_upload" %}{{ _image_upload(field_ctx) }} {% elif m == "file_upload" %}{{ _file_upload(field_ctx) }} {% elif m == "tag_input" %}{{ _tag_input(field_ctx) }} {% elif m == "relation_picker" %}{{ _relation_picker(field_ctx) }} {% elif m == "multi_relation" %}{{ _multi_relation(field_ctx) }} {% else %} {{ _text_input(field_ctx, model_name) }} {% endif %} {% endmacro %} {# ─── Shared helpers ────────────────────────────────────────────────────── #} {% macro _field_wrapper_open(field_ctx) %} {% set name = field_ctx.meta.name %} {% set has_error = field_ctx.errors | length > 0 %} {% set full_width = field_ctx.meta.extra.get('full_width', false) %}
{% endmacro %} {% macro _field_wrapper_close() %}
{% endmacro %} {% macro _label(field_ctx) %} {% set name = field_ctx.meta.name %} {% endmacro %} {% macro _errors(field_ctx) %} {% if field_ctx.errors %} {% endif %} {% endmacro %} {% macro _help_text(field_ctx) %} {% if field_ctx.meta.help_text %}

{{ field_ctx.meta.help_text }}

{% endif %} {% endmacro %} {% macro _htmx_validate(field_ctx, model_name, trigger="blur") %} hx-post="{{ admin_path }}/{{ model_name }}/validate-field" hx-trigger="{{ trigger }}" hx-target="#field-wrapper-{{ field_ctx.meta.name }}" hx-swap="outerHTML" hx-vals='{"field_name": "{{ field_ctx.meta.name }}"}' {% endmacro %} {# ─── Text Input ────────────────────────────────────────────────────────── #} {% macro _text_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Textarea ──────────────────────────────────────────────────────────── #} {% macro _textarea(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set rows = field_ctx.widget_context.rows | default(5) %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Number Input ──────────────────────────────────────────────────────── #} {% macro _number_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set step = field_ctx.widget_context.step | default('1') %} {% set min = field_ctx.widget_context.min %} {% set max = field_ctx.widget_context.max %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Toggle (Boolean) ─────────────────────────────────────────────────── #} {% macro _toggle(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Select (Enum) ────────────────────────────────────────────────────── #} {% macro _select(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set choices = field_ctx.widget_context.choices | default([]) %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Date Picker ──────────────────────────────────────────────────────── #} {% macro _date_picker(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── DateTime Picker ──────────────────────────────────────────────────── #} {% macro _datetime_picker(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Time Picker ──────────────────────────────────────────────────────── #} {% macro _time_picker(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── JSON Editor ──────────────────────────────────────────────────────── #} {% macro _json_editor(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Password Input ───────────────────────────────────────────────────── #} {% macro _password_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Email Input ──────────────────────────────────────────────────────── #} {% macro _email_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── URL Input ────────────────────────────────────────────────────────── #} {% macro _url_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Phone Input ──────────────────────────────────────────────────────── #} {% macro _phone_input(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }} {{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Color Picker ─────────────────────────────────────────────────────── #} {% macro _color_picker(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('#000000') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Slug Input ───────────────────────────────────────────────────────── #} {% macro _slug(field_ctx, model_name) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set source = field_ctx.widget_context.source_field | default('title') %} {% set has_error = field_ctx.errors | length > 0 %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Read-only Field ──────────────────────────────────────────────────── #} {% macro _readonly(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('-') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ val }}
{{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Hidden Field ─────────────────────────────────────────────────────── #} {% macro _hidden(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% endmacro %} {# ─── Image Upload ─────────────────────────────────────────────────────── #} {% macro _image_upload(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{% if val %}
{% endif %}
{% if val %} {% endif %}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── File Upload ──────────────────────────────────────────────────────── #} {% macro _file_upload(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{% if val %}
Current: {{ val }}
{% endif %}
{% if val %} {% endif %}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Tag Input ────────────────────────────────────────────────────────── #} {% macro _tag_input(field_ctx) %} {% set name = field_ctx.meta.name %} {% set tags = field_ctx.widget_context.value | default('[]') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Relation Picker (ForeignKey) ─────────────────────────────────────── #} {% macro _relation_picker(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('') %} {% set label_text = field_ctx.widget_context.label_text | default('') %} {% set related_table = field_ctx.widget_context.related_table | default('') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{% if val %}
Selected: {{ label_text }}
{% endif %}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %} {# ─── Multi-Relation (Many-to-Many) ────────────────────────────────────── #} {% macro _multi_relation(field_ctx) %} {% set name = field_ctx.meta.name %} {% set val = field_ctx.widget_context.value | default('[]') %} {% set related_table = field_ctx.widget_context.related_table | default('') %} {{ _field_wrapper_open(field_ctx) }} {{ _label(field_ctx) }}
{{ _errors(field_ctx) }} {{ _help_text(field_ctx) }} {{ _field_wrapper_close() }} {% endmacro %}