@truenas/ui-components 0.2.0 → 0.2.1

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.
@@ -3,7 +3,7 @@ import { TemplatePortal, ComponentPortal, PortalModule } from '@angular/cdk/port
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, inject, Renderer2, ElementRef, input, effect, Directive, ViewContainerRef, output, viewChild, computed, signal, forwardRef, Component, model, afterNextRender, ChangeDetectionStrategy, Injectable, isDevMode, ViewEncapsulation, contentChildren, HostListener, contentChild, ChangeDetectorRef, DestroyRef, TemplateRef, isSignal, untracked, IterableDiffers, Pipe, ApplicationRef, EnvironmentInjector, createComponent, PLATFORM_ID } from '@angular/core';
5
5
  import * as i1$3 from '@angular/forms';
6
- import { NG_VALUE_ACCESSOR, FormsModule, NgControl } from '@angular/forms';
6
+ import { NG_VALUE_ACCESSOR, FormsModule, NgControl, Validators } from '@angular/forms';
7
7
  import { ComponentHarness, HarnessPredicate, TestKey } from '@angular/cdk/testing';
8
8
  import * as i1 from '@angular/cdk/a11y';
9
9
  import { A11yModule, FocusMonitor } from '@angular/cdk/a11y';
@@ -2628,6 +2628,36 @@ class TnInputComponent {
2628
2628
  * the a11y tree.
2629
2629
  */
2630
2630
  ariaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
2631
+ /**
2632
+ * Native `autocomplete` hint rendered on the input/textarea. Pass the standard
2633
+ * autofill tokens (`'username'`, `'current-password'`, `'new-password'`,
2634
+ * `'one-time-code'`, ...) so browsers and password managers can identify and
2635
+ * fill the field.
2636
+ */
2637
+ autocomplete = input(undefined, ...(ngDevMode ? [{ debugName: "autocomplete" }] : []));
2638
+ /**
2639
+ * Native `name` attribute rendered on the input/textarea. Browsers and password
2640
+ * managers use it to identify the field; typically mirrors the form control name.
2641
+ */
2642
+ name = input(undefined, ...(ngDevMode ? [{ debugName: "name" }] : []));
2643
+ /**
2644
+ * Renders the native `readonly` attribute: the value is visible, focusable and
2645
+ * selectable but not editable. Unlike `disabled`, a readonly field stays in the
2646
+ * tab order and its form control stays enabled.
2647
+ */
2648
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
2649
+ /**
2650
+ * Renders the native `required` attribute so browsers and assistive technology
2651
+ * expose the field as required. Validation itself stays with the consumer's form
2652
+ * validators (e.g. `Validators.required`); forms that fully own validation UX
2653
+ * should also set `novalidate` to suppress native submit blocking.
2654
+ *
2655
+ * Note: this drives only the native/a11y semantics. When wrapped in a
2656
+ * `tn-form-field`, the visual `*` indicator is inferred automatically from the
2657
+ * control's `Validators.required`; only validator-less setups need the form
2658
+ * field's own `required` input alongside this one.
2659
+ */
2660
+ required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
2631
2661
  /**
2632
2662
  * Integer/decimal switch — only meaningful when `inputType` is `InputType.Number`.
2633
2663
  *
@@ -2663,6 +2693,13 @@ class TnInputComponent {
2663
2693
  suffixIcon = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIcon" }] : []));
2664
2694
  suffixIconLibrary = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIconLibrary" }] : []));
2665
2695
  suffixIconAriaLabel = input(undefined, ...(ngDevMode ? [{ debugName: "suffixIconAriaLabel" }] : []));
2696
+ /**
2697
+ * Semantic test-id base for the suffix-action button. The library prepends the
2698
+ * element type (`button`) and renders the result under whichever attribute name
2699
+ * is configured via `TN_TEST_ATTR` (default `data-testid`) — e.g.
2700
+ * `suffixActionTestId="toggle-password"` → `button-toggle-password`.
2701
+ */
2702
+ suffixActionTestId = input(undefined, ...(ngDevMode ? [{ debugName: "suffixActionTestId" }] : []));
2666
2703
  onSuffixAction = output();
2667
2704
  hasPrefixIcon = computed(() => !!this.prefixIcon(), ...(ngDevMode ? [{ debugName: "hasPrefixIcon" }] : []));
2668
2705
  hasSuffixIcon = computed(() => !!this.suffixIcon(), ...(ngDevMode ? [{ debugName: "hasSuffixIcon" }] : []));
@@ -2818,13 +2855,13 @@ class TnInputComponent {
2818
2855
  return Number.isNaN(parsed) ? null : parsed;
2819
2856
  }
2820
2857
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2821
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnInputComponent, isStandalone: true, selector: "tn-input", inputs: { inputType: { classPropertyName: "inputType", publicName: "inputType", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiline: { classPropertyName: "multiline", publicName: "multiline", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, allowDecimals: { classPropertyName: "allowDecimals", publicName: "allowDecimals", isSignal: true, isRequired: false, transformFunction: null }, sizeStandard: { classPropertyName: "sizeStandard", publicName: "sizeStandard", isSignal: true, isRequired: false, transformFunction: null }, sizeDefaultUnit: { classPropertyName: "sizeDefaultUnit", publicName: "sizeDefaultUnit", isSignal: true, isRequired: false, transformFunction: null }, sizeRound: { classPropertyName: "sizeRound", publicName: "sizeRound", isSignal: true, isRequired: false, transformFunction: null }, prefixIcon: { classPropertyName: "prefixIcon", publicName: "prefixIcon", isSignal: true, isRequired: false, transformFunction: null }, prefixIconLibrary: { classPropertyName: "prefixIconLibrary", publicName: "prefixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIcon: { classPropertyName: "suffixIcon", publicName: "suffixIcon", isSignal: true, isRequired: false, transformFunction: null }, suffixIconLibrary: { classPropertyName: "suffixIconLibrary", publicName: "suffixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIconAriaLabel: { classPropertyName: "suffixIconAriaLabel", publicName: "suffixIconAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSuffixAction: "onSuffixAction" }, providers: [
2858
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnInputComponent, isStandalone: true, selector: "tn-input", inputs: { inputType: { classPropertyName: "inputType", publicName: "inputType", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiline: { classPropertyName: "multiline", publicName: "multiline", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, allowDecimals: { classPropertyName: "allowDecimals", publicName: "allowDecimals", isSignal: true, isRequired: false, transformFunction: null }, sizeStandard: { classPropertyName: "sizeStandard", publicName: "sizeStandard", isSignal: true, isRequired: false, transformFunction: null }, sizeDefaultUnit: { classPropertyName: "sizeDefaultUnit", publicName: "sizeDefaultUnit", isSignal: true, isRequired: false, transformFunction: null }, sizeRound: { classPropertyName: "sizeRound", publicName: "sizeRound", isSignal: true, isRequired: false, transformFunction: null }, prefixIcon: { classPropertyName: "prefixIcon", publicName: "prefixIcon", isSignal: true, isRequired: false, transformFunction: null }, prefixIconLibrary: { classPropertyName: "prefixIconLibrary", publicName: "prefixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIcon: { classPropertyName: "suffixIcon", publicName: "suffixIcon", isSignal: true, isRequired: false, transformFunction: null }, suffixIconLibrary: { classPropertyName: "suffixIconLibrary", publicName: "suffixIconLibrary", isSignal: true, isRequired: false, transformFunction: null }, suffixIconAriaLabel: { classPropertyName: "suffixIconAriaLabel", publicName: "suffixIconAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, suffixActionTestId: { classPropertyName: "suffixActionTestId", publicName: "suffixActionTestId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSuffixAction: "onSuffixAction" }, providers: [
2822
2859
  {
2823
2860
  provide: NG_VALUE_ACCESSOR,
2824
2861
  useExisting: forwardRef(() => TnInputComponent),
2825
2862
  multi: true
2826
2863
  }
2827
- ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!showTextarea()) {\n <input\n #inputEl\n class=\"tn-input\"\n tnTestIdType=\"input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"resolvedType()\"\n [attr.inputmode]=\"numericInputMode()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [tnTestId]=\"testId()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (showTextarea()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n tnTestIdType=\"textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [tnTestId]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: A11yModule }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }] });
2864
+ ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!showTextarea()) {\n <input\n #inputEl\n class=\"tn-input\"\n tnTestIdType=\"input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"resolvedType()\"\n [attr.inputmode]=\"numericInputMode()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.name]=\"name()\"\n [tnTestId]=\"testId()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (showTextarea()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n tnTestIdType=\"textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.name]=\"name()\"\n [tnTestId]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n tnTestIdType=\"button\"\n [tnTestId]=\"suffixActionTestId()\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: A11yModule }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }] });
2828
2865
  }
2829
2866
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnInputComponent, decorators: [{
2830
2867
  type: Component,
@@ -2834,8 +2871,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
2834
2871
  useExisting: forwardRef(() => TnInputComponent),
2835
2872
  multi: true
2836
2873
  }
2837
- ], template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!showTextarea()) {\n <input\n #inputEl\n class=\"tn-input\"\n tnTestIdType=\"input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"resolvedType()\"\n [attr.inputmode]=\"numericInputMode()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [tnTestId]=\"testId()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (showTextarea()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n tnTestIdType=\"textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [tnTestId]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"] }]
2838
- }], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], inputType: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputType", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], allowDecimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDecimals", required: false }] }], sizeStandard: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeStandard", required: false }] }], sizeDefaultUnit: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeDefaultUnit", required: false }] }], sizeRound: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeRound", required: false }] }], prefixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIcon", required: false }] }], prefixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIconLibrary", required: false }] }], suffixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIcon", required: false }] }], suffixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconLibrary", required: false }] }], suffixIconAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconAriaLabel", required: false }] }], onSuffixAction: [{ type: i0.Output, args: ["onSuffixAction"] }] } });
2874
+ ], template: "<div\n class=\"tn-input-container\"\n [class.tn-input-container--has-prefix]=\"hasPrefixIcon()\"\n [class.tn-input-container--has-suffix]=\"hasSuffixIcon()\"\n>\n <!-- Prefix icon -->\n @if (hasPrefixIcon()) {\n <tn-icon\n class=\"tn-input__prefix-icon\"\n size=\"sm\"\n aria-hidden=\"true\"\n [name]=\"prefixIcon()!\"\n [library]=\"prefixIconLibrary()\"\n />\n }\n\n <!-- Regular input field -->\n @if (!showTextarea()) {\n <input\n #inputEl\n class=\"tn-input\"\n tnTestIdType=\"input\"\n [id]=\"id\"\n [value]=\"value\"\n [type]=\"resolvedType()\"\n [attr.inputmode]=\"numericInputMode()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.name]=\"name()\"\n [tnTestId]=\"testId()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n />\n }\n\n <!-- Textarea field -->\n @if (showTextarea()) {\n <textarea\n #inputEl\n class=\"tn-input tn-textarea\"\n tnTestIdType=\"textarea\"\n [id]=\"id\"\n [value]=\"value\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.name]=\"name()\"\n [tnTestId]=\"testId()\"\n [rows]=\"rows()\"\n [disabled]=\"isDisabled()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n (input)=\"onValueChange($event)\"\n (blur)=\"onBlur()\"\n ></textarea>\n }\n\n <!-- Suffix icon action button -->\n @if (hasSuffixIcon()) {\n <button\n type=\"button\"\n class=\"tn-input__suffix-action\"\n tnTestIdType=\"button\"\n [tnTestId]=\"suffixActionTestId()\"\n [attr.aria-label]=\"suffixIconAriaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"onSuffixAction.emit($event)\"\n >\n <tn-icon\n size=\"sm\"\n [name]=\"suffixIcon()!\"\n [library]=\"suffixIconLibrary()\"\n />\n </button>\n }\n</div>\n", styles: [".tn-input-container{position:relative;display:flex;align-items:center;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif;background-color:var(--tn-bg1, #ffffff);border:1px solid var(--tn-lines, #d1d5db);border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.tn-input-container:focus-within{border-color:var(--tn-primary, #007bff);box-shadow:0 0 0 2px #007bff40}.tn-input-container:has(.tn-input:disabled){background-color:var(--tn-alt-bg1, #f8f9fa);opacity:.6;cursor:not-allowed}.tn-input-container:has(.tn-input.error){border-color:var(--tn-error, #dc3545)}.tn-input-container:has(.tn-input.error):focus-within{border-color:var(--tn-error, #dc3545);box-shadow:0 0 0 2px #dc354540}.tn-input{display:block;flex:1;min-width:0;min-height:2.5rem;padding:.5rem .75rem;font-size:1rem;line-height:1.5;color:var(--tn-fg1, #212529);background:transparent;border:none;border-radius:.375rem;outline:none;box-sizing:border-box}.tn-input::placeholder{color:var(--tn-alt-fg1, #999);opacity:1}.tn-input:disabled{color:var(--tn-fg2, #6c757d);cursor:not-allowed}.tn-input-container--has-prefix .tn-input{padding-left:.75rem;border-left:1px solid var(--tn-lines, #d1d5db);border-top-left-radius:0;border-bottom-left-radius:0}.tn-input-container--has-suffix .tn-input{padding-right:.25rem}.tn-input__prefix-icon{flex-shrink:0;margin-left:.75rem;margin-right:.75rem;color:var(--tn-fg2, #6c757d);pointer-events:none}.tn-input__suffix-action{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;margin-right:.375rem;padding:.25rem;border:none;border-radius:.25rem;background:transparent;color:var(--tn-fg2, #6c757d);cursor:pointer;transition:background-color .15s ease-in-out,color .15s ease-in-out}.tn-input__suffix-action:hover:not(:disabled){background-color:var(--tn-bg3, #f3f4f6);color:var(--tn-fg1, #1f2937)}.tn-input__suffix-action:focus-visible{outline:2px solid var(--tn-primary, #2563eb);outline-offset:1px}.tn-input__suffix-action:disabled{cursor:not-allowed;pointer-events:none}.tn-textarea{min-height:6rem;resize:vertical;line-height:1.4}@media(prefers-reduced-motion:reduce){.tn-input-container,.tn-input__suffix-action{transition:none}}@media(prefers-contrast:high){.tn-input-container{border-width:2px}}\n"] }]
2875
+ }], propDecorators: { inputEl: [{ type: i0.ViewChild, args: ['inputEl', { isSignal: true }] }], inputType: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputType", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], multiline: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiline", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], allowDecimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowDecimals", required: false }] }], sizeStandard: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeStandard", required: false }] }], sizeDefaultUnit: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeDefaultUnit", required: false }] }], sizeRound: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeRound", required: false }] }], prefixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIcon", required: false }] }], prefixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefixIconLibrary", required: false }] }], suffixIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIcon", required: false }] }], suffixIconLibrary: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconLibrary", required: false }] }], suffixIconAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixIconAriaLabel", required: false }] }], suffixActionTestId: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffixActionTestId", required: false }] }], onSuffixAction: [{ type: i0.Output, args: ["onSuffixAction"] }] } });
2839
2876
 
