@mk-kit/ui 0.42.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +3 -3
  2. package/THIRD_PARTY_NOTICES.md +4 -2
  3. package/fesm2022/mk-kit-ui-block-editor.mjs +4 -2
  4. package/fesm2022/mk-kit-ui-block-editor.mjs.map +1 -1
  5. package/fesm2022/mk-kit-ui-checkbox.mjs +4 -2
  6. package/fesm2022/mk-kit-ui-checkbox.mjs.map +1 -1
  7. package/fesm2022/mk-kit-ui-core.mjs +131 -1
  8. package/fesm2022/mk-kit-ui-core.mjs.map +1 -1
  9. package/fesm2022/mk-kit-ui-data.mjs +437 -7
  10. package/fesm2022/mk-kit-ui-data.mjs.map +1 -1
  11. package/fesm2022/mk-kit-ui-datetime.mjs +88 -50
  12. package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -1
  13. package/fesm2022/mk-kit-ui-dynamic-form.mjs +149 -2
  14. package/fesm2022/mk-kit-ui-dynamic-form.mjs.map +1 -1
  15. package/fesm2022/mk-kit-ui-feedback.mjs +1 -1
  16. package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
  17. package/fesm2022/mk-kit-ui-forms.mjs +325 -77
  18. package/fesm2022/mk-kit-ui-forms.mjs.map +1 -1
  19. package/fesm2022/mk-kit-ui-icon-extended.mjs +425 -0
  20. package/fesm2022/mk-kit-ui-icon-extended.mjs.map +1 -0
  21. package/fesm2022/mk-kit-ui-icon.mjs +296 -459
  22. package/fesm2022/mk-kit-ui-icon.mjs.map +1 -1
  23. package/fesm2022/mk-kit-ui-query-builder.mjs +1 -1
  24. package/fesm2022/mk-kit-ui-query-builder.mjs.map +1 -1
  25. package/fesm2022/mk-kit-ui-rich-text.mjs +4 -2
  26. package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -1
  27. package/fesm2022/mk-kit-ui-table.mjs +10 -2
  28. package/fesm2022/mk-kit-ui-table.mjs.map +1 -1
  29. package/fesm2022/mk-kit-ui.mjs +1 -0
  30. package/fesm2022/mk-kit-ui.mjs.map +1 -1
  31. package/package.json +5 -1
  32. package/schematics/migrate-primeng/transform.js +4 -1
  33. package/schematics/ng-add/index.js +30 -2
  34. package/schematics/ng-add/schema.json +6 -0
  35. package/types/mk-kit-ui-block-editor.d.ts +2 -0
  36. package/types/mk-kit-ui-checkbox.d.ts +2 -0
  37. package/types/mk-kit-ui-core.d.ts +72 -2
  38. package/types/mk-kit-ui-data.d.ts +203 -2
  39. package/types/mk-kit-ui-datetime.d.ts +41 -19
  40. package/types/mk-kit-ui-dynamic-form.d.ts +44 -1
  41. package/types/mk-kit-ui-forms.d.ts +154 -16
  42. package/types/mk-kit-ui-icon-extended.d.ts +83 -0
  43. package/types/mk-kit-ui-icon.d.ts +100 -17
  44. package/types/mk-kit-ui-rich-text.d.ts +2 -0
  45. package/types/mk-kit-ui-table.d.ts +0 -17
  46. package/types/mk-kit-ui.d.ts +1 -0
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, input, booleanAttribute, ElementRef, contentChild, signal, computed, afterNextRender, effect, DestroyRef, forwardRef, ChangeDetectionStrategy, Component, DOCUMENT, Injector, viewChild, model, output, contentChildren, numberAttribute, afterRenderEffect, untracked, ViewContainerRef, Directive, viewChildren, TemplateRef, PLATFORM_ID } from '@angular/core';
2
+ import { inject, input, booleanAttribute, ElementRef, contentChild, computed, signal, afterNextRender, effect, DestroyRef, forwardRef, ChangeDetectionStrategy, Component, DOCUMENT, Injector, viewChild, model, output, contentChildren, numberAttribute, afterRenderEffect, untracked, ViewContainerRef, Directive, viewChildren, TemplateRef, PLATFORM_ID } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { NgForm, FormGroupDirective, NgControl, Validators, FormGroup, FormArray, NG_VALUE_ACCESSOR, FormsModule, NG_VALIDATORS } from '@angular/forms';
5
- import { MK_I18N, mkUniqueId, mkFirstErrorMessage, MkFieldContext, MkAnchoredPanel, MkLiveAnnouncer, mkValidatorChange, mkHighlight } from '@mk-kit/ui/core';
5
+ import { FORM_FIELD } from '@angular/forms/signals';
6
+ import { MK_I18N, mkUniqueId, mkSignalErrorMessage, mkFirstErrorMessage, MkFieldContext, mkInjectFieldTouched, MkAnchoredPanel, MkLiveAnnouncer, mkValidatorChange, mkHighlight } from '@mk-kit/ui/core';
6
7
  export { mkHighlight } from '@mk-kit/ui/core';
7
8
  import { MkButton } from '@mk-kit/ui/button';
8
9
  import { MkIcon } from '@mk-kit/ui/icon';
@@ -33,6 +34,29 @@ export * from '@mk-kit/ui/checkbox';
33
34
  * is touched or dirty, or its form has been submitted; `errorOn` changes that.
34
35
  * `required` and the disabled styling are derived from the control too.
35
36
  *
