@mk-kit/ui 0.41.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 (53) hide show
  1. package/README.md +4 -4
  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 +218 -1
  8. package/fesm2022/mk-kit-ui-core.mjs.map +1 -1
  9. package/fesm2022/mk-kit-ui-data.mjs +801 -16
  10. package/fesm2022/mk-kit-ui-data.mjs.map +1 -1
  11. package/fesm2022/mk-kit-ui-datetime.mjs +90 -52
  12. package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -1
  13. package/fesm2022/mk-kit-ui-directives.mjs +395 -2
  14. package/fesm2022/mk-kit-ui-directives.mjs.map +1 -1
  15. package/fesm2022/mk-kit-ui-dnd.mjs +183 -29
  16. package/fesm2022/mk-kit-ui-dnd.mjs.map +1 -1
  17. package/fesm2022/mk-kit-ui-dynamic-form.mjs +149 -2
  18. package/fesm2022/mk-kit-ui-dynamic-form.mjs.map +1 -1
  19. package/fesm2022/mk-kit-ui-feedback.mjs +1 -1
  20. package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -1
  21. package/fesm2022/mk-kit-ui-forms.mjs +331 -81
  22. package/fesm2022/mk-kit-ui-forms.mjs.map +1 -1
  23. package/fesm2022/mk-kit-ui-icon-extended.mjs +425 -0
  24. package/fesm2022/mk-kit-ui-icon-extended.mjs.map +1 -0
  25. package/fesm2022/mk-kit-ui-icon.mjs +296 -459
  26. package/fesm2022/mk-kit-ui-icon.mjs.map +1 -1
  27. package/fesm2022/mk-kit-ui-query-builder.mjs +1 -1
  28. package/fesm2022/mk-kit-ui-query-builder.mjs.map +1 -1
  29. package/fesm2022/mk-kit-ui-rich-text.mjs +4 -2
  30. package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -1
  31. package/fesm2022/mk-kit-ui-table.mjs +36 -8
  32. package/fesm2022/mk-kit-ui-table.mjs.map +1 -1
  33. package/fesm2022/mk-kit-ui.mjs +1 -0
  34. package/fesm2022/mk-kit-ui.mjs.map +1 -1
  35. package/package.json +5 -1
  36. package/schematics/migrate-primeng/transform.js +4 -1
  37. package/schematics/ng-add/index.js +30 -2
  38. package/schematics/ng-add/schema.json +6 -0
  39. package/styles/mk-kit.css +604 -0
  40. package/types/mk-kit-ui-block-editor.d.ts +2 -0
  41. package/types/mk-kit-ui-checkbox.d.ts +2 -0
  42. package/types/mk-kit-ui-core.d.ts +119 -2
  43. package/types/mk-kit-ui-data.d.ts +359 -3
  44. package/types/mk-kit-ui-datetime.d.ts +41 -19
  45. package/types/mk-kit-ui-directives.d.ts +234 -3
  46. package/types/mk-kit-ui-dnd.d.ts +96 -14
  47. package/types/mk-kit-ui-dynamic-form.d.ts +44 -1
  48. package/types/mk-kit-ui-forms.d.ts +160 -20
  49. package/types/mk-kit-ui-icon-extended.d.ts +83 -0
  50. package/types/mk-kit-ui-icon.d.ts +100 -17
  51. package/types/mk-kit-ui-rich-text.d.ts +2 -0
  52. package/types/mk-kit-ui-table.d.ts +38 -21
  53. 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(() => {
@@ -4361,10 +4563,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
4361
4563
  *
4362
4564
  * With `reorderable`, each row gets a drag handle wired through the dnd
4363
4565
  * module's touch-safe handle-based configuration (a swipe on the row body
4364
- * still scrolls the page). Reordering also works by keyboard: focus the
4365
- * handle (or the row) and press Space/Enter to pick up, arrows to move,
4366
- * Space/Enter to drop, Escape to cancel. Each completed reorder is announced
4367
- * via {@link MkLiveAnnouncer}.
4566
+ * still scrolls the page). The handle button is the only control the dnd
4567
+ * layer adds rows stay plain list items, so the inputs and buttons inside
4568
+ * them are never nested in an interactive role. Reordering also works by
4569
+ * keyboard: focus the handle and press Space/Enter to pick up, arrows to
4570
+ * move, Space/Enter to drop, Escape to cancel. Each completed reorder is
4571
+ * announced via {@link MkLiveAnnouncer}.
4368
4572
  *