2840
2877
  /**
2841
2878
  * Harness for interacting with tn-icon in tests.
@@ -3106,6 +3143,9 @@ class TnInputHarness extends ComponentHarness {
3106
3143
  return new HarnessPredicate(TnInputHarness, options)
3107
3144
  .addOption('placeholder', options.placeholder, async (harness, placeholder) => {
3108
3145
  return (await harness.getPlaceholder()) === placeholder;
3146
+ })
3147
+ .addOption('name', options.name, async (harness, name) => {
3148
+ return (await harness.getName()) === name;
3109
3149
  });
3110
3150
  }
3111
3151
  /**
@@ -3206,6 +3246,58 @@ class TnInputHarness extends ComponentHarness {
3206
3246
  const input = await this.inputEl();
3207
3247
  return input.getAttribute('aria-label');
3208
3248
  }
3249
+ /**
3250
+ * Gets the native `name` attribute (set via the `name` input).
3251
+ *
3252
+ * @returns Promise resolving to the name string, or null if unset.
3253
+ */
3254
+ async getName() {
3255
+ if (await this.isMultiline()) {
3256
+ const textarea = await this.textareaEl();
3257
+ return textarea.getAttribute('name');
3258
+ }
3259
+ const input = await this.inputEl();
3260
+ return input.getAttribute('name');
3261
+ }
3262
+ /**
3263
+ * Gets the native `autocomplete` attribute (set via the `autocomplete` input).
3264
+ *
3265
+ * @returns Promise resolving to the autocomplete token, or null if unset.
3266
+ */
3267
+ async getAutocomplete() {
3268
+ if (await this.isMultiline()) {
3269
+ const textarea = await this.textareaEl();
3270
+ return textarea.getAttribute('autocomplete');
3271
+ }
3272
+ const input = await this.inputEl();
3273
+ return input.getAttribute('autocomplete');
3274
+ }
3275
+ /**
3276
+ * Checks whether the input is readonly (set via the `readonly` input).
3277
+ *
3278
+ * @returns Promise resolving to true if the input is readonly.
3279
+ */
3280
+ async isReadonly() {
3281
+ if (await this.isMultiline()) {
3282
+ const textarea = await this.textareaEl();
3283
+ return (await textarea.getProperty('readOnly')) ?? false;
3284
+ }
3285
+ const input = await this.inputEl();
3286
+ return (await input.getProperty('readOnly')) ?? false;
3287
+ }
3288
+ /**
3289
+ * Checks whether the input is marked required (set via the `required` input).
3290
+ *
3291
+ * @returns Promise resolving to true if the input is required.
3292
+ */
3293
+ async isRequired() {
3294
+ if (await this.isMultiline()) {
3295
+ const textarea = await this.textareaEl();
3296
+ return (await textarea.getProperty('required')) ?? false;
3297
+ }
3298
+ const input = await this.inputEl();
3299
+ return (await input.getProperty('required')) ?? false;
3300
+ }
3209
3301
  /**
3210
3302
  * Gets the placeholder text.
3211
3303
  *
@@ -6196,6 +6288,17 @@ function activeErrorKey(errors) {
6196
6288
  class TnFormFieldComponent {
6197
6289
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
6198
6290
  hint = input('', ...(ngDevMode ? [{ debugName: "hint" }] : []));
6291
+ /**
6292
+ * Forces the visual `*` required indicator next to the label. Usually
6293
+ * unnecessary: the indicator is inferred automatically when the projected
6294
+ * control carries `Validators.required`. Set this only when inference can't
6295
+ * see the requirement — e.g. a validator wrapped in `Validators.compose(...)`
6296
+ * or a custom validator that emits a `required`-style error.
6297
+ *
6298
+ * The indicator is purely visual — for native/a11y semantics pair it with the
6299
+ * projected control's own `required` input (e.g. `tn-input`'s, which renders
6300
+ * the native attribute).
6301
+ */
6199
6302
  required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