37
+ * ## Signal Forms
38
+ *
39
+ * The same applies to a control bound with Signal Forms' `[formField]`
40
+ * directive (`@angular/forms/signals`): the field adopts the projected
41
+ * `FormField` binding and reads `touched()` / `dirty()` / `invalid()` /
42
+ * `errors()` / `required()` / `disabled()` from its `FieldState`. Errors
43
+ * written by the schema (`required(p.email)`, `minLength(p.name, 2)`, …) are
44
+ * rendered through the same `validation` i18n table, a `message` supplied to
45
+ * the schema rule wins over the table, and `errorMessages` / `errorOn` work
46
+ * unchanged. Calling `submit()` marks every field touched, so errors surface
47
+ * on a failed submit exactly as with `formControlName`. When the control is
48
+ * not projected (or lives deeper), point the field at it with `[field]`.
49
+ *
50
+ * ```html
51
+ * <mk-form-field label="Email">
52
+ * <input mkInput type="email" [formField]="f.email" />
53
+ * </mk-form-field>
54
+ *
55
+ * <mk-form-field label="Country" [field]="f.country">
56
+ * <mk-select [formField]="f.country" [options]="countries" />
57
+ * </mk-form-field>
58
+ * ```
59
+ *
36
60
  * ```html
37
61
  * <!-- automatic: message + required marker come from the validators -->
38
62
  * <mk-form-field label="Email">
@@ -103,9 +127,31 @@ class MkFormField {
103
127
  */
104
128
  labelPosition = input('top', /* @ts-ignore */
105
129
  ...(ngDevMode ? [{ debugName: "labelPosition" }] : /* istanbul ignore next */ []));
130
+ /**
131
+ * Signal Forms field to read state from explicitly (`[field]="f.email"`).
132
+ * Not needed when the projected control carries the `[formField]` binding —
133
+ * the wrapper finds that itself; set it when the control is nested deeper
134
+ * or when the wrapper should follow a field it does not contain.
135
+ */
136
+ field = input(null, /* @ts-ignore */
137
+ ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
106
138
  host = inject(ElementRef);
107
139
  /** The projected control's form binding, when it has one. */
108
140
  ngControl = contentChild(NgControl, { ...(ngDevMode ? { debugName: "ngControl" } : /* istanbul ignore next */ {}), descendants: true });
141
+ /** The projected control's Signal Forms `[formField]` binding, when it has one. */
142
+ signalField = contentChild(FORM_FIELD, { ...(ngDevMode ? { debugName: "signalField" } : /* istanbul ignore next */ {}), descendants: true });
143
+ /**
144
+ * The Signal Forms field state driving this wrapper — the explicit `field`
145
+ * input first, else the projected `[formField]` binding. `undefined` for a
146
+ * reactive-forms / template-driven control.
147
+ */
148
+ fieldState = computed(() => {
149
+ const explicit = this.field();
150
+ if (explicit)
151
+ return explicit();
152
+ return this.signalField()?.state();
153
+ }, /* @ts-ignore */
154
+ ...(ngDevMode ? [{ debugName: "fieldState" }] : /* istanbul ignore next */ []));
109
155
  /** A control inside the field has focus. */
110
156
  focused = signal(false, /* @ts-ignore */
111
157
  ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
@@ -115,8 +161,9 @@ class MkFormField {
115
161
  /** The field holds a non-empty value (drives the floating label). */
116
162
  hasValue = computed(() => {
117
163
  this.controlTick();
118
- const control = this.ngControl();
119
- const v = control ? control.value : this.domValue();
164
+ const state = this.fieldState();
165
+ const control = state ? null : this.ngControl();
166
+ const v = state ? state.value() : control ? control.value : this.domValue();
120
167
  if (v == null || v === '')
121
168
  return false;
122
169
  if (Array.isArray(v))
@@ -160,9 +207,13 @@ class MkFormField {
160
207
  const submit = this.parentFormGroup?.ngSubmit ?? this.parentForm?.ngSubmit;
161
208
  const submitSub = submit?.subscribe(bump) ?? null;
162
209
  effect(() => {
163
- const control = this.ngControl()?.control;
210
+ // A `[formField]` binding provides an interop `NgControl` without an
211
+ // `events` stream; its state is read reactively via `fieldState` instead.
212
+ const control = this.signalField()
213
+ ? null
214
+ : this.ngControl()?.control;
164
215
  sub?.unsubscribe();
165
- sub = control ? control.events.subscribe(bump) : null;
216
+ sub = control?.events ? control.events.subscribe(bump) : null;
166
217
  });
167
218
  inject(DestroyRef).onDestroy(() => {
168
219
  sub?.unsubscribe();
@@ -177,8 +228,19 @@ class MkFormField {
177
228
  ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
178
229
  /** The automatic message for the bound control, when one should show. */
179
230
  autoError = computed(() => {
231
+ const state = this.fieldState();
232
+ if (state) {
233
+ if (state.disabled() || state.hidden() || !state.invalid())
234
+ return null;
235
+ const gate = this.errorOn();
236
+ const visible = gate === 'always' ||
237
+ (gate === 'dirty' ? state.dirty() : state.touched() || state.dirty());
238
+ if (!visible)
239
+ return null;
240
+ return mkSignalErrorMessage(state.errors(), this.i18n.validation, this.errorMessages() ?? undefined);
241
+ }
180
242
  this.controlTick();
181
- const control = this.ngControl()?.control;
243
+ const control = this.signalField() ? null : this.ngControl()?.control;
182
244
  if (!control || control.disabled || !control.invalid)
183
245
  return null;
184
246
  const gate = this.errorOn();
@@ -200,6 +262,9 @@ class MkFormField {
200
262
  isRequired = computed(() => {
201
263
  if (this.required())
202
264
  return true;
265
+ const state = this.fieldState();
266
+ if (state)
267
+ return state.required();
203
268
  this.controlTick();
204
269
  return this.ngControl()?.control?.hasValidator(Validators.required) ?? false;
205
270
  }, /* @ts-ignore */
@@ -208,6 +273,9 @@ class MkFormField {
208
273
  isDisabled = computed(() => {
209
274
  if (this.disabled())
210
275
  return true;
276
+ const state = this.fieldState();
277
+ if (state)
278
+ return state.disabled();
211
279
  this.controlTick();
212
280
  return this.ngControl()?.control?.disabled ?? false;
213
281
  }, /* @ts-ignore */
@@ -229,12 +297,12 @@ class MkFormField {
229
297
  }, /* @ts-ignore */
230
298
  ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
231
299
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
232
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkFormField, isStandalone: true, selector: "mk-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null }, errorOn: { classPropertyName: "errorOn", publicName: "errorOn", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focusin": "focused.set(true)", "focusout": "focused.set(false)", "input": "readDomValue($event.target)", "change": "readDomValue($event.target)" }, properties: { "class.mk-form-field--sm": "size() === 'sm'", "class.mk-form-field--md": "size() === 'md'", "class.mk-form-field--lg": "size() === 'lg'", "class.mk-form-field--invalid": "hasError()", "class.mk-form-field--required": "isRequired()", "class.mk-form-field--disabled": "isDisabled()", "class.mk-form-field--float": "labelPosition() === 'float'", "class.mk-form-field--focused": "focused()", "class.mk-form-field--filled": "hasValue()" }, classAttribute: "mk-form-field" }, providers: [
300
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkFormField, isStandalone: true, selector: "mk-form-field", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null }, errorOn: { classPropertyName: "errorOn", publicName: "errorOn", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focusin": "focused.set(true)", "focusout": "focused.set(false)", "input": "readDomValue($event.target)", "change": "readDomValue($event.target)" }, properties: { "class.mk-form-field--sm": "size() === 'sm'", "class.mk-form-field--md": "size() === 'md'", "class.mk-form-field--lg": "size() === 'lg'", "class.mk-form-field--invalid": "hasError()", "class.mk-form-field--required": "isRequired()", "class.mk-form-field--disabled": "isDisabled()", "class.mk-form-field--float": "labelPosition() === 'float'", "class.mk-form-field--focused": "focused()", "class.mk-form-field--filled": "hasValue()" }, classAttribute: "mk-form-field" }, providers: [
233
301
  {
234
302
  provide: MkFieldContext,
235
303
  useExisting: forwardRef(() => MkFormField),
236
304
  },
237
- ], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "@if (label() && labelPosition() !== 'float') {\n <label class=\"mk-form-field__label\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n}\n\n<div class=\"mk-form-field__control\">\n <ng-content />\n @if (label() && labelPosition() === 'float') {\n <label class=\"mk-form-field__label mk-form-field__label--float\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n</div>\n\n@if (hintVisible()) {\n <p class=\"mk-form-field__hint\" [id]=\"hintId\">{{ hint() }}</p>\n}\n\n@if (hasError()) {\n <p class=\"mk-form-field__error\" [id]=\"errorId\" role=\"alert\">{{ errorText() }}</p>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--mk-space-2);font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-form-field__label{display:inline-flex;align-items:center;gap:var(--mk-space-1);font-size:var(--mk-font-size-sm);font-weight:var(--mk-font-weight-medium);line-height:var(--mk-line-height-snug);color:var(--mk-text);width:fit-content}:host(.mk-form-field--lg) .mk-form-field__label{font-size:var(--mk-font-size-md)}:host(.mk-form-field--sm) .mk-form-field__label{font-size:var(--mk-font-size-xs)}.mk-form-field__required{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-bold);line-height:1}.mk-form-field__control{display:block;min-width:0}.mk-form-field__hint,.mk-form-field__error{margin:0;font-size:var(--mk-font-size-xs);line-height:var(--mk-line-height-snug)}.mk-form-field__hint{color:var(--mk-text-muted)}.mk-form-field__error{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--disabled){opacity:.6}:host(.mk-form-field--float) .mk-form-field__control{position:relative}.mk-form-field__label--float{position:absolute;inset-inline-start:var(--mk-space-3);top:50%;translate:0 -50%;max-width:calc(100% - 2 * var(--mk-space-3));overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:var(--mk-font-size-md);font-weight:var(--mk-font-weight-normal);color:var(--mk-text-muted);pointer-events:none;transition:top var(--mk-duration-fast) var(--mk-ease-standard),translate var(--mk-duration-fast) var(--mk-ease-standard),font-size var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-form-field--focused) .mk-form-field__label--float,:host(.mk-form-field--filled) .mk-form-field__label--float{top:.35rem;translate:0 0;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--focused) .mk-form-field__label--float{color:var(--mk-primary)}:host(.mk-form-field--invalid) .mk-form-field__label--float{color:var(--mk-danger-text)}:host(.mk-form-field--float) ::ng-deep .mk-input:not(textarea){height:calc(var(--mk-control-height-md) + .75rem);padding-top:1.05rem}:host(.mk-form-field--float) ::ng-deep textarea.mk-input{padding-top:1.5rem}:host(.mk-form-field--float:not(.mk-form-field--focused):not(.mk-form-field--filled)) ::ng-deep .mk-input::placeholder{color:transparent}@media(prefers-reduced-motion:reduce){.mk-form-field__label--float{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
305
+ ], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }, { propertyName: "signalField", first: true, predicate: FORM_FIELD, descendants: true, isSignal: true }], ngImport: i0, template: "@if (label() && labelPosition() !== 'float') {\n <label class=\"mk-form-field__label\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n}\n\n<div class=\"mk-form-field__control\">\n <ng-content />\n @if (label() && labelPosition() === 'float') {\n <label class=\"mk-form-field__label mk-form-field__label--float\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n</div>\n\n@if (hintVisible()) {\n <p class=\"mk-form-field__hint\" [id]=\"hintId\">{{ hint() }}</p>\n}\n\n@if (hasError()) {\n <p class=\"mk-form-field__error\" [id]=\"errorId\" role=\"alert\">{{ errorText() }}</p>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--mk-space-2);font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-form-field__label{display:inline-flex;align-items:center;gap:var(--mk-space-1);font-size:var(--mk-font-size-sm);font-weight:var(--mk-font-weight-medium);line-height:var(--mk-line-height-snug);color:var(--mk-text);width:fit-content}:host(.mk-form-field--lg) .mk-form-field__label{font-size:var(--mk-font-size-md)}:host(.mk-form-field--sm) .mk-form-field__label{font-size:var(--mk-font-size-xs)}.mk-form-field__required{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-bold);line-height:1}.mk-form-field__control{display:block;min-width:0}.mk-form-field__hint,.mk-form-field__error{margin:0;font-size:var(--mk-font-size-xs);line-height:var(--mk-line-height-snug)}.mk-form-field__hint{color:var(--mk-text-muted)}.mk-form-field__error{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--disabled){opacity:.6}:host(.mk-form-field--float) .mk-form-field__control{position:relative}.mk-form-field__label--float{position:absolute;inset-inline-start:var(--mk-space-3);top:50%;translate:0 -50%;max-width:calc(100% - 2 * var(--mk-space-3));overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:var(--mk-font-size-md);font-weight:var(--mk-font-weight-normal);color:var(--mk-text-muted);pointer-events:none;transition:top var(--mk-duration-fast) var(--mk-ease-standard),translate var(--mk-duration-fast) var(--mk-ease-standard),font-size var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-form-field--focused) .mk-form-field__label--float,:host(.mk-form-field--filled) .mk-form-field__label--float{top:.35rem;translate:0 0;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--focused) .mk-form-field__label--float{color:var(--mk-primary)}:host(.mk-form-field--invalid) .mk-form-field__label--float{color:var(--mk-danger-text)}:host(.mk-form-field--float) ::ng-deep .mk-input:not(textarea){height:calc(var(--mk-control-height-md) + .75rem);padding-top:1.05rem}:host(.mk-form-field--float) ::ng-deep textarea.mk-input{padding-top:1.5rem}:host(.mk-form-field--float:not(.mk-form-field--focused):not(.mk-form-field--filled)) ::ng-deep .mk-input::placeholder{color:transparent}@media(prefers-reduced-motion:reduce){.mk-form-field__label--float{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
238
306
  }
239
307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormField, decorators: [{
240
308
  type: Component,
@@ -259,7 +327,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
259
327
  useExisting: forwardRef(() => MkFormField),
260
328
  },
261
329
  ], template: "@if (label() && labelPosition() !== 'float') {\n <label class=\"mk-form-field__label\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n}\n\n<div class=\"mk-form-field__control\">\n <ng-content />\n @if (label() && labelPosition() === 'float') {\n <label class=\"mk-form-field__label mk-form-field__label--float\" [id]=\"labelId\" [attr.for]=\"controlId\">\n <span class=\"mk-form-field__label-text\">{{ label() }}</span>\n @if (isRequired()) {\n <span class=\"mk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </label>\n }\n</div>\n\n@if (hintVisible()) {\n <p class=\"mk-form-field__hint\" [id]=\"hintId\">{{ hint() }}</p>\n}\n\n@if (hasError()) {\n <p class=\"mk-form-field__error\" [id]=\"errorId\" role=\"alert\">{{ errorText() }}</p>\n}\n", styles: [":host{display:flex;flex-direction:column;gap:var(--mk-space-2);font-family:var(--mk-font-sans);color:var(--mk-text)}.mk-form-field__label{display:inline-flex;align-items:center;gap:var(--mk-space-1);font-size:var(--mk-font-size-sm);font-weight:var(--mk-font-weight-medium);line-height:var(--mk-line-height-snug);color:var(--mk-text);width:fit-content}:host(.mk-form-field--lg) .mk-form-field__label{font-size:var(--mk-font-size-md)}:host(.mk-form-field--sm) .mk-form-field__label{font-size:var(--mk-font-size-xs)}.mk-form-field__required{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-bold);line-height:1}.mk-form-field__control{display:block;min-width:0}.mk-form-field__hint,.mk-form-field__error{margin:0;font-size:var(--mk-font-size-xs);line-height:var(--mk-line-height-snug)}.mk-form-field__hint{color:var(--mk-text-muted)}.mk-form-field__error{color:var(--mk-danger-text);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--disabled){opacity:.6}:host(.mk-form-field--float) .mk-form-field__control{position:relative}.mk-form-field__label--float{position:absolute;inset-inline-start:var(--mk-space-3);top:50%;translate:0 -50%;max-width:calc(100% - 2 * var(--mk-space-3));overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:var(--mk-font-size-md);font-weight:var(--mk-font-weight-normal);color:var(--mk-text-muted);pointer-events:none;transition:top var(--mk-duration-fast) var(--mk-ease-standard),translate var(--mk-duration-fast) var(--mk-ease-standard),font-size var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-form-field--focused) .mk-form-field__label--float,:host(.mk-form-field--filled) .mk-form-field__label--float{top:.35rem;translate:0 0;font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-medium)}:host(.mk-form-field--focused) .mk-form-field__label--float{color:var(--mk-primary)}:host(.mk-form-field--invalid) .mk-form-field__label--float{color:var(--mk-danger-text)}:host(.mk-form-field--float) ::ng-deep .mk-input:not(textarea){height:calc(var(--mk-control-height-md) + .75rem);padding-top:1.05rem}:host(.mk-form-field--float) ::ng-deep textarea.mk-input{padding-top:1.5rem}:host(.mk-form-field--float:not(.mk-form-field--focused):not(.mk-form-field--filled)) ::ng-deep .mk-input::placeholder{color:transparent}@media(prefers-reduced-motion:reduce){.mk-form-field__label--float{transition:none}}\n"] }]
262
- }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], errorOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorOn", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { ...{ descendants: true }, isSignal: true }] }] } });
330
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], errorOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorOn", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { ...{ descendants: true }, isSignal: true }] }], signalField: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FORM_FIELD), { ...{ descendants: true }, isSignal: true }] }] } });
263
331
 
264
332
  /**
265
333
  * FormErrorSummary — an accessible summary of a form's validation errors,
@@ -282,6 +350,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
282
350
  * `formControlName` must be a static attribute (`formControlName="email"`,
283
351
  * the usual form) rather than a binding.
284
352
  *
353
+ * ## Signal Forms
354
+ *
355
+ * Point `field` at a Signal Forms root (`[field]="f"`) instead of `form` and
356
+ * the summary reads `errorSummary()` from the `FieldTree`: one entry per
357
+ * invalid field, keyed by dotted path in `labels` / `errorMessages` like the
358
+ * reactive path. With the default `showOn="submit"` an entry appears once its
359
+ * field is touched — `submit()` marks every field touched, so a failed submit
360
+ * lists them all. Clicking an entry focuses the control bound with
361
+ * `[formField]`. When the summary sits inside the `<form>`, focus also moves
362
+ * to it after the form's `submit` event, as with `ngSubmit`.
363
+ *
364
+ * ```html
365
+ * <form (submit)="save($event)">
366
+ * <mk-form-error-summary [field]="f" [labels]="{ email: 'Email address' }" />
367
+ * …
368
+ * </form>
369
+ * ```
370
+ *
285
371
  * ```html
286
372
  * <!-- automatic -->
287
373
  * <form [formGroup]="form" (ngSubmit)="submit()">
