@frame-kit/ui-ng 0.2.7 → 0.2.9

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']
@@ -289,6 +289,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
289
289
  args: ['attr.for']
290
290
  }] } });
291
291
 
292
+ const FK_LABEL_ACTION = new InjectionToken('FK_LABEL_ACTION');
293
+ /**
294
+ * Marks content that belongs on the label's own row, aligned to the trailing
295
+ * edge — a "Forgot password?" link beside a Password label being the canonical
296
+ * case.
297
+ *
298
+ * It sits outside the `<label>` element deliberately. A link inside a label is
299
+ * activated when the label is clicked, so the user reaches for the link and
300
+ * focuses the input instead; and its text would join the input's accessible
301
+ * name.
302
+ */
303
+ class FkLabelActionDirective {
304
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLabelActionDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
305
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: FkLabelActionDirective, isStandalone: true, selector: "[fkLabelAction]", providers: [{ provide: FK_LABEL_ACTION, useExisting: FkLabelActionDirective }], ngImport: i0 });
306
+ }
307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkLabelActionDirective, decorators: [{
308
+ type: Directive,
309
+ args: [{
310
+ selector: '[fkLabelAction]',
311
+ standalone: true,
312
+ providers: [{ provide: FK_LABEL_ACTION, useExisting: FkLabelActionDirective }],
313
+ }]
314
+ }] });
315
+
292
316
  const FK_PREFIX = new InjectionToken('FK_PREFIX');
293
317
  class FkPrefixDirective {
294
318
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FkPrefixDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
@@ -349,11 +373,53 @@ class FormFieldComponent {
349
373
  hintRef = contentChild(HintComponent, ...(ngDevMode ? [{ debugName: "hintRef" }] : /* istanbul ignore next */ []));
350
374
  errorRef = contentChild(ErrorComponent, ...(ngDevMode ? [{ debugName: "errorRef" }] : /* istanbul ignore next */ []));
351
375
  labelRef = contentChild(LabelComponent, ...(ngDevMode ? [{ debugName: "labelRef" }] : /* istanbul ignore next */ []));
376
+ labelActionRef = contentChild(FK_LABEL_ACTION, ...(ngDevMode ? [{ debugName: "labelActionRef" }] : /* istanbul ignore next */ []));
352
377
  prefixRef = contentChild(FK_PREFIX, ...(ngDevMode ? [{ debugName: "prefixRef" }] : /* istanbul ignore next */ []));
353
378
  suffixRef = contentChild(FK_SUFFIX, ...(ngDevMode ? [{ debugName: "suffixRef" }] : /* istanbul ignore next */ []));
354
379
  // ===== INJECTED SERVICES =====
355
380
  globalErrorMessages = inject(ERROR_MESSAGES);
356
381
  cdr = inject(ChangeDetectorRef);
382
+ host = inject((ElementRef));
383
+ /**
384
+ * `fkPrefix` and `fkSuffix` project only when their directive resolves, and
385
+ * it only resolves when the consumer imports it. Forget the import and the
386
+ * attribute stays inert markup: Angular does not warn about an unknown
387
+ * attribute on a known element, the content query returns undefined, the
388
+ * wrapper is never rendered, and the icon simply is not there. Nothing fails
389
+ * — which is what makes it slow to diagnose.
390
+ *
391
+ * Content queries have resolved by this point, and the element is still in
392
+ * the DOM either way, so an element carrying the attribute with no resolved
393
+ * directive is an unambiguous signal.
394
+ */
395
+ ngAfterContentInit() {
396
+ if (isDevMode()) {
397
+ this.warnOnUnresolvedProjection();
398
+ }
399
+ }
400
+ warnOnUnresolvedProjection() {
401
+ const checks = [
402
+ ['fkPrefix', 'FkPrefixDirective', 'prefix', this.prefixRef()],
403
+ [
404
+ 'fkLabelAction',
405
+ 'FkLabelActionDirective',
406
+ 'label-action',
407
+ this.labelActionRef(),
408
+ ],
409
+ ['fkSuffix', 'FkSuffixDirective', 'suffix', this.suffixRef()],
410
+ ];
411
+ for (const [attribute, directive, slot, resolved] of checks) {
412
+ if (resolved)
413
+ continue;
414
+ const wrapper = this.host.nativeElement.querySelector(`.fk-form-field__${slot}`);
415
+ if (!wrapper?.hasChildNodes())
416
+ continue;
417
+ console.warn(`[fk-form-field] Content was projected into the ${slot} slot but ` +
418
+ `\`${directive}\` did not resolve, so the field will not be spaced ` +
419
+ `for it. Add \`${directive}\` to the imports of the component whose ` +
420
+ `template uses \`${attribute}\`.`);
421
+ }
422
+ }
357
423
  // ===== HOST CLASS =====
358
424
  get hostClass() {
359
425
  return [
@@ -451,6 +517,12 @@ class FormFieldComponent {
451
517
  return this.showError();
452
518
  }
453
519
  constructor() {
520
+ // `fkPrefix` and `fkSuffix` project only when their directive resolves, and
521
+ // it only resolves when the consumer imports it. Forget the import and the
522
+ // attribute stays inert markup: Angular does not warn about an unknown
523
+ // attribute on a known element, the `contentChild` query returns undefined,
524
+ // the wrapper is never rendered, and the icon simply is not there. Nothing
525
+ // fails — which is what makes it slow to diagnose.
454
526
  effect(() => {
455
527
  this.syncDescribedByIds();
456
528
  });
@@ -494,12 +566,12 @@ class FormFieldComponent {
494
566
  control.setDescribedByIds(ids);
495
567
  }
496
568
  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 });
569
+ 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: "labelActionRef", first: true, predicate: FK_LABEL_ACTION, 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\n <!-- Trailing content on the label's row. Outside the <label> so it is not\n activated by a label click and does not join the input's accessible\n name. Rendered unconditionally for the same reason as the prefix and\n suffix slots: an unprojected node is never placed in the DOM, so\n gating this on the directive would make a forgotten import vanish\n silently. `:empty` keeps it from taking space. -->\n <div class=\"fk-form-field__label-action\">\n <ng-content select=\"[fkLabelAction]\" />\n </div>\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-action{margin-inline-start:auto;display:flex;align-items:baseline}.fk-form-field__label-action:empty{display:none}.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
570
  }
499
571
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: FormFieldComponent, decorators: [{
500
572
  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"] }]
502
- }], 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: [{
573
+ 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\n <!-- Trailing content on the label's row. Outside the <label> so it is not\n activated by a label click and does not join the input's accessible\n name. Rendered unconditionally for the same reason as the prefix and\n suffix slots: an unprojected node is never placed in the DOM, so\n gating this on the directive would make a forgotten import vanish\n silently. `:empty` keeps it from taking space. -->\n <div class=\"fk-form-field__label-action\">\n <ng-content select=\"[fkLabelAction]\" />\n </div>\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-action{margin-inline-start:auto;display:flex;align-items:baseline}.fk-form-field__label-action:empty{display:none}.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"] }]
574
+ }], 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 }] }], labelActionRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FK_LABEL_ACTION), { 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
575
  type: HostBinding,
504
576
  args: ['class']
505
577
  }] } });