4369
4573
  * ```html
4370
4574
  * <mk-repeater [(items)]="rows" [min]="1" [max]="10" reorderable
@@ -4526,6 +4730,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
4526
4730
  */
4527
4731
  class MkTreeSelect {
4528
4732
  field = inject(MkFormField, { optional: true });
4733
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
4734
+ fieldTouched = mkInjectFieldTouched();
4529
4735
  i18n = inject(MK_I18N);
4530
4736
  host = inject(ElementRef);
4531
4737
  injector = inject(Injector);
@@ -4565,7 +4771,7 @@ class MkTreeSelect {
4565
4771
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
4566
4772
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
4567
4773
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
4568
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
4774
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
4569
4775
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
4570
4776
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
4571
4777
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -5251,7 +5457,9 @@ class MkCodeEditor {
5251
5457
  }
5252
5458
  }, /* @ts-ignore */
5253
5459
  ...(ngDevMode ? [{ debugName: "jsonError" }] : /* istanbul ignore next */ []));
5254
- 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 */
5255
5463
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5256
5464
  /** `aria-describedby` merging the field's ids with the JSON error id. */
5257
5465
  ariaDescribedBy = computed(() => {
@@ -5484,6 +5692,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5484
5692
  */
5485
5693
  class MkRating {
5486
5694
  field = inject(MkFormField, { optional: true });
5695
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
5696
+ fieldTouched = mkInjectFieldTouched();
5487
5697
  i18n = inject(MK_I18N);
5488
5698
  /** Number of stars. */
5489
5699
  max = input(5, { ...(ngDevMode ? { debugName: "max" } : /* istanbul ignore next */ {}), transform: numberAttribute });
@@ -5510,7 +5720,7 @@ class MkRating {
5510
5720
  onTouched = () => { };
5511
5721
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
5512
5722
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
5513
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
5723
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
5514
5724
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5515
5725
  /** The surrounding field's label labels this control (`aria-labelledby`). */
5516
5726
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
@@ -5674,17 +5884,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5674
5884
  */
5675
5885
  class MkNumberInput {
5676
5886
  field = inject(MkFormField, { optional: true });
5887
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
5888
+ fieldTouched = mkInjectFieldTouched();
5677
5889
  i18n = inject(MK_I18N);
5678
5890
  inputRef = viewChild('input', /* @ts-ignore */
5679
5891
  ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
5680
5892
  /** Minimum value. */
5681
5893
  /** Accessible name when the control is used without an `mk-form-field` label. */
5682
5894
  ariaLabel = input('', { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: 'aria-label' });
5683
- min = input(null, /* @ts-ignore */
5684
- ...(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 });
5685
5898
  /** Maximum value. */
5686
- max = input(null, /* @ts-ignore */
5687
- ...(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 });
5688
5902
  /** Step increment for buttons + Arrow keys. */
5689
5903
  step = input(1, { ...(ngDevMode ? { debugName: "step" } : /* istanbul ignore next */ {}), transform: numberAttribute });
5690
5904
  /** Two-way value. */
@@ -5697,6 +5911,8 @@ class MkNumberInput {
5697
5911
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5698
5912
  /** Force invalid styling when standalone. */
5699
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 });
5700
5916
  /** Control size. Ignored inside an `mk-form-field`. */
5701
5917
  size = input('md', /* @ts-ignore */
5702
5918
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -5709,9 +5925,9 @@ class MkNumberInput {
5709
5925
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
5710
5926
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
5711
5927
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
5712
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
5928
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
5713
5929
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
5714
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
5930
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
5715
5931
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
5716
5932
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
5717
5933
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -5831,7 +6047,7 @@ class MkNumberInput {
5831
6047
  this.validatorChange.register(fn);
5832
6048
  }
5833
6049
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkNumberInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
5834
- 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: [
5835
6051
  {
5836
6052
  provide: NG_VALUE_ACCESSOR,
5837
6053
  useExisting: forwardRef(() => MkNumberInput),
@@ -5864,7 +6080,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
5864
6080
  multi: true,
5865
6081
  },
5866
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"] }]
5867
- }], 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 }] }] } });
5868
6084
 
5869
6085
  /**
5870
6086
  * Numeric keypad — a touch-first 3×4 digit pad for PIN entry, quantity entry
@@ -5898,11 +6114,13 @@ class MkNumericKeypad {
5898
6114
  /** PIN length (`pin` mode only). Reaching it emits `submit`. */
5899
6115
  length = input(4, { ...(ngDevMode ? { debugName: "length" } : /* istanbul ignore next */ {}), transform: numberAttribute });
5900
6116
  /** Lower bound reported through the validator (`quantity`/`amount`). */
5901
- min = input(null, /* @ts-ignore */
5902
- ...(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 });
5903
6120
  /** Upper bound reported through the validator (`quantity`/`amount`). */
5904
- max = input(null, /* @ts-ignore */
5905
- ...(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 });
5906
6124
  /** Mask the echo row in `pin` mode. Default `true`. */
5907
6125
  mask = input(true, { ...(ngDevMode ? { debugName: "mask" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
5908
6126
  /** Show the echo row above the keys. Default `true`. */
@@ -6446,6 +6664,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
6446
6664
  */
6447
6665
  class MkOtp {
6448
6666
  field = inject(MkFormField, { optional: true });
6667
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
6668
+ fieldTouched = mkInjectFieldTouched();
6449
6669
  i18n = inject(MK_I18N);
6450
6670
  cells = viewChildren('cell', /* @ts-ignore */
6451
6671
  ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
@@ -6482,7 +6702,7 @@ class MkOtp {
6482
6702
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
6483
6703
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
6484
6704
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
6485
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
6705
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
6486
6706
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
6487
6707
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
6488
6708
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -6762,6 +6982,8 @@ function mkCountryFlag(code) {
6762
6982
  */
6763
6983
  class MkPhoneInput {
6764
6984
  field = inject(MkFormField, { optional: true });
6985
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
6986
+ fieldTouched = mkInjectFieldTouched();
6765
6987
  /** Localised strings (override globally via `provideMkI18n`). */
6766
6988
  i18n = inject(MK_I18N);
6767
6989
  triggerRef = viewChild('trigger', /* @ts-ignore */
@@ -6825,7 +7047,7 @@ class MkPhoneInput {
6825
7047
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
6826
7048
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
6827
7049
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
6828
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
7050
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
6829
7051
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
6830
7052
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
6831
7053
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -7243,6 +7465,8 @@ function mkPostalCodeValidator(country, formats = MK_POSTAL_FORMATS) {
7243
7465
  */
7244
7466
  class MkPostalCodeInput {
7245
7467
  field = inject(MkFormField, { optional: true });
7468
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
7469
+ fieldTouched = mkInjectFieldTouched();
7246
7470
  /** Localised strings (override globally via `provideMkI18n`). */
7247
7471
  i18n = inject(MK_I18N);
7248
7472
  /** ISO 3166-1 alpha-2 country whose format applies. */
@@ -7299,7 +7523,7 @@ class MkPostalCodeInput {
7299
7523
  return fmt.pattern.test(v);
7300
7524
  }, /* @ts-ignore */
7301
7525
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
7302
- isInvalid = computed(() => this.invalid() ||
7526
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
7303
7527
  (this.field?.hasError() ?? false) ||
7304
7528
  (this.touched() && this.valid() === false), /* @ts-ignore */
7305
7529
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -7451,6 +7675,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
7451
7675
  */
7452
7676
  class MkCurrencyInput {
7453
7677
  field = inject(MkFormField, { optional: true });
7678
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
7679
+ fieldTouched = mkInjectFieldTouched();
7454
7680
  /** Localised strings (override globally via `provideMkI18n`). */
7455
7681
  i18n = inject(MK_I18N);
7456
7682
  /** ISO 4217 currency code (`PLN`, `USD`, …). Empty = plain decimal. */
@@ -7465,11 +7691,13 @@ class MkCurrencyInput {
7465
7691
  decimals = input(null, /* @ts-ignore */
7466
7692
  ...(ngDevMode ? [{ debugName: "decimals" }] : /* istanbul ignore next */ []));
7467
7693
  /** Clamp the value to at least this on blur. */
7468
- min = input(null, /* @ts-ignore */
7469
- ...(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 });
7470
7697
  /** Clamp the value to at most this on blur. */
7471
- max = input(null, /* @ts-ignore */
7472
- ...(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 });
7473
7701
  /** Allow a leading minus sign. */
7474
7702
  allowNegative = input(true, { ...(ngDevMode ? { debugName: "allowNegative" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
7475
7703
  /** Control size. Ignored when nested in an `mk-form-field`. */
@@ -7499,7 +7727,7 @@ class MkCurrencyInput {
7499
7727
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
7500
7728
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
7501
7729
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
7502
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
7730
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
7503
7731
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
7504
7732
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
7505
7733
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -7817,6 +8045,8 @@ const DEFAULT_MASK = '0000 0000 0000 0000';
7817
8045
  */
7818
8046
  class MkCardNumberInput {
7819
8047
  field = inject(MkFormField, { optional: true });
8048
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8049
+ fieldTouched = mkInjectFieldTouched();
7820
8050
  /** Localised strings (override globally via `provideMkI18n`). */
7821
8051
  i18n = inject(MK_I18N);
7822
8052
  /** Show the detected brand as a badge inside the field. */
@@ -7888,7 +8118,7 @@ class MkCardNumberInput {
7888
8118
  return mkLuhnCheck(digits);
7889
8119
  }, /* @ts-ignore */
7890
8120
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
7891
- isInvalid = computed(() => this.invalid() ||
8121
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
7892
8122
  (this.field?.hasError() ?? false) ||
7893
8123
  (this.touched() && this.valid() === false), /* @ts-ignore */
7894
8124
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8050,6 +8280,8 @@ function mkIbanValidator() {
8050
8280
  */
8051
8281
  class MkIbanInput {
8052
8282
  field = inject(MkFormField, { optional: true });
8283
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8284
+ fieldTouched = mkInjectFieldTouched();
8053
8285
  /** Localised strings (override globally via `provideMkI18n`). */
8054
8286
  i18n = inject(MK_I18N);
8055
8287
  /** Control size. Ignored when nested in an `mk-form-field`. */
@@ -8108,7 +8340,7 @@ class MkIbanInput {
8108
8340
  return mkIbanChecksum(compact);
8109
8341
  }, /* @ts-ignore */
8110
8342
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
8111
- isInvalid = computed(() => this.invalid() ||
8343
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
8112
8344
  (this.field?.hasError() ?? false) ||
8113
8345
  (this.touched() && this.valid() === false), /* @ts-ignore */
8114
8346
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8327,6 +8559,8 @@ function mkTaxIdValidator(country) {
8327
8559
  */
8328
8560
  class MkTaxIdInput {
8329
8561
  field = inject(MkFormField, { optional: true });
8562
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8563
+ fieldTouched = mkInjectFieldTouched();
8330
8564
  /** Localised strings (override globally via `provideMkI18n`). */
8331
8565
  i18n = inject(MK_I18N);
8332
8566
  /** ISO 3166-1 alpha-2 country whose format applies. */
@@ -8391,7 +8625,7 @@ class MkTaxIdInput {
8391
8625
  return compact.length < digits ? null : false;
8392
8626
  }, /* @ts-ignore */
8393
8627
  ...(ngDevMode ? [{ debugName: "valid" }] : /* istanbul ignore next */ []));
8394
- isInvalid = computed(() => this.invalid() ||
8628
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) ||
8395
8629
  (this.field?.hasError() ?? false) ||
8396
8630
  (this.touched() && this.valid() === false), /* @ts-ignore */
8397
8631
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
@@ -8519,6 +8753,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8519
8753
  class MkSignaturePad {
8520
8754
  host = inject(ElementRef);
8521
8755
  field = inject(MkFormField, { optional: true });
8756
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
8757
+ fieldTouched = mkInjectFieldTouched();
8522
8758
  isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
8523
8759
  /** Localised strings (override globally via `provideMkI18n`). */
8524
8760
  i18n = inject(MK_I18N);
@@ -8565,7 +8801,7 @@ class MkSignaturePad {
8565
8801
  padId = this.field?.controlId ?? mkUniqueId('mk-signature');
8566
8802
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
8567
8803
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
8568
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
8804
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
8569
8805
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
8570
8806
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
8571
8807
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -8923,6 +9159,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8923
9159
  class MkRadioGroup {
8924
9160
  host = inject(ElementRef);
8925
9161
  field = inject(MkFormField, { optional: true });
9162
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9163
+ fieldTouched = mkInjectFieldTouched();
8926
9164
  /** Two-way selected value. */
8927
9165
  value = model(null, /* @ts-ignore */
8928
9166
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
@@ -8954,7 +9192,7 @@ class MkRadioGroup {
8954
9192
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
8955
9193
  isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
8956
9194
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
8957
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9195
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
8958
9196
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
8959
9197
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
8960
9198
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
@@ -9182,6 +9420,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9182
9420
  */
9183
9421
  class MkSwitch {
9184
9422
  field = inject(MkFormField, { optional: true });
9423
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9424
+ fieldTouched = mkInjectFieldTouched();
9185
9425
  /** Two-way checked (on/off) state. */
9186
9426
  checked = model(false, /* @ts-ignore */
9187
9427
  ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
@@ -9189,6 +9429,8 @@ class MkSwitch {
9189
9429
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9190
9430
  /** Force the invalid visual + `aria-invalid` when used standalone. */
9191
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 });
9192
9434
  /** Control size. */
9193
9435
  size = input('md', /* @ts-ignore */
9194
9436
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -9204,9 +9446,9 @@ class MkSwitch {
9204
9446
  labelId = mkUniqueId('mk-switch-label');
9205
9447
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9206
9448
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9207
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9449
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9208
9450
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9209
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9451
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
9210
9452
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
9211
9453
  describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
9212
9454
  ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
@@ -9244,7 +9486,7 @@ class MkSwitch {
9244
9486
  this.cvaDisabled.set(isDisabled);
9245
9487
  }
9246
9488
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSwitch, deps: [], target: i0.ɵɵFactoryTarget.Component });
9247
- 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: [
9248
9490
  {
9249
9491
  provide: NG_VALUE_ACCESSOR,
9250
9492
  useExisting: forwardRef(() => MkSwitch),
@@ -9269,7 +9511,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9269
9511
  multi: true,
9270
9512
  },
9271
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"] }]
9272
- }], 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 }] }] } });
9273
9515
 
9274
9516
  /**
9275
9517
  * Slider — a single-value range slider with `role="slider"`, a filled track and
@@ -9285,6 +9527,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9285
9527
  */
9286
9528
  class MkSlider {
9287
9529
  field = inject(MkFormField, { optional: true });
9530
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9531
+ fieldTouched = mkInjectFieldTouched();
9288
9532
  trackRef = viewChild('track', /* @ts-ignore */
9289
9533
  ...(ngDevMode ? [{ debugName: "trackRef" }] : /* istanbul ignore next */ []));
9290
9534
  thumbRef = viewChild('thumb', /* @ts-ignore */
@@ -9299,6 +9543,8 @@ class MkSlider {
9299
9543
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9300
9544
  /** Force the invalid visual + `aria-invalid` when used standalone. */
9301
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 });
9302
9548
  /** Control size (track/thumb thickness). */
9303
9549
  size = input('md', /* @ts-ignore */
9304
9550
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
@@ -9323,9 +9569,9 @@ class MkSlider {
9323
9569
  onTouched = () => { };
9324
9570
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9325
9571
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9326
- isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9572
+ isRequired = computed(() => this.required() || (this.field?.isRequired() ?? false), /* @ts-ignore */
9327
9573
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
9328
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9574
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9329
9575
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9330
9576
  labelledBy = computed(() => {
9331
9577
  if (this.ariaLabel())
@@ -9522,7 +9768,7 @@ class MkSlider {
9522
9768
  this.validatorChange.register(fn);
9523
9769
  }
9524
9770
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
9525
- 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: [
9526
9772
  {
9527
9773
  provide: NG_VALUE_ACCESSOR,
9528
9774
  useExisting: forwardRef(() => MkSlider),
@@ -9556,7 +9802,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9556
9802
  multi: true,
9557
9803
  },
9558
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"] }]
9559
- }], 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"] }] } });
9560
9806
 
9561
9807
  /** Whether `value` is a valid `#rgb` / `#rrggbb` hex color. */