6200
6303
  testId = input(undefined, ...(ngDevMode ? [{ debugName: "testId" }] : []));
6201
6304
  subscriptSizing = input('dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : []));
@@ -6227,7 +6330,15 @@ class TnFormFieldComponent {
6227
6330
  invalid: false,
6228
6331
  interacted: false,
6229
6332
  errors: null,
6333
+ required: false,
6230
6334
  }, ...(ngDevMode ? [{ debugName: "controlState" }] : []));
6335
+ /**
6336
+ * Whether the required indicator renders: forced via the `required` input, or
6337
+ * inferred from the projected control's validators (mirrors Angular Material's
6338
+ * `hasValidator(Validators.required)` approach — reference equality, so composed
6339
+ * or custom required-like validators need the explicit input).
6340
+ */
6341
+ showRequired = computed(() => this.required() || this.controlState().required, ...(ngDevMode ? [{ debugName: "showRequired" }] : []));
6231
6342
  hasError = computed(() => {
6232
6343
  const state = this.controlState();
6233
6344
  return state.invalid && state.interacted;
@@ -6260,6 +6371,7 @@ class TnFormFieldComponent {
6260
6371
  invalid: !!control.invalid,
6261
6372
  interacted: !!(control.dirty || control.touched),
6262
6373
  errors: control.errors ?? null,
6374
+ required: !!control.control?.hasValidator(Validators.required),
6263
6375
  });
6264
6376
  }
