@akcelik/strct 4.18.0 → 4.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DOCUMENT, signal, computed, Injectable, input, isDevMode, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, booleanAttribute, afterNextRender, Directive, model, output, contentChildren, HostListener, effect, ApplicationRef, EnvironmentInjector, createComponent, PLATFORM_ID, forwardRef, DestroyRef, TemplateRef, contentChild, viewChild, InjectionToken, untracked, ViewContainerRef, Injector, viewChildren, Renderer2, Pipe, afterRenderEffect } from '@angular/core';
3
3
  import { DomSanitizer } from '@angular/platform-browser';
4
- import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1, isPlatformBrowser } from '@angular/common';
4
+ import { DOCUMENT as DOCUMENT$1, NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
5
5
  import { RouterLink, RouterLinkActive } from '@angular/router';
6
6
  import * as i1 from '@angular/forms';
7
7
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
@@ -1033,21 +1033,66 @@ class StrctShellService {
1033
1033
  * </strct-shell>
1034
1034
  */
1035
1035
  class StrctShell {
1036
+ /**
1037
+ * Id of the main region. Given one, the shell renders "Skip to main content"
1038
+ * as its first focusable element — invisible until focused. Null (the
1039
+ * default) renders nothing, so existing shells are unchanged.
1040
+ */
1041
+ skipLinkTarget = input(null, /* @ts-ignore */
1042
+ ...(ngDevMode ? [{ debugName: "skipLinkTarget" }] : /* istanbul ignore next */ []));
1043
+ /** The link's text (localisable). */
1044
+ skipLinkLabel = input('Skip to main content', /* @ts-ignore */
1045
+ ...(ngDevMode ? [{ debugName: "skipLinkLabel" }] : /* istanbul ignore next */ []));
1046
+ doc = inject(DOCUMENT$1);
1047
+ /**
1048
+ * Moves focus rather than only the scroll position: an href alone scrolls
1049
+ * without focusing, so the next Tab would resume from the link.
1050
+ */
1051
+ skipToMain(event) {
1052
+ const id = this.skipLinkTarget();
1053
+ const target = id ? this.doc.getElementById(id) : null;
1054
+ if (!target)
1055
+ return;
1056
+ event.preventDefault();
1057
+ if (!target.hasAttribute('tabindex'))
1058
+ target.setAttribute('tabindex', '-1');
1059
+ target.focus();
1060
+ // Optional: not every environment implements it (jsdom does not).
1061
+ target.scrollIntoView?.({ block: 'start' });
1062
+ }
1036
1063
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
1037
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.4", type: StrctShell, isStandalone: true, selector: "strct-shell", host: { classAttribute: "strct-shell" }, providers: [StrctShellService], ngImport: i0, template: `
1064
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctShell, isStandalone: true, selector: "strct-shell", inputs: { skipLinkTarget: { classPropertyName: "skipLinkTarget", publicName: "skipLinkTarget", isSignal: true, isRequired: false, transformFunction: null }, skipLinkLabel: { classPropertyName: "skipLinkLabel", publicName: "skipLinkLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-shell" }, providers: [StrctShellService], ngImport: i0, template: `
1065
+ @if (skipLinkTarget()) {
1066
+ <!-- The first focusable element on the page, invisible until focused. -->
1067
+ <a
1068
+ class="strct-shell__skip"
1069
+ [attr.href]="'#' + skipLinkTarget()"
1070
+ (click)="skipToMain($event)"
1071
+ >{{ skipLinkLabel() }}</a
1072
+ >
1073
+ }
1038
1074
  <ng-content select="strct-header" />
1039
1075
  <div class="strct-shell__main"><ng-content /></div>
1040
1076
  <ng-content select="strct-footer" />
1041
- `, isInline: true, styles: [".strct-shell{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden;background:var(--bg-2)}.strct-shell__main{display:flex;min-height:0;overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1077
+ `, isInline: true, styles: [".strct-shell{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden;background:var(--bg-2)}.strct-shell__main{display:flex;min-height:0;overflow:hidden}.strct-shell__skip{position:absolute;inset-inline-start:0;top:0;z-index:1;transform:translateY(-120%);padding:var(--space-2) var(--space-3);border-radius:0 0 var(--radius-md) 0;background:var(--acc);color:var(--inv);font-size:var(--text-sm);text-decoration:none}.strct-shell__skip:focus-visible{transform:none;outline:2px solid var(--acc50);outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1042
1078
  }
1043
1079
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctShell, decorators: [{
1044
1080
  type: Component,
1045
1081
  args: [{ selector: 'strct-shell', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [StrctShellService], template: `
1082
+ @if (skipLinkTarget()) {
1083
+ <!-- The first focusable element on the page, invisible until focused. -->
1084
+ <a
1085
+ class="strct-shell__skip"
1086
+ [attr.href]="'#' + skipLinkTarget()"
1087
+ (click)="skipToMain($event)"
1088
+ >{{ skipLinkLabel() }}</a
1089
+ >
1090
+ }
1046
1091
  <ng-content select="strct-header" />
1047
1092
  <div class="strct-shell__main"><ng-content /></div>
1048
1093
  <ng-content select="strct-footer" />
1049
- `, host: { class: 'strct-shell' }, styles: [".strct-shell{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden;background:var(--bg-2)}.strct-shell__main{display:flex;min-height:0;overflow:hidden}\n"] }]
1050
- }] });
1094
+ `, host: { class: 'strct-shell' }, styles: [".strct-shell{display:grid;grid-template-rows:auto 1fr auto;height:100vh;overflow:hidden;background:var(--bg-2)}.strct-shell__main{display:flex;min-height:0;overflow:hidden}.strct-shell__skip{position:absolute;inset-inline-start:0;top:0;z-index:1;transform:translateY(-120%);padding:var(--space-2) var(--space-3);border-radius:0 0 var(--radius-md) 0;background:var(--acc);color:var(--inv);font-size:var(--text-sm);text-decoration:none}.strct-shell__skip:focus-visible{transform:none;outline:2px solid var(--acc50);outline-offset:2px}\n"] }]
1095
+ }], propDecorators: { skipLinkTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLinkTarget", required: false }] }], skipLinkLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "skipLinkLabel", required: false }] }] } });
1051
1096
  /** Top application bar. Holds brand on the left and actions on the right. */
1052
1097
  class StrctHeader {
1053
1098
  /** Accessible label of the mobile drawer toggle. */
@@ -2790,13 +2835,20 @@ class StrctAccordionPanel {
2790
2835
  /** Whether the panel is open (two-way). */
2791
2836
  expanded = model(false, /* @ts-ignore */
2792
2837
  ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
2838
+ /**
2839
+ * `quiet` is a fold in running text — no border or background, a `--text-sm`
2840
+ * summary in `--t2` with a chevron, the body indented. It reads like a raw
2841
+ * `<details>` while keeping the button + region semantics.
2842
+ */
2843
+ appearance = input('boxed', /* @ts-ignore */
2844
+ ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
2793
2845
  bodyId = `strct-acc-${++accordionCounter}`;
2794
2846
  headId = `${this.bodyId}-head`;
2795
2847
  toggle() {
2796
2848
  this.expanded.update((v) => !v);
2797
2849
  }
2798
2850
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctAccordionPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2799
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctAccordionPanel, isStandalone: true, selector: "strct-accordion-panel", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { classAttribute: "strct-acc" }, ngImport: i0, template: `
2851
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctAccordionPanel, isStandalone: true, selector: "strct-accordion-panel", inputs: { heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: true, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange" }, host: { properties: { "class.strct-acc--quiet": "appearance() === 'quiet'" }, classAttribute: "strct-acc" }, ngImport: i0, template: `
2800
2852
  <button
2801
2853
  type="button"
2802
2854
  class="strct-acc__head"
@@ -2815,7 +2867,7 @@ class StrctAccordionPanel {
2815
2867
  <ng-content />
2816
2868
  </div>
2817
2869
  }
2818
- `, isInline: true, styles: [".strct-acc{display:block}.strct-acc+.strct-acc{border-top:1px solid var(--b2)}.strct-acc__head{display:flex;align-items:center;gap:9px;width:100%;padding:11px 14px;border:0;background:transparent;cursor:pointer;font-family:var(--font);font-size:13px;font-weight:500;color:var(--t1);text-align:start}.strct-acc__head:hover{background:var(--bg-3)}.strct-acc__chevron{display:inline-flex;color:var(--t3);transition:transform .16s ease}.strct-acc__chevron--open{transform:rotate(90deg);color:var(--acc)}[dir=rtl] .strct-acc__chevron:not(.strct-acc__chevron--open){transform:rotate(180deg)}.strct-acc__title{flex:1}.strct-acc__body{padding:4px 14px 14px;padding-inline-start:32px;color:var(--t2);font-size:13px;border-top:1px solid var(--b1)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2870
+ `, isInline: true, styles: [".strct-acc{display:block}.strct-acc+.strct-acc{border-top:1px solid var(--b2)}.strct-acc__head{display:flex;align-items:center;gap:9px;width:100%;padding:11px 14px;border:0;background:transparent;cursor:pointer;font-family:var(--font);font-size:13px;font-weight:500;color:var(--t1);text-align:start}.strct-acc__head:hover{background:var(--bg-3)}.strct-acc__chevron{display:inline-flex;color:var(--t3);transition:transform .16s ease}.strct-acc__chevron--open{transform:rotate(90deg);color:var(--acc)}[dir=rtl] .strct-acc__chevron:not(.strct-acc__chevron--open){transform:rotate(180deg)}.strct-acc__title{flex:1}.strct-acc__body{padding:4px 14px 14px;padding-inline-start:32px;color:var(--t2);font-size:13px;border-top:1px solid var(--b1)}.strct-acc--quiet+.strct-acc--quiet{border-top:0}.strct-acc--quiet .strct-acc__head{width:auto;padding:2px 0;gap:6px;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-acc--quiet .strct-acc__head:hover{background:none;color:var(--t1);text-decoration:underline}.strct-acc--quiet .strct-acc__head:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-acc--quiet .strct-acc__title{flex:none}.strct-acc--quiet .strct-acc__body{padding:4px 0 8px;padding-inline-start:var(--space-3);border-top:0;font-size:var(--text-sm)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2819
2871
  }
2820
2872
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctAccordionPanel, decorators: [{
2821
2873
  type: Component,
@@ -2838,8 +2890,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
2838
2890
  <ng-content />
2839
2891
  </div>
2840
2892
  }
2841
- `, host: { class: 'strct-acc' }, styles: [".strct-acc{display:block}.strct-acc+.strct-acc{border-top:1px solid var(--b2)}.strct-acc__head{display:flex;align-items:center;gap:9px;width:100%;padding:11px 14px;border:0;background:transparent;cursor:pointer;font-family:var(--font);font-size:13px;font-weight:500;color:var(--t1);text-align:start}.strct-acc__head:hover{background:var(--bg-3)}.strct-acc__chevron{display:inline-flex;color:var(--t3);transition:transform .16s ease}.strct-acc__chevron--open{transform:rotate(90deg);color:var(--acc)}[dir=rtl] .strct-acc__chevron:not(.strct-acc__chevron--open){transform:rotate(180deg)}.strct-acc__title{flex:1}.strct-acc__body{padding:4px 14px 14px;padding-inline-start:32px;color:var(--t2);font-size:13px;border-top:1px solid var(--b1)}\n"] }]
2842
- }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }] } });
2893
+ `, host: {
2894
+ class: 'strct-acc',
2895
+ '[class.strct-acc--quiet]': "appearance() === 'quiet'",
2896
+ }, styles: [".strct-acc{display:block}.strct-acc+.strct-acc{border-top:1px solid var(--b2)}.strct-acc__head{display:flex;align-items:center;gap:9px;width:100%;padding:11px 14px;border:0;background:transparent;cursor:pointer;font-family:var(--font);font-size:13px;font-weight:500;color:var(--t1);text-align:start}.strct-acc__head:hover{background:var(--bg-3)}.strct-acc__chevron{display:inline-flex;color:var(--t3);transition:transform .16s ease}.strct-acc__chevron--open{transform:rotate(90deg);color:var(--acc)}[dir=rtl] .strct-acc__chevron:not(.strct-acc__chevron--open){transform:rotate(180deg)}.strct-acc__title{flex:1}.strct-acc__body{padding:4px 14px 14px;padding-inline-start:32px;color:var(--t2);font-size:13px;border-top:1px solid var(--b1)}.strct-acc--quiet+.strct-acc--quiet{border-top:0}.strct-acc--quiet .strct-acc__head{width:auto;padding:2px 0;gap:6px;font-size:var(--text-sm);font-weight:400;color:var(--t2)}.strct-acc--quiet .strct-acc__head:hover{background:none;color:var(--t1);text-decoration:underline}.strct-acc--quiet .strct-acc__head:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-acc--quiet .strct-acc__title{flex:none}.strct-acc--quiet .strct-acc__body{padding:4px 0 8px;padding-inline-start:var(--space-3);border-top:0;font-size:var(--text-sm)}\n"] }]
2897
+ }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: true }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }] } });
2843
2898
 
