|
1 | 1 | {% comment %} |
2 | 2 | V3 text input field. |
3 | 3 | Variables: |
4 | | - name (required) — input name attribute |
5 | | - label (optional) — label text |
6 | | - placeholder (optional) — placeholder text |
7 | | - value (optional) — pre-filled value; with submit_icon, also seeds the Alpine x-model state so it survives init |
8 | | - type (optional) — input type, default "text" |
9 | | - help_text (optional) — help text below the field |
10 | | - error (optional) — static server-side error message, rendered on page load |
11 | | - alpine_error (optional) — Alpine expression that resolves to an error string for dynamic |
12 | | - server-driven errors without a page reload (e.g. "errors.title") |
13 | | - validate_type (optional) — client-side validation before submit, currently supports "email" |
14 | | - validate_error (optional) — error message shown when validate_type check fails |
15 | | - icon_left (optional) — icon name for left slot (e.g. "search") |
16 | | - icon_right (optional) — icon name for a decorative right-slot icon (non-interactive); mutually exclusive with submit_icon |
17 | | - submit_icon (optional) — icon name for a submit button in the right slot (e.g. "arrow-right") |
18 | | - submit_label (optional) — aria-label for the submit button, default "Submit" |
19 | | - dispatch_field_change (optional) — when truthy (requires submit_icon), dispatches a debounced |
20 | | - `field-change` event (`{ name, value }`) on each keystroke, matching the |
21 | | - dropdown/combo pattern so parent filter components can react live. |
22 | | - required (optional) — if truthy, adds required attribute |
23 | | - disabled (optional) — if truthy, adds disabled attribute |
24 | | - extra_class (optional) — additional classes on the wrapper |
| 4 | + name (required) — input name attribute |
| 5 | + label (optional) — label text |
| 6 | + placeholder (optional) — placeholder text |
| 7 | + value (optional) — pre-filled value; with submit_icon, also seeds the Alpine x-model state so it survives init |
| 8 | + type (optional) — input type, default "text" |
| 9 | + help_text (optional) — help text below the field |
| 10 | + error (optional) — static server-side error message, rendered on page load |
| 11 | + alpine_error (optional) — Alpine expression that resolves to an error string for dynamic |
| 12 | + server-driven errors without a page reload (e.g. "errors.title") |
| 13 | + validate_type (optional) — client-side validation before submit, currently supports "email" |
| 14 | + validate_error (optional) — error message shown when validate_type check fails |
| 15 | + icon_left (optional) — icon name for left slot (e.g. "search") |
| 16 | + icon_right (optional) — icon name for a decorative right-slot icon (non-interactive); mutually exclusive with submit_icon |
| 17 | + submit_icon (optional) — icon name for a submit button in the right slot (e.g. "arrow-right") |
| 18 | + submit_label (optional) — aria-label for the submit button, default "Submit" |
| 19 | + dispatch_field_change (optional) — when truthy (requires submit_icon), dispatches a debounced |
| 20 | + `field-change` event (`{ name, value }`) on each keystroke, matching the |
| 21 | + dropdown/combo pattern so parent filter components can react live. |
| 22 | + required (optional) — if truthy, adds required attribute |
| 23 | + disabled (optional) — if truthy, adds disabled attribute |
| 24 | + extra_class (optional) — additional classes on the wrapper |
| 25 | + display_max_chars (optional) — controls whether the max_char limit should be displayed. Requires the following argument as well. |
| 26 | + max_chars (optional) — if provided, will present a maximum length counter on this field. If JS is enabled, this |
| 27 | + field will dynamically show characters left |
25 | 28 |
|
26 | 29 | Error handling: |
27 | 30 | error is the base layer for server-side validation and can be combined with either |
|
56 | 59 | }" |
57 | 60 | :class="{ 'field--error': isInvalid || isEmpty }" |
58 | 61 | @validate="touched = true" |
59 | | - {% elif alpine_error %}:class="{ 'field--error': {{ alpine_error }} }"{% endif %}> |
| 62 | + {% elif alpine_error %}:class="{ 'field--error': {{ alpine_error }} }" |
| 63 | + {% elif max_chars %} |
| 64 | + x-data="{ |
| 65 | + value: '', |
| 66 | + maxChars: {{max_chars}}, |
| 67 | + get charLeft() { |
| 68 | + return Number(this.maxChars) - this.value?.length || 0; |
| 69 | + }, |
| 70 | + get isTooLong() { |
| 71 | + return this.charLeft < 0; |
| 72 | + } |
| 73 | + }" |
| 74 | + :class="{ 'field--error': isTooLong }" |
| 75 | + {% endif %}> |
60 | 76 | {# djlint:on #} |
| 77 | + {% if label or display_max_chars %} |
| 78 | + <div class="field__label-row"> |
61 | 79 | {% if label %}<label class="field__label" for="field-{{ name }}">{{ label }}</label>{% endif %} |
| 80 | + {% if max_chars and display_max_chars %} |
| 81 | + <span |
| 82 | + class="field__max-chars" x-text="`${charLeft} left`"> |
| 83 | + {{max_chars}} left |
| 84 | + </span> |
| 85 | + {% endif %} |
| 86 | + </div> |
| 87 | + {% endif %} |
62 | 88 | <div class="field__control {% if disabled %}field__control--disabled{% endif %}" |
63 | 89 | {% if submit_icon %}x-data="{ q: '{{ value|default:''|escapejs }}' }"{% endif %}> |
64 | 90 | {% if icon_left %} |
|
82 | 108 | {% elif help_text %} |
83 | 109 | aria-describedby="field-{{ name }}-help" |
84 | 110 | {% endif %} |
85 | | - {% if validate_type == "email" %}x-model="value" @blur="touched = true"{% elif submit_icon %}x-model="q" {% if dispatch_field_change %}@input.debounce.150ms="$dispatch('field-change', { name: '{{ name }}', value: q })"{% endif %}{% endif %}> |
| 111 | + {% if validate_type == "email" %}x-model="value" @blur="touched = true"{% elif submit_icon %}x-model="q" {% if dispatch_field_change %}@input.debounce.150ms="$dispatch('field-change', { name: '{{ name }}', value: q })"{% endif %}{% else %}x-model="value"{% endif %}> |
86 | 112 | {% if submit_icon %} |
87 | 113 | <button type="submit" |
88 | 114 | class="field__submit" |
|
100 | 126 | <p class="field__error" x-cloak x-show="isInvalid" role="alert">{{ validate_error }}</p> |
101 | 127 | <p class="field__error" x-cloak x-show="isEmpty" role="alert">This field is required.</p> |
102 | 128 | {% endif %} |
| 129 | + {% if max_chars and display_max_chars %} |
| 130 | + <p class="field__error" x-cloak x-show="isTooLong" role="alert">This field is too long.</p> |
| 131 | + {% endif %} |
103 | 132 | {% if alpine_error %} |
104 | 133 | <p class="field__error" |
105 | 134 | id="field-{{ name }}-error" |
|
0 commit comments