6265
6377
  }
@@ -6331,11 +6443,11 @@ class TnFormFieldComponent {
6331
6443
  return this.subscriptSizing() === 'fixed' || this.showError() || this.showHint();
6332
6444
  }, ...(ngDevMode ? [{ debugName: "showSubscript" }] : []));
6333
6445
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6334
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-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 }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"], dependencies: [{ kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
6446
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnFormFieldComponent, isStandalone: true, selector: "tn-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 }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null }, subscriptSizing: { classPropertyName: "subscriptSizing", publicName: "subscriptSizing", isSignal: true, isRequired: false, transformFunction: null }, tooltip: { classPropertyName: "tooltip", publicName: "tooltip", isSignal: true, isRequired: false, transformFunction: null }, tooltipPosition: { classPropertyName: "tooltipPosition", publicName: "tooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, errorMessages: { classPropertyName: "errorMessages", publicName: "errorMessages", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "control", first: true, predicate: NgControl, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"showRequired()\">\n {{ label() }}\n @if (showRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"], dependencies: [{ kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "component", type: TnIconComponent, selector: "tn-icon", inputs: ["name", "size", "color", "tooltip", "ariaLabel", "library", "testId", "fullSize", "customSize"] }, { kind: "directive", type: TnTooltipDirective, selector: "[tnTooltip]", inputs: ["tnTooltip", "tnTooltipPosition", "tnTooltipDisabled", "tnTooltipShowDelay", "tnTooltipHideDelay", "tnTooltipClass"] }] });
6335
6447
  }