@@ -3678,5 +3750,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3678
3750
  * Generated bundle index. Do not edit.
3679
3751
  */
3680
3752
 
3681
- export { AutocompleteComponent, CheckboxComponent, DEFAULT_ERROR_MESSAGES, DateInputComponent, DatetimeInputComponent, ERROR_MESSAGES, ErrorComponent, FIELD_CONTROL, FK_AUTOCOMPLETE_OPTION_TPL, FK_FILE_DROPZONE_ICON, FK_PREFIX, FK_SUFFIX, FORM, FieldArrayOutletComponent, FieldGroupComponent, FieldOutletComponent, FieldRegistry, FileDropzoneComponent, FileUploadComponent, FkAutocompleteOptionTplDirective, FkFileDropzoneIconDirective, FkPrefixDirective, FkSuffixDirective, FormComponent, FormFieldComponent, FormRendererComponent, FormSectionComponent, HintComponent, InputComponent, LabelComponent, NumberInputComponent, PasswordInputComponent, RadioComponent, SchemaParser, SectionOutletComponent, SegmentedBarComponent, SelectComponent, SliderComponent, SwitchComponent, TagInputComponent, TextareaComponent, TimeInputComponent, ValidationRegistry, getError, getFieldState, getInvalidFields, hasError, interpolateMessage, isFormValid, markAllTouched, resolveErrorMessage };
3753
+ export { AutocompleteComponent, CheckboxComponent, DEFAULT_ERROR_MESSAGES, DateInputComponent, DatetimeInputComponent, ERROR_MESSAGES, ErrorComponent, FIELD_CONTROL, FK_AUTOCOMPLETE_OPTION_TPL, FK_FILE_DROPZONE_ICON, FK_LABEL_ACTION, FK_PREFIX, FK_SUFFIX, FORM, FieldArrayOutletComponent, FieldGroupComponent, FieldOutletComponent, FieldRegistry, FileDropzoneComponent, FileUploadComponent, FkAutocompleteOptionTplDirective, FkFileDropzoneIconDirective, FkLabelActionDirective, FkPrefixDirective, FkSuffixDirective, FormComponent, FormFieldComponent, FormRendererComponent, FormSectionComponent, HintComponent, InputComponent, LabelComponent, NumberInputComponent, PasswordInputComponent, RadioComponent, SchemaParser, SectionOutletComponent, SegmentedBarComponent, SelectComponent, SliderComponent, SwitchComponent, TagInputComponent, TextareaComponent, TimeInputComponent, ValidationRegistry, getError, getFieldState, getInvalidFields, hasError, interpolateMessage, isFormValid, markAllTouched, resolveErrorMessage };
3682
3754
  //# sourceMappingURL=frame-kit-ui-ng-forms.mjs.map