2844
2899
  let tabsCounter = 0;
2845
2900
  /** A single tab. Place inside `<strct-tabs>`; `label` names its button. */
@@ -18108,6 +18163,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
18108
18163
  `, host: { class: 'strct-gauge' }, styles: [".strct-gauge{display:inline-block}.strct-gauge__wrap{position:relative}.strct-gauge__track{stroke:var(--bg-3)}@media(prefers-reduced-motion:no-preference){.strct-gauge__value{transition:stroke-dasharray .4s ease}}.strct-gauge__center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.strct-gauge__num{font-size:var(--text-2xl);font-weight:600;color:var(--t1);line-height:1}.strct-gauge__pct{font-size:12px;color:var(--t2);margin-inline-start:1px}.strct-gauge__label{font-size:12px;color:var(--t2);margin-top:3px}\n"] }]
18109
18164
  }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], thresholds: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholds", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], thickness: [{ type: i0.Input, args: [{ isSignal: true, alias: "thickness", required: false }] }] } });
18110
18165
 
18166
+ /**
18167
+ * A node's own content in a fan-out diagram — chips, a bar, a count. The
18168
+ * template's context is the node (`let-node`), including its `data`.
18169
+ */
18170
+ class StrctFlowNodeTemplate {
18171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctFlowNodeTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
18172
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.4", type: StrctFlowNodeTemplate, isStandalone: true, selector: "ng-template[strctFlowNode]", ngImport: i0 });
18173
+ }
18174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctFlowNodeTemplate, decorators: [{
18175
+ type: Directive,
18176
+ args: [{ selector: 'ng-template[strctFlowNode]' }]
18177
+ }] });
18111
18178
  /**
18112
18179
  * Shows a connection between two (or N) endpoints with an optional animated
18113
18180
  * "flow" — moving dots travelling along the connector — to represent live data
@@ -18141,6 +18208,148 @@ class StrctFlow {
18141
18208
  /** Layout axis. */
18142
18209
  orientation = input('horizontal', /* @ts-ignore */
18143
18210
  ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
18211
+ /**
18212
+ * `chain` (default) is the straight A → B connector. `fan-out` places nodes
18213
+ * in columns and draws the edges between them; `tree` is `fan-out` with the
18214
+ * columns derived from each node's depth.
18215
+ */
18216
+ layout = input('chain', /* @ts-ignore */
18217
+ ...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
18218
+ /** The edges of a fan-out / tree. `null` keeps the consecutive chain. */
18219
+ edges = input(null, /* @ts-ignore */
18220
+ ...(ngDevMode ? [{ debugName: "edges" }] : /* istanbul ignore next */ []));
18221
+ /** Column headings, in order. */
18222
+ columns = input(null, /* @ts-ignore */
18223
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
18224
+ host = inject(ElementRef);
18225
+ destroyRef = inject(DestroyRef);
18226
+ nodeTpl = contentChild(StrctFlowNodeTemplate, { ...(ngDevMode ? { debugName: "nodeTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
18227
+ /** Which column each node sits in: its own, or its depth in the edge graph. */
18228
+ columnOf = computed(() => {
18229
+ const nodes = this.nodes();
18230
+ const map = new Map();
18231
+ if (this.layout() === 'tree') {
18232
+ const parents = new Map();
18233
+ for (const e of this.edges() ?? [])
18234
+ parents.set(e.to, e.from);
18235
+ const depth = (id, seen = new Set()) => {
18236
+ const parent = parents.get(id);
18237
+ if (parent === undefined || seen.has(id))
18238
+ return 0;
18239
+ seen.add(id);
18240
+ return depth(parent, seen) + 1;
18241
+ };
18242
+ for (const n of nodes)
18243
+ map.set(n.id, n.column ?? depth(n.id));
18244
+ return map;
18245
+ }
18246
+ for (const [i, n] of nodes.entries())
18247
+ map.set(n.id, n.column ?? i);
18248
+ return map;
18249
+ }, /* @ts-ignore */
18250
+ ...(ngDevMode ? [{ debugName: "columnOf" }] : /* istanbul ignore next */ []));
18251
+ /** The columns as rendered: a heading (when given) and the nodes in it. */
18252
+ columnsView = computed(() => {
18253
+ const byColumn = new Map();
18254
+ for (const n of this.nodes()) {
18255
+ const c = this.columnOf().get(n.id) ?? 0;
18256
+ byColumn.set(c, [...(byColumn.get(c) ?? []), n]);
18257
+ }
18258
+ const headings = this.columns() ?? [];
18259
+ return [...byColumn.keys()]
18260
+ .sort((a, b) => a - b)
18261
+ .map((index) => ({ index, heading: headings[index] ?? '', nodes: byColumn.get(index) }));
18262
+ }, /* @ts-ignore */
18263
+ ...(ngDevMode ? [{ debugName: "columnsView" }] : /* istanbul ignore next */ []));
18264
+ /** What a node says to assistive tech: itself, then where it leads. */
18265
+ nodeSummary(node) {
18266
+ const out = (this.edges() ?? [])
18267
+ .filter((e) => e.from === node.id)
18268
+ .map((e) => this.nodes().find((n) => n.id === e.to)?.label ?? e.to);
18269
+ return out.length ? `→ ${out.join(', ')}` : '';
18270
+ }
18271
+ /** Measured edge geometry — recomputed whenever the boxes move or resize. */
18272
+ edgePaths = signal([], /* @ts-ignore */
18273
+ ...(ngDevMode ? [{ debugName: "edgePaths" }] : /* istanbul ignore next */ []));
18274
+ viewBox = signal('0 0 0 0', /* @ts-ignore */
18275
+ ...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
18276
+ constructor() {
18277
+ afterNextRender(() => this.observe());
18278
+ // Re-measure when the data changes; the observer covers size changes.
18279
+ effect(() => {
18280
+ this.nodes();
18281
+ this.edges();
18282
+ this.layout();
18283
+ queueMicrotask(() => this.measure());
18284
+ });
18285
+ }
18286
+ observe() {
18287
+ if (this.layout() === 'chain' || typeof ResizeObserver === 'undefined')
18288
+ return;
18289
+ const fan = this.host.nativeElement.querySelector('.strct-flow__fan');
18290
+ if (!fan)
18291
+ return;
18292
+ const ro = new ResizeObserver(() => this.measure());
18293
+ ro.observe(fan);
18294
+ for (const box of fan.querySelectorAll('.strct-flow__box'))
18295
+ ro.observe(box);
18296
+ this.destroyRef.onDestroy(() => ro.disconnect());
18297
+ this.measure();
18298
+ }
18299
+ /**
18300
+ * Orthogonal connectors, from the boxes' real positions: out of the source's
18301
+ * inline end, across the gap, then into the target's inline start.
18302
+ */
18303
+ measure() {
18304
+ if (this.layout() === 'chain')
18305
+ return;
18306
+ const fan = this.host.nativeElement.querySelector('.strct-flow__fan');
18307
+ const edges = this.edges();
18308
+ if (!fan || !edges?.length) {
18309
+ this.edgePaths.set([]);
18310
+ return;
18311
+ }
18312
+ const base = fan.getBoundingClientRect();
18313
+ this.viewBox.set(`0 0 ${Math.round(base.width)} ${Math.round(base.height)}`);
18314
+ // Looked up from the boxes themselves rather than through a selector: a
18315
+ // node id is consumer data, and CSS.escape is not everywhere (jsdom).
18316
+ const boxes = new Map();
18317
+ for (const el of fan.querySelectorAll('[data-flow-node]')) {
18318
+ const id = el.dataset['flowNode'];
18319
+ if (id)
18320
+ boxes.set(id, el);
18321
+ }
18322
+ const rect = (id) => {
18323
+ const el = boxes.get(id);
18324
+ if (!el)
18325
+ return null;
18326
+ const r = el.getBoundingClientRect();
18327
+ return { x: r.left - base.left, y: r.top - base.top, w: r.width, h: r.height };
18328
+ };
18329
+ const paths = [];
18330
+ for (const [i, e] of edges.entries()) {
18331
+ const a = rect(e.from);
18332
+ const b = rect(e.to);
18333
+ if (!a || !b)
18334
+ continue;
18335
+ const x1 = a.x + a.w;
18336
+ const y1 = a.y + a.h / 2;
18337
+ const x2 = b.x;
18338
+ const y2 = b.y + b.h / 2;
18339
+ const mid = x1 + (x2 - x1) / 2;
18340
+ const d = Math.abs(y1 - y2) < 1
18341
+ ? `M ${x1} ${y1} L ${x2} ${y2}`
18342
+ : `M ${x1} ${y1} H ${mid} V ${y2} H ${x2}`;
18343
+ paths.push({
18344
+ key: `${e.from}-${e.to}-${i}`,
18345
+ d,
18346
+ status: e.status ?? 'neutral',
18347
+ dashed: e.style === 'dashed',
18348
+ animated: !!e.animated,
18349
+ });
18350
+ }
18351
+ this.edgePaths.set(paths);
18352
+ }
18144
18353
  /** A flow needs at least two terminals to animate. */
18145
18354
  connected = computed(() => this.nodes().length > 1, /* @ts-ignore */
18146
18355
  ...(ngDevMode ? [{ debugName: "connected" }] : /* istanbul ignore next */ []));
@@ -18172,103 +18381,210 @@ class StrctFlow {
18172
18381
  }, /* @ts-ignore */
18173
18382
  ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
18174
18383
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctFlow, deps: [], target: i0.ɵɵFactoryTarget.Component });
18175
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctFlow, isStandalone: true, selector: "strct-flow", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "img" }, properties: { "class.strct-flow--vertical": "orientation() === 'vertical'", "class.strct-flow--neutral": "status() === 'neutral'", "class.strct-flow--accent": "status() === 'accent'", "class.strct-flow--success": "status() === 'success'", "class.strct-flow--warning": "status() === 'warning'", "class.strct-flow--critical": "status() === 'critical'", "class.strct-flow--live": "animated()", "attr.aria-label": "ariaLabel()" }, classAttribute: "strct-flow" }, ngImport: i0, template: `
18176
- <div class="strct-flow__row">
18177
- @for (node of nodes(); track node.id; let last = $last) {
18178
- <div class="strct-flow__node strct-flow__node--{{ node.status ?? 'neutral' }}">
18179
- <span class="strct-flow__dot" aria-hidden="true"></span>
18180
- <span class="strct-flow__node-text">
18181
- <span class="strct-flow__label">{{ node.label }}</span>
18182
- @if (node.role) {
18183
- <span class="strct-flow__role">{{ node.role }}</span>
18184
- }
18185
- @if (node.sublabel) {
18186
- <span class="strct-flow__sub">{{ node.sublabel }}</span>
18384
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctFlow, isStandalone: true, selector: "strct-flow", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, edges: { classPropertyName: "edges", publicName: "edges", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.role": "layout() === 'chain' ? 'img' : null", "class.strct-flow--fan": "layout() !== 'chain'", "class.strct-flow--vertical": "orientation() === 'vertical'", "class.strct-flow--neutral": "status() === 'neutral'", "class.strct-flow--accent": "status() === 'accent'", "class.strct-flow--success": "status() === 'success'", "class.strct-flow--warning": "status() === 'warning'", "class.strct-flow--critical": "status() === 'critical'", "class.strct-flow--live": "animated()", "attr.aria-label": "layout() === 'chain' ? ariaLabel() : null" }, classAttribute: "strct-flow" }, queries: [{ propertyName: "nodeTpl", first: true, predicate: StrctFlowNodeTemplate, descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: `
18385
+ @if (layout() !== 'chain') {
18386
+ <div class="strct-flow__fan" #fan>
18387
+ <!-- The edges are geometry, not content: they are measured from the
18388
+ boxes after they render, and hidden from assistive tech, which is
18389
+ given the structure instead. -->
18390
+ <svg class="strct-flow__edges" aria-hidden="true" [attr.viewBox]="viewBox()">
18391
+ @for (e of edgePaths(); track e.key) {
18392
+ <path
18393
+ class="strct-flow__edge strct-flow__edge--{{ e.status }}"
18394
+ [class.strct-flow__edge--dashed]="e.dashed"
18395
+ [class.strct-flow__edge--animated]="e.animated"
18396
+ [attr.d]="e.d"
18397
+ fill="none"
18398
+ />
18399
+ }
18400
+ </svg>
18401
+ @for (col of columnsView(); track col.index) {
18402
+ <div class="strct-flow__col" role="group" [attr.aria-label]="col.heading || null">
18403
+ @if (col.heading) {
18404
+ <div class="strct-flow__colhead">{{ col.heading }}</div>
18187
18405
  }
18188
- </span>
18189
- </div>
18406
+ <ul class="strct-flow__boxes">
18407
+ @for (node of col.nodes; track node.id) {
18408
+ <li
18409
+ class="strct-flow__box strct-flow__box--{{ node.status ?? 'neutral' }}"
18410
+ [attr.data-flow-node]="node.id"
18411
+ >
18412
+ @if (nodeTpl(); as tpl) {
18413
+ <ng-container
18414
+ [ngTemplateOutlet]="tpl"
18415
+ [ngTemplateOutletContext]="{ $implicit: node, node }"
18416
+ />
18417
+ } @else {
18418
+ <span class="strct-flow__label">{{ node.label }}</span>
18419
+ @if (node.role) {
18420
+ <span class="strct-flow__role">{{ node.role }}</span>
18421
+ }
18422
+ @if (node.sublabel) {
18423
+ <span class="strct-flow__sub">{{ node.sublabel }}</span>
18424
+ }
18425
+ }
18426
+ <span class="strct-flow__sr">{{ nodeSummary(node) }}</span>
18427
+ </li>
18428
+ }
18429
+ </ul>
18430
+ </div>
18431
+ }
18432
+ </div>
18433
+ @if (label()) {
18434
+ <div class="strct-flow__caption">{{ label() }}</div>
18435
+ }
18436
+ } @else {
18437
+ <div class="strct-flow__row">
18438
+ @for (node of nodes(); track node.id; let last = $last) {
18439
+ <div class="strct-flow__node strct-flow__node--{{ node.status ?? 'neutral' }}">
18440
+ <span class="strct-flow__dot" aria-hidden="true"></span>
18441
+ <span class="strct-flow__node-text">
18442
+ <span class="strct-flow__label">{{ node.label }}</span>
18443
+ @if (node.role) {
18444
+ <span class="strct-flow__role">{{ node.role }}</span>
18445
+ }
18446
+ @if (node.sublabel) {
18447
+ <span class="strct-flow__sub">{{ node.sublabel }}</span>
18448
+ }
18449
+ </span>
18450
+ </div>
18190
18451
 
18191
- @if (!last) {
18192
- <div class="strct-flow__conn" aria-hidden="true">
18193
- <span class="strct-flow__line"></span>
18194
- @if (showArrow('forward')) {
18195
- <span class="strct-flow__arrow strct-flow__arrow--fwd"></span>
18196
- }
18197
- @if (showArrow('reverse')) {
18198
- <span class="strct-flow__arrow strct-flow__arrow--rev"></span>
18199
- }
18200
- @if (animated()) {
18452
+ @if (!last) {
18453
+ <div class="strct-flow__conn" aria-hidden="true">
18454
+ <span class="strct-flow__line"></span>
18201
18455
  @if (showArrow('forward')) {
18202
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--1"></span>
18203
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--2"></span>
18204
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--3"></span>
18456
+ <span class="strct-flow__arrow strct-flow__arrow--fwd"></span>
18205
18457
  }
18206
18458
  @if (showArrow('reverse')) {
18207
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--1"></span>
18208
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--2"></span>
18209
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--3"></span>
18459
+ <span class="strct-flow__arrow strct-flow__arrow--rev"></span>
18210
18460
  }
18211
- }
18212
- </div>
18461
+ @if (animated()) {
18462
+ @if (showArrow('forward')) {
18463
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--1"></span>
18464
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--2"></span>
18465
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--3"></span>
18466
+ }
18467
+ @if (showArrow('reverse')) {
18468
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--1"></span>
18469
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--2"></span>
18470
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--3"></span>
18471
+ }
18472
+ }
18473
+ </div>
18474
+ }
18213
18475
  }
18214
- }
18215
- </div>
18476
+ </div>
18216
18477
 
18217
- @if (caption()) {
18218
- <div class="strct-flow__caption">{{ caption() }}</div>
18478
+ @if (caption()) {
18479
+ <div class="strct-flow__caption">{{ caption() }}</div>
18480
+ }
18219
18481
  }
18220
- `, isInline: true, styles: [".strct-flow{--strct-flow-color: var(--acc);display:block}.strct-flow--neutral{--strct-flow-color: var(--t3)}.strct-flow--accent{--strct-flow-color: var(--acc)}.strct-flow--success{--strct-flow-color: var(--success)}.strct-flow--warning{--strct-flow-color: var(--warning)}.strct-flow--critical{--strct-flow-color: var(--critical)}.strct-flow__row{display:flex;align-items:center;gap:var(--space-2)}.strct-flow--vertical .strct-flow__row{flex-direction:column;align-items:stretch}.strct-flow__node{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);flex-shrink:0}.strct-flow__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--t3)}.strct-flow__node--accent .strct-flow__dot{background:var(--acc)}.strct-flow__node--success .strct-flow__dot{background:var(--success)}.strct-flow__node--warning .strct-flow__dot{background:var(--warning)}.strct-flow__node--critical .strct-flow__dot{background:var(--critical)}.strct-flow__node-text{display:flex;flex-direction:column;gap:1px;min-width:0}.strct-flow__label{font-size:var(--text-md);font-weight:600;color:var(--t1);line-height:1.2}.strct-flow__role{font-size:12px;font-weight:700;letter-spacing:.4px;color:var(--t3)}.strct-flow__sub{font-size:var(--text-sm);color:var(--t3)}.strct-flow__conn{position:relative;flex:1 1 auto;min-width:40px;height:2px;align-self:center}.strct-flow--vertical .strct-flow__conn{width:2px;min-width:0;height:28px;flex:0 0 28px;align-self:center}.strct-flow__line{position:absolute;inset:0;border-radius:2px;background:linear-gradient(to right,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow--vertical .strct-flow__line{background:linear-gradient(to bottom,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow__arrow{position:absolute;top:50%;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.strct-flow__arrow--fwd{right:-1px;transform:translateY(-50%);border-inline-start:6px solid var(--strct-flow-color)}.strct-flow__arrow--rev{left:-1px;transform:translateY(-50%);border-inline-end:6px solid var(--strct-flow-color)}.strct-flow--vertical .strct-flow__arrow{top:auto;left:50%}.strct-flow--vertical .strct-flow__arrow--fwd{right:auto;bottom:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-top:6px solid var(--strct-flow-color);border-bottom:0}.strct-flow--vertical .strct-flow__arrow--rev{left:50%;top:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-bottom:6px solid var(--strct-flow-color);border-top:0}.strct-flow__pkt{position:absolute;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;background:var(--strct-flow-color);box-shadow:0 0 5px var(--strct-flow-color)}.strct-flow--vertical .strct-flow__pkt{top:0;left:50%}.strct-flow__pkt--fwd{animation:strct-flow-fwd 1.8s linear infinite}.strct-flow__pkt--rev{animation:strct-flow-rev 1.8s linear infinite}.strct-flow--vertical .strct-flow__pkt--fwd{animation-name:strct-flow-fwd-v}.strct-flow--vertical .strct-flow__pkt--rev{animation-name:strct-flow-rev-v}.strct-flow__pkt--2{animation-delay:.6s}.strct-flow__pkt--3{animation-delay:1.2s}@keyframes strct-flow-fwd{0%{left:0;opacity:0}15%,85%{opacity:1}to{left:100%;opacity:0}}@keyframes strct-flow-rev{0%{left:100%;opacity:0}15%,85%{opacity:1}to{left:0;opacity:0}}@keyframes strct-flow-fwd-v{0%{top:0;opacity:0}15%,85%{opacity:1}to{top:100%;opacity:0}}@keyframes strct-flow-rev-v{0%{top:100%;opacity:0}15%,85%{opacity:1}to{top:0;opacity:0}}.strct-flow__caption{margin-top:var(--space-2);text-align:center;font-size:var(--text-sm);color:var(--t3)}.strct-flow--vertical .strct-flow__caption{text-align:start}@media(prefers-reduced-motion:reduce){.strct-flow__pkt{display:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18482
+ `, isInline: true, styles: [".strct-flow{--strct-flow-color: var(--acc);display:block}.strct-flow--neutral{--strct-flow-color: var(--t3)}.strct-flow--accent{--strct-flow-color: var(--acc)}.strct-flow--success{--strct-flow-color: var(--success)}.strct-flow--warning{--strct-flow-color: var(--warning)}.strct-flow--critical{--strct-flow-color: var(--critical)}.strct-flow__row{display:flex;align-items:center;gap:var(--space-2)}.strct-flow--vertical .strct-flow__row{flex-direction:column;align-items:stretch}.strct-flow--fan{container-type:inline-size}.strct-flow__fan{position:relative;display:flex;align-items:flex-start;gap:var(--strct-flow-col-gap, 48px)}.strct-flow__edges{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}.strct-flow__edge{stroke:var(--b3);stroke-width:1.5}.strct-flow__edge--accent{stroke:var(--acc)}.strct-flow__edge--success{stroke:var(--success)}.strct-flow__edge--warning{stroke:var(--warning)}.strct-flow__edge--critical{stroke:var(--critical)}.strct-flow__edge--dashed{stroke-dasharray:4 3}.strct-flow__edge--animated{stroke-dasharray:5 4;animation:strct-flow-dash 1s linear infinite}@keyframes strct-flow-dash{to{stroke-dashoffset:-9}}@media(prefers-reduced-motion:reduce){.strct-flow__edge--animated{animation:none}}.strct-flow__col{position:relative;flex:1 1 0;min-width:0}.strct-flow__colhead{font-size:var(--text-sm);font-weight:600;color:var(--t3);margin-block-end:var(--space-2)}.strct-flow__boxes{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-2)}.strct-flow__box{display:flex;flex-direction:column;gap:3px;padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);min-width:0}.strct-flow__box--accent{border-color:var(--acc30)}.strct-flow__box--success{border-color:var(--success)}.strct-flow__box--warning{border-color:var(--warning)}.strct-flow__box--critical{border-color:var(--critical)}.strct-flow__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@container (max-width: 480px){.strct-flow__fan{flex-direction:column;gap:var(--space-3)}.strct-flow__edges{display:none}.strct-flow__col{padding-inline-start:var(--space-3);border-inline-start:2px solid var(--b2)}}.strct-flow__node{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);flex-shrink:0}.strct-flow__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--t3)}.strct-flow__node--accent .strct-flow__dot{background:var(--acc)}.strct-flow__node--success .strct-flow__dot{background:var(--success)}.strct-flow__node--warning .strct-flow__dot{background:var(--warning)}.strct-flow__node--critical .strct-flow__dot{background:var(--critical)}.strct-flow__node-text{display:flex;flex-direction:column;gap:1px;min-width:0}.strct-flow__label{font-size:var(--text-md);font-weight:600;color:var(--t1);line-height:1.2}.strct-flow__role{font-size:12px;font-weight:700;letter-spacing:.4px;color:var(--t3)}.strct-flow__sub{font-size:var(--text-sm);color:var(--t3)}.strct-flow__conn{position:relative;flex:1 1 auto;min-width:40px;height:2px;align-self:center}.strct-flow--vertical .strct-flow__conn{width:2px;min-width:0;height:28px;flex:0 0 28px;align-self:center}.strct-flow__line{position:absolute;inset:0;border-radius:2px;background:linear-gradient(to right,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow--vertical .strct-flow__line{background:linear-gradient(to bottom,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow__arrow{position:absolute;top:50%;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.strct-flow__arrow--fwd{right:-1px;transform:translateY(-50%);border-inline-start:6px solid var(--strct-flow-color)}.strct-flow__arrow--rev{left:-1px;transform:translateY(-50%);border-inline-end:6px solid var(--strct-flow-color)}.strct-flow--vertical .strct-flow__arrow{top:auto;left:50%}.strct-flow--vertical .strct-flow__arrow--fwd{right:auto;bottom:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-top:6px solid var(--strct-flow-color);border-bottom:0}.strct-flow--vertical .strct-flow__arrow--rev{left:50%;top:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-bottom:6px solid var(--strct-flow-color);border-top:0}.strct-flow__pkt{position:absolute;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;background:var(--strct-flow-color);box-shadow:0 0 5px var(--strct-flow-color)}.strct-flow--vertical .strct-flow__pkt{top:0;left:50%}.strct-flow__pkt--fwd{animation:strct-flow-fwd 1.8s linear infinite}.strct-flow__pkt--rev{animation:strct-flow-rev 1.8s linear infinite}.strct-flow--vertical .strct-flow__pkt--fwd{animation-name:strct-flow-fwd-v}.strct-flow--vertical .strct-flow__pkt--rev{animation-name:strct-flow-rev-v}.strct-flow__pkt--2{animation-delay:.6s}.strct-flow__pkt--3{animation-delay:1.2s}@keyframes strct-flow-fwd{0%{left:0;opacity:0}15%,85%{opacity:1}to{left:100%;opacity:0}}@keyframes strct-flow-rev{0%{left:100%;opacity:0}15%,85%{opacity:1}to{left:0;opacity:0}}@keyframes strct-flow-fwd-v{0%{top:0;opacity:0}15%,85%{opacity:1}to{top:100%;opacity:0}}@keyframes strct-flow-rev-v{0%{top:100%;opacity:0}15%,85%{opacity:1}to{top:0;opacity:0}}.strct-flow__caption{margin-top:var(--space-2);text-align:center;font-size:var(--text-sm);color:var(--t3)}.strct-flow--vertical .strct-flow__caption{text-align:start}@media(prefers-reduced-motion:reduce){.strct-flow__pkt{display:none}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
18221
18483
  }
18222
18484
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctFlow, decorators: [{
18223
18485
  type: Component,
18224
- args: [{ selector: 'strct-flow', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
18225
- <div class="strct-flow__row">
18226
- @for (node of nodes(); track node.id; let last = $last) {
18227
- <div class="strct-flow__node strct-flow__node--{{ node.status ?? 'neutral' }}">
18228
- <span class="strct-flow__dot" aria-hidden="true"></span>
18229
- <span class="strct-flow__node-text">
18230
- <span class="strct-flow__label">{{ node.label }}</span>
18231
- @if (node.role) {
18232
- <span class="strct-flow__role">{{ node.role }}</span>
18233
- }
18234
- @if (node.sublabel) {
18235
- <span class="strct-flow__sub">{{ node.sublabel }}</span>
18486
+ args: [{ selector: 'strct-flow', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet], template: `
18487
+ @if (layout() !== 'chain') {
18488
+ <div class="strct-flow__fan" #fan>
18489
+ <!-- The edges are geometry, not content: they are measured from the
18490
+ boxes after they render, and hidden from assistive tech, which is
18491
+ given the structure instead. -->
18492
+ <svg class="strct-flow__edges" aria-hidden="true" [attr.viewBox]="viewBox()">
18493
+ @for (e of edgePaths(); track e.key) {
18494
+ <path
18495
+ class="strct-flow__edge strct-flow__edge--{{ e.status }}"
18496
+ [class.strct-flow__edge--dashed]="e.dashed"
18497
+ [class.strct-flow__edge--animated]="e.animated"
18498
+ [attr.d]="e.d"
18499
+ fill="none"
18500
+ />
18501
+ }
18502
+ </svg>
18503
+ @for (col of columnsView(); track col.index) {
18504
+ <div class="strct-flow__col" role="group" [attr.aria-label]="col.heading || null">
18505
+ @if (col.heading) {
18506
+ <div class="strct-flow__colhead">{{ col.heading }}</div>
18236
18507
  }
18237
- </span>
18238
- </div>
18508
+ <ul class="strct-flow__boxes">
18509
+ @for (node of col.nodes; track node.id) {
18510
+ <li
18511
+ class="strct-flow__box strct-flow__box--{{ node.status ?? 'neutral' }}"
18512
+ [attr.data-flow-node]="node.id"
18513
+ >
18514
+ @if (nodeTpl(); as tpl) {
18515
+ <ng-container
18516
+ [ngTemplateOutlet]="tpl"
18517
+ [ngTemplateOutletContext]="{ $implicit: node, node }"
18518
+ />
18519
+ } @else {
18520
+ <span class="strct-flow__label">{{ node.label }}</span>
18521
+ @if (node.role) {
18522
+ <span class="strct-flow__role">{{ node.role }}</span>
18523
+ }
18524
+ @if (node.sublabel) {
18525
+ <span class="strct-flow__sub">{{ node.sublabel }}</span>
18526
+ }
18527
+ }
18528
+ <span class="strct-flow__sr">{{ nodeSummary(node) }}</span>
18529
+ </li>
18530
+ }
18531
+ </ul>
18532
+ </div>
18533
+ }
18534
+ </div>
18535
+ @if (label()) {
18536
+ <div class="strct-flow__caption">{{ label() }}</div>
18537
+ }
18538
+ } @else {
18539
+ <div class="strct-flow__row">
18540
+ @for (node of nodes(); track node.id; let last = $last) {
18541
+ <div class="strct-flow__node strct-flow__node--{{ node.status ?? 'neutral' }}">
18542
+ <span class="strct-flow__dot" aria-hidden="true"></span>
18543
+ <span class="strct-flow__node-text">
18544
+ <span class="strct-flow__label">{{ node.label }}</span>
18545
+ @if (node.role) {
18546
+ <span class="strct-flow__role">{{ node.role }}</span>
18547
+ }
18548
+ @if (node.sublabel) {
18549
+ <span class="strct-flow__sub">{{ node.sublabel }}</span>
18550
+ }
18551
+ </span>
18552
+ </div>
18239
18553
 
18240
- @if (!last) {
18241
- <div class="strct-flow__conn" aria-hidden="true">
18242
- <span class="strct-flow__line"></span>
18243
- @if (showArrow('forward')) {
18244
- <span class="strct-flow__arrow strct-flow__arrow--fwd"></span>
18245
- }
18246
- @if (showArrow('reverse')) {
18247
- <span class="strct-flow__arrow strct-flow__arrow--rev"></span>
18248
- }
18249
- @if (animated()) {
18554
+ @if (!last) {
18555
+ <div class="strct-flow__conn" aria-hidden="true">
18556
+ <span class="strct-flow__line"></span>
18250
18557
  @if (showArrow('forward')) {
18251
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--1"></span>
18252
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--2"></span>
18253
- <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--3"></span>
18558
+ <span class="strct-flow__arrow strct-flow__arrow--fwd"></span>
18254
18559
  }
18255
18560
  @if (showArrow('reverse')) {
18256
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--1"></span>
18257
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--2"></span>
18258
- <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--3"></span>
18561
+ <span class="strct-flow__arrow strct-flow__arrow--rev"></span>
18259
18562
  }
18260
- }
18261
- </div>
18563
+ @if (animated()) {
18564
+ @if (showArrow('forward')) {
18565
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--1"></span>
18566
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--2"></span>
18567
+ <span class="strct-flow__pkt strct-flow__pkt--fwd strct-flow__pkt--3"></span>
18568
+ }
18569
+ @if (showArrow('reverse')) {
18570
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--1"></span>
18571
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--2"></span>
18572
+ <span class="strct-flow__pkt strct-flow__pkt--rev strct-flow__pkt--3"></span>
18573
+ }
18574
+ }
18575
+ </div>
18576
+ }
18262
18577
  }
18263
- }
18264
- </div>
18578
+ </div>
18265
18579
 
18266
- @if (caption()) {
18267
- <div class="strct-flow__caption">{{ caption() }}</div>
18580
+ @if (caption()) {
18581
+ <div class="strct-flow__caption">{{ caption() }}</div>
18582
+ }
18268
18583
  }
18269
18584
  `, host: {
18270
18585
  class: 'strct-flow',
18271
- role: 'img',
18586
+ '[attr.role]': "layout() === 'chain' ? 'img' : null",
18587
+ '[class.strct-flow--fan]': "layout() !== 'chain'",
18272
18588
  '[class.strct-flow--vertical]': "orientation() === 'vertical'",
18273
18589
  '[class.strct-flow--neutral]': "status() === 'neutral'",
18274
18590
  '[class.strct-flow--accent]': "status() === 'accent'",
@@ -18276,9 +18592,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
18276
18592
  '[class.strct-flow--warning]': "status() === 'warning'",
18277
18593
  '[class.strct-flow--critical]': "status() === 'critical'",
18278
18594
  '[class.strct-flow--live]': 'animated()',
18279
- '[attr.aria-label]': 'ariaLabel()',
18280
- }, styles: [".strct-flow{--strct-flow-color: var(--acc);display:block}.strct-flow--neutral{--strct-flow-color: var(--t3)}.strct-flow--accent{--strct-flow-color: var(--acc)}.strct-flow--success{--strct-flow-color: var(--success)}.strct-flow--warning{--strct-flow-color: var(--warning)}.strct-flow--critical{--strct-flow-color: var(--critical)}.strct-flow__row{display:flex;align-items:center;gap:var(--space-2)}.strct-flow--vertical .strct-flow__row{flex-direction:column;align-items:stretch}.strct-flow__node{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);flex-shrink:0}.strct-flow__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--t3)}.strct-flow__node--accent .strct-flow__dot{background:var(--acc)}.strct-flow__node--success .strct-flow__dot{background:var(--success)}.strct-flow__node--warning .strct-flow__dot{background:var(--warning)}.strct-flow__node--critical .strct-flow__dot{background:var(--critical)}.strct-flow__node-text{display:flex;flex-direction:column;gap:1px;min-width:0}.strct-flow__label{font-size:var(--text-md);font-weight:600;color:var(--t1);line-height:1.2}.strct-flow__role{font-size:12px;font-weight:700;letter-spacing:.4px;color:var(--t3)}.strct-flow__sub{font-size:var(--text-sm);color:var(--t3)}.strct-flow__conn{position:relative;flex:1 1 auto;min-width:40px;height:2px;align-self:center}.strct-flow--vertical .strct-flow__conn{width:2px;min-width:0;height:28px;flex:0 0 28px;align-self:center}.strct-flow__line{position:absolute;inset:0;border-radius:2px;background:linear-gradient(to right,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow--vertical .strct-flow__line{background:linear-gradient(to bottom,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow__arrow{position:absolute;top:50%;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.strct-flow__arrow--fwd{right:-1px;transform:translateY(-50%);border-inline-start:6px solid var(--strct-flow-color)}.strct-flow__arrow--rev{left:-1px;transform:translateY(-50%);border-inline-end:6px solid var(--strct-flow-color)}.strct-flow--vertical .strct-flow__arrow{top:auto;left:50%}.strct-flow--vertical .strct-flow__arrow--fwd{right:auto;bottom:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-top:6px solid var(--strct-flow-color);border-bottom:0}.strct-flow--vertical .strct-flow__arrow--rev{left:50%;top:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-bottom:6px solid var(--strct-flow-color);border-top:0}.strct-flow__pkt{position:absolute;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;background:var(--strct-flow-color);box-shadow:0 0 5px var(--strct-flow-color)}.strct-flow--vertical .strct-flow__pkt{top:0;left:50%}.strct-flow__pkt--fwd{animation:strct-flow-fwd 1.8s linear infinite}.strct-flow__pkt--rev{animation:strct-flow-rev 1.8s linear infinite}.strct-flow--vertical .strct-flow__pkt--fwd{animation-name:strct-flow-fwd-v}.strct-flow--vertical .strct-flow__pkt--rev{animation-name:strct-flow-rev-v}.strct-flow__pkt--2{animation-delay:.6s}.strct-flow__pkt--3{animation-delay:1.2s}@keyframes strct-flow-fwd{0%{left:0;opacity:0}15%,85%{opacity:1}to{left:100%;opacity:0}}@keyframes strct-flow-rev{0%{left:100%;opacity:0}15%,85%{opacity:1}to{left:0;opacity:0}}@keyframes strct-flow-fwd-v{0%{top:0;opacity:0}15%,85%{opacity:1}to{top:100%;opacity:0}}@keyframes strct-flow-rev-v{0%{top:100%;opacity:0}15%,85%{opacity:1}to{top:0;opacity:0}}.strct-flow__caption{margin-top:var(--space-2);text-align:center;font-size:var(--text-sm);color:var(--t3)}.strct-flow--vertical .strct-flow__caption{text-align:start}@media(prefers-reduced-motion:reduce){.strct-flow__pkt{display:none}}\n"] }]
18281
- }], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }] } });
18595
+ '[attr.aria-label]': "layout() === 'chain' ? ariaLabel() : null",
18596
+ }, styles: [".strct-flow{--strct-flow-color: var(--acc);display:block}.strct-flow--neutral{--strct-flow-color: var(--t3)}.strct-flow--accent{--strct-flow-color: var(--acc)}.strct-flow--success{--strct-flow-color: var(--success)}.strct-flow--warning{--strct-flow-color: var(--warning)}.strct-flow--critical{--strct-flow-color: var(--critical)}.strct-flow__row{display:flex;align-items:center;gap:var(--space-2)}.strct-flow--vertical .strct-flow__row{flex-direction:column;align-items:stretch}.strct-flow--fan{container-type:inline-size}.strct-flow__fan{position:relative;display:flex;align-items:flex-start;gap:var(--strct-flow-col-gap, 48px)}.strct-flow__edges{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}.strct-flow__edge{stroke:var(--b3);stroke-width:1.5}.strct-flow__edge--accent{stroke:var(--acc)}.strct-flow__edge--success{stroke:var(--success)}.strct-flow__edge--warning{stroke:var(--warning)}.strct-flow__edge--critical{stroke:var(--critical)}.strct-flow__edge--dashed{stroke-dasharray:4 3}.strct-flow__edge--animated{stroke-dasharray:5 4;animation:strct-flow-dash 1s linear infinite}@keyframes strct-flow-dash{to{stroke-dashoffset:-9}}@media(prefers-reduced-motion:reduce){.strct-flow__edge--animated{animation:none}}.strct-flow__col{position:relative;flex:1 1 0;min-width:0}.strct-flow__colhead{font-size:var(--text-sm);font-weight:600;color:var(--t3);margin-block-end:var(--space-2)}.strct-flow__boxes{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-2)}.strct-flow__box{display:flex;flex-direction:column;gap:3px;padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);min-width:0}.strct-flow__box--accent{border-color:var(--acc30)}.strct-flow__box--success{border-color:var(--success)}.strct-flow__box--warning{border-color:var(--warning)}.strct-flow__box--critical{border-color:var(--critical)}.strct-flow__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@container (max-width: 480px){.strct-flow__fan{flex-direction:column;gap:var(--space-3)}.strct-flow__edges{display:none}.strct-flow__col{padding-inline-start:var(--space-3);border-inline-start:2px solid var(--b2)}}.strct-flow__node{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-1);flex-shrink:0}.strct-flow__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;background:var(--t3)}.strct-flow__node--accent .strct-flow__dot{background:var(--acc)}.strct-flow__node--success .strct-flow__dot{background:var(--success)}.strct-flow__node--warning .strct-flow__dot{background:var(--warning)}.strct-flow__node--critical .strct-flow__dot{background:var(--critical)}.strct-flow__node-text{display:flex;flex-direction:column;gap:1px;min-width:0}.strct-flow__label{font-size:var(--text-md);font-weight:600;color:var(--t1);line-height:1.2}.strct-flow__role{font-size:12px;font-weight:700;letter-spacing:.4px;color:var(--t3)}.strct-flow__sub{font-size:var(--text-sm);color:var(--t3)}.strct-flow__conn{position:relative;flex:1 1 auto;min-width:40px;height:2px;align-self:center}.strct-flow--vertical .strct-flow__conn{width:2px;min-width:0;height:28px;flex:0 0 28px;align-self:center}.strct-flow__line{position:absolute;inset:0;border-radius:2px;background:linear-gradient(to right,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow--vertical .strct-flow__line{background:linear-gradient(to bottom,var(--b3),color-mix(in srgb,var(--strct-flow-color) 55%,transparent),var(--b3))}.strct-flow__arrow{position:absolute;top:50%;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.strct-flow__arrow--fwd{right:-1px;transform:translateY(-50%);border-inline-start:6px solid var(--strct-flow-color)}.strct-flow__arrow--rev{left:-1px;transform:translateY(-50%);border-inline-end:6px solid var(--strct-flow-color)}.strct-flow--vertical .strct-flow__arrow{top:auto;left:50%}.strct-flow--vertical .strct-flow__arrow--fwd{right:auto;bottom:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-top:6px solid var(--strct-flow-color);border-bottom:0}.strct-flow--vertical .strct-flow__arrow--rev{left:50%;top:-1px;transform:translate(-50%);border-inline-start:4px solid transparent;border-inline-end:4px solid transparent;border-bottom:6px solid var(--strct-flow-color);border-top:0}.strct-flow__pkt{position:absolute;top:50%;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;background:var(--strct-flow-color);box-shadow:0 0 5px var(--strct-flow-color)}.strct-flow--vertical .strct-flow__pkt{top:0;left:50%}.strct-flow__pkt--fwd{animation:strct-flow-fwd 1.8s linear infinite}.strct-flow__pkt--rev{animation:strct-flow-rev 1.8s linear infinite}.strct-flow--vertical .strct-flow__pkt--fwd{animation-name:strct-flow-fwd-v}.strct-flow--vertical .strct-flow__pkt--rev{animation-name:strct-flow-rev-v}.strct-flow__pkt--2{animation-delay:.6s}.strct-flow__pkt--3{animation-delay:1.2s}@keyframes strct-flow-fwd{0%{left:0;opacity:0}15%,85%{opacity:1}to{left:100%;opacity:0}}@keyframes strct-flow-rev{0%{left:100%;opacity:0}15%,85%{opacity:1}to{left:0;opacity:0}}@keyframes strct-flow-fwd-v{0%{top:0;opacity:0}15%,85%{opacity:1}to{top:100%;opacity:0}}@keyframes strct-flow-rev-v{0%{top:100%;opacity:0}15%,85%{opacity:1}to{top:0;opacity:0}}.strct-flow__caption{margin-top:var(--space-2);text-align:center;font-size:var(--text-sm);color:var(--t3)}.strct-flow--vertical .strct-flow__caption{text-align:start}@media(prefers-reduced-motion:reduce){.strct-flow__pkt{display:none}}\n"] }]
18597
+ }], ctorParameters: () => [], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], edges: [{ type: i0.Input, args: [{ isSignal: true, alias: "edges", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], nodeTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctFlowNodeTemplate), { ...{ read: TemplateRef }, isSignal: true }] }] } });
18282
18598
 
