@frame-kit/ui-ng 0.2.7 → 0.2.8

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 { input, computed, HostBinding, ChangeDetectionStrategy, Component, InjectionToken, signal, output, Directive, contentChild, inject, ChangeDetectorRef, effect, Injectable, TemplateRef, viewChild, forwardRef, DestroyRef, ViewContainerRef, ElementRef } from '@angular/core';
2
+ import { input, computed, HostBinding, ChangeDetectionStrategy, Component, InjectionToken, signal, output, Directive, contentChild, inject, ChangeDetectorRef, ElementRef, isDevMode, effect, Injectable, TemplateRef, viewChild, forwardRef, DestroyRef, ViewContainerRef } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { Validators, FormControl, FormGroup, FormArray, NgControl, ReactiveFormsModule } from '@angular/forms';
5
5
  import { NgTemplateOutlet } from '@angular/common';
@@ -128,11 +128,11 @@ class FormComponent {
128
128
  this.status.set('idle');
129
129
  }
130
130
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
131
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FormComponent, isStandalone: true, selector: "fk-form", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit" }, host: { properties: { "class": "this.hostClass" } }, providers: [{ provide: FORM, useExisting: FormComponent }], ngImport: i0, template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: [":host{display:block}.fk-form__native{display:flex;flex-direction:column}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
131
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: FormComponent, isStandalone: true, selector: "fk-form", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { formSubmit: "formSubmit" }, host: { properties: { "class": "this.hostClass" } }, providers: [{ provide: FORM, useExisting: FormComponent }], ngImport: i0, template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: ["@charset \"UTF-8\";:host{display:block}.fk-form__native{display:flex;flex-direction:column;gap:var(--fk-form-gap, 0)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
132
132
  }