@@ -313,6 +399,12 @@ class MkFormErrorSummary {
313
399
  /** Collect errors from this control tree instead of passing them in. */
314
400
  form = input(null, /* @ts-ignore */
315
401
  ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
402
+ /**
403
+ * Collect errors from this Signal Forms field (usually the root returned by
404
+ * `form()`) instead of `form`. Entries are keyed by dotted path for `labels`.
405
+ */
406
+ field = input(null, /* @ts-ignore */
407
+ ...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
316
408
  /** Field names for automatic entries, keyed by control path. */
317
409
  labels = input({}, /* @ts-ignore */
318
410
  ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
@@ -339,15 +431,26 @@ class MkFormErrorSummary {
339
431
  const bump = () => this.formTick.update((n) => n + 1);
340
432
  let sub = null;
341
433
  const injector = inject(Injector);
342
- const submit = this.parentFormGroup?.ngSubmit ?? this.parentForm?.ngSubmit;
343
- const submitSub = submit?.subscribe(() => {
434
+ const onSubmit = () => {
344
435
  bump();
345
436
  // After the entries have rendered (and [hidden] cleared), take the
346
437
  // user straight to the problems. `focus()` no-ops without errors.
347
438
  if (this.autoFocus()) {
348
439
  afterNextRender(() => this.focus(), { injector });
349
440
  }
350
- }) ?? null;
441
+ };
442
+ const submit = this.parentFormGroup?.ngSubmit ?? this.parentForm?.ngSubmit;
443
+ const submitSub = submit?.subscribe(onSubmit) ?? null;
444
+ // Signal Forms has no `ngSubmit`; a summary placed inside the `<form>`
445
+ // follows the native submit event instead (`submit()` marks every field
446
+ // touched synchronously, so the entries exist by the next render).
447
+ let nativeForm = null;
448
+ afterNextRender(() => {
449
+ if (!this.field())
450
+ return;
451
+ nativeForm = this.host.nativeElement.closest('form');
452
+ nativeForm?.addEventListener('submit', onSubmit);
453
+ });
351
454
  effect(() => {
352
455
  const form = this.form();
353
456
  sub?.unsubscribe();
@@ -356,6 +459,7 @@ class MkFormErrorSummary {
356
459
  inject(DestroyRef).onDestroy(() => {
357
460
  sub?.unsubscribe();
358
461
  submitSub?.unsubscribe();
462
+ nativeForm?.removeEventListener('submit', onSubmit);
359
463
  });
360
464
  }
361
465
  /** Whether the surrounding form (if any) has been submitted. */
@@ -364,8 +468,48 @@ class MkFormErrorSummary {
364
468
  return (this.parentFormGroup?.submitted || this.parentForm?.submitted || false);
365
469
  }, /* @ts-ignore */
366
470
  ...(ngDevMode ? [{ debugName: "submitted" }] : /* istanbul ignore next */ []));
471
+ /**
472
+ * Entries collected from `field` (a Signal Forms root), each with the element
473
+ * carrying the field's `[formField]` binding so a click can focus it the way
474
+ * `focusBoundControl()` would.
475
+ */
476
+ signalEntries = computed(() => {
477
+ const field = this.field();
478
+ if (!field)
479
+ return [];
480
+ const root = field();
481
+ const gateOnTouch = this.showOn() === 'submit';
482
+ const labels = this.labels();
483
+ const overrides = this.errorMessages() ?? undefined;
484
+ const seen = new Set();
485
+ const out = [];
486
+ for (const err of root.errorSummary()) {
487
+ const state = err.fieldTree();
488
+ const name = state.name();
489
+ if (seen.has(name) || state.disabled() || state.hidden())
490
+ continue;
491
+ if (gateOnTouch && !state.touched())
492
+ continue;
493
+ seen.add(name);
494
+ const message = mkSignalErrorMessage([err], this.i18n.validation, overrides);
495
+ if (!message)
496
+ continue;
497
+ const path = signalFieldPath(root, state) ?? '';
498
+ const label = labels[path] ?? path;
499
+ const element = state.formFieldBindings()[0]?.element ?? null;
500
+ const fieldId = element?.id || name;
501
+ out.push({
502
+ error: { fieldId, message: label ? `${label}: ${message}` : message },
503
+ element,
504
+ });
505
+ }
506
+ return out;
507
+ }, /* @ts-ignore */
508
+ ...(ngDevMode ? [{ debugName: "signalEntries" }] : /* istanbul ignore next */ []));
367
509
  /** Entries collected from `form`, when one is set and they should show. */
368
510
  autoErrors = computed(() => {
511
+ if (this.field())
512
+ return this.signalEntries().map((e) => e.error);
369
513
  this.formTick();
370
514
  const form = this.form();
371
515
  if (!form)
@@ -403,7 +547,8 @@ class MkFormErrorSummary {
403
547
  }
404
548
  /** Focus the field an entry points at (native anchor jump is unreliable). */
405
549
  focusField(event, fieldId) {
406
- const target = this.document.getElementById(fieldId) ??
550
+ const target = this.signalEntries().find((e) => e.error.fieldId === fieldId)?.element ??
551
+ this.document.getElementById(fieldId) ??
407
552
  this.document.querySelector(`[formcontrolname="${cssEscape(fieldId)}"]`);
408
553
  if (!target)
409
554
  return;
@@ -422,7 +567,7 @@ class MkFormErrorSummary {
422
567
  return el?.id || path;
423
568
  }
424
569
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormErrorSummary, deps: [], target: i0.ɵɵFactoryTarget.Component });
425
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkFormErrorSummary, isStandalone: true, selector: "mk-form-error-summary", inputs: { errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null }, showOn: { classPropertyName: "showOn", publicName: "showOn", isSignal: true, isRequired: false, transformFunction: null }, summaryTitle: { classPropertyName: "summaryTitle", publicName: "summaryTitle", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "alert", "tabindex": "-1" }, properties: { "attr.hidden": "hasErrors() ? null : \"\"", "attr.aria-labelledby": "hasErrors() ? titleId : null" }, classAttribute: "mk-form-error-summary" }, exportAs: ["mkFormErrorSummary"], ngImport: i0, template: "@if (hasErrors()) {\n <p [id]=\"titleId\" class=\"mk-form-error-summary__title\">{{ summaryTitle() }}</p>\n <ul class=\"mk-form-error-summary__list\">\n @for (error of shownErrors(); track error.fieldId) {\n <li class=\"mk-form-error-summary__item\">\n <a\n class=\"mk-form-error-summary__link\"\n [href]=\"'#' + error.fieldId\"\n (click)=\"focusField($event, error.fieldId)\"\n >{{ error.message }}</a\n >\n </li>\n }\n </ul>\n}\n", styles: [":host{display:block;padding:var(--mk-space-4);color:var(--mk-text);background-color:var(--mk-danger-subtle, var(--mk-surface-2));border:var(--mk-border-width) solid var(--mk-danger);border-radius:var(--mk-radius-md);border-inline-start-width:4px}:host(:focus-visible){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-form-error-summary__title{margin:0 0 var(--mk-space-2);font-weight:var(--mk-font-weight-semibold);color:var(--mk-danger-text)}.mk-form-error-summary__list{margin:0;padding-inline-start:var(--mk-space-4);display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-form-error-summary__link{color:var(--mk-danger-text);text-decoration:underline;cursor:pointer}.mk-form-error-summary__link:hover{text-decoration:none}.mk-form-error-summary__link:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-radius:var(--mk-radius-sm)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
570
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkFormErrorSummary, isStandalone: true, selector: "mk-form-error-summary", inputs: { errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null }, showOn: { classPropertyName: "showOn", publicName: "showOn", isSignal: true, isRequired: false, transformFunction: null }, summaryTitle: { classPropertyName: "summaryTitle", publicName: "summaryTitle", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "alert", "tabindex": "-1" }, properties: { "attr.hidden": "hasErrors() ? null : \"\"", "attr.aria-labelledby": "hasErrors() ? titleId : null" }, classAttribute: "mk-form-error-summary" }, exportAs: ["mkFormErrorSummary"], ngImport: i0, template: "@if (hasErrors()) {\n <p [id]=\"titleId\" class=\"mk-form-error-summary__title\">{{ summaryTitle() }}</p>\n <ul class=\"mk-form-error-summary__list\">\n @for (error of shownErrors(); track error.fieldId) {\n <li class=\"mk-form-error-summary__item\">\n <a\n class=\"mk-form-error-summary__link\"\n [href]=\"'#' + error.fieldId\"\n (click)=\"focusField($event, error.fieldId)\"\n >{{ error.message }}</a\n >\n </li>\n }\n </ul>\n}\n", styles: [":host{display:block;padding:var(--mk-space-4);color:var(--mk-text);background-color:var(--mk-danger-subtle, var(--mk-surface-2));border:var(--mk-border-width) solid var(--mk-danger);border-radius:var(--mk-radius-md);border-inline-start-width:4px}:host(:focus-visible){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-form-error-summary__title{margin:0 0 var(--mk-space-2);font-weight:var(--mk-font-weight-semibold);color:var(--mk-danger-text)}.mk-form-error-summary__list{margin:0;padding-inline-start:var(--mk-space-4);display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-form-error-summary__link{color:var(--mk-danger-text);text-decoration:underline;cursor:pointer}.mk-form-error-summary__link:hover{text-decoration:none}.mk-form-error-summary__link:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-radius:var(--mk-radius-sm)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
426
571
  }
427
572
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormErrorSummary, decorators: [{
428
573
  type: Component,
@@ -433,7 +578,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
433
578
  '[attr.hidden]': 'hasErrors() ? null : ""',
434
579
  '[attr.aria-labelledby]': 'hasErrors() ? titleId : null',
435
580
  }, template: "@if (hasErrors()) {\n <p [id]=\"titleId\" class=\"mk-form-error-summary__title\">{{ summaryTitle() }}</p>\n <ul class=\"mk-form-error-summary__list\">\n @for (error of shownErrors(); track error.fieldId) {\n <li class=\"mk-form-error-summary__item\">\n <a\n class=\"mk-form-error-summary__link\"\n [href]=\"'#' + error.fieldId\"\n (click)=\"focusField($event, error.fieldId)\"\n >{{ error.message }}</a\n >\n </li>\n }\n </ul>\n}\n", styles: [":host{display:block;padding:var(--mk-space-4);color:var(--mk-text);background-color:var(--mk-danger-subtle, var(--mk-surface-2));border:var(--mk-border-width) solid var(--mk-danger);border-radius:var(--mk-radius-md);border-inline-start-width:4px}:host(:focus-visible){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-form-error-summary__title{margin:0 0 var(--mk-space-2);font-weight:var(--mk-font-weight-semibold);color:var(--mk-danger-text)}.mk-form-error-summary__list{margin:0;padding-inline-start:var(--mk-space-4);display:flex;flex-direction:column;gap:var(--mk-space-1)}.mk-form-error-summary__link{color:var(--mk-danger-text);text-decoration:underline;cursor:pointer}.mk-form-error-summary__link:hover{text-decoration:none}.mk-form-error-summary__link:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-radius:var(--mk-radius-sm)}\n"] }]
436
- }], ctorParameters: () => [], propDecorators: { errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], showOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOn", required: false }] }], summaryTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryTitle", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }] } });
581
+ }], ctorParameters: () => [], propDecorators: { errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], showOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOn", required: false }] }], summaryTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryTitle", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }] } });
437
582
  /** Depth-first walk yielding `[dotted path, control]` for every leaf control. */