9562
9808
  function isHex(value) {
@@ -9581,6 +9827,8 @@ function toLongHex(value) {
9581
9827
  */
9582
9828
  class MkColorPicker {
9583
9829
  field = inject(MkFormField, { optional: true });
9830
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9831
+ fieldTouched = mkInjectFieldTouched();
9584
9832
  i18n = inject(MK_I18N);
9585
9833
  nativeRef = viewChild('native', /* @ts-ignore */
9586
9834
  ...(ngDevMode ? [{ debugName: "nativeRef" }] : /* istanbul ignore next */ []));
@@ -9618,7 +9866,7 @@ class MkColorPicker {
9618
9866
  ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
9619
9867
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9620
9868
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9621
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
9869
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9622
9870
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9623
9871
  isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
9624
9872
  ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
@@ -9715,6 +9963,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9715
9963
  class MkRangeSlider {
9716
9964
  host = inject(ElementRef);
9717
9965
  field = inject(MkFormField, { optional: true });
9966
+ /** Signal Forms: gates `invalid` until the bound field is touched or dirty. */
9967
+ fieldTouched = mkInjectFieldTouched();
9718
9968
  i18n = inject(MK_I18N);
9719
9969
  trackRef = viewChildren('track', /* @ts-ignore */
9720
9970
  ...(ngDevMode ? [{ debugName: "trackRef" }] : /* istanbul ignore next */ []));
@@ -9762,7 +10012,7 @@ class MkRangeSlider {
9762
10012
  ...(ngDevMode ? [{ debugName: "high" }] : /* istanbul ignore next */ []));
9763
10013
  isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
9764
10014
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
9765
- isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
10015
+ isInvalid = computed(() => (this.invalid() && this.fieldTouched()) || (this.field?.hasError() ?? false), /* @ts-ignore */
9766
10016
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
9767
10017
  labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
9768
10018
  ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));