@truenas/ui-components 0.3.15 → 0.3.17

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.
@@ -8865,7 +8865,6 @@ class TnSelectComponent {
8865
8865
  }
8866
8866
  onChange = (_value) => { };
8867
8867
  onTouched = () => { };
8868
- elementRef = inject(ElementRef);
8869
8868
  cdr = inject(ChangeDetectorRef);
8870
8869
  overlay = inject(Overlay);
8871
8870
  viewContainerRef = inject(ViewContainerRef);
@@ -8937,11 +8936,19 @@ class TnSelectComponent {
8937
8936
  *
8938
8937
  * Why an overlay (vs. an inline absolutely-positioned panel):
8939
8938
  * - Escapes parent `overflow: hidden`/clipping in surrounding layouts.
8940
- * - `outsidePointerEvents()` notifies on outside pointerdown WITHOUT
8941
- * intercepting the click (no backdrop) — so the user's click reaches
8942
- * the underlying target while the select closes silently.
8943
8939
  * - Position is recomputed on scroll so the panel stays attached.
8944
- * - Width is matched to the trigger so the panel doesn't jump in size.
8940
+ *
8941
+ * Dismissal uses a transparent, full-viewport backdrop (`backdropClick()`).
8942
+ * We previously ran backdrop-less with `outsidePointerEvents()`, but that
8943
+ * only closes when the click lands strictly OUTSIDE the overlay pane — and
8944
+ * the pane was sized to the (often full-width) trigger while the panel itself
8945
+ * is only as wide as its content. The empty pane area to the right of the
8946
+ * options stayed `pointer-events: auto`, so clicks there never counted as
8947
+ * "outside" and the dropdown wouldn't close. A transparent backdrop closes on
8948
+ * ANY click outside the panel regardless of pane geometry — the standard
8949
+ * pattern used by native `<select>` and Material's `mat-select`. We therefore
8950
+ * also drop the explicit `width`: the pane sizes to the panel content so the
8951
+ * clickable surface matches what the user sees.
8945
8952
  */
8946
8953
  attachOverlay() {
8947
8954
  const trigger = this.triggerEl().nativeElement;
@@ -8955,25 +8962,17 @@ class TnSelectComponent {
8955
8962
  this.overlayRef = this.overlay.create({
8956
8963
  positionStrategy,
8957
8964
  scrollStrategy: this.overlay.scrollStrategies.reposition(),
8958
- hasBackdrop: false,
8959
- width: trigger.offsetWidth,
8965
+ hasBackdrop: true,
8966
+ backdropClass: 'cdk-overlay-transparent-backdrop',
8960
8967
  });
8961
8968
  const portal = new TemplatePortal(this.dropdownTemplate(), this.viewContainerRef);
8962
8969
  this.overlayRef.attach(portal);
8963
- // Click-outside (non-intercepting). The pointer event still reaches the
8964
- // element the user clicked; we just notice and close.
8965
- //
8966
- // Important: ignore events whose target is inside the select host. A
8967
- // pointerdown on the trigger is "outside the overlay" from CDK's POV but
8968
- // it's our own toggle target — letting closeDropdown fire here races the
8969
- // trigger's click handler and the dropdown immediately reopens.
8970
- this.overlaySubs.push(this.overlayRef.outsidePointerEvents().subscribe((event) => {
8971
- const target = event.target;
8972
- if (target && this.elementRef.nativeElement.contains(target)) {
8973
- return;
8974
- }
8975
- this.closeDropdown(false);
8976
- }));
8970
+ // Dismiss on any click outside the panel. The transparent backdrop spans
8971
+ // the viewport and captures the click, so this fires no matter where the
8972
+ // user clicks (including the empty area beside a narrow panel). Clicking
8973
+ // the trigger while open also hits the backdrop it closes here and the
8974
+ // trigger's own click never fires, so there's no reopen race.
8975
+ this.overlaySubs.push(this.overlayRef.backdropClick().subscribe(() => this.closeDropdown(false)));
8977
8976
  // Escape as a fallback (the trigger keydown handler covers the common case,
8978
8977
  // but if focus ever moves into the panel, this catches it too).
8979
8978
  this.overlaySubs.push(this.overlayRef.keydownEvents().subscribe((event) => {
@@ -15738,10 +15737,12 @@ class TnStepComponent {
15738
15737
  optional = input(false, ...(ngDevMode ? [{ debugName: "optional" }] : []));
15739
15738
  completed = input(false, ...(ngDevMode ? [{ debugName: "completed" }] : []));
15740
15739
  hasError = input(false, ...(ngDevMode ? [{ debugName: "hasError" }] : []));
15740
+ /** Message shown beneath the step label while the step is in an error state. */
15741
+ errorMessage = input(undefined, ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
15741
15742
  data = input(null, ...(ngDevMode ? [{ debugName: "data" }] : []));
15742
15743
  content = viewChild.required('content');
15743
15744
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15744
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.0", type: TnStepComponent, isStandalone: true, selector: "tn-step", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, completed: { classPropertyName: "completed", publicName: "completed", isSignal: true, isRequired: false, transformFunction: null }, hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true, isSignal: true }], ngImport: i0, template: `
15745
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.1.0", type: TnStepComponent, isStandalone: true, selector: "tn-step", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, completed: { classPropertyName: "completed", publicName: "completed", isSignal: true, isRequired: false, transformFunction: null }, hasError: { classPropertyName: "hasError", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "content", first: true, predicate: ["content"], descendants: true, isSignal: true }], ngImport: i0, template: `
15745
15746
  <ng-template #content>
15746
15747
  <ng-content />
15747
15748
  </ng-template>
@@ -15758,7 +15759,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
15758
15759
  `,
15759
15760
  standalone: true
15760
15761
  }]
15761
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], completed: [{ type: i0.Input, args: [{ isSignal: true, alias: "completed", required: false }] }], hasError: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasError", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }] } });
15762
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], completed: [{ type: i0.Input, args: [{ isSignal: true, alias: "completed", required: false }] }], hasError: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasError", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }] } });
15762
15763
 
15763
15764
  class TnStepperComponent {
15764
15765
  orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : []));
@@ -15772,8 +15773,15 @@ class TnStepperComponent {
15772
15773
  selectionChange = output();
15773
15774
  completed = output();
15774
15775
  steps = contentChildren(TnStepComponent, { ...(ngDevMode ? { debugName: "steps" } : {}), descendants: true });
15775
- cdr = inject(ChangeDetectorRef);
15776
+ // Highest step index the user has navigated to. Used to reveal the "edit" (pencil)
15777
+ // affordance only on steps that have actually been visited — a step that is valid by
15778
+ // default but never reached stays a plain number.
15779
+ maxReachedIndex = signal(0, ...(ngDevMode ? [{ debugName: "maxReachedIndex" }] : []));
15776
15780
  constructor() {
15781
+ effect(() => {
15782
+ const index = this.selectedIndex();
15783
+ this.maxReachedIndex.update((max) => Math.max(max, index));
15784
+ });
15777
15785
  // Effect to check if all steps are completed
15778
15786
  effect(() => {
15779
15787
  // Trigger on any step completion change
@@ -15784,8 +15792,8 @@ class TnStepperComponent {
15784
15792
  }
15785
15793
  });
15786
15794
  }
15787
- onWindowResize(_event) {
15788
- this.cdr.detectChanges();
15795
+ onWindowResize() {
15796
+ this.isWideScreen.set(window.innerWidth > 768);
15789
15797
  }
15790
15798
  _getStepData() {
15791
15799
  return this.steps().map(step => ({
@@ -15794,10 +15802,33 @@ class TnStepperComponent {
15794
15802
  data: step.data()
15795
15803
  }));
15796
15804
  }
15797
- isWideScreen = computed(() => {
15798
- // Note: This will only update on window resize due to ChangeDetectorRef trigger
15799
- return window.innerWidth > 768;
15800
- }, ...(ngDevMode ? [{ debugName: "isWideScreen" }] : []));
15805
+ // Tracks whether the viewport is wide enough for the horizontal layout. A signal
15806
+ // (not a computed over `window.innerWidth`, which is non-reactive and would freeze on
15807
+ // first read) so `onWindowResize` can `.set()` it and `auto` orientation re-evaluates.
15808
+ isWideScreen = signal(window.innerWidth > 768, ...(ngDevMode ? [{ debugName: "isWideScreen" }] : []));
15809
+ // Vertical mode lays the active step's content out inline beneath its header
15810
+ // (mat-vertical-stepper style), so it fits narrow containers such as side panels.
15811
+ isVertical = computed(() => {
15812
+ return this.orientation() === 'vertical' || (this.orientation() === 'auto' && !this.isWideScreen());
15813
+ }, ...(ngDevMode ? [{ debugName: "isVertical" }] : []));
15814
+ // Per-index "editable" flags. A step shows the "edit" (pencil) icon once it has been
15815
+ // visited and is valid (completed) but isn't the current step — signalling the user can
15816
+ // go back and change it. Computed (not a per-call method) so it's evaluated once per
15817
+ // change-detection cycle and cheaply indexed in the template, even as step counts grow.
15818
+ stepEditable = computed(() => {
15819
+ const max = this.maxReachedIndex();
15820
+ const current = this.selectedIndex();
15821
+ return this.steps().map((step, index) => !!step.completed() && index <= max && index !== current);
15822
+ }, ...(ngDevMode ? [{ debugName: "stepEditable" }] : []));
15823
+ // Per-index "gated" flags for linear mode: a step is gated (not yet selectable) while
15824
+ // any prior step is incomplete. Memoized so the header's aria-disabled / tabindex
15825
+ // bindings don't re-run canSelectStep() for every step on each change-detection cycle.
15826
+ stepGated = computed(() => {
15827
+ if (!this.linear()) {
15828
+ return this.steps().map(() => false);
15829
+ }
15830
+ return this.steps().map((_step, index) => !this.canSelectStep(index));
15831
+ }, ...(ngDevMode ? [{ debugName: "stepGated" }] : []));
15801
15832
  selectStep(index) {
15802
15833
  if (!this.linear() || this.canSelectStep(index)) {
15803
15834
  const previousIndex = this.selectedIndex();
@@ -15836,7 +15867,7 @@ class TnStepperComponent {
15836
15867
  return index;
15837
15868
  }
15838
15869
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15839
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnStepperComponent, isStandalone: true, selector: "tn-stepper", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", selectionChange: "selectionChange", completed: "completed" }, host: { listeners: { "window:resize": "onWindowResize($event)" } }, queries: [{ propertyName: "steps", predicate: TnStepComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-stepper\"\n tnTestIdType=\"stepper\"\n [class.tn-stepper--horizontal]=\"orientation() === 'horizontal' || (orientation() === 'auto' && isWideScreen())\"\n [class.tn-stepper--vertical]=\"orientation() === 'vertical' || (orientation() === 'auto' && !isWideScreen())\"\n [tnTestId]=\"testId()\">\n\n <!-- Step Headers -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <div class=\"tn-stepper__step-header\"\n tabindex=\"0\"\n role=\"button\"\n [class.tn-stepper__step-header--active]=\"selectedIndex() === i\"\n [class.tn-stepper__step-header--completed]=\"step.completed()\"\n [class.tn-stepper__step-header--error]=\"step.hasError()\"\n [class.tn-stepper__step-header--optional]=\"step.optional()\"\n (click)=\"selectStep(i)\"\n (keydown.enter)=\"selectStep(i)\"\n (keydown.space)=\"selectStep(i)\">\n\n <!-- Step Number/Icon -->\n <div class=\"tn-stepper__step-indicator\">\n @if (step.completed() && !step.hasError()) {\n <span class=\"tn-stepper__step-check\">\u2713</span>\n }\n @if (step.hasError()) {\n <span class=\"tn-stepper__step-error\">!</span>\n }\n @if (!step.completed() && !step.hasError()) {\n @if (step.icon()) {\n <span class=\"tn-stepper__step-icon\">{{ step.icon() }}</span>\n } @else {\n <span class=\"tn-stepper__step-number\">{{ i + 1 }}</span>\n }\n }\n </div>\n\n <!-- Step Label -->\n <div class=\"tn-stepper__step-label\">\n <div class=\"tn-stepper__step-title\" [innerHTML]=\"step.label() | tnLabelMarkup\"></div>\n @if (step.optional()) {\n <span class=\"tn-stepper__step-subtitle\">Optional</span>\n }\n </div>\n </div>\n\n <!-- Connector Line (except for last step) -->\n @if (i < steps().length - 1) {\n <div class=\"tn-stepper__connector\"></div>\n }\n }\n </div>\n\n <!-- Step Content -->\n <div class=\"tn-stepper__content\">\n @for (step of steps(); track $index; let i = $index) {\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\"\n [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n }\n </div>\n</div>", styles: [".tn-stepper{display:flex;font-family:inherit;--step-diameter: 48px;--step-diameter-sm: 32px;--step-padding: 12px}.tn-stepper--horizontal{flex-direction:column}.tn-stepper--horizontal .tn-stepper__header{display:flex;justify-content:center;margin-bottom:32px;padding:0 16px}.tn-stepper--horizontal .tn-stepper__step-header{display:flex;flex-direction:column;align-items:center;text-align:center;cursor:pointer;transition:all .2s ease-in-out}.tn-stepper--horizontal .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--horizontal .tn-stepper__connector{flex:1;height:2px;background:var(--tn-lines, #e5e7eb);margin:0 16px;position:relative;top:calc(var(--step-diameter) / 2)}.tn-stepper--vertical{flex-direction:row}.tn-stepper--vertical .tn-stepper__header{display:flex;flex-direction:column;width:280px;padding:16px;border-right:1px solid var(--tn-lines, #e5e7eb)}.tn-stepper--vertical .tn-stepper__step-header{display:flex;flex-direction:row;align-items:center;text-align:left;cursor:pointer;transition:all .2s ease-in-out;padding:var(--step-padding);border-radius:8px;margin-bottom:8px}.tn-stepper--vertical .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin-left:12px;margin-top:0}.tn-stepper--vertical .tn-stepper__connector{width:2px;height:24px;background:var(--tn-lines, #e5e7eb);position:relative;left:calc(var(--step-diameter) / 2 + var(--step-padding));margin-bottom:8px}.tn-stepper--vertical .tn-stepper__content{flex:1;padding:16px}.tn-stepper__step-indicator{display:flex;align-items:center;justify-content:center;width:var(--step-diameter);height:var(--step-diameter);border-radius:50%;background:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-alt-fg1, #495057);font-weight:400;font-size:14px;transition:all .2s ease-in-out;position:relative;transform:scale(.65)}.tn-stepper__step-number{font-weight:400}.tn-stepper__step-label{margin-top:8px}.tn-stepper__step-title ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-stepper__step-title{display:block;font-weight:400;font-size:14px;color:var(--tn-fg1, #000000);line-height:1.2}.tn-stepper__step-subtitle{display:block;font-size:12px;color:var(--tn-fg2, #6c757d);margin-top:2px}.tn-stepper__step-header--active .tn-stepper__step-indicator{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #ffffff);transform:scale(1)}.tn-stepper__step-header--active .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--completed .tn-stepper__step-indicator{background:var(--tn-green, #28a745);color:#fff}.tn-stepper__step-header--completed .tn-stepper__step-check{font-size:1rem;font-weight:700}.tn-stepper__step-header--error .tn-stepper__step-indicator{background:var(--tn-red, #dc3545);color:#fff}.tn-stepper__step-header--error .tn-stepper__step-error{font-size:18px;font-weight:700}.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg1, #000000)}.tn-stepper__step-header--active.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--optional .tn-stepper__step-indicator{border:2px dashed var(--tn-lines, #e5e7eb);background:transparent}.tn-stepper--horizontal .tn-stepper__step-header--completed+.tn-stepper__connector{background:var(--tn-green, #28a745)}.tn-stepper--horizontal .tn-stepper__step-header--completed+.tn-stepper__connector:after{content:\"\";position:absolute;top:0;left:0;right:0;height:100%;background:var(--tn-green, #28a745);animation:progressFill .3s ease-in-out}.tn-stepper--vertical .tn-stepper__step-header--completed+.tn-stepper__connector{background:var(--tn-green, #28a745)}.tn-stepper__content{min-height:200px}.tn-stepper__step-content{padding:16px;background:var(--tn-bg1, #ffffff);border-radius:8px;border:1px solid var(--tn-lines, #e5e7eb)}@keyframes progressFill{0%{width:0}to{width:100%}}.tn-stepper__step-header:focus{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px}.tn-stepper__step-header:focus:not(:focus-visible){outline:none}@media(max-width:780px){.tn-stepper--vertical .tn-stepper__header{width:180px;border-right:none;border-bottom:1px solid var(--tn-lines, #e5e7eb);padding:16px 0}.tn-stepper--vertical .tn-stepper__step-header{flex-direction:column;align-items:center;text-align:center;min-width:80px;padding:8px 4px}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin-left:0;margin-top:8px}.tn-stepper--vertical .tn-stepper__connector{display:none}}@media(max-width:780px){.tn-stepper .tn-stepper__step-label{display:none}.tn-stepper .tn-stepper__step-indicator{width:var(--step-diameter-sm);height:var(--step-diameter-sm);font-size:12px}.tn-stepper--horizontal .tn-stepper__header{padding:0 8px}.tn-stepper--horizontal .tn-stepper__connector{top:calc(var(--step-diameter-sm) / 2);margin:0 8px}.tn-stepper--vertical .tn-stepper__header{width:60px}.tn-stepper--vertical .tn-stepper__step-header{padding:4px;margin-bottom:4px}.tn-stepper--vertical .tn-stepper__connector{left:calc(var(--step-diameter-sm) / 2 + 4px);height:16px;margin-bottom:4px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TnTestIdDirective, selector: "[tnTestId]", inputs: ["tnTestId", "tnTestIdType"] }, { kind: "pipe", type: LabelMarkupPipe, name: "tnLabelMarkup" }], animations: [
15870
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: TnStepperComponent, isStandalone: true, selector: "tn-stepper", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null }, testId: { classPropertyName: "testId", publicName: "testId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", selectionChange: "selectionChange", completed: "completed" }, host: { listeners: { "window:resize": "onWindowResize()" } }, queries: [{ propertyName: "steps", predicate: TnStepComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"tn-stepper\"\n tnTestIdType=\"stepper\"\n [class.tn-stepper--horizontal]=\"!isVertical()\"\n [class.tn-stepper--vertical]=\"isVertical()\"\n [tnTestId]=\"testId()\">\n\n <!-- Shared step header (indicator + label) -->\n <ng-template #stepHeader let-step=\"step\" let-i=\"i\">\n <div class=\"tn-stepper__step-header\"\n role=\"button\"\n [attr.tabindex]=\"stepGated()[i] ? -1 : 0\"\n [attr.aria-current]=\"selectedIndex() === i ? 'step' : null\"\n [attr.aria-disabled]=\"stepGated()[i] ? 'true' : null\"\n [class.tn-stepper__step-header--active]=\"selectedIndex() === i\"\n [class.tn-stepper__step-header--completed]=\"step.completed()\"\n [class.tn-stepper__step-header--error]=\"step.hasError()\"\n [class.tn-stepper__step-header--optional]=\"step.optional()\"\n (click)=\"selectStep(i)\"\n (keydown.enter)=\"selectStep(i)\"\n (keydown.space)=\"$event.preventDefault(); selectStep(i)\">\n\n <!-- Step Number/Icon -->\n <div class=\"tn-stepper__step-indicator\"\n [class.tn-stepper__step-indicator--glyph]=\"step.hasError()\">\n @if (step.hasError()) {\n <tn-icon class=\"tn-stepper__step-error\" name=\"alert\" library=\"mdi\" [attr.aria-hidden]=\"true\" />\n } @else if (stepEditable()[i]) {\n <tn-icon class=\"tn-stepper__step-edit\" name=\"pencil\" library=\"mdi\" [attr.aria-hidden]=\"true\" />\n } @else if (step.icon()) {\n <span class=\"tn-stepper__step-icon\">{{ step.icon() }}</span>\n } @else {\n <span class=\"tn-stepper__step-number\">{{ i + 1 }}</span>\n }\n </div>\n\n <!-- Step Label -->\n <div class=\"tn-stepper__step-label\">\n <div class=\"tn-stepper__step-title\" [innerHTML]=\"step.label() | tnLabelMarkup\"></div>\n @if (step.hasError() && step.errorMessage()) {\n <span class=\"tn-stepper__step-error-message\">{{ step.errorMessage() }}</span>\n } @else if (step.optional()) {\n <span class=\"tn-stepper__step-subtitle\">Optional</span>\n }\n </div>\n </div>\n </ng-template>\n\n @if (isVertical()) {\n <!-- Vertical: content renders inline beneath the active step's header -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <div class=\"tn-stepper__step\" [class.tn-stepper__step--last]=\"i === steps().length - 1\">\n <ng-container *ngTemplateOutlet=\"stepHeader; context: { step, i }\" />\n\n <div class=\"tn-stepper__step-body\">\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\" [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- Horizontal: headers in a row, active content below -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <ng-container *ngTemplateOutlet=\"stepHeader; context: { step, i }\" />\n\n @if (i < steps().length - 1) {\n <div class=\"tn-stepper__connector\"></div>\n }\n }\n </div>\n\n <div class=\"tn-stepper__content\">\n @for (step of steps(); track $index; let i = $index) {\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\"\n [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [".tn-stepper{display:flex;font-family:inherit;--step-diameter: 48px;--step-diameter-sm: 32px;--step-padding: 12px;--v-indicator: 32px;--v-gap: 12px}.tn-stepper--horizontal{flex-direction:column}.tn-stepper--horizontal .tn-stepper__header{display:flex;justify-content:center;margin-bottom:32px;padding:0 16px}.tn-stepper--horizontal .tn-stepper__step-header{display:flex;flex-direction:column;align-items:center;text-align:center;cursor:pointer;transition:all .2s ease-in-out}.tn-stepper--horizontal .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--horizontal .tn-stepper__connector{flex:1;height:2px;background:var(--tn-lines, #e5e7eb);margin:0 16px;position:relative;top:calc(var(--step-diameter) / 2)}.tn-stepper--vertical{flex-direction:column}.tn-stepper--vertical .tn-stepper__header,.tn-stepper--vertical .tn-stepper__step{display:flex;flex-direction:column}.tn-stepper--vertical .tn-stepper__step-header{display:flex;flex-direction:row;align-items:center;gap:var(--v-gap);text-align:left;cursor:pointer;padding:8px 0}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin:0}.tn-stepper--vertical .tn-stepper__step-indicator{width:var(--v-indicator);height:var(--v-indicator);min-width:var(--v-indicator);font-size:13px;transform:none}.tn-stepper--vertical .tn-stepper__step-indicator--glyph tn-icon{--tn-icon-size: var(--v-indicator)}.tn-stepper--vertical .tn-stepper__step-header--active .tn-stepper__step-indicator{transform:none}.tn-stepper--vertical .tn-stepper__step-body{margin-left:calc(var(--v-indicator) / 2 - 1px);border-left:2px solid var(--tn-lines, #e5e7eb);padding-left:calc(var(--v-indicator) / 2 + var(--v-gap));padding-bottom:8px;min-height:12px}.tn-stepper--vertical .tn-stepper__step--last .tn-stepper__step-body{border-left-color:transparent;min-height:0}.tn-stepper--vertical .tn-stepper__step-content{padding:4px 0 8px;background:transparent;border:none;border-radius:0}.tn-stepper__step-indicator{display:flex;align-items:center;justify-content:center;width:var(--step-diameter);height:var(--step-diameter);border-radius:50%;background:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-alt-fg1, #495057);font-weight:400;font-size:14px;transition:all .2s ease-in-out;position:relative;transform:scale(.65)}.tn-stepper__step-number{font-weight:400}.tn-stepper__step-edit{--tn-icon-size: 18px}.tn-stepper__step-label{margin-top:8px}.tn-stepper__step-title ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-stepper__step-title{display:block;font-weight:400;font-size:14px;color:var(--tn-fg1, #000000);line-height:1.2}.tn-stepper__step-subtitle{display:block;font-size:12px;color:var(--tn-fg2, #6c757d);margin-top:2px}.tn-stepper__step-error-message{display:block;font-size:12px;color:var(--tn-red, #dc3545);margin-top:2px}.tn-stepper__step-header--active .tn-stepper__step-indicator{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #ffffff);transform:scale(1)}.tn-stepper__step-header--active .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-indicator.tn-stepper__step-indicator--glyph{background:transparent;transform:scale(1)}.tn-stepper__step-indicator.tn-stepper__step-indicator--glyph tn-icon{--tn-icon-size: var(--step-diameter);line-height:1}.tn-stepper__step-header--active .tn-stepper__step-indicator.tn-stepper__step-indicator--glyph{background:transparent}.tn-stepper__step-header--error .tn-stepper__step-error{--tn-icon-color: var(--tn-red, #dc3545)}.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg1, #000000)}.tn-stepper__step-header--active.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--optional .tn-stepper__step-indicator{border:2px dashed var(--tn-lines, #e5e7eb);background:transparent}.tn-stepper__content{min-height:200px}.tn-stepper__step-content{padding:16px;background:var(--tn-bg1, #ffffff);border-radius:8px;border:1px solid var(--tn-lines, #e5e7eb)}.tn-stepper__step-header:focus{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px}.tn-stepper__step-header:focus:not(:focus-visible){outline:none}.tn-stepper__step-header[aria-disabled=true]{cursor:not-allowed}@media(max-width:780px){.tn-stepper--horizontal .tn-stepper__step-label{display:none}.tn-stepper--horizontal .tn-stepper__step-indicator{width:var(--step-diameter-sm);height:var(--step-diameter-sm);font-size:12px}.tn-stepper--horizontal .tn-stepper__header{padding:0 8px}.tn-stepper--horizontal .tn-stepper__connector{top:calc(var(--step-diameter-sm) / 2);margin:0 8px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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: "pipe", type: LabelMarkupPipe, name: "tnLabelMarkup" }], animations: [
15840
15871
  trigger('stepTransition', [
15841
15872
  transition(':enter', [
15842
15873
  style({ opacity: 0, transform: 'translateX(50px)' }),
@@ -15847,7 +15878,7 @@ class TnStepperComponent {
15847
15878
  }
15848
15879
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperComponent, decorators: [{
15849
15880
  type: Component,
15850
- args: [{ selector: 'tn-stepper', standalone: true, imports: [CommonModule, TnTestIdDirective, LabelMarkupPipe], animations: [
15881
+ args: [{ selector: 'tn-stepper', standalone: true, imports: [CommonModule, TnTestIdDirective, LabelMarkupPipe, TnIconComponent], animations: [
15851
15882
  trigger('stepTransition', [
15852
15883
  transition(':enter', [
15853
15884
  style({ opacity: 0, transform: 'translateX(50px)' }),
@@ -15855,10 +15886,139 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
15855
15886
  ])
15856
15887
  ])
15857
15888
  ], host: {
15858
- '(window:resize)': 'onWindowResize($event)'
15859
- }, template: "<div class=\"tn-stepper\"\n tnTestIdType=\"stepper\"\n [class.tn-stepper--horizontal]=\"orientation() === 'horizontal' || (orientation() === 'auto' && isWideScreen())\"\n [class.tn-stepper--vertical]=\"orientation() === 'vertical' || (orientation() === 'auto' && !isWideScreen())\"\n [tnTestId]=\"testId()\">\n\n <!-- Step Headers -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <div class=\"tn-stepper__step-header\"\n tabindex=\"0\"\n role=\"button\"\n [class.tn-stepper__step-header--active]=\"selectedIndex() === i\"\n [class.tn-stepper__step-header--completed]=\"step.completed()\"\n [class.tn-stepper__step-header--error]=\"step.hasError()\"\n [class.tn-stepper__step-header--optional]=\"step.optional()\"\n (click)=\"selectStep(i)\"\n (keydown.enter)=\"selectStep(i)\"\n (keydown.space)=\"selectStep(i)\">\n\n <!-- Step Number/Icon -->\n <div class=\"tn-stepper__step-indicator\">\n @if (step.completed() && !step.hasError()) {\n <span class=\"tn-stepper__step-check\">\u2713</span>\n }\n @if (step.hasError()) {\n <span class=\"tn-stepper__step-error\">!</span>\n }\n @if (!step.completed() && !step.hasError()) {\n @if (step.icon()) {\n <span class=\"tn-stepper__step-icon\">{{ step.icon() }}</span>\n } @else {\n <span class=\"tn-stepper__step-number\">{{ i + 1 }}</span>\n }\n }\n </div>\n\n <!-- Step Label -->\n <div class=\"tn-stepper__step-label\">\n <div class=\"tn-stepper__step-title\" [innerHTML]=\"step.label() | tnLabelMarkup\"></div>\n @if (step.optional()) {\n <span class=\"tn-stepper__step-subtitle\">Optional</span>\n }\n </div>\n </div>\n\n <!-- Connector Line (except for last step) -->\n @if (i < steps().length - 1) {\n <div class=\"tn-stepper__connector\"></div>\n }\n }\n </div>\n\n <!-- Step Content -->\n <div class=\"tn-stepper__content\">\n @for (step of steps(); track $index; let i = $index) {\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\"\n [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n }\n </div>\n</div>", styles: [".tn-stepper{display:flex;font-family:inherit;--step-diameter: 48px;--step-diameter-sm: 32px;--step-padding: 12px}.tn-stepper--horizontal{flex-direction:column}.tn-stepper--horizontal .tn-stepper__header{display:flex;justify-content:center;margin-bottom:32px;padding:0 16px}.tn-stepper--horizontal .tn-stepper__step-header{display:flex;flex-direction:column;align-items:center;text-align:center;cursor:pointer;transition:all .2s ease-in-out}.tn-stepper--horizontal .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--horizontal .tn-stepper__connector{flex:1;height:2px;background:var(--tn-lines, #e5e7eb);margin:0 16px;position:relative;top:calc(var(--step-diameter) / 2)}.tn-stepper--vertical{flex-direction:row}.tn-stepper--vertical .tn-stepper__header{display:flex;flex-direction:column;width:280px;padding:16px;border-right:1px solid var(--tn-lines, #e5e7eb)}.tn-stepper--vertical .tn-stepper__step-header{display:flex;flex-direction:row;align-items:center;text-align:left;cursor:pointer;transition:all .2s ease-in-out;padding:var(--step-padding);border-radius:8px;margin-bottom:8px}.tn-stepper--vertical .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin-left:12px;margin-top:0}.tn-stepper--vertical .tn-stepper__connector{width:2px;height:24px;background:var(--tn-lines, #e5e7eb);position:relative;left:calc(var(--step-diameter) / 2 + var(--step-padding));margin-bottom:8px}.tn-stepper--vertical .tn-stepper__content{flex:1;padding:16px}.tn-stepper__step-indicator{display:flex;align-items:center;justify-content:center;width:var(--step-diameter);height:var(--step-diameter);border-radius:50%;background:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-alt-fg1, #495057);font-weight:400;font-size:14px;transition:all .2s ease-in-out;position:relative;transform:scale(.65)}.tn-stepper__step-number{font-weight:400}.tn-stepper__step-label{margin-top:8px}.tn-stepper__step-title ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-stepper__step-title{display:block;font-weight:400;font-size:14px;color:var(--tn-fg1, #000000);line-height:1.2}.tn-stepper__step-subtitle{display:block;font-size:12px;color:var(--tn-fg2, #6c757d);margin-top:2px}.tn-stepper__step-header--active .tn-stepper__step-indicator{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #ffffff);transform:scale(1)}.tn-stepper__step-header--active .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--completed .tn-stepper__step-indicator{background:var(--tn-green, #28a745);color:#fff}.tn-stepper__step-header--completed .tn-stepper__step-check{font-size:1rem;font-weight:700}.tn-stepper__step-header--error .tn-stepper__step-indicator{background:var(--tn-red, #dc3545);color:#fff}.tn-stepper__step-header--error .tn-stepper__step-error{font-size:18px;font-weight:700}.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg1, #000000)}.tn-stepper__step-header--active.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--optional .tn-stepper__step-indicator{border:2px dashed var(--tn-lines, #e5e7eb);background:transparent}.tn-stepper--horizontal .tn-stepper__step-header--completed+.tn-stepper__connector{background:var(--tn-green, #28a745)}.tn-stepper--horizontal .tn-stepper__step-header--completed+.tn-stepper__connector:after{content:\"\";position:absolute;top:0;left:0;right:0;height:100%;background:var(--tn-green, #28a745);animation:progressFill .3s ease-in-out}.tn-stepper--vertical .tn-stepper__step-header--completed+.tn-stepper__connector{background:var(--tn-green, #28a745)}.tn-stepper__content{min-height:200px}.tn-stepper__step-content{padding:16px;background:var(--tn-bg1, #ffffff);border-radius:8px;border:1px solid var(--tn-lines, #e5e7eb)}@keyframes progressFill{0%{width:0}to{width:100%}}.tn-stepper__step-header:focus{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px}.tn-stepper__step-header:focus:not(:focus-visible){outline:none}@media(max-width:780px){.tn-stepper--vertical .tn-stepper__header{width:180px;border-right:none;border-bottom:1px solid var(--tn-lines, #e5e7eb);padding:16px 0}.tn-stepper--vertical .tn-stepper__step-header{flex-direction:column;align-items:center;text-align:center;min-width:80px;padding:8px 4px}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin-left:0;margin-top:8px}.tn-stepper--vertical .tn-stepper__connector{display:none}}@media(max-width:780px){.tn-stepper .tn-stepper__step-label{display:none}.tn-stepper .tn-stepper__step-indicator{width:var(--step-diameter-sm);height:var(--step-diameter-sm);font-size:12px}.tn-stepper--horizontal .tn-stepper__header{padding:0 8px}.tn-stepper--horizontal .tn-stepper__connector{top:calc(var(--step-diameter-sm) / 2);margin:0 8px}.tn-stepper--vertical .tn-stepper__header{width:60px}.tn-stepper--vertical .tn-stepper__step-header{padding:4px;margin-bottom:4px}.tn-stepper--vertical .tn-stepper__connector{left:calc(var(--step-diameter-sm) / 2 + 4px);height:16px;margin-bottom:4px}}\n"] }]
15889
+ '(window:resize)': 'onWindowResize()'
15890
+ }, template: "<div class=\"tn-stepper\"\n tnTestIdType=\"stepper\"\n [class.tn-stepper--horizontal]=\"!isVertical()\"\n [class.tn-stepper--vertical]=\"isVertical()\"\n [tnTestId]=\"testId()\">\n\n <!-- Shared step header (indicator + label) -->\n <ng-template #stepHeader let-step=\"step\" let-i=\"i\">\n <div class=\"tn-stepper__step-header\"\n role=\"button\"\n [attr.tabindex]=\"stepGated()[i] ? -1 : 0\"\n [attr.aria-current]=\"selectedIndex() === i ? 'step' : null\"\n [attr.aria-disabled]=\"stepGated()[i] ? 'true' : null\"\n [class.tn-stepper__step-header--active]=\"selectedIndex() === i\"\n [class.tn-stepper__step-header--completed]=\"step.completed()\"\n [class.tn-stepper__step-header--error]=\"step.hasError()\"\n [class.tn-stepper__step-header--optional]=\"step.optional()\"\n (click)=\"selectStep(i)\"\n (keydown.enter)=\"selectStep(i)\"\n (keydown.space)=\"$event.preventDefault(); selectStep(i)\">\n\n <!-- Step Number/Icon -->\n <div class=\"tn-stepper__step-indicator\"\n [class.tn-stepper__step-indicator--glyph]=\"step.hasError()\">\n @if (step.hasError()) {\n <tn-icon class=\"tn-stepper__step-error\" name=\"alert\" library=\"mdi\" [attr.aria-hidden]=\"true\" />\n } @else if (stepEditable()[i]) {\n <tn-icon class=\"tn-stepper__step-edit\" name=\"pencil\" library=\"mdi\" [attr.aria-hidden]=\"true\" />\n } @else if (step.icon()) {\n <span class=\"tn-stepper__step-icon\">{{ step.icon() }}</span>\n } @else {\n <span class=\"tn-stepper__step-number\">{{ i + 1 }}</span>\n }\n </div>\n\n <!-- Step Label -->\n <div class=\"tn-stepper__step-label\">\n <div class=\"tn-stepper__step-title\" [innerHTML]=\"step.label() | tnLabelMarkup\"></div>\n @if (step.hasError() && step.errorMessage()) {\n <span class=\"tn-stepper__step-error-message\">{{ step.errorMessage() }}</span>\n } @else if (step.optional()) {\n <span class=\"tn-stepper__step-subtitle\">Optional</span>\n }\n </div>\n </div>\n </ng-template>\n\n @if (isVertical()) {\n <!-- Vertical: content renders inline beneath the active step's header -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <div class=\"tn-stepper__step\" [class.tn-stepper__step--last]=\"i === steps().length - 1\">\n <ng-container *ngTemplateOutlet=\"stepHeader; context: { step, i }\" />\n\n <div class=\"tn-stepper__step-body\">\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\" [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n </div>\n </div>\n }\n </div>\n } @else {\n <!-- Horizontal: headers in a row, active content below -->\n <div class=\"tn-stepper__header\">\n @for (step of steps(); track $index; let i = $index) {\n <ng-container *ngTemplateOutlet=\"stepHeader; context: { step, i }\" />\n\n @if (i < steps().length - 1) {\n <div class=\"tn-stepper__connector\"></div>\n }\n }\n </div>\n\n <div class=\"tn-stepper__content\">\n @for (step of steps(); track $index; let i = $index) {\n @if (selectedIndex() === i) {\n <div class=\"tn-stepper__step-content\"\n [@stepTransition]=\"selectedIndex()\">\n <ng-container *ngTemplateOutlet=\"step.content()\" />\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [".tn-stepper{display:flex;font-family:inherit;--step-diameter: 48px;--step-diameter-sm: 32px;--step-padding: 12px;--v-indicator: 32px;--v-gap: 12px}.tn-stepper--horizontal{flex-direction:column}.tn-stepper--horizontal .tn-stepper__header{display:flex;justify-content:center;margin-bottom:32px;padding:0 16px}.tn-stepper--horizontal .tn-stepper__step-header{display:flex;flex-direction:column;align-items:center;text-align:center;cursor:pointer;transition:all .2s ease-in-out}.tn-stepper--horizontal .tn-stepper__step-header:not(.tn-stepper__step-header--active):hover .tn-stepper__step-indicator{transform:scale(.95)}.tn-stepper--horizontal .tn-stepper__connector{flex:1;height:2px;background:var(--tn-lines, #e5e7eb);margin:0 16px;position:relative;top:calc(var(--step-diameter) / 2)}.tn-stepper--vertical{flex-direction:column}.tn-stepper--vertical .tn-stepper__header,.tn-stepper--vertical .tn-stepper__step{display:flex;flex-direction:column}.tn-stepper--vertical .tn-stepper__step-header{display:flex;flex-direction:row;align-items:center;gap:var(--v-gap);text-align:left;cursor:pointer;padding:8px 0}.tn-stepper--vertical .tn-stepper__step-header .tn-stepper__step-label{margin:0}.tn-stepper--vertical .tn-stepper__step-indicator{width:var(--v-indicator);height:var(--v-indicator);min-width:var(--v-indicator);font-size:13px;transform:none}.tn-stepper--vertical .tn-stepper__step-indicator--glyph tn-icon{--tn-icon-size: var(--v-indicator)}.tn-stepper--vertical .tn-stepper__step-header--active .tn-stepper__step-indicator{transform:none}.tn-stepper--vertical .tn-stepper__step-body{margin-left:calc(var(--v-indicator) / 2 - 1px);border-left:2px solid var(--tn-lines, #e5e7eb);padding-left:calc(var(--v-indicator) / 2 + var(--v-gap));padding-bottom:8px;min-height:12px}.tn-stepper--vertical .tn-stepper__step--last .tn-stepper__step-body{border-left-color:transparent;min-height:0}.tn-stepper--vertical .tn-stepper__step-content{padding:4px 0 8px;background:transparent;border:none;border-radius:0}.tn-stepper__step-indicator{display:flex;align-items:center;justify-content:center;width:var(--step-diameter);height:var(--step-diameter);border-radius:50%;background:var(--tn-alt-bg1, #f8f9fa);color:var(--tn-alt-fg1, #495057);font-weight:400;font-size:14px;transition:all .2s ease-in-out;position:relative;transform:scale(.65)}.tn-stepper__step-number{font-weight:400}.tn-stepper__step-edit{--tn-icon-size: 18px}.tn-stepper__step-label{margin-top:8px}.tn-stepper__step-title ::ng-deep code{font-family:var(--tn-font-family-monospace, monospace);font-size:.875em;background:var(--tn-bg2, #f5f5f5);border:1px solid var(--tn-lines, #ccc);border-radius:4px;padding:.125em .45em}.tn-stepper__step-title{display:block;font-weight:400;font-size:14px;color:var(--tn-fg1, #000000);line-height:1.2}.tn-stepper__step-subtitle{display:block;font-size:12px;color:var(--tn-fg2, #6c757d);margin-top:2px}.tn-stepper__step-error-message{display:block;font-size:12px;color:var(--tn-red, #dc3545);margin-top:2px}.tn-stepper__step-header--active .tn-stepper__step-indicator{background:var(--tn-primary, #007bff);color:var(--tn-primary-txt, #ffffff);transform:scale(1)}.tn-stepper__step-header--active .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-indicator.tn-stepper__step-indicator--glyph{background:transparent;transform:scale(1)}.tn-stepper__step-indicator.tn-stepper__step-indicator--glyph tn-icon{--tn-icon-size: var(--step-diameter);line-height:1}.tn-stepper__step-header--active .tn-stepper__step-indicator.tn-stepper__step-indicator--glyph{background:transparent}.tn-stepper__step-header--error .tn-stepper__step-error{--tn-icon-color: var(--tn-red, #dc3545)}.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg1, #000000)}.tn-stepper__step-header--active.tn-stepper__step-header--error .tn-stepper__step-title{color:var(--tn-fg2, #6c757d);font-weight:600}.tn-stepper__step-header--optional .tn-stepper__step-indicator{border:2px dashed var(--tn-lines, #e5e7eb);background:transparent}.tn-stepper__content{min-height:200px}.tn-stepper__step-content{padding:16px;background:var(--tn-bg1, #ffffff);border-radius:8px;border:1px solid var(--tn-lines, #e5e7eb)}.tn-stepper__step-header:focus{outline:2px solid var(--tn-primary, #007bff);outline-offset:2px}.tn-stepper__step-header:focus:not(:focus-visible){outline:none}.tn-stepper__step-header[aria-disabled=true]{cursor:not-allowed}@media(max-width:780px){.tn-stepper--horizontal .tn-stepper__step-label{display:none}.tn-stepper--horizontal .tn-stepper__step-indicator{width:var(--step-diameter-sm);height:var(--step-diameter-sm);font-size:12px}.tn-stepper--horizontal .tn-stepper__header{padding:0 8px}.tn-stepper--horizontal .tn-stepper__connector{top:calc(var(--step-diameter-sm) / 2);margin:0 8px}}\n"] }]
15860
15891
  }], ctorParameters: () => [], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }, { type: i0.Output, args: ["selectedIndexChange"] }], testId: [{ type: i0.Input, args: [{ isSignal: true, alias: "testId", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], completed: [{ type: i0.Output, args: ["completed"] }], steps: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TnStepComponent), { ...{ descendants: true }, isSignal: true }] }] } });
15861
15892
 
15893
+ /**
15894
+ * Advances the ancestor `<tn-stepper>` to the next step when the host is clicked.
15895
+ *
15896
+ * Mirrors Angular Material's `matStepperNext`. Place it on a button (native
15897
+ * `<button>` or `<tn-button>`) rendered inside a `<tn-step>` — including step
15898
+ * content that lives in a projected child component. The directive resolves the
15899
+ * stepper through the element injector, so a disabled host simply emits no click
15900
+ * and navigation is naturally gated.
15901
+ *
15902
+ * @example
15903
+ * ```html
15904
+ * <tn-button [tnStepperNext] [disabled]="form.invalid" [label]="'Next' | translate" />
15905
+ * ```
15906
+ */
15907
+ class TnStepperNextDirective {
15908
+ stepper = inject(TnStepperComponent);
15909
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperNextDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
15910
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: TnStepperNextDirective, isStandalone: true, selector: "[tnStepperNext]", host: { listeners: { "click": "stepper.next()" } }, ngImport: i0 });
15911
+ }
15912
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperNextDirective, decorators: [{
15913
+ type: Directive,
15914
+ args: [{
15915
+ selector: '[tnStepperNext]',
15916
+ standalone: true,
15917
+ host: {
15918
+ '(click)': 'stepper.next()',
15919
+ },
15920
+ }]
15921
+ }] });
15922
+
15923
+ /**
15924
+ * Moves the ancestor `<tn-stepper>` to the previous step when the host is clicked.
15925
+ *
15926
+ * Mirrors Angular Material's `matStepperPrevious`. Place it on a button (native
15927
+ * `<button>` or `<tn-button>`) rendered inside a `<tn-step>` — including step
15928
+ * content that lives in a projected child component. The directive resolves the
15929
+ * stepper through the element injector.
15930
+ *
15931
+ * @example
15932
+ * ```html
15933
+ * <tn-button [tnStepperPrevious] [label]="'Back' | translate" />
15934
+ * ```
15935
+ */
15936
+ class TnStepperPreviousDirective {
15937
+ stepper = inject(TnStepperComponent);
15938
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperPreviousDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
15939
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: TnStepperPreviousDirective, isStandalone: true, selector: "[tnStepperPrevious]", host: { listeners: { "click": "stepper.previous()" } }, ngImport: i0 });
15940
+ }
15941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: TnStepperPreviousDirective, decorators: [{
15942
+ type: Directive,
15943
+ args: [{
15944
+ selector: '[tnStepperPrevious]',
15945
+ standalone: true,
15946
+ host: {
15947
+ '(click)': 'stepper.previous()',
15948
+ },
15949
+ }]
15950
+ }] });
15951
+
15952
+ /**
15953
+ * Harness for interacting with `tn-stepper` in tests.
15954
+ *
15955
+ * @example
15956
+ * ```typescript
15957
+ * const stepper = await loader.getHarness(TnStepperHarness);
15958
+ * expect(await stepper.getStepLabels()).toEqual(['Provider', 'What and When']);
15959
+ * expect(await stepper.getSelectedIndex()).toBe(0);
15960
+ * await stepper.selectStep(1);
15961
+ * ```
15962
+ */
15963
+ class TnStepperHarness extends ComponentHarness {
15964
+ /** The selector for the host element of a `TnStepperComponent` instance. */
15965
+ static hostSelector = 'tn-stepper';
15966
+ /**
15967
+ * Gets a `HarnessPredicate` used to search for a stepper with specific attributes.
15968
+ *
15969
+ * @param options Options for filtering which stepper instances are considered a match.
15970
+ */
15971
+ static with(options = {}) {
15972
+ return new HarnessPredicate(TnStepperHarness, options)
15973
+ .addOption('orientation', options.orientation, async (harness, orientation) => {
15974
+ return (await harness.getOrientation()) === orientation;
15975
+ });
15976
+ }
15977
+ headers = this.locatorForAll('.tn-stepper__step-header');
15978
+ /** Gets the number of steps. */
15979
+ async getStepCount() {
15980
+ return (await this.headers()).length;
15981
+ }
15982
+ /** Gets all step labels as strings, in order. */
15983
+ async getStepLabels() {
15984
+ const titles = await this.locatorForAll('.tn-stepper__step-title')();
15985
+ return Promise.all(titles.map(title => title.text()));
15986
+ }
15987
+ /** Gets the zero-based index of the currently selected step, or -1 if none. */
15988
+ async getSelectedIndex() {
15989
+ const headers = await this.headers();
15990
+ for (let i = 0; i < headers.length; i++) {
15991
+ if (await headers[i].hasClass('tn-stepper__step-header--active')) {
15992
+ return i;
15993
+ }
15994
+ }
15995
+ return -1;
15996
+ }
15997
+ /** Whether the step at `index` is marked completed. */
15998
+ async isStepCompleted(index) {
15999
+ const header = (await this.headers())[index];
16000
+ return header ? header.hasClass('tn-stepper__step-header--completed') : false;
16001
+ }
16002
+ /** Whether the step at `index` is marked as having an error. */
16003
+ async isStepError(index) {
16004
+ const header = (await this.headers())[index];
16005
+ return header ? header.hasClass('tn-stepper__step-header--error') : false;
16006
+ }
16007
+ /** Selects the step at `index` by clicking its header. Respects linear gating. */
16008
+ async selectStep(index) {
16009
+ const header = (await this.headers())[index];
16010
+ if (!header) {
16011
+ throw new Error(`No step at index ${index}`);
16012
+ }
16013
+ await header.click();
16014
+ }
16015
+ /** Gets the orientation of the stepper. */
16016
+ async getOrientation() {
16017
+ const root = await this.locatorFor('.tn-stepper')();
16018
+ return (await root.hasClass('tn-stepper--vertical')) ? 'vertical' : 'horizontal';
16019
+ }
16020
+ }
16021
+
15862
16022
  /**
15863
16023
  * Auto-generated from SVG files - DO NOT EDIT MANUALLY
15864
16024
  *
@@ -17675,5 +17835,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
17675
17835
  * Generated bundle index. Do not edit.
17676
17836
  */
17677
17837
 
17678
- export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_FORM_FIELD_ERRORS, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnFormSectionComponent, TnFormSectionHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, composeTestId, controlTestId, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, formatSize, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, parseLabelMarkup, parseSize, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
17838
+ export { CommonShortcuts, DEFAULT_THEME, DiskIconComponent, DiskType, FileSizePipe, InputType, LIGHT_THEME, LabelMarkupPipe, LabelTextPipe, LinuxModifierKeys, LinuxShortcuts, ModifierKeys, QuickShortcuts, ShortcutBuilder, StripMntPrefixPipe, THEME_MAP, THEME_STORAGE_KEY, TN_FORM_FIELD_ERRORS, TN_TABLE_PAGER_DEFAULT_LABELS, TN_TABLE_PAGER_LABELS, TN_TEST_ATTR, TN_THEME_DEFINITIONS, TnAutocompleteComponent, TnAutocompleteHarness, TnBannerActionDirective, TnBannerComponent, TnBannerHarness, TnBrandedSpinnerComponent, TnButtonComponent, TnButtonHarness, TnButtonToggleComponent, TnButtonToggleGroupComponent, TnButtonToggleGroupHarness, TnButtonToggleHarness, TnCalendarComponent, TnCalendarHeaderComponent, TnCardComponent, TnCardFooterActionsDirective, TnCardHeaderActionsDirective, TnCardHeaderDirective, TnCellDefDirective, TnCheckboxComponent, TnCheckboxHarness, TnCheckboxLabelDirective, TnChipComponent, TnChipHarness, TnChipInputComponent, TnChipInputHarness, TnConfirmDialogComponent, TnDateInputComponent, TnDateInputHarness, TnDateRangeInputComponent, TnDateRangeInputHarness, TnDetailRowDefDirective, TnDialog, TnDialogHarness, TnDialogShellComponent, TnDialogTesting, TnDividerComponent, TnDividerDirective, TnDrawerComponent, TnDrawerContainerComponent, TnDrawerContainerHarness, TnDrawerContentComponent, TnDrawerHarness, TnEmptyComponent, TnEmptyHarness, TnExpansionPanelComponent, TnExpansionPanelHarness, TnFileInputComponent, TnFileInputHarness, TnFilePickerComponent, TnFilePickerPopupComponent, TnFormFieldComponent, TnFormFieldHarness, TnFormSectionComponent, TnFormSectionHarness, TnHeaderCellDefDirective, TnIconButtonComponent, TnIconButtonHarness, TnIconComponent, TnIconHarness, TnIconRegistryService, TnIconTesting, TnInputComponent, TnInputDirective, TnInputHarness, TnKeyboardShortcutComponent, TnKeyboardShortcutService, TnListAvatarDirective, TnListComponent, TnListIconDirective, TnListItemComponent, TnListItemLineDirective, TnListItemPrimaryDirective, TnListItemSecondaryDirective, TnListItemTitleDirective, TnListItemTrailingDirective, TnListOptionComponent, TnListSubheaderComponent, TnMenuActivateHoverDirective, TnMenuComponent, TnMenuHarness, TnMenuItemComponent, TnMenuTesting, TnMenuTriggerDirective, TnMonthViewComponent, TnMultiYearViewComponent, TnNestedTreeNodeComponent, TnParticleProgressBarComponent, TnProgressBarComponent, TnRadioComponent, TnRadioHarness, TnSelectComponent, TnSelectHarness, TnSelectionListComponent, TnSidePanelActionDirective, TnSidePanelComponent, TnSidePanelHarness, TnSidePanelHeaderActionDirective, TnSlideToggleComponent, TnSlideToggleHarness, TnSliderComponent, TnSliderThumbDirective, TnSliderWithLabelDirective, TnSpinnerComponent, TnSpriteLoaderService, TnStepComponent, TnStepperComponent, TnStepperHarness, TnStepperNextDirective, TnStepperPreviousDirective, TnTabComponent, TnTabHarness, TnTabPanelComponent, TnTabPanelHarness, TnTableColumnDirective, TnTableComponent, TnTableHarness, TnTablePagerComponent, TnTablePagerHarness, TnTabsComponent, TnTabsHarness, TnTestIdDirective, TnTheme, TnThemeService, TnTimeInputComponent, TnToastComponent, TnToastMock, TnToastPosition, TnToastRef, TnToastService, TnToastTesting, TnToastType, TnTooltipComponent, TnTooltipDirective, TnTreeComponent, TnTreeFlatDataSource, TnTreeFlattener, TnTreeNodeComponent, TnTreeNodeOutletDirective, TruncatePathPipe, WindowsModifierKeys, WindowsShortcuts, composeTestId, controlTestId, createLucideLibrary, createShortcut, defaultSpriteBasePath, defaultSpriteConfigPath, formatSize, kebabTestSegment, labelMarkupToHtml, labelMarkupToText, libIconMarker, parseLabelMarkup, parseSize, registerLucideIcons, scopeTestId, setupLucideIntegration, tnIconMarker, writeTestId };
17679
17839
  //# sourceMappingURL=truenas-ui-components.mjs.map