6336
6448
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnFormFieldComponent, decorators: [{
6337
6449
  type: Component,
6338
- args: [{ selector: 'tn-form-field', standalone: true, imports: [TnTestIdDirective, TnIconComponent, TnTooltipDirective], template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"required()\">\n {{ label() }}\n @if (required()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"] }]
6450
+ args: [{ selector: 'tn-form-field', standalone: true, imports: [TnTestIdDirective, TnIconComponent, TnTooltipDirective], template: "<div class=\"tn-form-field\" tnTestIdType=\"form-field\" [tnTestId]=\"testId()\">\n <!-- Label -->\n @if (label() || tooltip()) {\n <div class=\"tn-form-field-label-row\">\n @if (label()) {\n <label class=\"tn-form-field-label\" [class.required]=\"showRequired()\">\n {{ label() }}\n @if (showRequired()) {\n <span class=\"required-asterisk\" aria-label=\"required\">*</span>\n }\n </label>\n }\n @if (tooltip()) {\n <button\n type=\"button\"\n class=\"tn-form-field-tooltip\"\n [attr.aria-label]=\"tooltip()\"\n [tnTooltip]=\"tooltip()\"\n [tnTooltipPosition]=\"tooltipPosition()\">\n <tn-icon name=\"help-circle\" library=\"mdi\" size=\"sm\" />\n </button>\n }\n </div>\n }\n\n <!-- Form Control Content -->\n <div class=\"tn-form-field-wrapper\">\n <ng-content />\n </div>\n\n <!-- Hint or Error Message -->\n @if (showSubscript()) {\n <div class=\"tn-form-field-subscript\" [class.tn-form-field-subscript-dynamic]=\"subscriptSizing() === 'dynamic'\">\n @if (showError()) {\n <div\n class=\"tn-form-field-error\"\n role=\"alert\"\n aria-live=\"polite\">\n {{ errorMessage() }}\n </div>\n }\n @if (showHint()) {\n <div class=\"tn-form-field-hint\">\n {{ hint() }}\n </div>\n }\n </div>\n }\n</div>\n", styles: [".tn-form-field{display:block;width:100%;font-family:var(--tn-font-family-body, \"Inter\"),sans-serif}.tn-form-field-label-row{display:flex;align-items:center;gap:.375rem;margin-bottom:.5rem}.tn-form-field-label{display:block;font-size:1rem;font-weight:500;color:var(--tn-fg1, #333);line-height:1.4}.tn-form-field-label.required .required-asterisk{color:var(--tn-error, #dc3545);margin-left:.25rem}.tn-form-field-tooltip{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:transparent;color:var(--tn-fg2, #6c757d);cursor:help;line-height:0}.tn-form-field-tooltip:hover,.tn-form-field-tooltip:focus-visible{color:var(--tn-primary, #007bff)}.tn-form-field-tooltip:focus-visible{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px;border-radius:50%}.tn-form-field-wrapper{position:relative;width:100%;overflow:visible}.tn-form-field-wrapper :ng-deep .tn-select-container,.tn-form-field-wrapper :ng-deep .tn-input-container{margin-bottom:0}.tn-form-field-wrapper :ng-deep .tn-select-label,.tn-form-field-wrapper :ng-deep .tn-input-label{display:none}.tn-form-field-wrapper :ng-deep .tn-select-error,.tn-form-field-wrapper :ng-deep .tn-input-error{display:none}.tn-form-field-wrapper :ng-deep .tn-select-dropdown{z-index:1000}.tn-form-field-subscript{min-height:1.25rem;margin-top:.25rem;font-size:.75rem;line-height:1.4}.tn-form-field-subscript-dynamic{min-height:0}.tn-form-field-error{color:var(--tn-error, #dc3545);margin:0}.tn-form-field-hint{color:var(--tn-fg2, #6c757d);margin:0}.tn-form-field-wrapper:has(:focus-visible) .tn-form-field-label{color:var(--tn-primary, #007bff)}.tn-form-field-wrapper:has(.error) .tn-form-field-label{color:var(--tn-error, #dc3545)}@media(prefers-reduced-motion:reduce){.tn-form-field-label{transition:none}}@media(prefers-contrast:high){.tn-form-field-label,.tn-form-field-error{font-weight:600}}\n"] }]
6339
6451
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], tooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltip", required: false }] }], tooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPosition", required: false }] }], errorMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessages", required: false }] }], control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => NgControl), { isSignal: true }] }] } });
6340
6452
 
6341
6453
  /**