@mk-kit/ui 0.36.0 → 0.38.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, input, booleanAttribute, contentChild, signal, effect, DestroyRef, computed, forwardRef, ChangeDetectionStrategy, Component, DOCUMENT, ElementRef, Injector, afterNextRender, viewChild, model, output, contentChildren, numberAttribute, untracked, ViewContainerRef, Directive, viewChildren, TemplateRef, PLATFORM_ID } 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';
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
5
  import { MK_I18N, mkUniqueId, mkFirstErrorMessage, MkFieldContext, MkAnchoredPanel, MkLiveAnnouncer, mkValidatorChange, mkHighlight } from '@mk-kit/ui/core';
@@ -95,8 +95,42 @@ class MkFormField {
95
95
  /** Control size; nested controls inherit it. */
96
96
  size = input('md', /* @ts-ignore */
97
97
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
98
+ /**
99
+ * Where the label sits: above the control (default), or `float` — inside
100
+ * the control, sliding up once it is focused or has a value. Floating works
101
+ * with `mkInput` / textarea and any control bound with `ngModel` or a
102
+ * `formControl`; the placeholder stays hidden until the label has moved.
103
+ */
104
+ labelPosition = input('top', /* @ts-ignore */
105
+ ...(ngDevMode ? [{ debugName: "labelPosition" }] : /* istanbul ignore next */ []));
106
+ host = inject(ElementRef);
98
107
  /** The projected control's form binding, when it has one. */
99
108
  ngControl = contentChild(NgControl, { ...(ngDevMode ? { debugName: "ngControl" } : /* istanbul ignore next */ {}), descendants: true });
109
+ /** A control inside the field has focus. */
110
+ focused = signal(false, /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
112
+ /** Last value read straight from a native control (fallback when no NgControl). */
113
+ domValue = signal(null, /* @ts-ignore */
114
+ ...(ngDevMode ? [{ debugName: "domValue" }] : /* istanbul ignore next */ []));
115
+ /** The field holds a non-empty value (drives the floating label). */
116
+ hasValue = computed(() => {
117
+ this.controlTick();
118
+ const control = this.ngControl();
119
+ const v = control ? control.value : this.domValue();
120
+ if (v == null || v === '')
121
+ return false;
122
+ if (Array.isArray(v))
123
+ return v.length > 0;
124
+ return true;
125
+ }, /* @ts-ignore */
126
+ ...(ngDevMode ? [{ debugName: "hasValue" }] : /* istanbul ignore next */ []));
127
+ /** Track a native control's value for the floating label when nothing else reports it. */
128
+ readDomValue(target) {
129
+ const el = target;
130
+ if (!el || typeof el.value !== 'string')
131
+ return;
132
+ this.domValue.set(el.value);
133
+ }
100
134
  /**
101
135
  * Bumped on every change of the bound control so the computed state below
102
136
  * re-reads it. `AbstractControl` is not signal-based, so this bridges its
@@ -113,6 +147,11 @@ class MkFormField {
113
147
  /** Id of the error element. */
114
148
  errorId = `${this.controlId}-error`;
115
149
  constructor() {
150
+ afterNextRender(() => {
151
+ const el = this.host.nativeElement.querySelector('input, textarea, select');
152
+ if (el)
153
+ this.readDomValue(el);
154
+ });
116
155
  const bump = () => this.controlTick.update((n) => n + 1);
117
156
  let sub = null;
118
157
  // `AbstractControl.events` emits on value, status, pristine and touched
@@ -190,12 +229,12 @@ class MkFormField {
190
229
  }, /* @ts-ignore */
191
230
  ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
192
231
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormField, deps: [], target: i0.ɵɵFactoryTarget.Component });
193
- 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 } }, host: { 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()" }, classAttribute: "mk-form-field" }, providers: [
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: [
194
233
  {
195
234
  provide: MkFieldContext,
196
235
  useExisting: forwardRef(() => MkFormField),
197
236
  },
198
- ], queries: [{ propertyName: "ngControl", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\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</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}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
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 });
199
238
  }
200
239
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkFormField, decorators: [{
201
240
  type: Component,
@@ -207,13 +246,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
207
246
  '[class.mk-form-field--invalid]': 'hasError()',
208
247
  '[class.mk-form-field--required]': 'isRequired()',
209
248
  '[class.mk-form-field--disabled]': 'isDisabled()',
249
+ '[class.mk-form-field--float]': "labelPosition() === 'float'",
250
+ '[class.mk-form-field--focused]': 'focused()',
251
+ '[class.mk-form-field--filled]': 'hasValue()',
252
+ '(focusin)': 'focused.set(true)',
253
+ '(focusout)': 'focused.set(false)',
254
+ '(input)': 'readDomValue($event.target)',
255
+ '(change)': 'readDomValue($event.target)',
210
256
  }, providers: [
211
257
  {
212
258
  provide: MkFieldContext,
213
259
  useExisting: forwardRef(() => MkFormField),
214
260
  },
215
- ], template: "@if (label()) {\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</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}\n"] }]
216
- }], 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 }] }], ngControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { ...{ descendants: true }, isSignal: true }] }] } });
261
+ ], 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 }] }] } });
217
263
 