133
133
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormComponent, decorators: [{
134
134
  type: Component,
135
- args: [{ selector: 'fk-form', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: FORM, useExisting: FormComponent }], template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: [":host{display:block}.fk-form__native{display:flex;flex-direction:column}\n"] }]
135
+ args: [{ selector: 'fk-form', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: FORM, useExisting: FormComponent }], template: "<form class=\"fk-form__native\" (submit)=\"$event.preventDefault(); onSubmit()\">\n <ng-content />\n</form>\n", styles: ["@charset \"UTF-8\";:host{display:block}.fk-form__native{display:flex;flex-direction:column;gap:var(--fk-form-gap, 0)}\n"] }]
136
136
  }], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], hostClass: [{
137
137
  type: HostBinding,
138
138
  args: ['class']
@@ -354,6 +354,41 @@ class FormFieldComponent {
354
354
  // ===== INJECTED SERVICES =====
355
355
  globalErrorMessages = inject(ERROR_MESSAGES);
356
356
  cdr = inject(ChangeDetectorRef);
357
+ host = inject((ElementRef));
358
+ /**
359
+ * `fkPrefix` and `fkSuffix` project only when their directive resolves, and
360
+ * it only resolves when the consumer imports it. Forget the import and the
361
+ * attribute stays inert markup: Angular does not warn about an unknown
362
+ * attribute on a known element, the content query returns undefined, the
363
+ * wrapper is never rendered, and the icon simply is not there. Nothing fails
364
+ * — which is what makes it slow to diagnose.
365
+ *
366
+ * Content queries have resolved by this point, and the element is still in
367
+ * the DOM either way, so an element carrying the attribute with no resolved
368
+ * directive is an unambiguous signal.
369
+ */
370
+ ngAfterContentInit() {
371
+ if (isDevMode()) {
372
+ this.warnOnUnresolvedProjection();
373
+ }
374
+ }
375
+ warnOnUnresolvedProjection() {
376
+ const checks = [
377
+ ['fkPrefix', 'FkPrefixDirective', 'prefix', this.prefixRef()],
378
+ ['fkSuffix', 'FkSuffixDirective', 'suffix', this.suffixRef()],
379
+ ];
380
+ for (const [attribute, directive, slot, resolved] of checks) {
381
+ if (resolved)
382
+ continue;
383
+ const wrapper = this.host.nativeElement.querySelector(`.fk-form-field__${slot}`);
384
+ if (!wrapper?.hasChildNodes())
385
+ continue;
386
+ console.warn(`[fk-form-field] Content was projected into the ${slot} slot but ` +
387
+ `\`${directive}\` did not resolve, so the field will not be spaced ` +
388
+ `for it. Add \`${directive}\` to the imports of the component whose ` +
389
+ `template uses \`${attribute}\`.`);
390
+ }
391
+ }
357
392
  // ===== HOST CLASS =====
358
393
  get hostClass() {
359
394
  return [
@@ -451,6 +486,12 @@ class FormFieldComponent {
451
486
  return this.showError();
452
487
  }
453
488
  constructor() {
489
+ // `fkPrefix` and `fkSuffix` project only when their directive resolves, and
490
+ // it only resolves when the consumer imports it. Forget the import and the
491
+ // attribute stays inert markup: Angular does not warn about an unknown
492
+ // attribute on a known element, the `contentChild` query returns undefined,
493
+ // the wrapper is never rendered, and the icon simply is not there. Nothing
494
+ // fails — which is what makes it slow to diagnose.
454
495
  effect(() => {
455
496
  this.syncDescribedByIds();
456
497
  });
@@ -494,11 +535,11 @@ class FormFieldComponent {
494
535
  control.setDescribedByIds(ids);
495
536
  }
496
537
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
497
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FormFieldComponent, isStandalone: true, selector: "fk-form-field", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showErrorsOn: { classPropertyName: "showErrorsOn", publicName: "showErrorsOn", isSignal: true, isRequired: false, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null }, externalDescribedBy: { classPropertyName: "externalDescribedBy", publicName: "externalDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, fallbackControl: { classPropertyName: "fallbackControl", publicName: "fallbackControl", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, queries: [{ propertyName: "control", first: true, predicate: FIELD_CONTROL, descendants: true, isSignal: true }, { propertyName: "hintRef", first: true, predicate: HintComponent, descendants: true, isSignal: true }, { propertyName: "errorRef", first: true, predicate: ErrorComponent, descendants: true, isSignal: true }, { propertyName: "labelRef", first: true, predicate: LabelComponent, descendants: true, isSignal: true }, { propertyName: "prefixRef", first: true, predicate: FK_PREFIX, descendants: true, isSignal: true }, { propertyName: "suffixRef", first: true, predicate: FK_SUFFIX, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <div class=\"fk-form-field__label-row\">\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n </label>\n <!-- The required marker lives OUTSIDE the <label> so it never leaks into\n the label's text/accessible name (keeping getByLabel + AT clean); the\n input's aria-required conveys the requirement to assistive tech. -->\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n @if (prefixRef()) {\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n }\n <ng-content />\n @if (suffixRef()) {\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n }\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label-row{display:flex;align-items:center;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem))}.fk-form-field__label{display:flex;align-items:center;gap:.25em;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__label fk-icon{flex:none;color:var(--fk-form-label-icon-color, currentColor)}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
538
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: FormFieldComponent, isStandalone: true, selector: "fk-form-field", inputs: { className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, showErrorsOn: { classPropertyName: "showErrorsOn", publicName: "showErrorsOn", isSignal: true, isRequired: false, transformFunction: null }, submitted: { classPropertyName: "submitted", publicName: "submitted", isSignal: true, isRequired: false, transformFunction: null }, externalDescribedBy: { classPropertyName: "externalDescribedBy", publicName: "externalDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, fallbackControl: { classPropertyName: "fallbackControl", publicName: "fallbackControl", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "this.hostClass" } }, queries: [{ propertyName: "control", first: true, predicate: FIELD_CONTROL, descendants: true, isSignal: true }, { propertyName: "hintRef", first: true, predicate: HintComponent, descendants: true, isSignal: true }, { propertyName: "errorRef", first: true, predicate: ErrorComponent, descendants: true, isSignal: true }, { propertyName: "labelRef", first: true, predicate: LabelComponent, descendants: true, isSignal: true }, { propertyName: "prefixRef", first: true, predicate: FK_PREFIX, descendants: true, isSignal: true }, { propertyName: "suffixRef", first: true, predicate: FK_SUFFIX, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <div class=\"fk-form-field__label-row\">\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n </label>\n <!-- The required marker lives OUTSIDE the <label> so it never leaks into\n the label's text/accessible name (keeping getByLabel + AT clean); the\n input's aria-required conveys the requirement to assistive tech. -->\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n <!-- Rendered unconditionally, and deliberately so. Gating these on the\n directive resolving means a consumer who marks an element `fkPrefix`\n but forgets to import `FkPrefixDirective` gets no slot, and because an\n unprojected node is never placed in the DOM at all, their icon\n disappears with no error anywhere. Both wrappers are absolutely\n positioned and `:empty`-hidden, so rendering them always costs nothing\n and turns that failure into something visible. -->\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n <ng-content />\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label-row{display:flex;align-items:center;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem))}.fk-form-field__label{display:flex;align-items:center;gap:.25em;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__label fk-icon{flex:none;color:var(--fk-form-label-icon-color, currentColor)}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix:empty,.fk-form-field__suffix:empty{display:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
498
539
  }
499
540
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormFieldComponent, decorators: [{
500
541
  type: Component,
501
- args: [{ selector: 'fk-form-field', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <div class=\"fk-form-field__label-row\">\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n </label>\n <!-- The required marker lives OUTSIDE the <label> so it never leaks into\n the label's text/accessible name (keeping getByLabel + AT clean); the\n input's aria-required conveys the requirement to assistive tech. -->\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n @if (prefixRef()) {\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n }\n <ng-content />\n @if (suffixRef()) {\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n }\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label-row{display:flex;align-items:center;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem))}.fk-form-field__label{display:flex;align-items:center;gap:.25em;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__label fk-icon{flex:none;color:var(--fk-form-label-icon-color, currentColor)}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"] }]
542
+ args: [{ selector: 'fk-form-field', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fk-form-field__wrapper\">\n @if (label() || labelRef()) {\n <div class=\"fk-form-field__label-row\">\n <label\n class=\"fk-form-field__label\"\n [attr.id]=\"labelId\"\n [attr.for]=\"control()?.id ?? null\"\n >\n @if (label()) {\n {{ label() }}\n } @else {\n <ng-content select=\"fk-label\" />\n }\n </label>\n <!-- The required marker lives OUTSIDE the <label> so it never leaks into\n the label's text/accessible name (keeping getByLabel + AT clean); the\n input's aria-required conveys the requirement to assistive tech. -->\n @if (isRequired()) {\n <span class=\"fk-form-field__required\" aria-hidden=\"true\">*</span>\n }\n </div>\n }\n\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <div\n class=\"fk-form-field__control\"\n (click)=\"control()?.onContainerClick?.($event)\"\n >\n <!-- Rendered unconditionally, and deliberately so. Gating these on the\n directive resolving means a consumer who marks an element `fkPrefix`\n but forgets to import `FkPrefixDirective` gets no slot, and because an\n unprojected node is never placed in the DOM at all, their icon\n disappears with no error anywhere. Both wrappers are absolutely\n positioned and `:empty`-hidden, so rendering them always costs nothing\n and turns that failure into something visible. -->\n <div class=\"fk-form-field__prefix\">\n <ng-content select=\"[fkPrefix]\" />\n </div>\n <ng-content />\n <div class=\"fk-form-field__suffix\">\n <ng-content select=\"[fkSuffix]\" />\n </div>\n </div>\n\n <div class=\"fk-form-field__subscript\">\n @if (showError()) {\n @if (hasAutoError) {\n <span class=\"fk-error\" role=\"alert\" [attr.id]=\"autoErrorId\">\n {{ autoErrorMessage() }}\n </span>\n } @else {\n <ng-content select=\"fk-error\" />\n }\n } @else {\n <ng-content select=\"fk-hint\" />\n }\n </div>\n</div>\n", styles: [":host{display:block}.fk-form-field__wrapper{display:flex;flex-direction:column}.fk-form-field__label-row{display:flex;align-items:center;gap:.25em;margin-top:var(--fk-form-label-margin-top, var(--fk-rhythm-2, .5rem))}.fk-form-field__label{display:flex;align-items:center;gap:.25em;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-label-font-size, var(--fk-typography-label-font-size, .8125rem));font-weight:var(--fk-form-label-font-weight, var(--fk-typography-label-font-weight, 600));line-height:var(--fk-form-label-line-height, var(--fk-typography-label-line-height, 1.25));color:var(--fk-form-label-color, var(--fk-color-text-strong, #0b1420));cursor:default}.fk-form-field__label fk-icon{flex:none;color:var(--fk-form-label-icon-color, currentColor)}.fk-form-field__required{color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}.fk-form-field__control{position:relative;margin-top:var(--fk-form-label-gap, var(--fk-rhythm-2, .5rem))}.fk-form-field__prefix,.fk-form-field__suffix{position:absolute;top:50%;transform:translateY(-50%);display:flex;align-items:center;color:var(--fk-input-placeholder-color, var(--fk-color-muted, #8a98a8));z-index:1}.fk-form-field__prefix{pointer-events:none}.fk-form-field__prefix:empty,.fk-form-field__suffix:empty{display:none}.fk-form-field__prefix{left:var(--fk-input-affix-offset, .75rem)}.fk-form-field__suffix{right:var(--fk-input-affix-offset, .75rem)}.fk-form-field__subscript{display:var(--fk-form-subscript-display, block);min-height:var(--fk-form-subscript-min-height, calc(var(--fk-form-hint-font-size, var(--fk-typography-small-font-size, .8125rem)) * 1.4));margin-top:var(--fk-form-subscript-gap, var(--fk-rhythm-1, .25rem))}.fk-form-field__subscript .fk-error{display:block;font-family:var(--fk-font-family-base, \"Inter\", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\");font-size:var(--fk-form-error-font-size, var(--fk-typography-small-font-size, .8125rem));line-height:1.4;color:var(--fk-form-error-color, var(--fk-color-danger, #e02424))}:host(.fk-form-field--focused) .fk-form-field__label{color:var(--fk-input-border-color-focus, var(--fk-color-primary, #0a84ff))}:host(.fk-form-field--disabled) .fk-form-field__label{opacity:var(--fk-input-disabled-opacity, .5);cursor:not-allowed}\n"] }]
502
543
  }], ctorParameters: () => [], propDecorators: { className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], showErrorsOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrorsOn", required: false }] }], submitted: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitted", required: false }] }], externalDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalDescribedBy", required: false }] }], fallbackControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackControl", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FIELD_CONTROL), { isSignal: true }] }], hintRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => HintComponent), { isSignal: true }] }], errorRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ErrorComponent), { isSignal: true }] }], labelRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => LabelComponent), { isSignal: true }] }], prefixRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_PREFIX), { isSignal: true }] }], suffixRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_SUFFIX), { isSignal: true }] }], hostClass: [{
503
544
  type: HostBinding,
504
545
  args: ['class']