18283
18599
  /**
18284
18600
  * Dense KPI tile: a label, a large value (+ unit), an optional change indicator
@@ -22532,6 +22848,106 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
22532
22848
  }, styles: [".strct-steps{display:block;min-width:0}.strct-steps__list{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;align-items:center;gap:6px 4px}.strct-steps--vertical .strct-steps__list{flex-direction:column;align-items:stretch;flex-wrap:nowrap}.strct-steps__step{position:relative;display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border:1px solid var(--b2);border-radius:999px;font-size:var(--text-sm);color:var(--t2);background:var(--bg-1);min-width:0}.strct-steps--dense .strct-steps__step{padding:2px 8px}.strct-steps__list>.strct-steps__step+.strct-steps__step:before{content:\"\";position:absolute;inset-inline-start:-5px;width:5px;height:1px;background:var(--b2)}.strct-steps--vertical .strct-steps__list>.strct-steps__step+.strct-steps__step:before{inset-inline-start:14px;top:-7px;width:1px;height:7px}.strct-steps__marker:empty{display:none}.strct-steps__marker{display:inline-flex;align-items:center;justify-content:center;flex:none;width:18px;height:18px;border-radius:50%;font-size:11px;font-weight:600;background:var(--bg-3);color:var(--t2)}.strct-steps__text{display:flex;flex-direction:column;min-width:0}.strct-steps__label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.strct-steps__desc{font-size:var(--text-sm);color:var(--t3);white-space:normal}.strct-steps__action{margin-inline-start:auto;padding-inline-start:8px;flex:none}.strct-steps__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-steps__step[data-state=done]{border-color:var(--success);color:var(--success)}.strct-steps__step[data-state=done] .strct-steps__marker{background:var(--success);color:var(--inv)}.strct-steps__step[data-state=active]{border-color:var(--acc);color:var(--acc);background:var(--acc-s)}.strct-steps__step[data-state=active] .strct-steps__marker{background:var(--acc);color:var(--inv)}.strct-steps__step[data-state=failed]{border-color:var(--critical);color:var(--critical)}.strct-steps__step[data-state=failed] .strct-steps__marker{background:var(--critical);color:var(--inv)}.strct-steps__step[data-state=blocked]{border-color:var(--warning);color:var(--warning)}.strct-steps__step[data-state=blocked] .strct-steps__marker{background:var(--warning);color:var(--inv)}.strct-steps__step[data-state=skipped]{color:var(--t3);border-style:dashed}.strct-steps__step[data-state=skipped] .strct-steps__label{text-decoration:line-through}.strct-steps__step[data-state=pending]{color:var(--t3);border-color:var(--t4)}.strct-steps__step[data-state=active]:after{content:\"\";position:absolute;inset:-1px;border-radius:inherit;border:1px solid var(--acc);animation:strct-steps-pulse 1.4s ease-out infinite;pointer-events:none}@keyframes strct-steps-pulse{0%{opacity:.9;transform:scale(1)}to{opacity:0;transform:scale(1.06)}}@media(prefers-reduced-motion:reduce){.strct-steps__step[data-state=active]:after{animation:none;opacity:.5;transform:none}}.strct-steps--dots .strct-steps__step{padding:0;border:0;background:none;gap:4px}.strct-steps--dots .strct-steps__label{display:none}.strct-steps--dots .strct-steps__marker{display:inline-flex;width:9px;height:9px;font-size:0}.strct-steps--dots .strct-steps__step[data-state=pending] .strct-steps__marker{background:var(--bg-3);box-shadow:inset 0 0 0 1px var(--b3)}.strct-steps--dots .strct-steps__list>.strct-steps__step+.strct-steps__step:before{inset-inline-start:-4px;width:4px}.strct-steps--cards .strct-steps__list{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(var(--strct-steps-card-min, 220px),100%),1fr));gap:var(--space-3)}.strct-steps--cards .strct-steps__step{align-items:flex-start;border-radius:var(--radius-lg);padding:var(--space-3);background:var(--bg-1)}.strct-steps--cards .strct-steps__label{font-weight:600;white-space:normal}.strct-steps--cards .strct-steps__list>.strct-steps__step+.strct-steps__step:before{content:none}\n"] }]
22533
22849
  }], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], numbered: [{ type: i0.Input, args: [{ isSignal: true, alias: "numbered", required: false }] }], dense: [{ type: i0.Input, args: [{ isSignal: true, alias: "dense", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], actionTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctStepAction), { ...{ read: TemplateRef }, isSignal: true }] }] } });
22534
22850
 
22851
+ const STATE_ICON = {
22852
+ empty: 'monitor',
22853
+ off: 'stopped',
22854
+ error: 'warning',
22855
+ };
22856
+ /**
22857
+ * A live picture — a console thumbnail, a camera — sits in a fixed-ratio frame,
22858
+ * and when there is no picture yet, or none at all, the frame says why **in its
22859
+ * own small space**: an empty state is too large for a 240px card.
22860
+ *
22861
+ * <strct-media-frame ratio="4 / 3"><img [src]="thumb" alt="" /></strct-media-frame>
22862
+ * <strct-media-frame state="off" message="The VM is off" />
22863
+ */
22864
+ class StrctMediaFrame {
22865
+ /** The frame's aspect ratio, as a CSS `aspect-ratio` value. */
22866
+ ratio = input('4 / 3', /* @ts-ignore */
22867
+ ...(ngDevMode ? [{ debugName: "ratio" }] : /* istanbul ignore next */ []));
22868
+ /** What the frame shows: the projected picture, or why there is none. */
22869
+ state = input('content', /* @ts-ignore */
22870
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
22871
+ /** The placeholder's line ("The VM is off"). */
22872
+ message = input('', /* @ts-ignore */
22873
+ ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
22874
+ /** Overrides the icon derived from `state`. */
22875
+ icon = input('', /* @ts-ignore */
22876
+ ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
22877
+ /** The whole frame becomes one tab stop that emits `activated`. */
22878
+ interactive = input(false, { ...(ngDevMode ? { debugName: "interactive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
22879
+ /** The accessible name of that control; falls back to `message`. */
22880
+ activateLabel = input('', /* @ts-ignore */
22881
+ ...(ngDevMode ? [{ debugName: "activateLabel" }] : /* istanbul ignore next */ []));
22882
+ /** The frame was activated. */
22883
+ activated = output();
22884
+ iconName = computed(() => {
22885
+ const explicit = this.icon();
22886
+ if (explicit)
22887
+ return explicit;
22888
+ const state = this.state();
22889
+ return state === 'content' || state === 'loading' ? '' : STATE_ICON[state];
22890
+ }, /* @ts-ignore */
22891
+ ...(ngDevMode ? [{ debugName: "iconName" }] : /* istanbul ignore next */ []));
22892
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctMediaFrame, deps: [], target: i0.ɵɵFactoryTarget.Component });
22893
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctMediaFrame, isStandalone: true, selector: "strct-media-frame", inputs: { ratio: { classPropertyName: "ratio", publicName: "ratio", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, activateLabel: { classPropertyName: "activateLabel", publicName: "activateLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activated: "activated" }, host: { properties: { "style.aspect-ratio": "ratio()", "attr.data-state": "state()", "class.strct-mf--interactive": "interactive()" }, classAttribute: "strct-mf" }, ngImport: i0, template: `
22894
+ @if (state() === 'content') {
22895
+ <ng-content />
22896
+ } @else {
22897
+ <span class="strct-mf__placeholder">
22898
+ @if (state() === 'loading') {
22899
+ <strct-spinner size="sm" />
22900
+ } @else if (iconName()) {
22901
+ <strct-icon [name]="iconName()" [size]="20" />
22902
+ }
22903
+ @if (message()) {
22904
+ <span class="strct-mf__msg">{{ message() }}</span>
22905
+ }
22906
+ </span>
22907
+ }
22908
+ @if (interactive()) {
22909
+ <button
22910
+ type="button"
22911
+ class="strct-mf__hit"
22912
+ [attr.aria-label]="activateLabel() || message() || null"
22913
+ (click)="activated.emit()"
22914
+ ></button>
22915
+ }
22916
+ `, isInline: true, styles: [".strct-mf{position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-3);min-width:0}.strct-mf>img,.strct-mf>video,.strct-mf>canvas{width:100%;height:100%;object-fit:cover;display:block}.strct-mf[data-state=off]{background:#000}.strct-mf__placeholder{display:flex;flex-direction:column;align-items:center;gap:6px;padding:var(--space-2);color:var(--t3);text-align:center}.strct-mf[data-state=off] .strct-mf__placeholder{color:#fff9}.strct-mf[data-state=error] .strct-mf__placeholder{color:var(--critical)}.strct-mf__msg{font-size:var(--text-sm);line-height:1.35}.strct-mf__hit{position:absolute;inset:-1px;padding:0;border:0;background:none;cursor:pointer}.strct-mf__hit:focus-visible{outline:2px solid var(--acc50);outline-offset:2px}.strct-mf--interactive:hover{border-color:var(--b3)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }, { kind: "component", type: StrctSpinner, selector: "strct-spinner", inputs: ["size", "label", "caption"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
22917
+ }
22918
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctMediaFrame, decorators: [{
22919
+ type: Component,
22920
+ args: [{ selector: 'strct-media-frame', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon, StrctSpinner], template: `
22921
+ @if (state() === 'content') {
22922
+ <ng-content />
22923
+ } @else {
22924
+ <span class="strct-mf__placeholder">
22925
+ @if (state() === 'loading') {
22926
+ <strct-spinner size="sm" />
22927
+ } @else if (iconName()) {
22928
+ <strct-icon [name]="iconName()" [size]="20" />
22929
+ }
22930
+ @if (message()) {
22931
+ <span class="strct-mf__msg">{{ message() }}</span>
22932
+ }
22933
+ </span>
22934
+ }
22935
+ @if (interactive()) {
22936
+ <button
22937
+ type="button"
22938
+ class="strct-mf__hit"
22939
+ [attr.aria-label]="activateLabel() || message() || null"
22940
+ (click)="activated.emit()"
22941
+ ></button>
22942
+ }
22943
+ `, host: {
22944
+ class: 'strct-mf',
22945
+ '[style.aspect-ratio]': 'ratio()',
22946
+ '[attr.data-state]': 'state()',
22947
+ '[class.strct-mf--interactive]': 'interactive()',
22948
+ }, styles: [".strct-mf{position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;border:1px solid var(--b2);border-radius:var(--radius-md);background:var(--bg-3);min-width:0}.strct-mf>img,.strct-mf>video,.strct-mf>canvas{width:100%;height:100%;object-fit:cover;display:block}.strct-mf[data-state=off]{background:#000}.strct-mf__placeholder{display:flex;flex-direction:column;align-items:center;gap:6px;padding:var(--space-2);color:var(--t3);text-align:center}.strct-mf[data-state=off] .strct-mf__placeholder{color:#fff9}.strct-mf[data-state=error] .strct-mf__placeholder{color:var(--critical)}.strct-mf__msg{font-size:var(--text-sm);line-height:1.35}.strct-mf__hit{position:absolute;inset:-1px;padding:0;border:0;background:none;cursor:pointer}.strct-mf__hit:focus-visible{outline:2px solid var(--acc50);outline-offset:2px}.strct-mf--interactive:hover{border-color:var(--b3)}\n"] }]
22949
+ }], propDecorators: { ratio: [{ type: i0.Input, args: [{ isSignal: true, alias: "ratio", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], activateLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "activateLabel", required: false }] }], activated: [{ type: i0.Output, args: ["activated"] }] } });
22950
+
22535
22951
  /**
22536
22952
  * A value change, as one phrase: "v10.27 → v10.28". The old value is muted, the
22537
22953
  * arrow is the library's, the new one is emphasised — and assistive tech hears
@@ -24448,5 +24864,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
24448
24864
  * Generated bundle index. Do not edit.
24449
24865
  */
24450
24866
 
24451
- export { FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFile, StrctFilterBar, StrctFlow, StrctFooter, StrctGauge, StrctHeader, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProgress, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderItem, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWizard, StrctWizardAside, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctValidationIcon, strctValidationTone, unlockBodyScroll };
24867
+ export { FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFile, StrctFilterBar, StrctFlow, StrctFlowNodeTemplate, StrctFooter, StrctGauge, StrctHeader, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMediaFrame, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProgress, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderItem, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWizard, StrctWizardAside, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctValidationIcon, strctValidationTone, unlockBodyScroll };
24452
24868
  //# sourceMappingURL=akcelik-strct.mjs.map