218
264
  /**
219
265
  * FormErrorSummary — an accessible summary of a form's validation errors,
@@ -1436,6 +1482,770 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
1436
1482
  ], 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-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"] }]
1437
1483
  }], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], 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"] }] } });
1438
1484
 
1485
+ /**
1486
+ * Listbox — a standalone, always-visible selection list (the ARIA `listbox`
1487
+ * pattern) for one or many values: settings panes, "choose a template"
1488
+ * pickers, side-by-side assignments, any place a dropdown would hide choices
1489
+ * the user should see at once.
1490
+ *
1491
+ * Keyboard: Up / Down move, Home / End jump, PageUp / PageDown skip ten,
1492
+ * type to jump to a label, Enter / Space select (toggle when `multiple`),
1493
+ * Shift+Arrow / Shift+click extend a range, Ctrl/Cmd+A selects all. Focus
1494
+ * stays on the list (`aria-activedescendant`). With `filterable`, a search
1495
+ * box above the list narrows it by label or description.
1496
+ *
1497
+ * Implements `ControlValueAccessor` and a two-way `value` model — a single
1498
+ * value, or an array when `multiple`.
1499
+ *
1500
+ * ```html
1501
+ * <mk-listbox [options]="plans" [(value)]="plan" ariaLabel="Plan" />
1502
+ * <mk-listbox multiple filterable [options]="people" [(ngModel)]="reviewers" />
1503
+ * ```
1504
+ */
1505
+ class MkListbox {
1506
+ field = inject(MkFormField, { optional: true });
1507
+ i18n = inject(MK_I18N);
1508
+ listRef = viewChild.required('list', /* @ts-ignore */
1509
+ ...(ngDevMode ? [{ debugName: "listRef" }] : /* istanbul ignore next */ []));
1510
+ /** The rows to choose from. */
1511
+ options = input([], /* @ts-ignore */
1512
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1513
+ /** Allow several values; the model becomes an array. */
1514
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1515
+ /** Show a search box that narrows the list by label / description. */
1516
+ filterable = input(false, { ...(ngDevMode ? { debugName: "filterable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1517
+ filterPlaceholder = input(undefined, /* @ts-ignore */
1518
+ ...(ngDevMode ? [{ debugName: "filterPlaceholder" }] : /* istanbul ignore next */ []));
1519
+ /**
1520
+ * Single-select only: moving with the arrow keys selects immediately
1521
+ * (default `true`); `false` requires Enter / Space to commit.
1522
+ */
1523
+ selectionFollowsFocus = input(true, { ...(ngDevMode ? { debugName: "selectionFollowsFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1524
+ /** Control size. Ignored when nested in an `mk-form-field`. */
1525
+ size = input('md', /* @ts-ignore */
1526
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1527
+ invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1528
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1529
+ /** Accessible name when there is no visible label / form field. */
1530
+ ariaLabel = input(undefined, /* @ts-ignore */
1531
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1532
+ ariaLabelledby = input(undefined, /* @ts-ignore */
1533
+ ...(ngDevMode ? [{ debugName: "ariaLabelledby" }] : /* istanbul ignore next */ []));
1534
+ /** Text shown when no option matches the filter. */
1535
+ emptyText = input(undefined, /* @ts-ignore */
1536
+ ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
1537
+ /** Two-way value: one value, or an array when `multiple`. */
1538
+ value = model(null, /* @ts-ignore */
1539
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1540
+ /** Emitted after the user changes the selection (not on programmatic writes). */
1541
+ change = output();
1542
+ listId = this.field?.controlId ?? mkUniqueId('mk-listbox');
1543
+ filterId = mkUniqueId('mk-listbox-filter');
1544
+ query = signal('', /* @ts-ignore */
1545
+ ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
1546
+ activeIndex = signal(-1, /* @ts-ignore */
1547
+ ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
1548
+ cvaDisabled = signal(false, /* @ts-ignore */
1549
+ ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
1550
+ onChange = () => { };
1551
+ onTouched = () => { };
1552
+ typeahead = '';
1553
+ typeaheadTimer;
1554
+ /** Range anchor for Shift selection (index in the visible list). */
1555
+ anchor = -1;
1556
+ effectiveSize = computed(() => (this.field ? this.field.size() : this.size()), /* @ts-ignore */
1557
+ ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
1558
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
1559
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1560
+ isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
1561
+ ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1562
+ labelledBy = computed(() => this.ariaLabelledby() ?? this.field?.labelId ?? null, /* @ts-ignore */
1563
+ ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
1564
+ describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
1565
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
1566
+ /** Options that match the filter, in source order. */
1567
+ visible = computed(() => {
1568
+ const q = this.query().trim().toLocaleLowerCase();
1569
+ const all = this.options();
1570
+ if (!q)
1571
+ return [...all];
1572
+ return all.filter((o) => o.label.toLocaleLowerCase().includes(q) || (o.description?.toLocaleLowerCase().includes(q) ?? false));
1573
+ }, /* @ts-ignore */
1574
+ ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
1575
+ /** Visible options with group headings interleaved. */
1576
+ rows = computed(() => {
1577
+ const out = [];
1578
+ let lastGroup;
1579
+ this.visible().forEach((option, index) => {
1580
+ if (option.group && option.group !== lastGroup) {
1581
+ out.push({ kind: 'group', label: option.group, id: `${this.listId}-grp-${out.length}` });
1582
+ }
1583
+ lastGroup = option.group;
1584
+ out.push({ kind: 'option', option, index });
1585
+ });
1586
+ return out;
1587
+ }, /* @ts-ignore */
1588
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
1589
+ selectedValues = computed(() => {
1590
+ const v = this.value();
1591
+ if (this.multiple())
1592
+ return Array.isArray(v) ? v : v == null ? [] : [v];
1593
+ return v == null ? [] : [v];
1594
+ }, /* @ts-ignore */
1595
+ ...(ngDevMode ? [{ debugName: "selectedValues" }] : /* istanbul ignore next */ []));
1596
+ activeId = computed(() => (this.activeIndex() >= 0 ? this.optionId(this.activeIndex()) : null), /* @ts-ignore */
1597
+ ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
1598
+ constructor() {
1599
+ // Keep the active row in view as it changes.
1600
+ afterRenderEffect(() => {
1601
+ const i = this.activeIndex();
1602
+ if (i < 0)
1603
+ return;
1604
+ const el = this.listRef().nativeElement.ownerDocument.getElementById(this.optionId(i));
1605
+ el?.scrollIntoView?.({ block: 'nearest' });
1606
+ });
1607
+ }
1608
+ optionId(index) {
1609
+ return `${this.listId}-opt-${index}`;
1610
+ }
1611
+ isSelected(option) {
1612
+ return this.selectedValues().includes(option.value);
1613
+ }
1614
+ /** Focus the list. */
1615
+ focus() {
1616
+ this.listRef().nativeElement.focus();
1617
+ }
1618
+ // --- Pointer -----------------------------------------------------------------
1619
+ onOptionClick(index, event) {
1620
+ if (this.isDisabled())
1621
+ return;
1622
+ const option = this.visible()[index];
1623
+ if (!option || option.disabled)
1624
+ return;
1625
+ this.activeIndex.set(index);
1626
+ if (this.multiple() && event.shiftKey && this.anchor >= 0) {
1627
+ this.selectRange(this.anchor, index, !(event.ctrlKey || event.metaKey));
1628
+ }
1629
+ else {
1630
+ this.commit(index, this.multiple() ? 'toggle' : 'select');
1631
+ this.anchor = index;
1632
+ }
1633
+ }
1634
+ onFilterInput(event) {
1635
+ this.query.set(event.target.value);
1636
+ this.activeIndex.set(-1);
1637
+ this.anchor = -1;
1638
+ }
1639
+ onFilterKeydown(event) {
1640
+ // Arrow keys from the search box hand over to the list.
1641
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
1642
+ event.preventDefault();
1643
+ this.focus();
1644
+ this.jump(event.key === 'ArrowDown' ? this.firstEnabled() : this.lastEnabled(), false);
1645
+ }
1646
+ }
1647
+ // --- Keyboard ----------------------------------------------------------------
1648
+ onKeydown(event) {
1649
+ if (this.isDisabled())
1650
+ return;
1651
+ const key = event.key;
1652
+ const multi = this.multiple();
1653
+ switch (key) {
1654
+ case 'ArrowDown':
1655
+ case 'ArrowUp':
1656
+ event.preventDefault();
1657
+ this.step(key === 'ArrowDown' ? 1 : -1, event.shiftKey);
1658
+ break;
1659
+ case 'Home':
1660
+ case 'End':
1661
+ event.preventDefault();
1662
+ this.jump(key === 'Home' ? this.firstEnabled() : this.lastEnabled(), event.shiftKey);
1663
+ break;
1664
+ case 'PageDown':
1665
+ case 'PageUp':
1666
+ event.preventDefault();
1667
+ this.step(key === 'PageDown' ? 10 : -10, event.shiftKey, false);
1668
+ break;
1669
+ case 'Enter':
1670
+ case ' ':
1671
+ event.preventDefault();
1672
+ if (this.activeIndex() >= 0) {
1673
+ this.commit(this.activeIndex(), multi ? 'toggle' : 'select');
1674
+ this.anchor = this.activeIndex();
1675
+ }
1676
+ break;
1677
+ case 'a':
1678
+ case 'A':
1679
+ if (multi && (event.ctrlKey || event.metaKey)) {
1680
+ event.preventDefault();
1681
+ this.selectAll();
1682
+ }
1683
+ else if (key.length === 1)
1684
+ this.typeaheadSearch(key);
1685
+ break;
1686
+ default:
1687
+ if (key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey)
1688
+ this.typeaheadSearch(key);
1689
+ }
1690
+ }
1691
+ onBlur() {
1692
+ this.onTouched();
1693
+ }
1694
+ /** Move the active row by `delta` (wrapping for ±1), extending the selection with Shift. */
1695
+ step(delta, extend, wrap = true) {
1696
+ const opts = this.visible();
1697
+ if (!opts.length)
1698
+ return;
1699
+ let i = this.activeIndex();
1700
+ if (i < 0) {
1701
+ this.jump(delta > 0 ? this.firstEnabled() : this.lastEnabled(), extend);
1702
+ return;
1703
+ }
1704
+ if (Math.abs(delta) === 1) {
1705
+ for (let s = 0; s < opts.length; s++) {
1706
+ i = wrap ? (i + delta + opts.length) % opts.length : Math.min(Math.max(i + delta, 0), opts.length - 1);
1707
+ if (!opts[i].disabled)
1708
+ break;
1709
+ }
1710
+ }
1711
+ else {
1712
+ i = Math.min(Math.max(i + delta, 0), opts.length - 1);
1713
+ // Land on an enabled row, searching in the direction of travel then back.
1714
+ let j = i;
1715
+ while (j >= 0 && j < opts.length && opts[j].disabled)
1716
+ j += Math.sign(delta);
1717
+ if (j < 0 || j >= opts.length) {
1718
+ j = i;
1719
+ while (j >= 0 && j < opts.length && opts[j].disabled)
1720
+ j -= Math.sign(delta);
1721
+ }
1722
+ i = j;
1723
+ }
1724
+ this.jump(i, extend);
1725
+ }
1726
+ jump(index, extend) {
1727
+ if (index < 0)
1728
+ return;
1729
+ this.activeIndex.set(index);
1730
+ if (this.multiple()) {
1731
+ if (extend) {
1732
+ if (this.anchor < 0)
1733
+ this.anchor = index;
1734
+ this.selectRange(this.anchor, index, true);
1735
+ }
1736
+ else {
1737
+ // Plain navigation moves the range anchor along with the cursor.
1738
+ this.anchor = index;
1739
+ }
1740
+ }
1741
+ else if (this.selectionFollowsFocus()) {
1742
+ this.commit(index, 'select');
1743
+ }
1744
+ }
1745
+ firstEnabled() {
1746
+ return this.visible().findIndex((o) => !o.disabled);
1747
+ }
1748
+ lastEnabled() {
1749
+ const opts = this.visible();
1750
+ for (let i = opts.length - 1; i >= 0; i--)
1751
+ if (!opts[i].disabled)
1752
+ return i;
1753
+ return -1;
1754
+ }
1755
+ typeaheadSearch(char) {
1756
+ this.typeahead += char.toLocaleLowerCase();
1757
+ if (this.typeaheadTimer)
1758
+ clearTimeout(this.typeaheadTimer);
1759
+ this.typeaheadTimer = setTimeout(() => (this.typeahead = ''), 500);
1760
+ const opts = this.visible();
1761
+ const start = this.activeIndex() + 1;
1762
+ for (let s = 0; s < opts.length; s++) {
1763
+ const i = (start + s) % opts.length;
1764
+ if (!opts[i].disabled && opts[i].label.toLocaleLowerCase().startsWith(this.typeahead)) {
1765
+ this.jump(i, false);
1766
+ return;
1767
+ }
1768
+ }
1769
+ }
1770
+ // --- Selection ---------------------------------------------------------------
1771
+ commit(index, mode) {
1772
+ const option = this.visible()[index];
1773
+ if (!option || option.disabled)
1774
+ return;
1775
+ if (!this.multiple()) {
1776
+ if (this.value() === option.value)
1777
+ return;
1778
+ this.emit(option.value);
1779
+ return;
1780
+ }
1781
+ const current = this.selectedValues();
1782
+ const next = mode === 'toggle' && current.includes(option.value)
1783
+ ? current.filter((v) => v !== option.value)
1784
+ : current.includes(option.value)
1785
+ ? current
1786
+ : [...current, option.value];
1787
+ this.emit(next);
1788
+ }
1789
+ /** Select every enabled option between two visible indexes (replacing, or adding to, the selection). */
1790
+ selectRange(from, to, replace) {
1791
+ const opts = this.visible();
1792
+ const [lo, hi] = from < to ? [from, to] : [to, from];
1793
+ const range = opts.slice(lo, hi + 1).filter((o) => !o.disabled).map((o) => o.value);
1794
+ const base = replace ? [] : this.selectedValues();
1795
+ this.emit([...base, ...range.filter((v) => !base.includes(v))]);
1796
+ }
1797
+ selectAll() {
1798
+ const all = this.visible().filter((o) => !o.disabled).map((o) => o.value);
1799
+ const current = this.selectedValues();
1800
+ // Ctrl+A toggles: everything selected → clear, else select all.
1801
+ this.emit(all.every((v) => current.includes(v)) ? [] : all);
1802
+ }
1803
+ emit(value) {
1804
+ this.value.set(value);
1805
+ this.onChange(value);
1806
+ this.change.emit(value);
1807
+ }
1808
+ ngOnDestroy() {
1809
+ if (this.typeaheadTimer)
1810
+ clearTimeout(this.typeaheadTimer);
1811
+ }
1812
+ // --- ControlValueAccessor ----------------------------------------------------
1813
+ writeValue(value) {
1814
+ this.value.set(value);
1815
+ }
1816
+ registerOnChange(fn) {
1817
+ this.onChange = fn;
1818
+ }
1819
+ registerOnTouched(fn) {
1820
+ this.onTouched = fn;
1821
+ }
1822
+ setDisabledState(isDisabled) {
1823
+ this.cvaDisabled.set(isDisabled);
1824
+ }
1825
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkListbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
1826
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkListbox, isStandalone: true, selector: "mk-listbox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, filterPlaceholder: { classPropertyName: "filterPlaceholder", publicName: "filterPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, selectionFollowsFocus: { classPropertyName: "selectionFollowsFocus", publicName: "selectionFollowsFocus", 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 }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledby: { classPropertyName: "ariaLabelledby", publicName: "ariaLabelledby", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", change: "change" }, host: { properties: { "class.mk-listbox--sm": "effectiveSize() === 'sm'", "class.mk-listbox--md": "effectiveSize() === 'md'", "class.mk-listbox--lg": "effectiveSize() === 'lg'", "class.mk-listbox--multiple": "multiple()", "class.mk-listbox--invalid": "isInvalid()", "class.mk-listbox--disabled": "isDisabled()" }, classAttribute: "mk-listbox" }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MkListbox), multi: true }], viewQueries: [{ propertyName: "listRef", first: true, predicate: ["list"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (filterable()) {\n <div class=\"mk-listbox__filter\">\n <input\n class=\"mk-listbox__filter-input\"\n type=\"search\"\n [id]=\"filterId\"\n [value]=\"query()\"\n [placeholder]=\"filterPlaceholder() ?? i18n.listboxFilter\"\n [disabled]=\"isDisabled()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-label]=\"i18n.listboxFilter\"\n autocomplete=\"off\"\n (input)=\"onFilterInput($event)\"\n (keydown)=\"onFilterKeydown($event)\"\n />\n </div>\n}\n\n<ul\n #list\n class=\"mk-listbox__list\"\n role=\"listbox\"\n [id]=\"listId\"\n [tabindex]=\"isDisabled() ? -1 : 0\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n [attr.aria-activedescendant]=\"activeId()\"\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-disabled]=\"isDisabled() || null\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n>\n @for (row of rows(); track row.kind === 'group' ? row.id : row.option.value) {\n @if (row.kind === 'group') {\n <li class=\"mk-listbox__group\" role=\"presentation\" [id]=\"row.id\">{{ row.label }}</li>\n } @else {\n @let selected = isSelected(row.option);\n <li\n class=\"mk-listbox__option\"\n role=\"option\"\n [id]=\"optionId(row.index)\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"row.option.disabled || null\"\n [class.mk-listbox__option--active]=\"row.index === activeIndex()\"\n [class.mk-listbox__option--selected]=\"selected\"\n [class.mk-listbox__option--disabled]=\"row.option.disabled\"\n (click)=\"onOptionClick(row.index, $event)\"\n >\n @if (multiple()) {\n <span class=\"mk-listbox__box\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" focusable=\"false\"><path d=\"M2.5 6.5l2.5 2.5 4.5-5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /></svg>\n </span>\n }\n <span class=\"mk-listbox__text\">\n <span class=\"mk-listbox__label\">{{ row.option.label }}</span>\n @if (row.option.description) {\n <span class=\"mk-listbox__description\">{{ row.option.description }}</span>\n }\n </span>\n @if (!multiple() && selected) {\n <span class=\"mk-listbox__check\" aria-hidden=\"true\"></span>\n }\n </li>\n }\n }\n @if (!rows().length) {\n <li class=\"mk-listbox__empty\" role=\"presentation\">{{ emptyText() ?? i18n.listboxEmpty }}</li>\n }\n</ul>\n", styles: [":host{--_pad-y: var(--mk-space-2);--_pad-x: var(--mk-space-3);--_font: var(--mk-font-size-md);display:flex;flex-direction:column;min-width:0;border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);background:var(--mk-surface);color:var(--mk-text);font-family:var(--mk-font-sans);overflow:hidden}:host(.mk-listbox--sm){--_pad-y: var(--mk-space-1);--_pad-x: var(--mk-space-2);--_font: var(--mk-font-size-sm)}:host(.mk-listbox--lg){--_pad-y: var(--mk-space-3);--_pad-x: var(--mk-space-4);--_font: var(--mk-font-size-lg)}:host(:focus-within){border-color:var(--mk-primary);box-shadow:0 0 0 var(--mk-focus-ring-width) var(--mk-focus-ring, var(--mk-primary-subtle))}:host(.mk-listbox--invalid){border-color:var(--mk-danger)}:host(.mk-listbox--disabled){opacity:.6;cursor:not-allowed}.mk-listbox__filter{flex:none;padding:var(--mk-space-2);border-bottom:var(--mk-border-width) solid var(--mk-border)}.mk-listbox__filter-input{width:100%;padding:var(--_pad-y) var(--_pad-x);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-sm);background:var(--mk-surface);color:inherit;font:inherit;font-size:var(--_font)}.mk-listbox__filter-input:focus-visible{outline:none;border-color:var(--mk-primary)}.mk-listbox__filter-input::placeholder{color:var(--mk-text-muted)}.mk-listbox__list{flex:1 1 auto;min-height:0;max-height:var(--mk-listbox-max-height, 16rem);margin:0;padding:var(--mk-space-1);list-style:none;overflow-y:auto;overscroll-behavior:contain;outline:none}.mk-listbox__group{padding:var(--mk-space-2) var(--_pad-x) var(--mk-space-1);color:var(--mk-text-muted);font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.mk-listbox__option{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--_pad-y) var(--_pad-x);border-radius:var(--mk-radius-sm);font-size:var(--_font);cursor:pointer;-webkit-user-select:none;user-select:none}.mk-listbox__option:hover:not(.mk-listbox__option--disabled){background-color:var(--mk-hover-bg, var(--mk-surface-2))}.mk-listbox__option--active{background-color:var(--mk-selected-bg)}.mk-listbox__list:focus-visible .mk-listbox__option--active{box-shadow:inset 0 0 0 2px var(--mk-primary)}.mk-listbox__option--selected{color:var(--mk-selected-text);font-weight:var(--mk-font-weight-medium)}.mk-listbox__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-listbox__text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}.mk-listbox__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-listbox__description{color:var(--mk-text-muted);font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-normal)}.mk-listbox__box{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;border:var(--mk-border-width) solid var(--mk-border-strong, var(--mk-border));border-radius:var(--mk-radius-sm);background:var(--mk-surface);color:transparent}.mk-listbox__option--selected .mk-listbox__box{border-color:var(--mk-primary);background:var(--mk-primary);color:var(--mk-primary-contrast, #fff)}.mk-listbox__check{flex:none;width:.4rem;height:.7rem;margin-inline-end:var(--mk-space-1);border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg)}.mk-listbox__empty{padding:var(--mk-space-3) var(--_pad-x);color:var(--mk-text-muted);font-size:var(--mk-font-size-sm)}@media(forced-colors:active){.mk-listbox__option--active{outline:2px solid Highlight}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1827
+ }
1828
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkListbox, decorators: [{
1829
+ type: Component,
1830
+ args: [{ selector: 'mk-listbox', changeDetection: ChangeDetectionStrategy.OnPush, host: {
1831
+ class: 'mk-listbox',
1832
+ '[class.mk-listbox--sm]': "effectiveSize() === 'sm'",
1833
+ '[class.mk-listbox--md]': "effectiveSize() === 'md'",
1834
+ '[class.mk-listbox--lg]': "effectiveSize() === 'lg'",
1835
+ '[class.mk-listbox--multiple]': 'multiple()',
1836
+ '[class.mk-listbox--invalid]': 'isInvalid()',
1837
+ '[class.mk-listbox--disabled]': 'isDisabled()',
1838
+ }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MkListbox), multi: true }], template: "@if (filterable()) {\n <div class=\"mk-listbox__filter\">\n <input\n class=\"mk-listbox__filter-input\"\n type=\"search\"\n [id]=\"filterId\"\n [value]=\"query()\"\n [placeholder]=\"filterPlaceholder() ?? i18n.listboxFilter\"\n [disabled]=\"isDisabled()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-label]=\"i18n.listboxFilter\"\n autocomplete=\"off\"\n (input)=\"onFilterInput($event)\"\n (keydown)=\"onFilterKeydown($event)\"\n />\n </div>\n}\n\n<ul\n #list\n class=\"mk-listbox__list\"\n role=\"listbox\"\n [id]=\"listId\"\n [tabindex]=\"isDisabled() ? -1 : 0\"\n [attr.aria-multiselectable]=\"multiple() ? 'true' : null\"\n [attr.aria-activedescendant]=\"activeId()\"\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-disabled]=\"isDisabled() || null\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\"\n>\n @for (row of rows(); track row.kind === 'group' ? row.id : row.option.value) {\n @if (row.kind === 'group') {\n <li class=\"mk-listbox__group\" role=\"presentation\" [id]=\"row.id\">{{ row.label }}</li>\n } @else {\n @let selected = isSelected(row.option);\n <li\n class=\"mk-listbox__option\"\n role=\"option\"\n [id]=\"optionId(row.index)\"\n [attr.aria-selected]=\"selected\"\n [attr.aria-disabled]=\"row.option.disabled || null\"\n [class.mk-listbox__option--active]=\"row.index === activeIndex()\"\n [class.mk-listbox__option--selected]=\"selected\"\n [class.mk-listbox__option--disabled]=\"row.option.disabled\"\n (click)=\"onOptionClick(row.index, $event)\"\n >\n @if (multiple()) {\n <span class=\"mk-listbox__box\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 12 12\" width=\"12\" height=\"12\" focusable=\"false\"><path d=\"M2.5 6.5l2.5 2.5 4.5-5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" /></svg>\n </span>\n }\n <span class=\"mk-listbox__text\">\n <span class=\"mk-listbox__label\">{{ row.option.label }}</span>\n @if (row.option.description) {\n <span class=\"mk-listbox__description\">{{ row.option.description }}</span>\n }\n </span>\n @if (!multiple() && selected) {\n <span class=\"mk-listbox__check\" aria-hidden=\"true\"></span>\n }\n </li>\n }\n }\n @if (!rows().length) {\n <li class=\"mk-listbox__empty\" role=\"presentation\">{{ emptyText() ?? i18n.listboxEmpty }}</li>\n }\n</ul>\n", styles: [":host{--_pad-y: var(--mk-space-2);--_pad-x: var(--mk-space-3);--_font: var(--mk-font-size-md);display:flex;flex-direction:column;min-width:0;border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);background:var(--mk-surface);color:var(--mk-text);font-family:var(--mk-font-sans);overflow:hidden}:host(.mk-listbox--sm){--_pad-y: var(--mk-space-1);--_pad-x: var(--mk-space-2);--_font: var(--mk-font-size-sm)}:host(.mk-listbox--lg){--_pad-y: var(--mk-space-3);--_pad-x: var(--mk-space-4);--_font: var(--mk-font-size-lg)}:host(:focus-within){border-color:var(--mk-primary);box-shadow:0 0 0 var(--mk-focus-ring-width) var(--mk-focus-ring, var(--mk-primary-subtle))}:host(.mk-listbox--invalid){border-color:var(--mk-danger)}:host(.mk-listbox--disabled){opacity:.6;cursor:not-allowed}.mk-listbox__filter{flex:none;padding:var(--mk-space-2);border-bottom:var(--mk-border-width) solid var(--mk-border)}.mk-listbox__filter-input{width:100%;padding:var(--_pad-y) var(--_pad-x);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-sm);background:var(--mk-surface);color:inherit;font:inherit;font-size:var(--_font)}.mk-listbox__filter-input:focus-visible{outline:none;border-color:var(--mk-primary)}.mk-listbox__filter-input::placeholder{color:var(--mk-text-muted)}.mk-listbox__list{flex:1 1 auto;min-height:0;max-height:var(--mk-listbox-max-height, 16rem);margin:0;padding:var(--mk-space-1);list-style:none;overflow-y:auto;overscroll-behavior:contain;outline:none}.mk-listbox__group{padding:var(--mk-space-2) var(--_pad-x) var(--mk-space-1);color:var(--mk-text-muted);font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.mk-listbox__option{display:flex;align-items:center;gap:var(--mk-space-2);padding:var(--_pad-y) var(--_pad-x);border-radius:var(--mk-radius-sm);font-size:var(--_font);cursor:pointer;-webkit-user-select:none;user-select:none}.mk-listbox__option:hover:not(.mk-listbox__option--disabled){background-color:var(--mk-hover-bg, var(--mk-surface-2))}.mk-listbox__option--active{background-color:var(--mk-selected-bg)}.mk-listbox__list:focus-visible .mk-listbox__option--active{box-shadow:inset 0 0 0 2px var(--mk-primary)}.mk-listbox__option--selected{color:var(--mk-selected-text);font-weight:var(--mk-font-weight-medium)}.mk-listbox__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-listbox__text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}.mk-listbox__label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-listbox__description{color:var(--mk-text-muted);font-size:var(--mk-font-size-xs);font-weight:var(--mk-font-weight-normal)}.mk-listbox__box{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1rem;height:1rem;border:var(--mk-border-width) solid var(--mk-border-strong, var(--mk-border));border-radius:var(--mk-radius-sm);background:var(--mk-surface);color:transparent}.mk-listbox__option--selected .mk-listbox__box{border-color:var(--mk-primary);background:var(--mk-primary);color:var(--mk-primary-contrast, #fff)}.mk-listbox__check{flex:none;width:.4rem;height:.7rem;margin-inline-end:var(--mk-space-1);border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg)}.mk-listbox__empty{padding:var(--mk-space-3) var(--_pad-x);color:var(--mk-text-muted);font-size:var(--mk-font-size-sm)}@media(forced-colors:active){.mk-listbox__option--active{outline:2px solid Highlight}}\n"] }]
1839
+ }], ctorParameters: () => [], propDecorators: { listRef: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], selectionFollowsFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionFollowsFocus", 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 }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaLabelledby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledby", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], change: [{ type: i0.Output, args: ["change"] }] } });
1840
+
1841
+ /**
1842
+ * Cascader — a select whose choices are a hierarchy shown as side-by-side
1843
+ * columns (region → country → city, category → subcategory, org unit → team).
1844
+ * Each pick opens the next column; picking a leaf commits the whole path.
1845
+ *
1846
+ * The value is the **path** of option values (`['eu', 'pl', 'krk']`) by
1847
+ * default, or just the leaf's value with `valueMode="leaf"`. Parents are only
1848
+ * selectable with `selectParents` (a click on one then commits, and its
1849
+ * children stay reachable).
1850
+ *
1851
+ * Keyboard: Up / Down move within a column, Right opens the children, Left
1852
+ * goes back, Enter / Space selects, typing jumps to a label, Escape closes.
1853
+ * `expandTrigger="hover"` opens columns on pointer hover as well as click.
1854
+ *
1855
+ * Implements `ControlValueAccessor` and a two-way `value` model.
1856
+ *
1857
+ * ```html
1858
+ * <mk-cascader [options]="regions" [(value)]="location" placeholder="Where?" clearable />
1859
+ * ```
1860
+ */
1861
+ class MkCascader {
1862
+ field = inject(MkFormField, { optional: true });
1863
+ i18n = inject(MK_I18N);
1864
+ host = inject(ElementRef);
1865
+ injector = inject(Injector);
1866
+ triggerRef = viewChild('trigger', /* @ts-ignore */
1867
+ ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
1868
+ panelRef = viewChild('panel', /* @ts-ignore */
1869
+ ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
1870
+ /** The hierarchy to choose from. */
1871
+ options = input([], /* @ts-ignore */
1872
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
1873
+ /** Two-way value: the path of values (default) or the leaf value (`valueMode="leaf"`). */
1874
+ value = model(null, /* @ts-ignore */
1875
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1876
+ /** Bind the full path of values (default) or only the chosen option's value. */
1877
+ valueMode = input('path', /* @ts-ignore */
1878
+ ...(ngDevMode ? [{ debugName: "valueMode" }] : /* istanbul ignore next */ []));
1879
+ /** Allow committing a parent option (click / Enter on it) instead of leaves only. */
1880
+ selectParents = input(false, { ...(ngDevMode ? { debugName: "selectParents" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1881
+ /** Open the next column on click (default) or also on pointer hover. */
1882
+ expandTrigger = input('click', /* @ts-ignore */
1883
+ ...(ngDevMode ? [{ debugName: "expandTrigger" }] : /* istanbul ignore next */ []));
1884
+ placeholder = input(this.i18n.selectPlaceholder, /* @ts-ignore */
1885
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1886
+ /** Text between the labels of the chosen path. */
1887
+ separator = input(' / ', /* @ts-ignore */
1888
+ ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
1889
+ clearable = input(false, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1890
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1891
+ invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1892
+ /** Control size. Ignored when nested in an `mk-form-field`. */
1893
+ size = input('md', /* @ts-ignore */
1894
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
1895
+ /** Emitted after the user commits or clears a value. */
1896
+ change = output();
1897
+ triggerId = this.field?.controlId ?? mkUniqueId('mk-cascader');
1898
+ panelId = mkUniqueId('mk-cascader-panel');
1899
+ valueId = mkUniqueId('mk-cascader-value');
1900
+ open = signal(false, /* @ts-ignore */
1901
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
1902
+ /** Row index highlighted in each open column, left to right. */
1903
+ activePath = signal([], /* @ts-ignore */
1904
+ ...(ngDevMode ? [{ debugName: "activePath" }] : /* istanbul ignore next */ []));
1905
+ cvaDisabled = signal(false, /* @ts-ignore */
1906
+ ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
1907
+ onChange = () => { };
1908
+ onTouched = () => { };
1909
+ typeahead = '';
1910
+ typeaheadTimer;
1911
+ effectiveSize = computed(() => (this.field ? this.field.size() : this.size()), /* @ts-ignore */
1912
+ ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
1913
+ isDisabled = computed(() => this.disabled() || this.cvaDisabled(), /* @ts-ignore */
1914
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1915
+ isInvalid = computed(() => this.invalid() || (this.field?.hasError() ?? false), /* @ts-ignore */
1916
+ ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1917
+ isRequired = computed(() => this.field?.isRequired() ?? false, /* @ts-ignore */
1918
+ ...(ngDevMode ? [{ debugName: "isRequired" }] : /* istanbul ignore next */ []));
1919
+ labelledBy = computed(() => this.field?.labelId ?? null, /* @ts-ignore */
1920
+ ...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
1921
+ describedBy = computed(() => this.field?.describedBy() ?? null, /* @ts-ignore */
1922
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
1923
+ /** The options along the committed value, or `[]`. */
1924
+ selectedPath = computed(() => {
1925
+ const v = this.value();
1926
+ if (v == null)
1927
+ return [];
1928
+ if (this.valueMode() === 'path') {
1929
+ if (!Array.isArray(v))
1930
+ return [];
1931
+ const out = [];
1932
+ let list = this.options();
1933
+ for (const step of v) {
1934
+ const hit = list.find((o) => o.value === step);
1935
+ if (!hit)
1936
+ return [];
1937
+ out.push(hit);
1938
+ list = hit.children ?? [];
1939
+ }
1940
+ return out;
1941
+ }
1942
+ return this.findPath(this.options(), v) ?? [];
1943
+ }, /* @ts-ignore */
1944
+ ...(ngDevMode ? [{ debugName: "selectedPath" }] : /* istanbul ignore next */ []));
1945
+ displayLabel = computed(() => this.selectedPath().map((o) => o.label).join(this.separator()), /* @ts-ignore */
1946
+ ...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
1947
+ /**
1948
+ * Columns rendered in the panel: the root list, then the children of every
1949
+ * opened parent along the active path. The last entry of the path is the
1950
+ * cursor — resting on a parent does not open it (click, hover or → does).
1951
+ */
1952
+ columns = computed(() => {
1953
+ const cols = [[...this.options()]];
1954
+ const path = this.activePath();
1955
+ for (let depth = 0; depth < path.length - 1; depth++) {
1956
+ const opt = cols[depth]?.[path[depth]];
1957
+ if (!opt?.children?.length)
1958
+ break;
1959
+ cols.push([...opt.children]);
1960
+ }
1961
+ return cols;
1962
+ }, /* @ts-ignore */
1963
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
1964
+ /** The column the keyboard cursor is in. */
1965
+ cursorColumn = computed(() => Math.max(0, Math.min(this.activePath().length - 1, this.columns().length - 1)), /* @ts-ignore */
1966
+ ...(ngDevMode ? [{ debugName: "cursorColumn" }] : /* istanbul ignore next */ []));
1967
+ activeId = computed(() => {
1968
+ const path = this.activePath();
1969
+ const col = this.cursorColumn();
1970
+ return path.length ? this.optionId(col, path[col]) : null;
1971
+ }, /* @ts-ignore */
1972
+ ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
1973
+ constructor() {
1974
+ afterRenderEffect(() => {
1975
+ const id = this.activeId();
1976
+ if (!id || !this.open())
1977
+ return;
1978
+ this.panelRef()?.nativeElement.ownerDocument.getElementById(id)?.scrollIntoView?.({ block: 'nearest' });
1979
+ });
1980
+ }
1981
+ optionId(column, row) {
1982
+ return `${this.panelId}-c${column}-r${row}`;
1983
+ }
1984
+ isOnPath(column, row) {
1985
+ return this.activePath()[column] === row;
1986
+ }
1987
+ isSelected(column, option) {
1988
+ return this.selectedPath()[column] === option;
1989
+ }
1990
+ isCursor(column, row) {
1991
+ return this.cursorColumn() === column && this.activePath()[column] === row;
1992
+ }
1993
+ // --- Open / close ------------------------------------------------------------
1994
+ toggle() {
1995
+ if (this.isDisabled())
1996
+ return;
1997
+ this.open() ? this.close() : this.openPanel();
1998
+ }
1999
+ openPanel() {
2000
+ if (this.isDisabled() || this.open())
2001
+ return;
2002
+ // Start on the committed path (its columns open), else on the first row.
2003
+ const sel = this.selectedPath();
2004
+ const path = sel.length ? this.indexPath(sel) : [this.firstEnabled(this.options())].filter((i) => i >= 0);
2005
+ this.activePath.set(path);
2006
+ this.open.set(true);
2007
+ afterNextRender(() => this.panelRef()?.nativeElement.focus(), { injector: this.injector });
2008
+ }
2009
+ close(restoreFocus = false) {
2010
+ if (!this.open())
2011
+ return;
2012
+ this.open.set(false);
2013
+ if (restoreFocus)
2014
+ this.triggerRef()?.nativeElement.focus();
2015
+ }
2016
+ onTriggerKeydown(event) {
2017
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
2018
+ event.preventDefault();
2019
+ this.openPanel();
2020
+ }
2021
+ else if (event.key === 'Escape') {
2022
+ event.preventDefault();
2023
+ this.close();
2024
+ }
2025
+ }
2026
+ onFocusOut(event) {
2027
+ const related = event.relatedTarget;
2028
+ if (related && (this.host.nativeElement.contains(related) || this.panelRef()?.nativeElement.contains(related)))
2029
+ return;
2030
+ this.close();
2031
+ this.onTouched();
2032
+ }
2033
+ onPanelFocusout(event) {
2034
+ const related = event.relatedTarget;
2035
+ if (!related)
2036
+ return;
2037
+ if (this.host.nativeElement.contains(related) || this.panelRef()?.nativeElement.contains(related))
2038
+ return;
2039
+ this.close();
2040
+ this.onTouched();
2041
+ }
2042
+ // --- Pointer -----------------------------------------------------------------
2043
+ onOptionClick(column, row) {
2044
+ const option = this.columns()[column]?.[row];
2045
+ if (!option || option.disabled)
2046
+ return;
2047
+ if (option.children?.length) {
2048
+ this.expand(column, row);
2049
+ if (this.selectParents())
2050
+ this.commit(column, row);
2051
+ return;
2052
+ }
2053
+ this.setCursor(column, row);
2054
+ this.commit(column, row);
2055
+ }
2056
+ onOptionHover(column, row) {
2057
+ if (this.expandTrigger() !== 'hover')
2058
+ return;
2059
+ const option = this.columns()[column]?.[row];
2060
+ if (!option || option.disabled)
2061
+ return;
2062
+ this.expand(column, row);
2063
+ }
2064
+ clear(event) {
2065
+ event.stopPropagation();
2066
+ this.emit(null);
2067
+ this.triggerRef()?.nativeElement.focus();
2068
+ }
2069
+ // --- Keyboard ----------------------------------------------------------------
2070
+ onPanelKeydown(event) {
2071
+ const col = this.cursorColumn();
2072
+ const column = this.columns()[col] ?? [];
2073
+ const row = this.activePath()[col] ?? -1;
2074
+ switch (event.key) {
2075
+ case 'ArrowDown':
2076
+ case 'ArrowUp': {
2077
+ event.preventDefault();
2078
+ const next = this.stepRow(column, row, event.key === 'ArrowDown' ? 1 : -1);
2079
+ if (next >= 0)
2080
+ this.setCursor(col, next);
2081
+ break;
2082
+ }
2083
+ case 'Home':
2084
+ case 'End': {
2085
+ event.preventDefault();
2086
+ const next = event.key === 'Home' ? this.firstEnabled(column) : this.lastEnabled(column);
2087
+ if (next >= 0)
2088
+ this.setCursor(col, next);
2089
+ break;
2090
+ }
2091
+ case 'ArrowRight':
2092
+ event.preventDefault();
2093
+ if (row >= 0)
2094
+ this.expand(col, row);
2095
+ break;
2096
+ case 'ArrowLeft':
2097
+ event.preventDefault();
2098
+ if (col > 0)
2099
+ this.activePath.set(this.activePath().slice(0, col));
2100
+ break;
2101
+ case 'Enter':
2102
+ case ' ': {
2103
+ event.preventDefault();
2104
+ const option = column[row];
2105
+ if (!option || option.disabled)
2106
+ break;
2107
+ if (option.children?.length && !this.selectParents()) {
2108
+ this.expand(col, row);
2109
+ }
2110
+ else {
2111
+ this.commit(col, row);
2112
+ }
2113
+ break;
2114
+ }
2115
+ case 'Escape':
2116
+ event.preventDefault();
2117
+ event.stopPropagation();
2118
+ this.close(true);
2119
+ break;
2120
+ case 'Tab':
2121
+ this.close();
2122
+ break;
2123
+ default:
2124
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
2125
+ this.typeaheadSearch(event.key, col, column, row);
2126
+ }
2127
+ }
2128
+ }
2129
+ /** Point the cursor at `(column, row)`, trimming any deeper columns. */
2130
+ setCursor(column, row) {
2131
+ this.activePath.set([...this.activePath().slice(0, column), row]);
2132
+ }
2133
+ /** Open the children of `(column, row)` and move the cursor onto the first enabled one. */
2134
+ expand(column, row) {
2135
+ const option = this.columns()[column]?.[row];
2136
+ const children = option?.children;
2137
+ const first = children?.length ? this.firstEnabled(children) : -1;
2138
+ this.activePath.set(first >= 0 ? [...this.activePath().slice(0, column), row, first] : [...this.activePath().slice(0, column), row]);
2139
+ }
2140
+ stepRow(column, from, delta) {
2141
+ if (!column.length)
2142
+ return -1;
2143
+ let i = from;
2144
+ for (let s = 0; s < column.length; s++) {
2145
+ i = (i + delta + column.length) % column.length;
2146
+ if (!column[i].disabled)
2147
+ return i;
2148
+ }
2149
+ return -1;
2150
+ }
2151
+ firstEnabled(list) {
2152
+ return list.findIndex((o) => !o.disabled);
2153
+ }
2154
+ lastEnabled(list) {
2155
+ for (let i = list.length - 1; i >= 0; i--)
2156
+ if (!list[i].disabled)
2157
+ return i;
2158
+ return -1;
2159
+ }
2160
+ typeaheadSearch(char, col, column, from) {
2161
+ this.typeahead += char.toLocaleLowerCase();
2162
+ if (this.typeaheadTimer)
2163
+ clearTimeout(this.typeaheadTimer);
2164
+ this.typeaheadTimer = setTimeout(() => (this.typeahead = ''), 500);
2165
+ for (let s = 1; s <= column.length; s++) {
2166
+ const i = (from + s) % column.length;
2167
+ if (!column[i].disabled && column[i].label.toLocaleLowerCase().startsWith(this.typeahead)) {
2168
+ this.setCursor(col, i);
2169
+ return;
2170
+ }
2171
+ }
2172
+ }
2173
+ // --- Value -------------------------------------------------------------------
2174
+ commit(column, row) {
2175
+ const cols = this.columns();
2176
+ const path = [];
2177
+ for (let c = 0; c < column; c++)
2178
+ path.push(cols[c][this.activePath()[c]]);
2179
+ path.push(cols[column][row]);
2180
+ const value = this.valueMode() === 'path' ? path.map((o) => o.value) : path[path.length - 1].value;
2181
+ this.emit(value);
2182
+ this.close(true);
2183
+ }
2184
+ emit(value) {
2185
+ this.value.set(value);
2186
+ this.onChange(value);
2187
+ this.change.emit(value);
2188
+ }
2189
+ /** Row indexes of a path of options. */
2190
+ indexPath(path) {
2191
+ const out = [];
2192
+ let list = this.options();
2193
+ for (const opt of path) {
2194
+ const i = list.indexOf(opt);
2195
+ if (i < 0)
2196
+ break;
2197
+ out.push(i);
2198
+ list = opt.children ?? [];
2199
+ }
2200
+ return out;
2201
+ }
2202
+ /** Depth-first search for the option whose value is `value` (leaf mode). */
2203
+ findPath(list, value, trail = []) {
2204
+ for (const opt of list) {
2205
+ const next = [...trail, opt];
2206
+ if (opt.value === value)
2207
+ return next;
2208
+ if (opt.children?.length) {
2209
+ const hit = this.findPath(opt.children, value, next);
2210
+ if (hit)
2211
+ return hit;
2212
+ }
2213
+ }
2214
+ return null;
2215
+ }
2216
+ ngOnDestroy() {
2217
+ if (this.typeaheadTimer)
2218
+ clearTimeout(this.typeaheadTimer);
2219
+ }
2220
+ // --- ControlValueAccessor ----------------------------------------------------
2221
+ writeValue(value) {
2222
+ this.value.set(value ?? null);
2223
+ }
2224
+ registerOnChange(fn) {
2225
+ this.onChange = fn;
2226
+ }
2227
+ registerOnTouched(fn) {
2228
+ this.onTouched = fn;
2229
+ }
2230
+ setDisabledState(isDisabled) {
2231
+ this.cvaDisabled.set(isDisabled);
2232
+ }
2233
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkCascader, deps: [], target: i0.ɵɵFactoryTarget.Component });
2234
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MkCascader, isStandalone: true, selector: "mk-cascader", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, valueMode: { classPropertyName: "valueMode", publicName: "valueMode", isSignal: true, isRequired: false, transformFunction: null }, selectParents: { classPropertyName: "selectParents", publicName: "selectParents", isSignal: true, isRequired: false, transformFunction: null }, expandTrigger: { classPropertyName: "expandTrigger", publicName: "expandTrigger", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", 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", change: "change" }, host: { listeners: { "focusout": "onFocusOut($event)" }, properties: { "class.mk-cascader--sm": "effectiveSize() === 'sm'", "class.mk-cascader--lg": "effectiveSize() === 'lg'", "class.mk-cascader--open": "open()", "class.mk-cascader--invalid": "isInvalid()", "class.mk-cascader--disabled": "isDisabled()" }, classAttribute: "mk-cascader" }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MkCascader), multi: true }], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #field class=\"mk-cascader__field\">\n <button\n #trigger\n type=\"button\"\n class=\"mk-cascader__trigger\"\n [id]=\"triggerId\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-labelledby]=\"labelledBy() ? labelledBy() + ' ' + valueId : 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)=\"onTriggerKeydown($event)\"\n >\n @if (displayLabel()) {\n <span class=\"mk-cascader__value\" [id]=\"valueId\">{{ displayLabel() }}</span>\n } @else {\n <span class=\"mk-cascader__value mk-cascader__value--placeholder\" [id]=\"valueId\">{{ placeholder() }}</span>\n }\n <span class=\"mk-cascader__icon\" aria-hidden=\"true\"></span>\n </button>\n\n @if (clearable() && selectedPath().length && !isDisabled()) {\n <button type=\"button\" class=\"mk-cascader__clear\" [attr.aria-label]=\"i18n.clear\" (click)=\"clear($event)\">\n <span class=\"mk-cascader__clear-icon\" aria-hidden=\"true\"></span>\n </button>\n }\n</div>\n\n@if (open()) {\n <div\n #panel\n class=\"mk-cascader__panel\"\n role=\"listbox\"\n tabindex=\"-1\"\n [id]=\"panelId\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-activedescendant]=\"activeId()\"\n mkAnchoredPanel\n [mkAnchoredPanelFor]=\"field\"\n (dismiss)=\"close()\"\n (keydown)=\"onPanelKeydown($event)\"\n (focusout)=\"onPanelFocusout($event)\"\n >\n @for (column of columns(); track $index; let c = $index) {\n <ul class=\"mk-cascader__column\" role=\"group\">\n @for (opt of column; track $index; let r = $index) {\n <li\n class=\"mk-cascader__option\"\n role=\"option\"\n [id]=\"optionId(c, r)\"\n [attr.aria-selected]=\"isSelected(c, opt)\"\n [attr.aria-disabled]=\"opt.disabled || null\"\n [class.mk-cascader__option--path]=\"isOnPath(c, r)\"\n [class.mk-cascader__option--cursor]=\"isCursor(c, r)\"\n [class.mk-cascader__option--selected]=\"isSelected(c, opt)\"\n [class.mk-cascader__option--disabled]=\"opt.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (mouseenter)=\"onOptionHover(c, r)\"\n (click)=\"onOptionClick(c, r)\"\n >\n <span class=\"mk-cascader__option-label\">{{ opt.label }}</span>\n @if (opt.children?.length) {\n <span class=\"mk-cascader__chevron\" aria-hidden=\"true\"></span>\n }\n </li>\n } @empty {\n <li class=\"mk-cascader__empty\" role=\"presentation\">{{ i18n.noOptions }}</li>\n }\n </ul>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative;width:100%;font-family:var(--mk-font-sans)}.mk-cascader__field{display:flex;align-items:center;position:relative}.mk-cascader__trigger{flex:1 1 auto;display:flex;align-items:center;gap:var(--mk-space-2);width:100%;height:var(--mk-control-height-md);padding:0 var(--mk-space-3);text-align:start;font: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);cursor:pointer}:host(.mk-cascader--sm) .mk-cascader__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-cascader--lg) .mk-cascader__trigger{height:var(--mk-control-height-lg);padding:0 var(--mk-space-4);font-size:var(--mk-font-size-lg)}.mk-cascader__trigger:hover:not(:disabled){border-color:var(--mk-border-strong)}.mk-cascader__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-cascader--invalid) .mk-cascader__trigger{border-color:var(--mk-danger)}:host(.mk-cascader--disabled) .mk-cascader__trigger{background-color:var(--mk-surface-2);color:var(--mk-text-disabled);cursor:not-allowed;opacity:.7}.mk-cascader__value{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-cascader__value--placeholder{color:var(--mk-text-subtle)}.mk-cascader__icon{flex:none;width:.6rem;height:.6rem;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(45deg);transform-origin:center;margin-bottom:.15rem}:host(.mk-cascader--open) .mk-cascader__icon{transform:rotate(-135deg);margin-bottom:-.15rem}.mk-cascader__clear{position:absolute;inset-inline-end:var(--mk-space-8);display:grid;place-items:center;width:1.25rem;height:1.25rem;color:var(--mk-text-muted);background:transparent;border:none;border-radius:var(--mk-radius-circle);cursor:pointer}.mk-cascader__clear:hover{background-color:var(--mk-hover-overlay);color:var(--mk-text)}.mk-cascader__clear-icon:before,.mk-cascader__clear-icon:after{content:\"\";position:absolute;width:.7rem;height:2px;background-color:currentColor}.mk-cascader__clear-icon:before{transform:rotate(45deg)}.mk-cascader__clear-icon:after{transform:rotate(-45deg)}.mk-cascader__panel{display:flex;align-items:stretch;background:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg);outline:none}.mk-cascader__column{min-width:10rem;max-height:16rem;margin:0;padding:var(--mk-space-1);list-style:none;overflow-y:auto;overscroll-behavior:contain}.mk-cascader__column+.mk-cascader__column{border-inline-start:var(--mk-border-width) solid var(--mk-border)}.mk-cascader__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-cascader__option:hover:not(.mk-cascader__option--disabled){background-color:var(--mk-hover-bg, var(--mk-surface-2))}.mk-cascader__option--path{background-color:var(--mk-selected-bg)}.mk-cascader__option--cursor{box-shadow:inset 0 0 0 2px var(--mk-primary)}.mk-cascader__option--selected{color:var(--mk-selected-text);font-weight:var(--mk-font-weight-medium)}.mk-cascader__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-cascader__option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-cascader__chevron{flex:none;width:.45rem;height:.45rem;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(-45deg)}[dir=rtl] .mk-cascader__chevron{transform:rotate(135deg)}.mk-cascader__empty{padding:var(--mk-space-2) var(--mk-space-3);color:var(--mk-text-muted);font-size:var(--mk-font-size-sm)}@media(max-width:640px){.mk-cascader__panel{flex-direction:column-reverse;max-height:60vh;overflow-y:auto}.mk-cascader__column{max-height:none}.mk-cascader__column+.mk-cascader__column{border-inline-start:0;border-bottom:var(--mk-border-width) solid var(--mk-border)}}\n"], dependencies: [{ kind: "directive", type: MkAnchoredPanel, selector: "[mkAnchoredPanel]", inputs: ["mkAnchoredPanelFor", "anchorRect", "placement", "gap", "matchWidth", "flip", "clamp", "keepOpenWhen"], outputs: ["dismiss"], exportAs: ["mkAnchoredPanel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2235
+ }
2236
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkCascader, decorators: [{
2237
+ type: Component,
2238
+ args: [{ selector: 'mk-cascader', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MkAnchoredPanel], host: {
2239
+ class: 'mk-cascader',
2240
+ '[class.mk-cascader--sm]': "effectiveSize() === 'sm'",
2241
+ '[class.mk-cascader--lg]': "effectiveSize() === 'lg'",
2242
+ '[class.mk-cascader--open]': 'open()',
2243
+ '[class.mk-cascader--invalid]': 'isInvalid()',
2244
+ '[class.mk-cascader--disabled]': 'isDisabled()',
2245
+ '(focusout)': 'onFocusOut($event)',
2246
+ }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MkCascader), multi: true }], template: "<div #field class=\"mk-cascader__field\">\n <button\n #trigger\n type=\"button\"\n class=\"mk-cascader__trigger\"\n [id]=\"triggerId\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-labelledby]=\"labelledBy() ? labelledBy() + ' ' + valueId : 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)=\"onTriggerKeydown($event)\"\n >\n @if (displayLabel()) {\n <span class=\"mk-cascader__value\" [id]=\"valueId\">{{ displayLabel() }}</span>\n } @else {\n <span class=\"mk-cascader__value mk-cascader__value--placeholder\" [id]=\"valueId\">{{ placeholder() }}</span>\n }\n <span class=\"mk-cascader__icon\" aria-hidden=\"true\"></span>\n </button>\n\n @if (clearable() && selectedPath().length && !isDisabled()) {\n <button type=\"button\" class=\"mk-cascader__clear\" [attr.aria-label]=\"i18n.clear\" (click)=\"clear($event)\">\n <span class=\"mk-cascader__clear-icon\" aria-hidden=\"true\"></span>\n </button>\n }\n</div>\n\n@if (open()) {\n <div\n #panel\n class=\"mk-cascader__panel\"\n role=\"listbox\"\n tabindex=\"-1\"\n [id]=\"panelId\"\n [attr.aria-labelledby]=\"labelledBy()\"\n [attr.aria-activedescendant]=\"activeId()\"\n mkAnchoredPanel\n [mkAnchoredPanelFor]=\"field\"\n (dismiss)=\"close()\"\n (keydown)=\"onPanelKeydown($event)\"\n (focusout)=\"onPanelFocusout($event)\"\n >\n @for (column of columns(); track $index; let c = $index) {\n <ul class=\"mk-cascader__column\" role=\"group\">\n @for (opt of column; track $index; let r = $index) {\n <li\n class=\"mk-cascader__option\"\n role=\"option\"\n [id]=\"optionId(c, r)\"\n [attr.aria-selected]=\"isSelected(c, opt)\"\n [attr.aria-disabled]=\"opt.disabled || null\"\n [class.mk-cascader__option--path]=\"isOnPath(c, r)\"\n [class.mk-cascader__option--cursor]=\"isCursor(c, r)\"\n [class.mk-cascader__option--selected]=\"isSelected(c, opt)\"\n [class.mk-cascader__option--disabled]=\"opt.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (mouseenter)=\"onOptionHover(c, r)\"\n (click)=\"onOptionClick(c, r)\"\n >\n <span class=\"mk-cascader__option-label\">{{ opt.label }}</span>\n @if (opt.children?.length) {\n <span class=\"mk-cascader__chevron\" aria-hidden=\"true\"></span>\n }\n </li>\n } @empty {\n <li class=\"mk-cascader__empty\" role=\"presentation\">{{ i18n.noOptions }}</li>\n }\n </ul>\n }\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative;width:100%;font-family:var(--mk-font-sans)}.mk-cascader__field{display:flex;align-items:center;position:relative}.mk-cascader__trigger{flex:1 1 auto;display:flex;align-items:center;gap:var(--mk-space-2);width:100%;height:var(--mk-control-height-md);padding:0 var(--mk-space-3);text-align:start;font: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);cursor:pointer}:host(.mk-cascader--sm) .mk-cascader__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-cascader--lg) .mk-cascader__trigger{height:var(--mk-control-height-lg);padding:0 var(--mk-space-4);font-size:var(--mk-font-size-lg)}.mk-cascader__trigger:hover:not(:disabled){border-color:var(--mk-border-strong)}.mk-cascader__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-cascader--invalid) .mk-cascader__trigger{border-color:var(--mk-danger)}:host(.mk-cascader--disabled) .mk-cascader__trigger{background-color:var(--mk-surface-2);color:var(--mk-text-disabled);cursor:not-allowed;opacity:.7}.mk-cascader__value{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-cascader__value--placeholder{color:var(--mk-text-subtle)}.mk-cascader__icon{flex:none;width:.6rem;height:.6rem;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(45deg);transform-origin:center;margin-bottom:.15rem}:host(.mk-cascader--open) .mk-cascader__icon{transform:rotate(-135deg);margin-bottom:-.15rem}.mk-cascader__clear{position:absolute;inset-inline-end:var(--mk-space-8);display:grid;place-items:center;width:1.25rem;height:1.25rem;color:var(--mk-text-muted);background:transparent;border:none;border-radius:var(--mk-radius-circle);cursor:pointer}.mk-cascader__clear:hover{background-color:var(--mk-hover-overlay);color:var(--mk-text)}.mk-cascader__clear-icon:before,.mk-cascader__clear-icon:after{content:\"\";position:absolute;width:.7rem;height:2px;background-color:currentColor}.mk-cascader__clear-icon:before{transform:rotate(45deg)}.mk-cascader__clear-icon:after{transform:rotate(-45deg)}.mk-cascader__panel{display:flex;align-items:stretch;background:var(--mk-surface);border:var(--mk-border-width) solid var(--mk-border);border-radius:var(--mk-radius-md);box-shadow:var(--mk-shadow-lg);outline:none}.mk-cascader__column{min-width:10rem;max-height:16rem;margin:0;padding:var(--mk-space-1);list-style:none;overflow-y:auto;overscroll-behavior:contain}.mk-cascader__column+.mk-cascader__column{border-inline-start:var(--mk-border-width) solid var(--mk-border)}.mk-cascader__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-cascader__option:hover:not(.mk-cascader__option--disabled){background-color:var(--mk-hover-bg, var(--mk-surface-2))}.mk-cascader__option--path{background-color:var(--mk-selected-bg)}.mk-cascader__option--cursor{box-shadow:inset 0 0 0 2px var(--mk-primary)}.mk-cascader__option--selected{color:var(--mk-selected-text);font-weight:var(--mk-font-weight-medium)}.mk-cascader__option--disabled{color:var(--mk-text-disabled);cursor:not-allowed}.mk-cascader__option-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mk-cascader__chevron{flex:none;width:.45rem;height:.45rem;border-right:2px solid var(--mk-text-muted);border-bottom:2px solid var(--mk-text-muted);transform:rotate(-45deg)}[dir=rtl] .mk-cascader__chevron{transform:rotate(135deg)}.mk-cascader__empty{padding:var(--mk-space-2) var(--mk-space-3);color:var(--mk-text-muted);font-size:var(--mk-font-size-sm)}@media(max-width:640px){.mk-cascader__panel{flex-direction:column-reverse;max-height:60vh;overflow-y:auto}.mk-cascader__column{max-height:none}.mk-cascader__column+.mk-cascader__column{border-inline-start:0;border-bottom:var(--mk-border-width) solid var(--mk-border)}}\n"] }]
2247
+ }], ctorParameters: () => [], propDecorators: { triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], valueMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueMode", required: false }] }], selectParents: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectParents", required: false }] }], expandTrigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandTrigger", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", 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 }] }], change: [{ type: i0.Output, args: ["change"] }] } });
2248
+
1439
2249
  /**
1440
2250
  * Autocomplete — an accessible text input that suggests options as the user
1441
2251
  * types, implementing the ARIA combobox (`aria-autocomplete="list"`) pattern
@@ -9204,5 +10014,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
9204
10014
  * Generated bundle index. Do not edit.
9205
10015
  */
9206
10016
 
9207
- export { MK_CARD_BRAND_NAMES, MK_IBAN_LENGTHS, MK_KEYBOARD_LAYOUT_QWERTY_PL, MK_PHONE_COUNTRIES, MK_POSTAL_FORMATS, MK_TAX_ID_FORMATS, MkAutocomplete, MkButtonToggle, MkButtonToggleGroup, MkCardNumberInput, MkCodeEditor, MkColorPicker, MkCurrencyInput, MkFileUpload, MkFormErrorSummary, MkFormField, MkI18nInput, MkIbanInput, MkInput, MkInputGroup, MkMention, MkMentionPanel, MkMultiSelect, MkNumberInput, MkNumericKeypad, MkOnScreenKeyboard, MkOnScreenKeyboardTrigger, MkOtp, MkPasswordInput, MkPhoneInput, MkPostalCodeInput, MkRadio, MkRadioGroup, MkRangeSlider, MkRating, MkRepeater, MkRepeaterEmpty, MkRepeaterRow, MkSelect, MkSignaturePad, MkSlider, MkSubmitInput, MkSwitch, MkTagInput, MkTaxIdInput, MkTransferList, MkTreeSelect, mkCountryFlag, mkDetectCardBrand, mkIbanChecksum, mkIbanIsValid, mkIbanValidator, mkLuhnCheck, mkNipChecksum, mkPostalCodeValidator, mkPostalFormat, mkTaxIdFormat, mkTaxIdIsValid, mkTaxIdValidator };
10017
+ export { MK_CARD_BRAND_NAMES, MK_IBAN_LENGTHS, MK_KEYBOARD_LAYOUT_QWERTY_PL, MK_PHONE_COUNTRIES, MK_POSTAL_FORMATS, MK_TAX_ID_FORMATS, MkAutocomplete, MkButtonToggle, MkButtonToggleGroup, MkCardNumberInput, MkCascader, MkCodeEditor, MkColorPicker, MkCurrencyInput, MkFileUpload, MkFormErrorSummary, MkFormField, MkI18nInput, MkIbanInput, MkInput, MkInputGroup, MkListbox, MkMention, MkMentionPanel, MkMultiSelect, MkNumberInput, MkNumericKeypad, MkOnScreenKeyboard, MkOnScreenKeyboardTrigger, MkOtp, MkPasswordInput, MkPhoneInput, MkPostalCodeInput, MkRadio, MkRadioGroup, MkRangeSlider, MkRating, MkRepeater, MkRepeaterEmpty, MkRepeaterRow, MkSelect, MkSignaturePad, MkSlider, MkSubmitInput, MkSwitch, MkTagInput, MkTaxIdInput, MkTransferList, MkTreeSelect, mkCountryFlag, mkDetectCardBrand, mkIbanChecksum, mkIbanIsValid, mkIbanValidator, mkLuhnCheck, mkNipChecksum, mkPostalCodeValidator, mkPostalFormat, mkTaxIdFormat, mkTaxIdIsValid, mkTaxIdValidator };
9208
10018
  //# sourceMappingURL=mk-kit-ui-forms.mjs.map