438
583
  function* eachControl(control, path = '') {
439
584
  if (control instanceof FormGroup) {
@@ -450,6 +595,33 @@ function* eachControl(control, path = '') {
450
595
  yield [path, control];
451
596
  }
452
597
  }
598
+ /**
599
+ * Dotted path of a Signal Forms field under `root` (`'address.city'`,
600
+ * `'contacts.0.name'`), found by walking the field tree along the model's
601
+ * keys; `''` for the root itself, `null` when the field is not under `root`.
602
+ */
603
+ function signalFieldPath(root, target) {
604
+ const wanted = target.name();
605
+ const walk = (tree, path) => {
606
+ const state = tree();
607
+ if (state.name() === wanted)
608
+ return path;
609
+ const value = state.value();
610
+ if (!value || typeof value !== 'object')
611
+ return null;
612
+ const children = tree;
613
+ for (const key of Object.keys(value)) {
614
+ const child = children[key];
615
+ if (typeof child !== 'function')
616
+ continue;
617
+ const found = walk(child, path ? `${path}.${key}` : key);
618
+ if (found !== null)
619
+ return found;
620
+ }
621
+ return null;
622
+ };
623
+ return walk(root.fieldTree, '');
624
+ }
453
625
  /** Minimal attribute-value escaping for the selectors built above. */
454
626
  function cssEscape(value) {
455
627
  return value.replace(/["\\]/g, '\\$&');
@@ -545,6 +717,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
545
717
  */
546
718
  class MkInput {
547
719
  field = inject(MkFormField, { optional: true });
720
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
721
+ fieldTouched = mkInjectFieldTouched();
548
722
  group = inject(MkInputGroup, { optional: true });
549
723
  host = inject(ElementRef);
550
724
  initialId = this.host.nativeElement.getAttribute('id');
@@ -559,7 +733,7 @@ class MkInput {
559
733
  ? this.field.size()
560
734
  : (this.group?.effectiveSize() ?? this.size()), /* @ts-ignore */
561
735
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
562
- isInvalid = computed(() => this.invalid() ||
736
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
563
737
  (this.field?.hasError() ?? false) ||
564
738
  (this.group?.isInvalid() ?? false), /* @ts-ignore */
565
739
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -679,6 +853,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
679
853
  */
680
854
  class MkSubmitInput {
681
855
  field = inject(MkFormField, { optional: true });
856
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
857
+ fieldTouched = mkInjectFieldTouched();
682
858
  /** Localised strings (override globally via `provideMkI18n`). */
683
859
  i18n = inject(MK_I18N);
684
860
  inputRef = viewChild('input', /* @ts-ignore */
@@ -742,7 +918,7 @@ class MkSubmitInput {
742
918
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
743
919
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
744
920
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
745
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
921
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
746
922
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
747
923
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
748
924
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -912,6 +1088,8 @@ class MkButtonToggleGroup {
912
1088
  host = inject(ElementRef);
913
1089
  /** Optional surrounding form field — supplies label/hint/error wiring. */
914
1090
  field = inject(MkFormField, { optional: true });
1091
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
1092
+ fieldTouched = mkInjectFieldTouched();
915
1093
  /** Live list of projected toggle items, in DOM order. */
916
1094
  toggles = contentChildren(MkButtonToggle, /* @ts-ignore */
917
1095
  ...(ngDevMode ? [{ debugName: "toggles" }] : /* istanbul ignore next */ []));
@@ -951,7 +1129,7 @@ class MkButtonToggleGroup {
951
1129
  onTouched = () => { };
952
1130
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
953
1131
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
954
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
1132
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
955
1133
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
956
1134
  /** The single item that currently owns the tab stop (roving tabindex). */
957
1135
  rovingTarget = computed(() => {
@@ -1259,6 +1437,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1259
1437
  */
1260
1438
  class MkSelect {
1261
1439
  field = inject(MkFormField, { optional: true });
1440
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
1441
+ fieldTouched = mkInjectFieldTouched();
1262
1442
  /** Localised strings (override globally via `provideMkI18n`). */
1263
1443
  i18n = inject(MK_I18N);
1264
1444
  triggerRef = viewChild('trigger', /* @ts-ignore */
@@ -1276,6 +1456,8 @@ class MkSelect {
1276
1456
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1277
1457
  /** Force invalid styling + `aria-invalid` when used standalone. */
1278
1458
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1459
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
1460
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1279
1461
  /** Disable the control. */
1280
1462
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1281
1463
  /** Two-way selected value. */
@@ -1297,9 +1479,9 @@ class MkSelect {
1297
1479
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
1298
1480
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
1299
1481
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1300
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
1482
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
1301
1483
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1302
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
1484
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
1303
1485
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
1304
1486
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
1305
1487
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -1457,7 +1639,7 @@ class MkSelect {
1457
1639
  this.cvaDisabled.set(isDisabled);
1458
1640
  }
1459
1641
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
1460
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkSelect, isStandalone: true, selector: "mk-select", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-select--sm": "effectiveSize() === 'sm'", "class.mk-select--md": "effectiveSize() === 'md'", "class.mk-select--lg": "effectiveSize() === 'lg'", "class.mk-select--open": "open()", "class.mk-select--invalid": "isInvalid()", "class.mk-select--disabled": "isDisabled()" }, classAttribute: "mk-select" }, providers: [
1642
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkSelect, isStandalone: true, selector: "mk-select", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-select--sm": "effectiveSize() === 'sm'", "class.mk-select--md": "effectiveSize() === 'md'", "class.mk-select--lg": "effectiveSize() === 'lg'", "class.mk-select--open": "open()", "class.mk-select--invalid": "isInvalid()", "class.mk-select--disabled": "isDisabled()" }, classAttribute: "mk-select" }, providers: [
1461
1643
  {
1462
1644
  provide: NG_VALUE_ACCESSOR,
1463
1645
  useExisting: forwardRef(() => MkSelect),
@@ -1482,7 +1664,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1482
1664
  multi: true,
1483
1665
  },
1484
1666
  ], template: "<button\n #trigger\n type=\"button\"\n class=\"mk-select__trigger\"\n [id]=\"triggerId\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-activedescendant]=\"\n open() && activeIndex() >= 0 ? optionId(activeIndex()) : null\n \"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : (ariaLabel() || null)\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n>\n @if (displayLabel()) {\n <span class=\"mk-select__value\">{{ displayLabel() }}</span>\n } @else {\n <span class=\"mk-select__value mk-select__value--placeholder\">{{\n placeholder()\n }}</span>\n }\n <span class=\"mk-select__arrow\" aria-hidden=\"true\"></span>\n</button>\n\n@if (open()) {\n <ul\n class=\"mk-select__list\"\n mkAnchoredPanel\n [mkAnchoredPanelFor]=\"trigger\"\n [matchWidth]=\"true\"\n (dismiss)=\"close()\"\n [id]=\"listId\"\n role=\"listbox\"\n [attr.aria-labelledby]=\"labelledBy()\"\n >\n @for (opt of options(); track $index; let i = $index) {\n <li\n class=\"mk-select__option\"\n [id]=\"optionId(i)\"\n role=\"option\"\n [attr.aria-selected]=\"i === selectedIndex()\"\n [attr.aria-disabled]=\"opt.disabled || null\"\n [class.mk-select__option--active]=\"i === activeIndex()\"\n [class.mk-select__option--selected]=\"i === selectedIndex()\"\n [class.mk-select__option--disabled]=\"opt.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (mousemove)=\"!opt.disabled && activeIndex.set(i)\"\n (click)=\"selectOption(i)\"\n >\n <span class=\"mk-select__option-label\">{{ opt.label }}</span>\n @if (i === selectedIndex()) {\n <span class=\"mk-select__check\" aria-hidden=\"true\"></span>\n }\n </li>\n } @empty {\n <li class=\"mk-select__empty\" role=\"presentation\">{{ i18n.noOptions }}</li>\n }\n </ul>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative;width:100%;font-family:var(--mk-font-sans)}.mk-select__trigger{display:flex;align-items:center;gap:var(--mk-space-2);width:100%;height:var(--mk-control-height-md);padding:0 var(--mk-space-3);font-family:inherit;font-size:var(--mk-font-size-md);color:var(--mk-text);text-align:start;background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);cursor:pointer;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-select--sm) .mk-select__trigger{height:var(--mk-control-height-sm);padding:0 var(--mk-space-2);font-size:var(--mk-font-size-sm);border-radius:var(--mk-radius-sm)}:host(.mk-select--lg) .mk-select__trigger{height:var(--mk-control-height-lg);padding:0 var(--mk-space-4);font-size:var(--mk-font-size-lg)}.mk-select__trigger:hover:not(:disabled){border-color:var(--mk-border-strong)}.mk-select__trigger:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-select--invalid) .mk-select__trigger{border-color:var(--mk-danger)}:host(.mk-select--invalid) .mk-select__trigger:focus-visible{outline-color:var(--mk-danger)}.mk-select__trigger:disabled{background-color:var(--mk-surface-2);color:var(--mk-text-disabled);border-color:var(--mk-border-subtle);cursor:not-allowed;opacity:.7}.mk-select__value{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-select__value--placeholder{color:var(--mk-text-subtle)}.mk-select__arrow{flex:none;width:.5rem;height:.5rem;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(45deg) translateY(-2px);transition:transform var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-select--open) .mk-select__arrow{transform:rotate(-135deg) translateY(-2px)}.mk-select__list{padding:var(--mk-space-1);list-style:none;max-height:16rem;overflow-y:auto;background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg)}.mk-select__option{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--mk-space-2) var(--mk-space-3);font-size:var(--mk-font-size-md);color:var(--mk-text);border-radius:var(--mk-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}.mk-select__option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-select__option--active{background-color:var(--mk-selected-bg)}.mk-select__option--selected{color:var(--mk-selected-text);font-weight:var(--mk-font-weight-medium)}.mk-select__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-select__check{flex:none;width:.4rem;height:.7rem;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg)}.mk-select__empty{padding:var(--mk-space-2) var(--mk-space-3);color:var(--mk-text-subtle);font-size:var(--mk-font-size-sm)}@media(prefers-reduced-motion:reduce){.mk-select__arrow,.mk-select__trigger{transition:none}}@media(pointer:coarse){.mk-select__trigger{font-size:max(var(--mk-font-size-md),16px)}:host(.mk-select--sm) .mk-select__trigger{font-size:max(var(--mk-font-size-sm),16px)}}\n"] }]
1485
- }], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1667
+ }], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
1486
1668
 
1487
1669
  /**
1488
1670
  * Listbox — a standalone, always-visible selection list (the ARIA `listbox`
@@ -1506,6 +1688,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1506
1688
  */
1507
1689
  class MkListbox {
1508
1690
  field = inject(MkFormField, { optional: true });
1691
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
1692
+ fieldTouched = mkInjectFieldTouched();
1509
1693
  i18n = inject(MK_I18N);
1510
1694
  listRef = viewChild.required('list', /* @ts-ignore */
1511
1695
  ...(ngDevMode ? [{ debugName: "listRef" }] : /* istanbul ignore next */ []));
@@ -1559,7 +1743,7 @@ class MkListbox {
1559
1743
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
1560
1744
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
1561
1745
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1562
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
1746
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
1563
1747
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1564
1748
  labelledBy = computed(() => this.ariaLabelledby() ?? this.field?.labelId ?? null, /* @ts-ignore */
1565
1749
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -1862,6 +2046,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1862
2046
  */
1863
2047
  class MkCascader {
1864
2048
  field = inject(MkFormField, { optional: true });
2049
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
2050
+ fieldTouched = mkInjectFieldTouched();
1865
2051
  i18n = inject(MK_I18N);
1866
2052
  host = inject(ElementRef);
1867
2053
  injector = inject(Injector);
@@ -1914,7 +2100,7 @@ class MkCascader {
1914
2100
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
1915
2101
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
1916
2102
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1917
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
2103
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
1918
2104
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1919
2105
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
1920
2106
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -2272,6 +2458,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2272
2458
  */
2273
2459
  class MkAutocomplete {
2274
2460
  field = inject(MkFormField, { optional: true });
2461
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
2462
+ fieldTouched = mkInjectFieldTouched();
2275
2463
  /** Localised strings (override globally via `provideMkI18n`). */
2276
2464
  i18n = inject(MK_I18N);
2277
2465
  announcer = inject(MkLiveAnnouncer);
@@ -2311,6 +2499,8 @@ class MkAutocomplete {
2311
2499
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2312
2500
  /** Force invalid styling + `aria-invalid` when used standalone. */
2313
2501
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2502
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
2503
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2314
2504
  /** Disable the control. */
2315
2505
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2316
2506
  /** How the option list is filtered against the typed text. */
@@ -2348,9 +2538,9 @@ class MkAutocomplete {
2348
2538
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
2349
2539
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
2350
2540
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
2351
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
2541
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
2352
2542
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
2353
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
2543
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
2354
2544
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
2355
2545
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
2356
2546
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -2545,7 +2735,7 @@ class MkAutocomplete {
2545
2735
  this.cvaDisabled.set(isDisabled);
2546
2736
  }
2547
2737
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
2548
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkAutocomplete, isStandalone: true, selector: "mk-autocomplete", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterMode: { classPropertyName: "filterMode", publicName: "filterMode", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, requireSelection: { classPropertyName: "requireSelection", publicName: "requireSelection", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", optionSelected: "optionSelected" }, host: { properties: { "class.mk-autocomplete--sm": "effectiveSize() === 'sm'", "class.mk-autocomplete--md": "effectiveSize() === 'md'", "class.mk-autocomplete--lg": "effectiveSize() === 'lg'", "class.mk-autocomplete--open": "open()", "class.mk-autocomplete--invalid": "isInvalid()", "class.mk-autocomplete--disabled": "isDisabled()" }, classAttribute: "mk-autocomplete" }, providers: [
2738
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkAutocomplete, isStandalone: true, selector: "mk-autocomplete", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterMode: { classPropertyName: "filterMode", publicName: "filterMode", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, requireSelection: { classPropertyName: "requireSelection", publicName: "requireSelection", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", optionSelected: "optionSelected" }, host: { properties: { "class.mk-autocomplete--sm": "effectiveSize() === 'sm'", "class.mk-autocomplete--md": "effectiveSize() === 'md'", "class.mk-autocomplete--lg": "effectiveSize() === 'lg'", "class.mk-autocomplete--open": "open()", "class.mk-autocomplete--invalid": "isInvalid()", "class.mk-autocomplete--disabled": "isDisabled()" }, classAttribute: "mk-autocomplete" }, providers: [
2549
2739
  {
2550
2740
  provide: NG_VALUE_ACCESSOR,
2551
2741
  useExisting: forwardRef(() => MkAutocomplete),
@@ -2570,7 +2760,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2570
2760
  multi: true,
2571
2761
  },
2572
2762
  ], template: "<div #field class=\"mk-autocomplete__field\">\n <input\n #input\n type=\"text\"\n class=\"mk-autocomplete__input\"\n [id]=\"inputId\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n aria-autocomplete=\"list\"\n [attr.aria-activedescendant]=\"\n open() && activeIndex() >= 0 ? optionId(activeIndex()) : null\n \"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : (ariaLabel() || null)\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n [placeholder]=\"placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n />\n\n @if (query() && !isDisabled()) {\n <button\n type=\"button\"\n class=\"mk-autocomplete__clear\"\n [attr.aria-label]=\"i18n.clear\"\n tabindex=\"-1\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"clear()\"\n ></button>\n }\n\n <span class=\"mk-autocomplete__arrow\" aria-hidden=\"true\"></span>\n</div>\n\n@if (open()) {\n <ul\n class=\"mk-autocomplete__list\"\n mkAnchoredPanel\n [mkAnchoredPanelFor]=\"field\"\n [matchWidth]=\"true\"\n (dismiss)=\"onOutsideDismiss()\"\n [id]=\"listId\"\n role=\"listbox\"\n [attr.aria-labelledby]=\"labelledBy()\"\n >\n @if (loading()) {\n <li class=\"mk-autocomplete__status\" role=\"presentation\">\n <span class=\"mk-autocomplete__spinner\" aria-hidden=\"true\"></span>\n {{ i18n.loading }}\n </li>\n } @else {\n @for (opt of filtered(); track $index; let i = $index) {\n <li\n class=\"mk-autocomplete__option\"\n [id]=\"optionId(i)\"\n role=\"option\"\n [attr.aria-selected]=\"i === activeIndex()\"\n [attr.aria-disabled]=\"opt.disabled || null\"\n [class.mk-autocomplete__option--active]=\"i === activeIndex()\"\n [class.mk-autocomplete__option--disabled]=\"opt.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (mousemove)=\"!opt.disabled && activeIndex.set(i)\"\n (click)=\"selectOption(i)\"\n >\n <span class=\"mk-autocomplete__option-label\">{{ opt.label }}</span>\n @if (opt.description) {\n <span class=\"mk-autocomplete__option-desc\">{{\n opt.description\n }}</span>\n }\n </li>\n } @empty {\n <li class=\"mk-autocomplete__status\" role=\"presentation\">\n {{ emptyMessage() }}\n </li>\n }\n }\n </ul>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative;width:100%;font-family:var(--mk-font-sans)}.mk-autocomplete__field{display:flex;align-items:center;position:relative}.mk-autocomplete__input{flex:1 1 auto;min-width:0;width:100%;height:var(--mk-control-height-md);padding-block:0;padding-inline:var(--mk-space-3) var(--mk-space-8);font-family:inherit;font-size:var(--mk-font-size-md);color:var(--mk-text);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}.mk-autocomplete__input::placeholder{color:var(--mk-text-subtle)}:host(.mk-autocomplete--sm) .mk-autocomplete__input{height:var(--mk-control-height-sm);padding:0;font-size:var(--mk-font-size-sm);padding-inline:var(--mk-space-2) var(--mk-space-7);border-radius:var(--mk-radius-sm)}:host(.mk-autocomplete--lg) .mk-autocomplete__input{height:var(--mk-control-height-lg);padding:0;font-size:var(--mk-font-size-lg);padding-inline:var(--mk-space-4) var(--mk-space-9)}.mk-autocomplete__input:hover:not(:disabled){border-color:var(--mk-border-strong)}.mk-autocomplete__input:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-autocomplete--invalid) .mk-autocomplete__input{border-color:var(--mk-danger)}:host(.mk-autocomplete--invalid) .mk-autocomplete__input:focus-visible{outline-color:var(--mk-danger)}.mk-autocomplete__input:disabled{background-color:var(--mk-surface-2);color:var(--mk-text-disabled);border-color:var(--mk-border-subtle);cursor:not-allowed;opacity:.7}.mk-autocomplete__clear{position:absolute;inset-inline-end:var(--mk-space-6);display:grid;place-items:center;width:1.25rem;height:1.25rem;padding:0;color:var(--mk-text-muted);background:transparent;border:none;border-radius:var(--mk-radius-circle);cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-autocomplete__clear:hover{background-color:var(--mk-hover-overlay);color:var(--mk-text)}.mk-autocomplete__clear:before,.mk-autocomplete__clear:after{content:\"\";position:absolute;width:.7rem;height:2px;background-color:currentColor;border-radius:var(--mk-radius-pill)}.mk-autocomplete__clear:before{transform:rotate(45deg)}.mk-autocomplete__clear:after{transform:rotate(-45deg)}.mk-autocomplete__arrow{position:absolute;inset-inline-end:var(--mk-space-3);flex:none;width:.5rem;height:.5rem;pointer-events:none;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(45deg) translateY(-2px);transition:transform var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-autocomplete--open) .mk-autocomplete__arrow{transform:rotate(-135deg) translateY(-2px)}.mk-autocomplete__list{padding:var(--mk-space-1);list-style:none;max-height:16rem;overflow-y:auto;background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg)}.mk-autocomplete__option{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--mk-space-2) var(--mk-space-3);font-size:var(--mk-font-size-md);color:var(--mk-text);border-radius:var(--mk-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}.mk-autocomplete__option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-autocomplete__option--active{background-color:var(--mk-selected-bg)}.mk-autocomplete__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-autocomplete__status{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--mk-space-2) var(--mk-space-3);color:var(--mk-text-subtle);font-size:var(--mk-font-size-sm)}.mk-autocomplete__spinner{flex:none;width:.9rem;height:.9rem;border:2px solid var(--mk-border-strong);border-top-color:var(--mk-primary);border-radius:var(--mk-radius-circle);animation:mk-autocomplete-spin var(--mk-duration-slow) linear infinite}@keyframes mk-autocomplete-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.mk-autocomplete__arrow,.mk-autocomplete__input,.mk-autocomplete__clear{transition:none}.mk-autocomplete__spinner{animation:none}}.mk-autocomplete__option-desc{display:block;font-size:var(--mk-font-size-xs);color:var(--mk-text-muted);line-height:var(--mk-line-height-snug)}@media(pointer:coarse){.mk-autocomplete__input{font-size:max(var(--mk-font-size-md),16px)}:host(.mk-autocomplete--sm) .mk-autocomplete__input{font-size:max(var(--mk-font-size-sm),16px)}}\n"] }]
2573
- }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMode", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], requireSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireSelection", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }] } });
2763
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMode", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], requireSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireSelection", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }] } });
2574
2764
 
2575
2765
  /**
2576
2766
  * Suggestion listbox for {@link MkMention}. Created dynamically by the
@@ -3072,6 +3262,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3072
3262
  */
3073
3263
  class MkMultiSelect {
3074
3264
  field = inject(MkFormField, { optional: true });
3265
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
3266
+ fieldTouched = mkInjectFieldTouched();
3075
3267
  host = inject(ElementRef);
3076
3268
  /** Localised strings (override globally via `provideMkI18n`). */
3077
3269
  i18n = inject(MK_I18N);
@@ -3091,6 +3283,8 @@ class MkMultiSelect {
3091
3283
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3092
3284
  /** Force invalid styling + `aria-invalid` when used standalone. */
3093
3285
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3286
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
3287
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3094
3288
  /** Disable the control. */
3095
3289
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3096
3290
  /** How the option list is filtered against the typed text. */
@@ -3183,9 +3377,9 @@ class MkMultiSelect {
3183
3377
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
3184
3378
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
3185
3379
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3186
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
3380
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
3187
3381
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
3188
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
3382
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
3189
3383
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
3190
3384
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
3191
3385
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -3439,7 +3633,7 @@ class MkMultiSelect {
3439
3633
  this.validatorChange.register(fn);
3440
3634
  }
3441
3635
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkMultiSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
3442
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkMultiSelect, isStandalone: true, selector: "mk-multi-select", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterMode: { classPropertyName: "filterMode", publicName: "filterMode", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", optionAdded: "optionAdded", optionRemoved: "optionRemoved" }, host: { properties: { "class.mk-multi-select--sm": "effectiveSize() === 'sm'", "class.mk-multi-select--md": "effectiveSize() === 'md'", "class.mk-multi-select--lg": "effectiveSize() === 'lg'", "class.mk-multi-select--open": "open()", "class.mk-multi-select--invalid": "isInvalid()", "class.mk-multi-select--disabled": "isDisabled()" }, classAttribute: "mk-multi-select" }, providers: [
3636
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkMultiSelect, isStandalone: true, selector: "mk-multi-select", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filterMode: { classPropertyName: "filterMode", publicName: "filterMode", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, closeOnSelect: { classPropertyName: "closeOnSelect", publicName: "closeOnSelect", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", optionAdded: "optionAdded", optionRemoved: "optionRemoved" }, host: { properties: { "class.mk-multi-select--sm": "effectiveSize() === 'sm'", "class.mk-multi-select--md": "effectiveSize() === 'md'", "class.mk-multi-select--lg": "effectiveSize() === 'lg'", "class.mk-multi-select--open": "open()", "class.mk-multi-select--invalid": "isInvalid()", "class.mk-multi-select--disabled": "isDisabled()" }, classAttribute: "mk-multi-select" }, providers: [
3443
3637
  {
3444
3638
  provide: NG_VALUE_ACCESSOR,
3445
3639
  useExisting: forwardRef(() => MkMultiSelect),
@@ -3474,7 +3668,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3474
3668
  multi: true,
3475
3669
  },
3476
3670
  ], template: "<div\n #control\n class=\"mk-multi-select__control\"\n (pointerdown)=\"onControlPointerdown($event)\"\n>\n @for (chip of chips(); track chip.value) {\n <mk-chip\n class=\"mk-multi-select__chip\"\n size=\"sm\"\n [removable]=\"!isDisabled()\"\n [disabled]=\"isDisabled()\"\n [removeLabel]=\"i18n.removeItem(chip.label)\"\n (removed)=\"removeChip(chip.value)\"\n >\n {{ chip.label }}\n </mk-chip>\n }\n\n <input\n #input\n type=\"text\"\n class=\"mk-multi-select__input\"\n [id]=\"inputId\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-activedescendant]=\"\n open() && activeIndex() >= 0 ? optionId(activeIndex()) : null\n \"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : (ariaLabel() || null)\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n [placeholder]=\"chips().length ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onInput($event)\"\n (focus)=\"onFocus()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur($event)\"\n />\n\n <span class=\"mk-multi-select__arrow\" aria-hidden=\"true\"></span>\n</div>\n\n@if (open()) {\n <ul\n #list\n class=\"mk-multi-select__list\"\n mkAnchoredPanel\n [mkAnchoredPanelFor]=\"control\"\n [matchWidth]=\"true\"\n (dismiss)=\"onOutsideDismiss()\"\n [id]=\"listId\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-labelledby]=\"labelledBy()\"\n >\n @if (loading()) {\n <li class=\"mk-multi-select__status\" role=\"presentation\">\n <span class=\"mk-multi-select__spinner\" aria-hidden=\"true\"></span>\n {{ i18n.loading }}\n </li>\n } @else {\n @for (opt of filtered(); track $index; let i = $index) {\n <li\n class=\"mk-multi-select__option\"\n [id]=\"optionId(i)\"\n role=\"option\"\n [attr.aria-selected]=\"isSelected(opt.value)\"\n [attr.aria-disabled]=\"\n opt.disabled || (atMax() && !isSelected(opt.value)) || null\n \"\n [attr.title]=\"\n atMax() && !isSelected(opt.value) && !opt.disabled\n ? atMaxHint()\n : null\n \"\n [class.mk-multi-select__option--active]=\"i === activeIndex()\"\n [class.mk-multi-select__option--selected]=\"isSelected(opt.value)\"\n [class.mk-multi-select__option--disabled]=\"\n opt.disabled || (atMax() && !isSelected(opt.value))\n \"\n (mousedown)=\"$event.preventDefault()\"\n (mousemove)=\"!opt.disabled && activeIndex.set(i)\"\n (click)=\"toggleOption(i)\"\n >\n <span class=\"mk-multi-select__check\" aria-hidden=\"true\"></span>\n <span class=\"mk-multi-select__option-label\">{{ opt.label }}</span>\n </li>\n } @empty {\n <li class=\"mk-multi-select__status\" role=\"presentation\">\n {{ emptyMessage() }}\n </li>\n }\n }\n </ul>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative;width:100%;font-family:var(--mk-font-sans)}.mk-multi-select__control{display:flex;flex-wrap:wrap;align-items:center;gap:var(--mk-space-1);min-height:var(--mk-control-height-md);padding-block:var(--mk-space-1);padding-inline:var(--mk-space-2) var(--mk-space-8);position:relative;background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);cursor:text;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}.mk-multi-select__control:hover{border-color:var(--mk-border-strong)}.mk-multi-select__control:focus-within{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-multi-select--sm) .mk-multi-select__control{min-height:var(--mk-control-height-sm);border-radius:var(--mk-radius-sm)}:host(.mk-multi-select--lg) .mk-multi-select__control{min-height:var(--mk-control-height-lg)}:host(.mk-multi-select--invalid) .mk-multi-select__control{border-color:var(--mk-danger)}:host(.mk-multi-select--invalid) .mk-multi-select__control:focus-within{outline-color:var(--mk-danger)}:host(.mk-multi-select--disabled) .mk-multi-select__control{background-color:var(--mk-surface-2);border-color:var(--mk-border-subtle);cursor:not-allowed;opacity:.7}.mk-multi-select__chip{flex:none;max-width:100%}.mk-multi-select__input{flex:1 1 4rem;min-width:4rem;height:var(--mk-line-height-lg, 1.5rem);padding:0 var(--mk-space-1);font-family:inherit;font-size:var(--mk-font-size-md);color:var(--mk-text);background:transparent;border:none;outline:none}.mk-multi-select__input::placeholder{color:var(--mk-text-subtle)}.mk-multi-select__input:disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-multi-select__arrow{position:absolute;top:50%;inset-inline-end:var(--mk-space-3);flex:none;width:.5rem;height:.5rem;pointer-events:none;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:translateY(-75%) rotate(45deg);transition:transform var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-multi-select--open) .mk-multi-select__arrow{transform:translateY(-25%) rotate(-135deg)}.mk-multi-select__list{padding:var(--mk-space-1);list-style:none;max-height:16rem;overflow-y:auto;background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg)}.mk-multi-select__option{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--mk-space-2) var(--mk-space-3);font-size:var(--mk-font-size-md);color:var(--mk-text);border-radius:var(--mk-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none}.mk-multi-select__option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-multi-select__check{flex:none;position:relative;width:1rem;height:1rem;border:2px solid var(--mk-border-strong);border-radius:var(--mk-radius-sm);transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),border-color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-multi-select__option--selected .mk-multi-select__check{background-color:var(--mk-primary);border-color:var(--mk-primary)}.mk-multi-select__option--selected .mk-multi-select__check:after{content:\"\";position:absolute;top:1px;left:4px;width:4px;height:8px;border-right:2px solid var(--mk-primary-contrast);border-bottom:2px solid var(--mk-primary-contrast);transform:rotate(45deg)}.mk-multi-select__option--active{background-color:var(--mk-selected-bg)}.mk-multi-select__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-multi-select__option--disabled .mk-multi-select__check{border-color:var(--mk-border-subtle)}.mk-multi-select__status{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--mk-space-2) var(--mk-space-3);color:var(--mk-text-subtle);font-size:var(--mk-font-size-sm)}.mk-multi-select__spinner{flex:none;width:.9rem;height:.9rem;border:2px solid var(--mk-border-strong);border-top-color:var(--mk-primary);border-radius:var(--mk-radius-circle);animation:mk-multi-select-spin var(--mk-duration-slow) linear infinite}@keyframes mk-multi-select-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.mk-multi-select__arrow,.mk-multi-select__control,.mk-multi-select__check{transition:none}.mk-multi-select__spinner{animation:none}}@media(pointer:coarse){.mk-multi-select__input{font-size:max(var(--mk-font-size-md),16px)}}\n"] }]
3477
- }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMode", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], optionAdded: [{ type: i0.Output, args: ["optionAdded"] }], optionRemoved: [{ type: i0.Output, args: ["optionRemoved"] }] } });
3671
+ }], ctorParameters: () => [], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filterMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterMode", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], closeOnSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeOnSelect", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], optionAdded: [{ type: i0.Output, args: ["optionAdded"] }], optionRemoved: [{ type: i0.Output, args: ["optionRemoved"] }] } });
3478
3672
 
3479
3673
  /**
3480
3674
  * TagInput — a freeform token input: type a value and press Enter (or a
@@ -3489,6 +3683,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3489
3683
  */
3490
3684
  class MkTagInput {
3491
3685
  field = inject(MkFormField, { optional: true });
3686
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
3687
+ fieldTouched = mkInjectFieldTouched();
3492
3688
  i18n = inject(MK_I18N);
3493
3689
  announcer = inject(MkLiveAnnouncer);
3494
3690
  host = inject(ElementRef);
@@ -3515,6 +3711,8 @@ class MkTagInput {
3515
3711
  ...(ngDevMode ? [{ debugName: "separators" }] : /* istanbul ignore next */ []));
3516
3712
  /** Force invalid styling + `aria-invalid` when used standalone. */
3517
3713
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3714
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
3715
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3518
3716
  /** Disable the control. */
3519
3717
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3520
3718
  /** Control size. Ignored when nested in an `mk-form-field`. */
@@ -3535,9 +3733,9 @@ class MkTagInput {
3535
3733
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
3536
3734
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
3537
3735
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3538
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
3736
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
3539
3737
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
3540
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
3738
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
3541
3739
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
3542
3740
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
3543
3741
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -3674,7 +3872,7 @@ class MkTagInput {
3674
3872
  this.validatorChange.register(fn);
3675
3873
  }
3676
3874
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkTagInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3677
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkTagInput, isStandalone: true, selector: "mk-tag-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicates: { classPropertyName: "allowDuplicates", publicName: "allowDuplicates", isSignal: true, isRequired: false, transformFunction: null }, addOnBlur: { classPropertyName: "addOnBlur", publicName: "addOnBlur", isSignal: true, isRequired: false, transformFunction: null }, separators: { classPropertyName: "separators", publicName: "separators", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", added: "added", removed: "removed" }, host: { listeners: { "click": "focusInput()" }, properties: { "class.mk-tag-input--sm": "effectiveSize() === 'sm'", "class.mk-tag-input--lg": "effectiveSize() === 'lg'", "class.mk-tag-input--invalid": "isInvalid()", "class.mk-tag-input--disabled": "isDisabled()" }, classAttribute: "mk-tag-input" }, providers: [
3875
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkTagInput, isStandalone: true, selector: "mk-tag-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, allowDuplicates: { classPropertyName: "allowDuplicates", publicName: "allowDuplicates", isSignal: true, isRequired: false, transformFunction: null }, addOnBlur: { classPropertyName: "addOnBlur", publicName: "addOnBlur", isSignal: true, isRequired: false, transformFunction: null }, separators: { classPropertyName: "separators", publicName: "separators", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", added: "added", removed: "removed" }, host: { listeners: { "click": "focusInput()" }, properties: { "class.mk-tag-input--sm": "effectiveSize() === 'sm'", "class.mk-tag-input--lg": "effectiveSize() === 'lg'", "class.mk-tag-input--invalid": "isInvalid()", "class.mk-tag-input--disabled": "isDisabled()" }, classAttribute: "mk-tag-input" }, providers: [
3678
3876
  {
3679
3877
  provide: NG_VALUE_ACCESSOR,
3680
3878
  useExisting: forwardRef(() => MkTagInput),
@@ -3708,7 +3906,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3708
3906
  multi: true,
3709
3907
  },
3710
3908
  ], template: "<div class=\"mk-tag-input__field\">\n <ul class=\"mk-tag-input__tags\">\n @for (tag of value(); track $index; let i = $index) {\n <li class=\"mk-tag-input__tag\">\n <mk-chip\n [size]=\"effectiveSize()\"\n [removable]=\"!isDisabled()\"\n [disabled]=\"isDisabled()\"\n [removeLabel]=\"i18n.removeItem(tag)\"\n (removed)=\"removeChipAt(i)\"\n >\n {{ tag }}\n </mk-chip>\n </li>\n }\n <li class=\"mk-tag-input__entry\">\n <input\n #input\n class=\"mk-tag-input__input\"\n type=\"text\"\n autocomplete=\"off\"\n [id]=\"inputId\"\n [value]=\"query()\"\n [placeholder]=\"value().length ? '' : placeholder()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"atMax()\"\n [attr.aria-disabled]=\"atMax() || null\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n (input)=\"onQueryInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (paste)=\"onPaste($event)\"\n (blur)=\"onBlur()\"\n />\n </li>\n </ul>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%;font-family:var(--mk-font-sans)}.mk-tag-input__field{display:flex;min-height:var(--mk-control-height-md);padding:var(--mk-space-1);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);cursor:text;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-tag-input--sm) .mk-tag-input__field{min-height:var(--mk-control-height-sm);border-radius:var(--mk-radius-sm)}:host(.mk-tag-input--lg) .mk-tag-input__field{min-height:var(--mk-control-height-lg)}.mk-tag-input__field:has(.mk-tag-input__input:focus){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset);border-color:var(--mk-primary)}:host(.mk-tag-input--invalid) .mk-tag-input__field{border-color:var(--mk-danger)}:host(.mk-tag-input--disabled) .mk-tag-input__field{background-color:var(--mk-surface-2);cursor:not-allowed;opacity:.7}.mk-tag-input__tags{display:flex;flex-wrap:wrap;align-items:center;gap:var(--mk-space-1);width:100%;margin:0;padding:0;list-style:none}.mk-tag-input__tag{display:inline-flex}.mk-tag-input__entry{flex:1 1 6rem;display:inline-flex;align-items:center;min-width:6rem}.mk-tag-input__input{width:100%;padding:0 var(--mk-space-2);font:inherit;font-size:var(--mk-font-size-md);color:var(--mk-text);background:transparent;border:0;outline:none}:host(.mk-tag-input--sm) .mk-tag-input__input{font-size:var(--mk-font-size-sm)}.mk-tag-input__input::placeholder{color:var(--mk-text-subtle)}.mk-tag-input__input:disabled{cursor:not-allowed}.mk-tag-input__input[readonly]{cursor:default}@media(pointer:coarse){.mk-tag-input__input{font-size:max(var(--mk-font-size-md),16px)}:host(.mk-tag-input--sm) .mk-tag-input__input{font-size:max(var(--mk-font-size-sm),16px)}}\n"] }]
3711
- }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], allowDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicates", required: false }] }], addOnBlur: [{ type: i0.Input, args: [{ isSignal: true, alias: "addOnBlur", required: false }] }], separators: [{ type: i0.Input, args: [{ isSignal: true, alias: "separators", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], added: [{ type: i0.Output, args: ["added"] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
3909
+ }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], allowDuplicates: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDuplicates", required: false }] }], addOnBlur: [{ type: i0.Input, args: [{ isSignal: true, alias: "addOnBlur", required: false }] }], separators: [{ type: i0.Input, args: [{ isSignal: true, alias: "separators", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], added: [{ type: i0.Output, args: ["added"] }], removed: [{ type: i0.Output, args: ["removed"] }] } });
3712
3910
 
3713
3911
  /**
3714
3912
  * PasswordInput — a password field with a reveal toggle, an optional strength
@@ -3722,6 +3920,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3722
3920
  */
3723
3921
  class MkPasswordInput {
3724
3922
  field = inject(MkFormField, { optional: true });
3923
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
3924
+ fieldTouched = mkInjectFieldTouched();
3725
3925
  i18n = inject(MK_I18N);
3726
3926
  inputRef = viewChild('input', /* @ts-ignore */
3727
3927
  ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
@@ -3765,7 +3965,7 @@ class MkPasswordInput {
3765
3965
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
3766
3966
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
3767
3967
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
3768
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
3968
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
3769
3969
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
3770
3970
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
3771
3971
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -3948,6 +4148,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3948
4148
  class MkTransferList {
3949
4149
  host = inject(ElementRef);
3950
4150
  field = inject(MkFormField, { optional: true });
4151
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
4152
+ fieldTouched = mkInjectFieldTouched();
3951
4153
  i18n = inject(MK_I18N);
3952
4154
  announcer = inject(MkLiveAnnouncer);
3953
4155
  injector = inject(Injector);
@@ -4010,7 +4212,7 @@ class MkTransferList {
4010
4212
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
4011
4213
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
4012
4214
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4013
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
4215
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
4014
4216
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
4015
4217
  /** Items keyed by value, for resolving the selected order. */
4016
4218
  byValue = computed(() => {
@@ -4528,6 +4730,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
4528
4730
  */
4529
4731
  class MkTreeSelect {
4530
4732
  field = inject(MkFormField, { optional: true });
4733
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
4734
+ fieldTouched = mkInjectFieldTouched();
4531
4735
  i18n = inject(MK_I18N);
4532
4736
  host = inject(ElementRef);
4533
4737
  injector = inject(Injector);
@@ -4567,7 +4771,7 @@ class MkTreeSelect {
4567
4771
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
4568
4772
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
4569
4773
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4570
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
4774
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
4571
4775
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
4572
4776
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
4573
4777
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -5253,7 +5457,9 @@ class MkCodeEditor {
5253
5457
  }
5254
5458
  }, /* @ts-ignore */
5255
5459
  ...(ngDevMode ? [{ debugName: "jsonError" }] : /* istanbul ignore next */ []));
5256
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false) || !!this.jsonError(), /* @ts-ignore */
5460
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
5461
+ fieldTouched = mkInjectFieldTouched();
5462
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false) || !!this.jsonError(), /* @ts-ignore */
5257
5463
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5258
5464
  /** `aria-describedby` merging the field's ids with the JSON error id. */
5259
5465
  ariaDescribedBy = computed(() => {
@@ -5486,6 +5692,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5486
5692
  */
5487
5693
  class MkRating {
5488
5694
  field = inject(MkFormField, { optional: true });
5695
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
5696
+ fieldTouched = mkInjectFieldTouched();
5489
5697
  i18n = inject(MK_I18N);
5490
5698
  /** Number of stars. */
5491
5699
  max = input(5, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: numberAttribute });
@@ -5512,7 +5720,7 @@ class MkRating {
5512
5720
  onTouched = () => { };
5513
5721
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
5514
5722
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
5515
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
5723
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
5516
5724
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5517
5725
  /** The surrounding field's label labels this control (`aria-labelledby`). */
5518
5726
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
@@ -5676,17 +5884,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5676
5884
  */
5677
5885
  class MkNumberInput {
5678
5886
  field = inject(MkFormField, { optional: true });
5887
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
5888
+ fieldTouched = mkInjectFieldTouched();
5679
5889
  i18n = inject(MK_I18N);
5680
5890
  inputRef = viewChild('input', /* @ts-ignore */
5681
5891
  ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
5682
5892
  /** Minimum value. */
5683
5893
  /** Accessible name when the control is used without an `mk-form-field` label. */
5684
5894
  ariaLabel = input('', { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
5685
- min = input(null, /* @ts-ignore */
5686
- ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
5895
+ min = input(null, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}),
5896
+ // Signal Forms binds the schema's `min()` limit here, `undefined` when unset.
5897
+ transform: (v) => v ?? null });
5687
5898
  /** Maximum value. */
5688
- max = input(null, /* @ts-ignore */
5689
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
5899
+ max = input(null, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}),
5900
+ // Signal Forms binds the schema's `max()` limit here, `undefined` when unset.
5901
+ transform: (v) => v ?? null });
5690
5902
  /** Step increment for buttons + Arrow keys. */
5691
5903
  step = input(1, { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: numberAttribute });
5692
5904
  /** Two-way value. */
@@ -5699,6 +5911,8 @@ class MkNumberInput {
5699
5911
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5700
5912
  /** Force invalid styling when standalone. */
5701
5913
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5914
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
5915
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5702
5916
  /** Control size. Ignored inside an `mk-form-field`. */
5703
5917
  size = input('md', /* @ts-ignore */
5704
5918
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -5711,9 +5925,9 @@ class MkNumberInput {
5711
5925
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
5712
5926
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
5713
5927
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
5714
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
5928
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
5715
5929
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5716
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
5930
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
5717
5931
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
5718
5932
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
5719
5933
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -5833,7 +6047,7 @@ class MkNumberInput {
5833
6047
  this.validatorChange.register(fn);
5834
6048
  }
5835
6049
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkNumberInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
5836
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MkNumberInput, isStandalone: true, selector: "mk-number-input", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-number-input--sm": "effectiveSize() === 'sm'", "class.mk-number-input--lg": "effectiveSize() === 'lg'", "class.mk-number-input--invalid": "isInvalid()", "class.mk-number-input--disabled": "isDisabled()" }, classAttribute: "mk-number-input" }, providers: [
6050
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MkNumberInput, isStandalone: true, selector: "mk-number-input", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-number-input--sm": "effectiveSize() === 'sm'", "class.mk-number-input--lg": "effectiveSize() === 'lg'", "class.mk-number-input--invalid": "isInvalid()", "class.mk-number-input--disabled": "isDisabled()" }, classAttribute: "mk-number-input" }, providers: [
5837
6051
  {
5838
6052
  provide: NG_VALUE_ACCESSOR,
5839
6053
  useExisting: forwardRef(() => MkNumberInput),
@@ -5866,7 +6080,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5866
6080
  multi: true,
5867
6081
  },
5868
6082
  ], template: "<button\n type=\"button\"\n class=\"mk-number-input__btn mk-number-input__btn--dec\"\n [attr.aria-label]=\"i18n.decrease\"\n tabindex=\"-1\"\n [disabled]=\"!canDecrement()\"\n (click)=\"step_(-1)\"\n>\n <span aria-hidden=\"true\">\u2212</span>\n</button>\n\n<input\n #input\n type=\"text\"\n inputmode=\"decimal\"\n class=\"mk-number-input__field\"\n [id]=\"inputId\"\n role=\"spinbutton\"\n [value]=\"display()\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-label]=\"labelledBy() ? null : (ariaLabel() || null)\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n/>\n\n<button\n type=\"button\"\n class=\"mk-number-input__btn mk-number-input__btn--inc\"\n [attr.aria-label]=\"i18n.increase\"\n tabindex=\"-1\"\n [disabled]=\"!canIncrement()\"\n (click)=\"step_(1)\"\n>\n <span aria-hidden=\"true\">+</span>\n</button>\n", styles: ["@charset \"UTF-8\";:host{display:inline-flex;align-items:stretch;height:var(--mk-control-height-md);background-color:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);font-family:var(--mk-font-sans);overflow:hidden;transition:border-color var(--mk-duration-fast) var(--mk-ease-standard),box-shadow var(--mk-duration-fast) var(--mk-ease-standard)}:host(.mk-number-input--sm){height:var(--mk-control-height-sm);border-radius:var(--mk-radius-sm)}:host(.mk-number-input--lg){height:var(--mk-control-height-lg)}:host(:focus-within){outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:-2px;border-color:var(--mk-primary)}:host(.mk-number-input--invalid){border-color:var(--mk-danger)}:host(.mk-number-input--disabled){background-color:var(--mk-surface-2);opacity:.7}.mk-number-input__field{width:4.5rem;min-width:0;padding:0 var(--mk-space-2);text-align:center;font-family:inherit;font-size:var(--mk-font-size-md);color:var(--mk-text);background:transparent;border:none;outline:none;appearance:textfield}.mk-number-input__field::placeholder{color:var(--mk-text-subtle)}.mk-number-input__btn{flex:none;display:grid;place-items:center;width:2rem;padding:0;font-size:var(--mk-font-size-lg);line-height:1;color:var(--mk-text-muted);background-color:var(--mk-surface-2);border:none;cursor:pointer;transition:background-color var(--mk-duration-fast) var(--mk-ease-standard),color var(--mk-duration-fast) var(--mk-ease-standard)}.mk-number-input__btn--dec{border-inline-end:var(--mk-border-width) solid var(--mk-border-subtle)}.mk-number-input__btn--inc{border-inline-start:var(--mk-border-width) solid var(--mk-border-subtle)}.mk-number-input__btn:not(:disabled):hover{background-color:var(--mk-hover-overlay);color:var(--mk-text)}.mk-number-input__btn:disabled{color:var(--mk-text-disabled);cursor:not-allowed}@media(prefers-reduced-motion:reduce){:host,.mk-number-input__btn{transition:none}}@media(pointer:coarse){.mk-number-input__field{font-size:max(var(--mk-font-size-md),16px)}}\n"] }]
5869
- }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
6083
+ }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
5870
6084
 
5871
6085
  /**
5872
6086
  * Numeric keypad — a touch-first 3×4 digit pad for PIN entry, quantity entry
@@ -5900,11 +6114,13 @@ class MkNumericKeypad {
5900
6114
  /** PIN length (`pin` mode only). Reaching it emits `submit`. */
5901
6115
  length = input(4, { ...(ngDevMode ? { debugName: "length" } : /* istanbul ignore next */ {}), transform: numberAttribute });
5902
6116
  /** Lower bound reported through the validator (`quantity`/`amount`). */
5903
- min = input(null, /* @ts-ignore */
5904
- ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
6117
+ min = input(null, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}),
6118
+ // Signal Forms binds the schema's `min()` limit here, `undefined` when unset.
6119
+ transform: (v) => v ?? null });
5905
6120
  /** Upper bound reported through the validator (`quantity`/`amount`). */
5906
- max = input(null, /* @ts-ignore */
5907
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
6121
+ max = input(null, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}),
6122
+ // Signal Forms binds the schema's `max()` limit here, `undefined` when unset.
6123
+ transform: (v) => v ?? null });
5908
6124
  /** Mask the echo row in `pin` mode. Default `true`. */
5909
6125
  mask = input(true, { ...(ngDevMode ? { debugName: "mask" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5910
6126
  /** Show the echo row above the keys. Default `true`. */
@@ -6448,6 +6664,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
6448
6664
  */
6449
6665
  class MkOtp {
6450
6666
  field = inject(MkFormField, { optional: true });
6667
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
6668
+ fieldTouched = mkInjectFieldTouched();
6451
6669
  i18n = inject(MK_I18N);
6452
6670
  cells = viewChildren('cell', /* @ts-ignore */
6453
6671
  ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
@@ -6484,7 +6702,7 @@ class MkOtp {
6484
6702
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
6485
6703
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
6486
6704
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
6487
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
6705
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
6488
6706
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
6489
6707
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
6490
6708
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -6764,6 +6982,8 @@ function mkCountryFlag(code) {
6764
6982
  */
6765
6983
  class MkPhoneInput {
6766
6984
  field = inject(MkFormField, { optional: true });
6985
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
6986
+ fieldTouched = mkInjectFieldTouched();
6767
6987
  /** Localised strings (override globally via `provideMkI18n`). */
6768
6988
  i18n = inject(MK_I18N);
6769
6989
  triggerRef = viewChild('trigger', /* @ts-ignore */
@@ -6827,7 +7047,7 @@ class MkPhoneInput {
6827
7047
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
6828
7048
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
6829
7049
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
6830
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
7050
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
6831
7051
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
6832
7052
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
6833
7053
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -7245,6 +7465,8 @@ function mkPostalCodeValidator(country, formats = MK_POSTAL_FORMATS) {
7245
7465
  */
7246
7466
  class MkPostalCodeInput {
7247
7467
  field = inject(MkFormField, { optional: true });
7468
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
7469
+ fieldTouched = mkInjectFieldTouched();
7248
7470
  /** Localised strings (override globally via `provideMkI18n`). */
7249
7471
  i18n = inject(MK_I18N);
7250
7472
  /** ISO 3166-1 alpha-2 country whose format applies. */
@@ -7301,7 +7523,7 @@ class MkPostalCodeInput {
7301
7523
  return fmt.pattern.test(v);
7302
7524
  }, /* @ts-ignore */
7303
7525
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
7304
- isInvalid = computed(() => this.invalid() ||
7526
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
7305
7527
  (this.field?.hasError() ?? false) ||
7306
7528
  (this.touched() && this.valid() === false), /* @ts-ignore */
7307
7529
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -7453,6 +7675,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
7453
7675
  */
7454
7676
  class MkCurrencyInput {
7455
7677
  field = inject(MkFormField, { optional: true });
7678
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
7679
+ fieldTouched = mkInjectFieldTouched();
7456
7680
  /** Localised strings (override globally via `provideMkI18n`). */
7457
7681
  i18n = inject(MK_I18N);
7458
7682
  /** ISO 4217 currency code (`PLN`, `USD`, …). Empty = plain decimal. */
@@ -7467,11 +7691,13 @@ class MkCurrencyInput {
7467
7691
  decimals = input(null, /* @ts-ignore */
7468
7692
  ...(ngDevMode ? [{ debugName: "decimals" }] : /* istanbul ignore next */ []));
7469
7693
  /** Clamp the value to at least this on blur. */
7470
- min = input(null, /* @ts-ignore */
7471
- ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
7694
+ min = input(null, { ...(ngDevMode ? { debugName: "min" } : /* istanbul ignore next */ {}),
7695
+ // Signal Forms binds the schema's `min()` limit here, `undefined` when unset.
7696
+ transform: (v) => v ?? null });
7472
7697
  /** Clamp the value to at most this on blur. */
7473
- max = input(null, /* @ts-ignore */
7474
- ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
7698
+ max = input(null, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}),
7699
+ // Signal Forms binds the schema's `max()` limit here, `undefined` when unset.
7700
+ transform: (v) => v ?? null });
7475
7701
  /** Allow a leading minus sign. */
7476
7702
  allowNegative = input(true, { ...(ngDevMode ? { debugName: "allowNegative" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
7477
7703
  /** Control size. Ignored when nested in an `mk-form-field`. */
@@ -7501,7 +7727,7 @@ class MkCurrencyInput {
7501
7727
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
7502
7728
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
7503
7729
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
7504
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
7730
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
7505
7731
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
7506
7732
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
7507
7733
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -7819,6 +8045,8 @@ const DEFAULT_MASK = '0000 0000 0000 0000';
7819
8045
  */
7820
8046
  class MkCardNumberInput {
7821
8047
  field = inject(MkFormField, { optional: true });
8048
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8049
+ fieldTouched = mkInjectFieldTouched();
7822
8050
  /** Localised strings (override globally via `provideMkI18n`). */
7823
8051
  i18n = inject(MK_I18N);
7824
8052
  /** Show the detected brand as a badge inside the field. */
@@ -7890,7 +8118,7 @@ class MkCardNumberInput {
7890
8118
  return mkLuhnCheck(digits);
7891
8119
  }, /* @ts-ignore */
7892
8120
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
7893
- isInvalid = computed(() => this.invalid() ||
8121
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
7894
8122
  (this.field?.hasError() ?? false) ||
7895
8123
  (this.touched() && this.valid() === false), /* @ts-ignore */
7896
8124
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8052,6 +8280,8 @@ function mkIbanValidator() {
8052
8280
  */
8053
8281
  class MkIbanInput {
8054
8282
  field = inject(MkFormField, { optional: true });
8283
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8284
+ fieldTouched = mkInjectFieldTouched();
8055
8285
  /** Localised strings (override globally via `provideMkI18n`). */
8056
8286
  i18n = inject(MK_I18N);
8057
8287
  /** Control size. Ignored when nested in an `mk-form-field`. */
@@ -8110,7 +8340,7 @@ class MkIbanInput {
8110
8340
  return mkIbanChecksum(compact);
8111
8341
  }, /* @ts-ignore */
8112
8342
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
8113
- isInvalid = computed(() => this.invalid() ||
8343
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
8114
8344
  (this.field?.hasError() ?? false) ||
8115
8345
  (this.touched() && this.valid() === false), /* @ts-ignore */
8116
8346
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8329,6 +8559,8 @@ function mkTaxIdValidator(country) {
8329
8559
  */
8330
8560
  class MkTaxIdInput {
8331
8561
  field = inject(MkFormField, { optional: true });
8562
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8563
+ fieldTouched = mkInjectFieldTouched();
8332
8564
  /** Localised strings (override globally via `provideMkI18n`). */
8333
8565
  i18n = inject(MK_I18N);
8334
8566
  /** ISO 3166-1 alpha-2 country whose format applies. */
@@ -8393,7 +8625,7 @@ class MkTaxIdInput {
8393
8625
  return compact.length < digits ? null : false;
8394
8626
  }, /* @ts-ignore */
8395
8627
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
8396
- isInvalid = computed(() => this.invalid() ||
8628
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
8397
8629
  (this.field?.hasError() ?? false) ||
8398
8630
  (this.touched() && this.valid() === false), /* @ts-ignore */
8399
8631
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8521,6 +8753,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8521
8753
  class MkSignaturePad {
8522
8754
  host = inject(ElementRef);
8523
8755
  field = inject(MkFormField, { optional: true });
8756
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8757
+ fieldTouched = mkInjectFieldTouched();
8524
8758
  isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
8525
8759
  /** Localised strings (override globally via `provideMkI18n`). */
8526
8760
  i18n = inject(MK_I18N);
@@ -8567,7 +8801,7 @@ class MkSignaturePad {
8567
8801
  padId = this.field?.controlId ?? mkUniqueId('mk-signature');
8568
8802
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
8569
8803
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
8570
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
8804
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
8571
8805
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
8572
8806
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
8573
8807
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -8925,6 +9159,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8925
9159
  class MkRadioGroup {
8926
9160
  host = inject(ElementRef);
8927
9161
  field = inject(MkFormField, { optional: true });
9162
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9163
+ fieldTouched = mkInjectFieldTouched();
8928
9164
  /** Two-way selected value. */
8929
9165
  value = model(null, /* @ts-ignore */
8930
9166
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -8956,7 +9192,7 @@ class MkRadioGroup {
8956
9192
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
8957
9193
  isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
8958
9194
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
8959
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9195
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
8960
9196
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
8961
9197
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
8962
9198
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -9184,6 +9420,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9184
9420
  */
9185
9421
  class MkSwitch {
9186
9422
  field = inject(MkFormField, { optional: true });
9423
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9424
+ fieldTouched = mkInjectFieldTouched();
9187
9425
  /** Two-way checked (on/off) state. */
9188
9426
  checked = model(false, /* @ts-ignore */
9189
9427
  ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
@@ -9191,6 +9429,8 @@ class MkSwitch {
9191
9429
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9192
9430
  /** Force the invalid visual + `aria-invalid` when used standalone. */
9193
9431
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9432
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
9433
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9194
9434
  /** Control size. */
9195
9435
  size = input('md', /* @ts-ignore */
9196
9436
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -9206,9 +9446,9 @@ class MkSwitch {
9206
9446
  labelId = mkUniqueId('mk-switch-label');
9207
9447
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9208
9448
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9209
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9449
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9210
9450
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9211
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9451
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
9212
9452
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
9213
9453
  describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
9214
9454
  ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
@@ -9246,7 +9486,7 @@ class MkSwitch {
9246
9486
  this.cvaDisabled.set(isDisabled);
9247
9487
  }
9248
9488
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSwitch, deps: [], target: i0.ɵɵFactoryTarget.Component });
9249
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: MkSwitch, isStandalone: true, selector: "mk-switch", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { properties: { "class.mk-switch--sm": "size() === 'sm'", "class.mk-switch--md": "size() === 'md'", "class.mk-switch--lg": "size() === 'lg'", "class.mk-switch--checked": "checked()", "class.mk-switch--disabled": "isDisabled()", "attr.data-tone": "tone()" }, classAttribute: "mk-switch" }, providers: [
9489
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: MkSwitch, isStandalone: true, selector: "mk-switch", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, host: { properties: { "class.mk-switch--sm": "size() === 'sm'", "class.mk-switch--md": "size() === 'md'", "class.mk-switch--lg": "size() === 'lg'", "class.mk-switch--checked": "checked()", "class.mk-switch--disabled": "isDisabled()", "attr.data-tone": "tone()" }, classAttribute: "mk-switch" }, providers: [
9250
9490
  {
9251
9491
  provide: NG_VALUE_ACCESSOR,
9252
9492
  useExisting: forwardRef(() => MkSwitch),
@@ -9271,7 +9511,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9271
9511
  multi: true,
9272
9512
  },
9273
9513
  ], template: "<button\n type=\"button\"\n class=\"mk-switch__control\"\n role=\"switch\"\n [attr.aria-checked]=\"checked()\"\n [disabled]=\"isDisabled()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n (click)=\"toggle()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onTouched()\"\n>\n <span class=\"mk-switch__track\">\n <span class=\"mk-switch__thumb\"></span>\n </span>\n</button>\n<span class=\"mk-switch__label\" [id]=\"labelId\" (click)=\"toggle()\">\n <ng-content />\n</span>\n", styles: ["@charset \"UTF-8\";:host{--_main: var(--mk-primary);--_track-w: 2.4rem;--_track-h: 1.4rem;--_gap: 2px;display:inline-flex;align-items:center;gap:var(--mk-space-2);font-family:var(--mk-font-sans);font-size:var(--mk-font-size-md);color:var(--mk-text)}:host(.mk-switch--sm){--_track-w: 2rem;--_track-h: 1.15rem;font-size:var(--mk-font-size-sm)}:host(.mk-switch--lg){--_track-w: 2.9rem;--_track-h: 1.7rem;font-size:var(--mk-font-size-lg)}:host([data-tone=primary]){--_main: var(--mk-primary)}:host([data-tone=success]){--_main: var(--mk-success)}:host([data-tone=warning]){--_main: var(--mk-warning)}:host([data-tone=danger]){--_main: var(--mk-danger)}:host([data-tone=info]){--_main: var(--mk-info)}:host([data-tone=neutral]){--_main: var(--mk-surface-inverse)}.mk-switch__control{flex:none;padding:0;border:none;background:none;cursor:pointer;border-radius:var(--mk-radius-pill)}.mk-switch__control:disabled{cursor:not-allowed}@media(pointer:coarse){.mk-switch__control{position:relative}.mk-switch__control:before{content:\"\";position:absolute;inset:min(0px,(100% - 24px) / 2)}}.mk-switch__track{display:block;position:relative;width:var(--_track-w);height:var(--_track-h);background-color:var(--mk-border-strong);border-radius:var(--mk-radius-pill);transition:background-color var(--mk-duration-normal) var(--mk-ease-standard)}.mk-switch__thumb{position:absolute;top:var(--_gap);inset-inline-start:var(--_gap);width:calc(var(--_track-h) - 2 * var(--_gap));height:calc(var(--_track-h) - 2 * var(--_gap));background-color:var(--mk-surface);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);transition:transform var(--mk-duration-normal) var(--mk-ease-emphasized)}:host(.mk-switch--checked) .mk-switch__track{background-color:var(--_main)}:host(.mk-switch--checked) .mk-switch__thumb{transform:translate(calc(var(--_track-w) - var(--_track-h)))}:host(.mk-switch--checked:dir(rtl)) .mk-switch__thumb{transform:translate(calc(-1 * (var(--_track-w) - var(--_track-h))))}.mk-switch__control:focus-visible .mk-switch__track{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}.mk-switch__label{cursor:pointer;-webkit-user-select:none;user-select:none}:host(.mk-switch--disabled){opacity:.6}:host(.mk-switch--disabled) .mk-switch__label{cursor:not-allowed}@media(prefers-reduced-motion:reduce){.mk-switch__track,.mk-switch__thumb{transition:none}}\n"] }]
9274
- }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }] } });
9514
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }] } });
9275
9515
 
9276
9516
  /**
9277
9517
  * Slider — a single-value range slider with `role="slider"`, a filled track and
@@ -9287,6 +9527,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9287
9527
  */
9288
9528
  class MkSlider {
9289
9529
  field = inject(MkFormField, { optional: true });
9530
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9531
+ fieldTouched = mkInjectFieldTouched();
9290
9532
  trackRef = viewChild('track', /* @ts-ignore */
9291
9533
  ...(ngDevMode ? [{ debugName: "trackRef" }] : /* istanbul ignore next */ []));
9292
9534
  thumbRef = viewChild('thumb', /* @ts-ignore */
@@ -9301,6 +9543,8 @@ class MkSlider {
9301
9543
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9302
9544
  /** Force the invalid visual + `aria-invalid` when used standalone. */
9303
9545
  invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9546
+ /** Mark required (adds `aria-required`). Set by Signal Forms' `[formField]` from the schema. */
9547
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9304
9548
  /** Control size (track/thumb thickness). */
9305
9549
  size = input('md', /* @ts-ignore */
9306
9550
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -9325,9 +9569,9 @@ class MkSlider {
9325
9569
  onTouched = () => { };
9326
9570
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9327
9571
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9328
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9572
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
9329
9573
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
9330
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9574
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9331
9575
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9332
9576
  labelledBy = computed(() => {
9333
9577
  if (this.ariaLabel())
@@ -9524,7 +9768,7 @@ class MkSlider {
9524
9768
  this.validatorChange.register(fn);
9525
9769
  }
9526
9770
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
9527
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MkSlider, isStandalone: true, selector: "mk-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-slider--sm": "size() === 'sm'", "class.mk-slider--md": "size() === 'md'", "class.mk-slider--lg": "size() === 'lg'", "class.mk-slider--disabled": "isDisabled()", "attr.data-tone": "tone()" }, classAttribute: "mk-slider" }, providers: [
9771
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MkSlider, isStandalone: true, selector: "mk-slider", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.mk-slider--sm": "size() === 'sm'", "class.mk-slider--md": "size() === 'md'", "class.mk-slider--lg": "size() === 'lg'", "class.mk-slider--disabled": "isDisabled()", "attr.data-tone": "tone()" }, classAttribute: "mk-slider" }, providers: [
9528
9772
  {
9529
9773
  provide: NG_VALUE_ACCESSOR,
9530
9774
  useExisting: forwardRef(() => MkSlider),
@@ -9558,7 +9802,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9558
9802
  multi: true,
9559
9803
  },
9560
9804
  ], template: "<div #track class=\"mk-slider__track\" (pointerdown)=\"onPointerDown($event)\">\n <div class=\"mk-slider__fill\" [style.width.%]=\"percent()\"></div>\n <div\n #thumb\n class=\"mk-slider__thumb\"\n role=\"slider\"\n [style.inset-inline-start.%]=\"percent()\"\n [attr.tabindex]=\"isDisabled() ? -1 : 0\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuetext]=\"value()\"\n aria-orientation=\"horizontal\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-describedby]=\"describedBy()\"\n [attr.aria-disabled]=\"isDisabled() || null\"\n [attr.aria-required]=\"isRequired() || null\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onPointerUp()\"\n ></div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{--_main: var(--mk-primary);--_track-h: .4rem;--_thumb: 1.15rem;display:block;width:100%;padding:calc(var(--_thumb) / 2) 0;font-family:var(--mk-font-sans);touch-action:none}:host(.mk-slider--sm){--_track-h: .3rem;--_thumb: .95rem}:host(.mk-slider--lg){--_track-h: .55rem;--_thumb: 1.4rem}:host([data-tone=primary]){--_main: var(--mk-primary)}:host([data-tone=success]){--_main: var(--mk-success)}:host([data-tone=warning]){--_main: var(--mk-warning)}:host([data-tone=danger]){--_main: var(--mk-danger)}:host([data-tone=info]){--_main: var(--mk-info)}:host([data-tone=neutral]){--_main: var(--mk-surface-inverse)}.mk-slider__track{position:relative;width:100%;height:var(--_track-h);background-color:var(--mk-surface-3);border-radius:var(--mk-radius-pill);cursor:pointer}.mk-slider__fill{position:absolute;top:0;inset-inline-start:0;height:100%;background-color:var(--_main);border-radius:var(--mk-radius-pill)}.mk-slider__thumb{position:absolute;top:50%;width:var(--_thumb);height:var(--_thumb);margin-inline-start:calc(var(--_thumb) / -2);background-color:var(--mk-surface);border:var(--mk-border-width-strong) solid var(--_main);border-radius:var(--mk-radius-circle);box-shadow:var(--mk-shadow-sm);transform:translateY(-50%);cursor:grab}.mk-slider__thumb:active{cursor:grabbing}@media(pointer:coarse){.mk-slider__thumb:before{content:\"\";position:absolute;inset:min(0px,(100% - 44px) / 2)}}.mk-slider__thumb:focus-visible{outline:var(--mk-focus-ring-width) solid var(--mk-focus-ring);outline-offset:var(--mk-focus-ring-offset)}:host(.mk-slider--disabled){opacity:.6}:host(.mk-slider--disabled) .mk-slider__track,:host(.mk-slider--disabled) .mk-slider__thumb{cursor:not-allowed}\n"] }]
9561
- }], propDecorators: { trackRef: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }], thumbRef: [{ type: i0.ViewChild, args: ['thumb', { isSignal: true }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
9805
+ }], propDecorators: { trackRef: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }], thumbRef: [{ type: i0.ViewChild, args: ['thumb', { isSignal: true }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
9562
9806
 
9563
9807
  /** Whether `value` is a valid `#rgb` / `#rrggbb` hex color. */
9564
9808
  function isHex(value) {
@@ -9583,6 +9827,8 @@ function toLongHex(value) {
9583
9827
  */
9584
9828
  class MkColorPicker {
9585
9829
  field = inject(MkFormField, { optional: true });
9830
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9831
+ fieldTouched = mkInjectFieldTouched();
9586
9832
  i18n = inject(MK_I18N);
9587
9833
  nativeRef = viewChild('native', /* @ts-ignore */
9588
9834
  ...(ngDevMode ? [{ debugName: "nativeRef" }] : /* istanbul ignore next */ []));
@@ -9620,7 +9866,7 @@ class MkColorPicker {
9620
9866
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
9621
9867
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9622
9868
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9623
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9869
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9624
9870
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9625
9871
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9626
9872
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -9717,6 +9963,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9717
9963
  class MkRangeSlider {
9718
9964
  host = inject(ElementRef);
9719
9965
  field = inject(MkFormField, { optional: true });
9966
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9967
+ fieldTouched = mkInjectFieldTouched();
9720
9968
  i18n = inject(MK_I18N);
9721
9969
  trackRef = viewChildren('track', /* @ts-ignore */
9722
9970
  ...(ngDevMode ? [{ debugName: "trackRef" }] : /* istanbul ignore next */ []));
@@ -9764,7 +10012,7 @@ class MkRangeSlider {
9764
10012
  ...(ngDevMode ? [{ debugName: "high" }] : /* istanbul ignore next */ []));
9765
10013
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9766
10014
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9767
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
10015
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9768
10016
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9769
10017
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
9770
10018
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));