@akcelik/strct 0.29.0 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, model, output, booleanAttribute, HostListener, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, DestroyRef, contentChild, viewChild, viewChildren, TemplateRef, untracked, Renderer2 } from '@angular/core';
3
3
  import { DomSanitizer } from '@angular/platform-browser';
4
- import { DOCUMENT as DOCUMENT$1, NgTemplateOutlet } from '@angular/common';
4
+ import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1 } from '@angular/common';
5
+ import { RouterLink, RouterLinkActive } from '@angular/router';
5
6
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
7
 
7
8
  /** All palettes the token system ships with, in display order. */
@@ -641,7 +642,7 @@ class StrctIcon {
641
642
  </svg>
642
643
  }
643
644
  </span>
644
- }`, isInline: true, styles: [".strct-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;line-height:0}.strct-icon__raw{display:inline-flex}.strct-icon__raw>svg{width:100%;height:100%;display:block}.strct-icon__badge{position:absolute;right:-3px;bottom:-3px;width:50%;height:50%;min-width:8px;min-height:8px;max-width:12px;max-height:12px;border-radius:50%;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--success,.strct-icon__badge--critical,.strct-icon__badge--info,.strct-icon__badge--off,.strct-icon__badge--paused,.strct-icon__badge--running{display:flex;align-items:center;justify-content:center;font-size:clamp(6px,68%,10px);font-weight:800;line-height:1;color:var(--inv)}.strct-icon__badge--success{background:var(--success)}.strct-icon__badge--success:before{content:\"\\2713\"}.strct-icon__badge--warning{background:var(--warning);border-radius:1px;clip-path:polygon(50% 10%,92% 90%,8% 90%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));right:-2px;bottom:-2px;width:60%;height:60%;min-width:11px;min-height:11px;max-width:18px;max-height:18px;display:flex;align-items:center;justify-content:center;font-size:clamp(6px,70%,10px);font-weight:800;color:var(--inv);line-height:1}.strct-icon__badge--warning:before{content:\"!\"}.strct-icon__badge--critical{background:var(--critical);border-radius:1px;clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));width:58%;height:58%;min-width:10px;min-height:10px;max-width:15px;max-height:15px;font-size:clamp(6px,56%,9px)}.strct-icon__badge--critical:before{content:\"\\d7\"}.strct-icon__badge--running{background:var(--success)}.strct-icon__badge--running:before{content:\"\";width:46%;height:46%;min-width:4px;min-height:4px;background:var(--inv);clip-path:polygon(15% 2%,100% 50%,15% 98%);transform:translate(9%)}.strct-icon__badge--paused{background:var(--warning)}.strct-icon__badge--paused:before{content:\"\";width:1.6px;height:42%;min-height:4px;background:var(--inv);box-shadow:2.6px 0 0 var(--inv);transform:translate(-1.3px)}.strct-icon__badge--off{background:var(--t3)}.strct-icon__badge--off:before{content:\"\";width:40%;height:40%;min-width:3.5px;min-height:3.5px;background:var(--inv);border-radius:1px}.strct-icon__badge--info{background:var(--acc)}.strct-icon__badge--info:before{content:\"i\"}.strct-icon__badge--maintenance{background:var(--warning);right:-2px;bottom:-2px;width:58%;height:58%;min-width:10px;min-height:10px;max-width:16px;max-height:16px;display:flex;align-items:center;justify-content:center;padding:1px;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--maintenance svg{width:84%;height:84%;color:var(--inv)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
645
+ }`, isInline: true, styles: [".strct-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;line-height:0}.strct-icon__raw{display:inline-flex}.strct-icon__raw>svg{width:100%;height:100%;display:block}.strct-icon__badge{position:absolute;inset-inline-end:-3px;bottom:-3px;width:50%;height:50%;min-width:8px;min-height:8px;max-width:12px;max-height:12px;border-radius:50%;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--success,.strct-icon__badge--critical,.strct-icon__badge--info,.strct-icon__badge--off,.strct-icon__badge--paused,.strct-icon__badge--running{display:flex;align-items:center;justify-content:center;font-size:clamp(6px,68%,10px);font-weight:800;line-height:1;color:var(--inv)}.strct-icon__badge--success{background:var(--success)}.strct-icon__badge--success:before{content:\"\\2713\"}.strct-icon__badge--warning{background:var(--warning);border-radius:1px;clip-path:polygon(50% 10%,92% 90%,8% 90%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));inset-inline-end:-2px;bottom:-2px;width:60%;height:60%;min-width:11px;min-height:11px;max-width:18px;max-height:18px;display:flex;align-items:center;justify-content:center;font-size:clamp(6px,70%,10px);font-weight:800;color:var(--inv);line-height:1}.strct-icon__badge--warning:before{content:\"!\"}.strct-icon__badge--critical{background:var(--critical);border-radius:1px;clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));width:58%;height:58%;min-width:10px;min-height:10px;max-width:15px;max-height:15px;font-size:clamp(6px,56%,9px)}.strct-icon__badge--critical:before{content:\"\\d7\"}.strct-icon__badge--running{background:var(--success)}.strct-icon__badge--running:before{content:\"\";width:46%;height:46%;min-width:4px;min-height:4px;background:var(--inv);clip-path:polygon(15% 2%,100% 50%,15% 98%);transform:translate(9%)}.strct-icon__badge--paused{background:var(--warning)}.strct-icon__badge--paused:before{content:\"\";width:1.6px;height:42%;min-height:4px;background:var(--inv);box-shadow:2.6px 0 0 var(--inv);transform:translate(-1.3px)}.strct-icon__badge--off{background:var(--t3)}.strct-icon__badge--off:before{content:\"\";width:40%;height:40%;min-width:3.5px;min-height:3.5px;background:var(--inv);border-radius:1px}.strct-icon__badge--info{background:var(--acc)}.strct-icon__badge--info:before{content:\"i\"}.strct-icon__badge--maintenance{background:var(--warning);inset-inline-end:-2px;bottom:-2px;width:58%;height:58%;min-width:10px;min-height:10px;max-width:16px;max-height:16px;display:flex;align-items:center;justify-content:center;padding:1px;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--maintenance svg{width:84%;height:84%;color:var(--inv)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
645
646
  }
646
647
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctIcon, decorators: [{
647
648
  type: Component,
@@ -685,7 +686,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
685
686
  </svg>
686
687
  }
687
688
  </span>
688
- }`, host: { class: 'strct-icon' }, styles: [".strct-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;line-height:0}.strct-icon__raw{display:inline-flex}.strct-icon__raw>svg{width:100%;height:100%;display:block}.strct-icon__badge{position:absolute;right:-3px;bottom:-3px;width:50%;height:50%;min-width:8px;min-height:8px;max-width:12px;max-height:12px;border-radius:50%;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--success,.strct-icon__badge--critical,.strct-icon__badge--info,.strct-icon__badge--off,.strct-icon__badge--paused,.strct-icon__badge--running{display:flex;align-items:center;justify-content:center;font-size:clamp(6px,68%,10px);font-weight:800;line-height:1;color:var(--inv)}.strct-icon__badge--success{background:var(--success)}.strct-icon__badge--success:before{content:\"\\2713\"}.strct-icon__badge--warning{background:var(--warning);border-radius:1px;clip-path:polygon(50% 10%,92% 90%,8% 90%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));right:-2px;bottom:-2px;width:60%;height:60%;min-width:11px;min-height:11px;max-width:18px;max-height:18px;display:flex;align-items:center;justify-content:center;font-size:clamp(6px,70%,10px);font-weight:800;color:var(--inv);line-height:1}.strct-icon__badge--warning:before{content:\"!\"}.strct-icon__badge--critical{background:var(--critical);border-radius:1px;clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));width:58%;height:58%;min-width:10px;min-height:10px;max-width:15px;max-height:15px;font-size:clamp(6px,56%,9px)}.strct-icon__badge--critical:before{content:\"\\d7\"}.strct-icon__badge--running{background:var(--success)}.strct-icon__badge--running:before{content:\"\";width:46%;height:46%;min-width:4px;min-height:4px;background:var(--inv);clip-path:polygon(15% 2%,100% 50%,15% 98%);transform:translate(9%)}.strct-icon__badge--paused{background:var(--warning)}.strct-icon__badge--paused:before{content:\"\";width:1.6px;height:42%;min-height:4px;background:var(--inv);box-shadow:2.6px 0 0 var(--inv);transform:translate(-1.3px)}.strct-icon__badge--off{background:var(--t3)}.strct-icon__badge--off:before{content:\"\";width:40%;height:40%;min-width:3.5px;min-height:3.5px;background:var(--inv);border-radius:1px}.strct-icon__badge--info{background:var(--acc)}.strct-icon__badge--info:before{content:\"i\"}.strct-icon__badge--maintenance{background:var(--warning);right:-2px;bottom:-2px;width:58%;height:58%;min-width:10px;min-height:10px;max-width:16px;max-height:16px;display:flex;align-items:center;justify-content:center;padding:1px;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--maintenance svg{width:84%;height:84%;color:var(--inv)}\n"] }]
689
+ }`, host: { class: 'strct-icon' }, styles: [".strct-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;line-height:0}.strct-icon__raw{display:inline-flex}.strct-icon__raw>svg{width:100%;height:100%;display:block}.strct-icon__badge{position:absolute;inset-inline-end:-3px;bottom:-3px;width:50%;height:50%;min-width:8px;min-height:8px;max-width:12px;max-height:12px;border-radius:50%;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--success,.strct-icon__badge--critical,.strct-icon__badge--info,.strct-icon__badge--off,.strct-icon__badge--paused,.strct-icon__badge--running{display:flex;align-items:center;justify-content:center;font-size:clamp(6px,68%,10px);font-weight:800;line-height:1;color:var(--inv)}.strct-icon__badge--success{background:var(--success)}.strct-icon__badge--success:before{content:\"\\2713\"}.strct-icon__badge--warning{background:var(--warning);border-radius:1px;clip-path:polygon(50% 10%,92% 90%,8% 90%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));inset-inline-end:-2px;bottom:-2px;width:60%;height:60%;min-width:11px;min-height:11px;max-width:18px;max-height:18px;display:flex;align-items:center;justify-content:center;font-size:clamp(6px,70%,10px);font-weight:800;color:var(--inv);line-height:1}.strct-icon__badge--warning:before{content:\"!\"}.strct-icon__badge--critical{background:var(--critical);border-radius:1px;clip-path:polygon(50% 0%,100% 50%,50% 100%,0% 50%);box-shadow:none;filter:drop-shadow(0 0 1.5px var(--bg-1));width:58%;height:58%;min-width:10px;min-height:10px;max-width:15px;max-height:15px;font-size:clamp(6px,56%,9px)}.strct-icon__badge--critical:before{content:\"\\d7\"}.strct-icon__badge--running{background:var(--success)}.strct-icon__badge--running:before{content:\"\";width:46%;height:46%;min-width:4px;min-height:4px;background:var(--inv);clip-path:polygon(15% 2%,100% 50%,15% 98%);transform:translate(9%)}.strct-icon__badge--paused{background:var(--warning)}.strct-icon__badge--paused:before{content:\"\";width:1.6px;height:42%;min-height:4px;background:var(--inv);box-shadow:2.6px 0 0 var(--inv);transform:translate(-1.3px)}.strct-icon__badge--off{background:var(--t3)}.strct-icon__badge--off:before{content:\"\";width:40%;height:40%;min-width:3.5px;min-height:3.5px;background:var(--inv);border-radius:1px}.strct-icon__badge--info{background:var(--acc)}.strct-icon__badge--info:before{content:\"i\"}.strct-icon__badge--maintenance{background:var(--warning);inset-inline-end:-2px;bottom:-2px;width:58%;height:58%;min-width:10px;min-height:10px;max-width:16px;max-height:16px;display:flex;align-items:center;justify-content:center;padding:1px;box-shadow:0 0 0 1.5px var(--bg-1)}.strct-icon__badge--maintenance svg{width:84%;height:84%;color:var(--inv)}\n"] }]
689
690
  }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
690
691
 
691
692
  /** Palette dots + light/dark pill, wired to {@link StrctThemeService}. */
@@ -965,7 +966,7 @@ class StrctVerticalNav {
965
966
  ></div>
966
967
  }
967
968
  <ng-content />
968
- `, isInline: true, styles: [".strct-vnav{display:flex;flex-direction:column;width:264px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden}@media(max-width:768px){.strct-vnav{position:fixed;top:0;left:0;bottom:0;z-index:300;transform:translate(-100%);transition:transform .2s ease}.strct-vnav--open{transform:translate(0)}.strct-vnav__backdrop{position:fixed;inset:0;z-index:299;background:#00000080;animation:strct-vnav-backdrop-in .2s ease}}@media(min-width:769px){.strct-vnav__backdrop{display:none}}@keyframes strct-vnav-backdrop-in{0%{opacity:0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
969
+ `, isInline: true, styles: [".strct-vnav{display:flex;flex-direction:column;width:264px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden}@media(max-width:768px){.strct-vnav{position:fixed;top:0;inset-inline-start:0;bottom:0;z-index:300;transform:translate(-100%);transition:transform .2s ease}[dir=rtl] .strct-vnav{transform:translate(100%)}.strct-vnav--open,[dir=rtl] .strct-vnav--open{transform:translate(0)}.strct-vnav__backdrop{position:fixed;inset:0;z-index:299;background:#00000080;animation:strct-vnav-backdrop-in .2s ease}}@media(min-width:769px){.strct-vnav__backdrop{display:none}}@keyframes strct-vnav-backdrop-in{0%{opacity:0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
969
970
  }
970
971
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctVerticalNav, decorators: [{
971
972
  type: Component,
@@ -985,7 +986,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
985
986
  `, host: {
986
987
  class: 'strct-vnav',
987
988
  '[class.strct-vnav--open]': 'shell.mobileNavOpen()',
988
- }, styles: [".strct-vnav{display:flex;flex-direction:column;width:264px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden}@media(max-width:768px){.strct-vnav{position:fixed;top:0;left:0;bottom:0;z-index:300;transform:translate(-100%);transition:transform .2s ease}.strct-vnav--open{transform:translate(0)}.strct-vnav__backdrop{position:fixed;inset:0;z-index:299;background:#00000080;animation:strct-vnav-backdrop-in .2s ease}}@media(min-width:769px){.strct-vnav__backdrop{display:none}}@keyframes strct-vnav-backdrop-in{0%{opacity:0}}\n"] }]
989
+ }, styles: [".strct-vnav{display:flex;flex-direction:column;width:264px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden}@media(max-width:768px){.strct-vnav{position:fixed;top:0;inset-inline-start:0;bottom:0;z-index:300;transform:translate(-100%);transition:transform .2s ease}[dir=rtl] .strct-vnav{transform:translate(100%)}.strct-vnav--open,[dir=rtl] .strct-vnav--open{transform:translate(0)}.strct-vnav__backdrop{position:fixed;inset:0;z-index:299;background:#00000080;animation:strct-vnav-backdrop-in .2s ease}}@media(min-width:769px){.strct-vnav__backdrop{display:none}}@keyframes strct-vnav-backdrop-in{0%{opacity:0}}\n"] }]
989
990
  }] });
990
991
  /** Horizontal strip of icon tabs (e.g. section switcher at the top of a sidebar). */
991
992
  class StrctNav {
@@ -1039,16 +1040,90 @@ class StrctRail {
1039
1040
  ariaLabel = input('Primary', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1040
1041
  /** Emitted when an item is chosen. */
1041
1042
  select = output();
1042
- pick(item) {
1043
+ topItems = computed(() => this.items().filter((i) => i.placement !== 'bottom'), ...(ngDevMode ? [{ debugName: "topItems" }] : /* istanbul ignore next */ []));
1044
+ bottomItems = computed(() => this.items().filter((i) => i.placement === 'bottom'), ...(ngDevMode ? [{ debugName: "bottomItems" }] : /* istanbul ignore next */ []));
1045
+ /** Enabled item with a router destination → renders as `<a routerLink>`. */
1046
+ isRouted(item) {
1047
+ return item.routerLink != null && !item.disabled;
1048
+ }
1049
+ /**
1050
+ * Active state of a router-link item: an explicit `activeId` is authoritative;
1051
+ * without one, the router's own active state drives it.
1052
+ */
1053
+ isActive(item, routerActive) {
1054
+ const id = this.activeId();
1055
+ return id !== null ? item.id === id : routerActive;
1056
+ }
1057
+ pick(item, event) {
1043
1058
  if (item.disabled)
1044
1059
  return;
1060
+ // A modified / non-primary click on a link is the browser's (new tab etc.) —
1061
+ // never ours: no activeId change, no (select).
1062
+ if (event &&
1063
+ (item.routerLink != null || item.href) &&
1064
+ (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
1065
+ return;
1066
+ }
1045
1067
  this.activeId.set(item.id);
1046
1068
  this.select.emit(item);
1047
1069
  }
1048
1070
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctRail, deps: [], target: i0.ɵɵFactoryTarget.Component });
1049
1071
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctRail, isStandalone: true, selector: "strct-rail", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeId: "activeIdChange", collapsed: "collapsedChange", select: "select" }, host: { properties: { "class.strct-rail--collapsed": "collapsed()" }, classAttribute: "strct-rail" }, ngImport: i0, template: `
1050
- <nav class="strct-rail__nav" [attr.aria-label]="ariaLabel()">
1051
- @for (item of items(); track item.id) {
1072
+ <!-- Shared row content: icon, label, trailing indicator, badge / dot. -->
1073
+ <ng-template #rowContent let-item>
1074
+ <strct-icon class="strct-rail__icon" [name]="item.icon" [size]="18" [strokeWidth]="1.6" />
1075
+ @if (!collapsed()) {
1076
+ <span class="strct-rail__label">{{ item.label }}</span>
1077
+ @if (item.trailingIcon) {
1078
+ <strct-icon
1079
+ class="strct-rail__trailing"
1080
+ [name]="item.trailingIcon"
1081
+ [size]="14"
1082
+ [strokeWidth]="1.5"
1083
+ />
1084
+ }
1085
+ }
1086
+ @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
1087
+ <span
1088
+ class="strct-rail__badge strct-rail__badge--{{ item.badgeStatus ?? 'accent' }}"
1089
+ [class.strct-rail__badge--dot]="collapsed()"
1090
+ >
1091
+ @if (!collapsed()) {
1092
+ {{ item.badge }}
1093
+ }
1094
+ </span>
1095
+ } @else if (item.dot) {
1096
+ <span class="strct-rail__dot strct-rail__dot--{{ item.dotStatus ?? 'accent' }}"></span>
1097
+ }
1098
+ </ng-template>
1099
+
1100
+ <!-- One rail row: a router link, a plain link, or a button (today's default). -->
1101
+ <ng-template #row let-item>
1102
+ @if (isRouted(item)) {
1103
+ <a
1104
+ class="strct-rail__item"
1105
+ [routerLink]="item.routerLink"
1106
+ routerLinkActive
1107
+ #rla="routerLinkActive"
1108
+ [class.strct-rail__item--active]="isActive(item, rla.isActive)"
1109
+ [attr.aria-current]="isActive(item, rla.isActive) ? 'page' : null"
1110
+ [attr.title]="collapsed() ? item.label : null"
1111
+ (click)="pick(item, $event)"
1112
+ >
1113
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1114
+ </a>
1115
+ } @else if (item.href && !item.disabled) {
1116
+ <a
1117
+ class="strct-rail__item"
1118
+ [href]="item.href"
1119
+ [class.strct-rail__item--active]="item.id === activeId()"
1120
+ [attr.aria-current]="item.id === activeId() ? 'page' : null"
1121
+ [attr.title]="collapsed() ? item.label : null"
1122
+ (click)="pick(item, $event)"
1123
+ >
1124
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1125
+ </a>
1126
+ } @else {
1052
1127
  <button
1053
1128
  type="button"
1054
1129
  class="strct-rail__item"
@@ -1056,24 +1131,26 @@ class StrctRail {
1056
1131
  [disabled]="item.disabled"
1057
1132
  [attr.aria-current]="item.id === activeId() ? 'page' : null"
1058
1133
  [attr.title]="collapsed() ? item.label : null"
1059
- (click)="pick(item)"
1134
+ (click)="pick(item, $event)"
1060
1135
  >
1061
- <strct-icon class="strct-rail__icon" [name]="item.icon" [size]="18" [strokeWidth]="1.6" />
1062
- @if (!collapsed()) {
1063
- <span class="strct-rail__label">{{ item.label }}</span>
1064
- }
1065
- @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
1066
- <span
1067
- class="strct-rail__badge strct-rail__badge--{{ item.badgeStatus ?? 'accent' }}"
1068
- [class.strct-rail__badge--dot]="collapsed()"
1069
- >
1070
- @if (!collapsed()) {
1071
- {{ item.badge }}
1072
- }
1073
- </span>
1074
- }
1136
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1075
1137
  </button>
1076
1138
  }
1139
+ </ng-template>
1140
+
1141
+ <nav class="strct-rail__nav" [attr.aria-label]="ariaLabel()">
1142
+ <div class="strct-rail__group strct-rail__group--top">
1143
+ @for (item of topItems(); track item.id) {
1144
+ <ng-container *ngTemplateOutlet="row; context: { $implicit: item }" />
1145
+ }
1146
+ </div>
1147
+ @if (bottomItems().length) {
1148
+ <div class="strct-rail__group strct-rail__group--bottom">
1149
+ @for (item of bottomItems(); track item.id) {
1150
+ <ng-container *ngTemplateOutlet="row; context: { $implicit: item }" />
1151
+ }
1152
+ </div>
1153
+ }
1077
1154
  </nav>
1078
1155
 
1079
1156
  @if (collapsible()) {
@@ -1090,13 +1167,66 @@ class StrctRail {
1090
1167
  }
1091
1168
  </button>
1092
1169
  }
1093
- `, isInline: true, styles: [".strct-rail{display:flex;flex-direction:column;width:232px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden;transition:width .16s ease}.strct-rail--collapsed{width:60px}.strct-rail__nav{flex:1;display:flex;flex-direction:column;gap:2px;padding:8px;overflow-y:auto}.strct-rail__item{position:relative;display:flex;align-items:center;gap:11px;width:100%;padding:8px 10px;border:0;border-radius:8px;background:transparent;color:var(--t2);cursor:pointer;font-size:13.5px;font-family:var(--font);text-align:start;white-space:nowrap;transition:background .14s ease,color .14s ease}.strct-rail--collapsed .strct-rail__item{justify-content:center;padding:8px}.strct-rail__item:hover:not(:disabled){background:var(--bg-3);color:var(--t1)}.strct-rail__item--active{background:var(--acc-m);color:var(--acc)}.strct-rail__item--active .strct-rail__icon{color:var(--acc)}.strct-rail__item:disabled{opacity:.5;cursor:not-allowed}.strct-rail__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-rail__icon{flex-shrink:0}.strct-rail__label{flex:1;overflow:hidden;text-overflow:ellipsis}.strct-rail__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-rail__badge--dot{position:absolute;top:5px;right:7px;min-width:8px;width:8px;height:8px;padding:0;border-radius:50%}.strct-rail__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-rail__badge--success{background:var(--success-bg);color:var(--success)}.strct-rail__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-rail__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-rail__badge--dot.strct-rail__badge--accent{background:var(--acc)}.strct-rail__badge--dot.strct-rail__badge--success{background:var(--success)}.strct-rail__badge--dot.strct-rail__badge--warning{background:var(--warning)}.strct-rail__badge--dot.strct-rail__badge--critical{background:var(--critical)}.strct-rail__toggle{display:flex;align-items:center;gap:10px;padding:9px 14px;border:0;border-top:1px solid var(--b1);background:transparent;color:var(--t3);cursor:pointer;font-size:12.5px;font-family:var(--font)}.strct-rail--collapsed .strct-rail__toggle{justify-content:center;padding:9px}.strct-rail__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-rail__toggle-icon{transition:transform .16s ease}.strct-rail:not(.strct-rail--collapsed) .strct-rail__toggle-icon{transform:rotate(180deg)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1170
+ `, isInline: true, styles: [".strct-rail{display:flex;flex-direction:column;width:232px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden;transition:width .16s ease}.strct-rail--collapsed{width:60px}.strct-rail__nav{flex:1;min-height:0;display:flex;flex-direction:column;padding:8px}.strct-rail__group{display:flex;flex-direction:column;gap:2px}.strct-rail__group--top{flex:1;min-height:0;overflow-y:auto}.strct-rail__group--bottom{margin-top:8px;padding-top:8px;border-top:1px solid var(--b1)}.strct-rail__item{position:relative;display:flex;align-items:center;gap:11px;width:100%;box-sizing:border-box;padding:8px 10px;border:0;border-radius:8px;background:transparent;color:var(--t2);cursor:pointer;font-size:13.5px;font-family:var(--font);text-align:start;text-decoration:none;white-space:nowrap;transition:background .14s ease,color .14s ease}.strct-rail--collapsed .strct-rail__item{justify-content:center;padding:8px}.strct-rail__item:hover:not(:disabled){background:var(--bg-3);color:var(--t1)}.strct-rail__item--active{background:var(--acc-m);color:var(--acc)}.strct-rail__item--active .strct-rail__icon{color:var(--acc)}.strct-rail__item:disabled{opacity:.5;cursor:not-allowed}.strct-rail__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-rail__icon{flex-shrink:0}.strct-rail__label{flex:1;overflow:hidden;text-overflow:ellipsis}.strct-rail__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-rail__badge--dot{position:absolute;top:5px;inset-inline-end:7px;min-width:8px;width:8px;height:8px;padding:0;border-radius:50%}.strct-rail__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-rail__badge--success{background:var(--success-bg);color:var(--success)}.strct-rail__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-rail__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-rail__badge--dot.strct-rail__badge--accent{background:var(--acc)}.strct-rail__badge--dot.strct-rail__badge--success{background:var(--success)}.strct-rail__badge--dot.strct-rail__badge--warning{background:var(--warning)}.strct-rail__badge--dot.strct-rail__badge--critical{background:var(--critical)}.strct-rail__trailing{flex-shrink:0;color:var(--t3)}.strct-rail__dot{flex-shrink:0;width:7px;height:7px;border-radius:50%}.strct-rail--collapsed .strct-rail__dot{position:absolute;top:5px;inset-inline-end:7px}.strct-rail__dot--accent{background:var(--acc)}.strct-rail__dot--success{background:var(--success)}.strct-rail__dot--warning{background:var(--warning)}.strct-rail__dot--critical{background:var(--critical)}.strct-rail__toggle{display:flex;align-items:center;gap:10px;padding:9px 14px;border:0;border-top:1px solid var(--b1);background:transparent;color:var(--t3);cursor:pointer;font-size:12.5px;font-family:var(--font)}.strct-rail--collapsed .strct-rail__toggle{justify-content:center;padding:9px}.strct-rail__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-rail__toggle-icon{transition:transform .16s ease}.strct-rail:not(.strct-rail--collapsed) .strct-rail__toggle-icon{transform:rotate(180deg)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1094
1171
  }
1095
1172
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctRail, decorators: [{
1096
1173
  type: Component,
1097
- args: [{ selector: 'strct-rail', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
1098
- <nav class="strct-rail__nav" [attr.aria-label]="ariaLabel()">
1099
- @for (item of items(); track item.id) {
1174
+ args: [{ selector: 'strct-rail', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet, RouterLink, RouterLinkActive, StrctIcon], template: `
1175
+ <!-- Shared row content: icon, label, trailing indicator, badge / dot. -->
1176
+ <ng-template #rowContent let-item>
1177
+ <strct-icon class="strct-rail__icon" [name]="item.icon" [size]="18" [strokeWidth]="1.6" />
1178
+ @if (!collapsed()) {
1179
+ <span class="strct-rail__label">{{ item.label }}</span>
1180
+ @if (item.trailingIcon) {
1181
+ <strct-icon
1182
+ class="strct-rail__trailing"
1183
+ [name]="item.trailingIcon"
1184
+ [size]="14"
1185
+ [strokeWidth]="1.5"
1186
+ />
1187
+ }
1188
+ }
1189
+ @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
1190
+ <span
1191
+ class="strct-rail__badge strct-rail__badge--{{ item.badgeStatus ?? 'accent' }}"
1192
+ [class.strct-rail__badge--dot]="collapsed()"
1193
+ >
1194
+ @if (!collapsed()) {
1195
+ {{ item.badge }}
1196
+ }
1197
+ </span>
1198
+ } @else if (item.dot) {
1199
+ <span class="strct-rail__dot strct-rail__dot--{{ item.dotStatus ?? 'accent' }}"></span>
1200
+ }
1201
+ </ng-template>
1202
+
1203
+ <!-- One rail row: a router link, a plain link, or a button (today's default). -->
1204
+ <ng-template #row let-item>
1205
+ @if (isRouted(item)) {
1206
+ <a
1207
+ class="strct-rail__item"
1208
+ [routerLink]="item.routerLink"
1209
+ routerLinkActive
1210
+ #rla="routerLinkActive"
1211
+ [class.strct-rail__item--active]="isActive(item, rla.isActive)"
1212
+ [attr.aria-current]="isActive(item, rla.isActive) ? 'page' : null"
1213
+ [attr.title]="collapsed() ? item.label : null"
1214
+ (click)="pick(item, $event)"
1215
+ >
1216
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1217
+ </a>
1218
+ } @else if (item.href && !item.disabled) {
1219
+ <a
1220
+ class="strct-rail__item"
1221
+ [href]="item.href"
1222
+ [class.strct-rail__item--active]="item.id === activeId()"
1223
+ [attr.aria-current]="item.id === activeId() ? 'page' : null"
1224
+ [attr.title]="collapsed() ? item.label : null"
1225
+ (click)="pick(item, $event)"
1226
+ >
1227
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1228
+ </a>
1229
+ } @else {
1100
1230
  <button
1101
1231
  type="button"
1102
1232
  class="strct-rail__item"
@@ -1104,24 +1234,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1104
1234
  [disabled]="item.disabled"
1105
1235
  [attr.aria-current]="item.id === activeId() ? 'page' : null"
1106
1236
  [attr.title]="collapsed() ? item.label : null"
1107
- (click)="pick(item)"
1237
+ (click)="pick(item, $event)"
1108
1238
  >
1109
- <strct-icon class="strct-rail__icon" [name]="item.icon" [size]="18" [strokeWidth]="1.6" />
1110
- @if (!collapsed()) {
1111
- <span class="strct-rail__label">{{ item.label }}</span>
1112
- }
1113
- @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
1114
- <span
1115
- class="strct-rail__badge strct-rail__badge--{{ item.badgeStatus ?? 'accent' }}"
1116
- [class.strct-rail__badge--dot]="collapsed()"
1117
- >
1118
- @if (!collapsed()) {
1119
- {{ item.badge }}
1120
- }
1121
- </span>
1122
- }
1239
+ <ng-container *ngTemplateOutlet="rowContent; context: { $implicit: item }" />
1123
1240
  </button>
1124
1241
  }
1242
+ </ng-template>
1243
+
1244
+ <nav class="strct-rail__nav" [attr.aria-label]="ariaLabel()">
1245
+ <div class="strct-rail__group strct-rail__group--top">
1246
+ @for (item of topItems(); track item.id) {
1247
+ <ng-container *ngTemplateOutlet="row; context: { $implicit: item }" />
1248
+ }
1249
+ </div>
1250
+ @if (bottomItems().length) {
1251
+ <div class="strct-rail__group strct-rail__group--bottom">
1252
+ @for (item of bottomItems(); track item.id) {
1253
+ <ng-container *ngTemplateOutlet="row; context: { $implicit: item }" />
1254
+ }
1255
+ </div>
1256
+ }
1125
1257
  </nav>
1126
1258
 
1127
1259
  @if (collapsible()) {
@@ -1141,7 +1273,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1141
1273
  `, host: {
1142
1274
  class: 'strct-rail',
1143
1275
  '[class.strct-rail--collapsed]': 'collapsed()',
1144
- }, styles: [".strct-rail{display:flex;flex-direction:column;width:232px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden;transition:width .16s ease}.strct-rail--collapsed{width:60px}.strct-rail__nav{flex:1;display:flex;flex-direction:column;gap:2px;padding:8px;overflow-y:auto}.strct-rail__item{position:relative;display:flex;align-items:center;gap:11px;width:100%;padding:8px 10px;border:0;border-radius:8px;background:transparent;color:var(--t2);cursor:pointer;font-size:13.5px;font-family:var(--font);text-align:start;white-space:nowrap;transition:background .14s ease,color .14s ease}.strct-rail--collapsed .strct-rail__item{justify-content:center;padding:8px}.strct-rail__item:hover:not(:disabled){background:var(--bg-3);color:var(--t1)}.strct-rail__item--active{background:var(--acc-m);color:var(--acc)}.strct-rail__item--active .strct-rail__icon{color:var(--acc)}.strct-rail__item:disabled{opacity:.5;cursor:not-allowed}.strct-rail__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-rail__icon{flex-shrink:0}.strct-rail__label{flex:1;overflow:hidden;text-overflow:ellipsis}.strct-rail__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-rail__badge--dot{position:absolute;top:5px;right:7px;min-width:8px;width:8px;height:8px;padding:0;border-radius:50%}.strct-rail__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-rail__badge--success{background:var(--success-bg);color:var(--success)}.strct-rail__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-rail__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-rail__badge--dot.strct-rail__badge--accent{background:var(--acc)}.strct-rail__badge--dot.strct-rail__badge--success{background:var(--success)}.strct-rail__badge--dot.strct-rail__badge--warning{background:var(--warning)}.strct-rail__badge--dot.strct-rail__badge--critical{background:var(--critical)}.strct-rail__toggle{display:flex;align-items:center;gap:10px;padding:9px 14px;border:0;border-top:1px solid var(--b1);background:transparent;color:var(--t3);cursor:pointer;font-size:12.5px;font-family:var(--font)}.strct-rail--collapsed .strct-rail__toggle{justify-content:center;padding:9px}.strct-rail__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-rail__toggle-icon{transition:transform .16s ease}.strct-rail:not(.strct-rail--collapsed) .strct-rail__toggle-icon{transform:rotate(180deg)}\n"] }]
1276
+ }, styles: [".strct-rail{display:flex;flex-direction:column;width:232px;flex-shrink:0;background:var(--bg-1);border-inline-end:1px solid var(--b2);overflow:hidden;transition:width .16s ease}.strct-rail--collapsed{width:60px}.strct-rail__nav{flex:1;min-height:0;display:flex;flex-direction:column;padding:8px}.strct-rail__group{display:flex;flex-direction:column;gap:2px}.strct-rail__group--top{flex:1;min-height:0;overflow-y:auto}.strct-rail__group--bottom{margin-top:8px;padding-top:8px;border-top:1px solid var(--b1)}.strct-rail__item{position:relative;display:flex;align-items:center;gap:11px;width:100%;box-sizing:border-box;padding:8px 10px;border:0;border-radius:8px;background:transparent;color:var(--t2);cursor:pointer;font-size:13.5px;font-family:var(--font);text-align:start;text-decoration:none;white-space:nowrap;transition:background .14s ease,color .14s ease}.strct-rail--collapsed .strct-rail__item{justify-content:center;padding:8px}.strct-rail__item:hover:not(:disabled){background:var(--bg-3);color:var(--t1)}.strct-rail__item--active{background:var(--acc-m);color:var(--acc)}.strct-rail__item--active .strct-rail__icon{color:var(--acc)}.strct-rail__item:disabled{opacity:.5;cursor:not-allowed}.strct-rail__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-rail__icon{flex-shrink:0}.strct-rail__label{flex:1;overflow:hidden;text-overflow:ellipsis}.strct-rail__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-rail__badge--dot{position:absolute;top:5px;inset-inline-end:7px;min-width:8px;width:8px;height:8px;padding:0;border-radius:50%}.strct-rail__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-rail__badge--success{background:var(--success-bg);color:var(--success)}.strct-rail__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-rail__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-rail__badge--dot.strct-rail__badge--accent{background:var(--acc)}.strct-rail__badge--dot.strct-rail__badge--success{background:var(--success)}.strct-rail__badge--dot.strct-rail__badge--warning{background:var(--warning)}.strct-rail__badge--dot.strct-rail__badge--critical{background:var(--critical)}.strct-rail__trailing{flex-shrink:0;color:var(--t3)}.strct-rail__dot{flex-shrink:0;width:7px;height:7px;border-radius:50%}.strct-rail--collapsed .strct-rail__dot{position:absolute;top:5px;inset-inline-end:7px}.strct-rail__dot--accent{background:var(--acc)}.strct-rail__dot--success{background:var(--success)}.strct-rail__dot--warning{background:var(--warning)}.strct-rail__dot--critical{background:var(--critical)}.strct-rail__toggle{display:flex;align-items:center;gap:10px;padding:9px 14px;border:0;border-top:1px solid var(--b1);background:transparent;color:var(--t3);cursor:pointer;font-size:12.5px;font-family:var(--font)}.strct-rail--collapsed .strct-rail__toggle{justify-content:center;padding:9px}.strct-rail__toggle:hover{color:var(--t1);background:var(--bg-3)}.strct-rail__toggle-icon{transition:transform .16s ease}.strct-rail:not(.strct-rail--collapsed) .strct-rail__toggle-icon{transform:rotate(180deg)}\n"] }]
1145
1277
  }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], activeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeId", required: false }] }, { type: i0.Output, args: ["activeIdChange"] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], select: [{ type: i0.Output, args: ["select"] }] } });
1146
1278
 
1147
1279
  /**
@@ -3074,7 +3206,7 @@ class StrctDrawer {
3074
3206
  }
3075
3207
  </aside>
3076
3208
  }
3077
- `, isInline: true, styles: [".strct-drawer__backdrop{position:fixed;inset:0;z-index:1100;background:#00000070;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:strct-drawer-fade .16s ease}.strct-drawer__backdrop--bare{background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.strct-drawer{position:fixed;z-index:1101;display:flex;flex-direction:column;background:var(--bg-1);box-shadow:var(--shh)}.strct-drawer--start,.strct-drawer--end{top:0;bottom:0;border-radius:0}.strct-drawer--start{left:0;border-inline-end:1px solid var(--b2);animation:strct-drawer-in-start .18s ease}.strct-drawer--end{right:0;border-inline-start:1px solid var(--b2);animation:strct-drawer-in-end .18s ease}.strct-drawer--top,.strct-drawer--bottom{left:0;right:0}.strct-drawer--top{top:0;border-bottom:1px solid var(--b2);animation:strct-drawer-in-top .18s ease}.strct-drawer--bottom{bottom:0;border-top:1px solid var(--b2);animation:strct-drawer-in-bottom .18s ease}.strct-drawer--start.strct-drawer--sm,.strct-drawer--end.strct-drawer--sm{width:300px}.strct-drawer--start.strct-drawer--md,.strct-drawer--end.strct-drawer--md{width:420px}.strct-drawer--start.strct-drawer--lg,.strct-drawer--end.strct-drawer--lg{width:600px}.strct-drawer--top.strct-drawer--sm,.strct-drawer--bottom.strct-drawer--sm{height:220px}.strct-drawer--top.strct-drawer--md,.strct-drawer--bottom.strct-drawer--md{height:340px}.strct-drawer--top.strct-drawer--lg,.strct-drawer--bottom.strct-drawer--lg{height:480px}.strct-drawer{max-width:100vw;max-height:100vh}.strct-drawer__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--b1);flex-shrink:0}.strct-drawer__title{font-size:15px;font-weight:600;color:var(--t1)}.strct-drawer__close{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-drawer__close:hover{color:var(--t1);background:var(--bg-3)}.strct-drawer__body{flex:1;min-height:0;overflow-y:auto;padding:16px 18px;font-size:13px;color:var(--t1)}.strct-drawer__foot{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-top:1px solid var(--b1);flex-shrink:0}@keyframes strct-drawer-fade{0%{opacity:0}}@keyframes strct-drawer-in-start{0%{transform:translate(-100%)}}@keyframes strct-drawer-in-end{0%{transform:translate(100%)}}@keyframes strct-drawer-in-top{0%{transform:translateY(-100%)}}@keyframes strct-drawer-in-bottom{0%{transform:translateY(100%)}}@media(prefers-reduced-motion:reduce){.strct-drawer__backdrop,.strct-drawer{animation:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3209
+ `, isInline: true, styles: [".strct-drawer__backdrop{position:fixed;inset:0;z-index:1100;background:#00000070;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:strct-drawer-fade .16s ease}.strct-drawer__backdrop--bare{background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.strct-drawer{position:fixed;z-index:1101;display:flex;flex-direction:column;background:var(--bg-1);box-shadow:var(--shh)}.strct-drawer--start,.strct-drawer--end{top:0;bottom:0;border-radius:0}.strct-drawer--start{inset-inline-start:0;border-inline-end:1px solid var(--b2);animation:strct-drawer-in-start .18s ease}.strct-drawer--end{inset-inline-end:0;border-inline-start:1px solid var(--b2);animation:strct-drawer-in-end .18s ease}[dir=rtl] .strct-drawer--start{animation-name:strct-drawer-in-end}[dir=rtl] .strct-drawer--end{animation-name:strct-drawer-in-start}.strct-drawer--top,.strct-drawer--bottom{left:0;right:0}.strct-drawer--top{top:0;border-bottom:1px solid var(--b2);animation:strct-drawer-in-top .18s ease}.strct-drawer--bottom{bottom:0;border-top:1px solid var(--b2);animation:strct-drawer-in-bottom .18s ease}.strct-drawer--start.strct-drawer--sm,.strct-drawer--end.strct-drawer--sm{width:300px}.strct-drawer--start.strct-drawer--md,.strct-drawer--end.strct-drawer--md{width:420px}.strct-drawer--start.strct-drawer--lg,.strct-drawer--end.strct-drawer--lg{width:600px}.strct-drawer--top.strct-drawer--sm,.strct-drawer--bottom.strct-drawer--sm{height:220px}.strct-drawer--top.strct-drawer--md,.strct-drawer--bottom.strct-drawer--md{height:340px}.strct-drawer--top.strct-drawer--lg,.strct-drawer--bottom.strct-drawer--lg{height:480px}.strct-drawer{max-width:100vw;max-height:100vh}.strct-drawer__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--b1);flex-shrink:0}.strct-drawer__title{font-size:15px;font-weight:600;color:var(--t1)}.strct-drawer__close{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-drawer__close:hover{color:var(--t1);background:var(--bg-3)}.strct-drawer__body{flex:1;min-height:0;overflow-y:auto;padding:16px 18px;font-size:13px;color:var(--t1)}.strct-drawer__foot{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-top:1px solid var(--b1);flex-shrink:0}@keyframes strct-drawer-fade{0%{opacity:0}}@keyframes strct-drawer-in-start{0%{transform:translate(-100%)}}@keyframes strct-drawer-in-end{0%{transform:translate(100%)}}@keyframes strct-drawer-in-top{0%{transform:translateY(-100%)}}@keyframes strct-drawer-in-bottom{0%{transform:translateY(100%)}}@media(prefers-reduced-motion:reduce){.strct-drawer__backdrop,.strct-drawer{animation:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3078
3210
  }
3079
3211
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDrawer, decorators: [{
3080
3212
  type: Component,
@@ -3114,7 +3246,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
3114
3246
  }
3115
3247
  </aside>
3116
3248
  }
3117
- `, host: { class: 'strct-drawer-host' }, styles: [".strct-drawer__backdrop{position:fixed;inset:0;z-index:1100;background:#00000070;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:strct-drawer-fade .16s ease}.strct-drawer__backdrop--bare{background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.strct-drawer{position:fixed;z-index:1101;display:flex;flex-direction:column;background:var(--bg-1);box-shadow:var(--shh)}.strct-drawer--start,.strct-drawer--end{top:0;bottom:0;border-radius:0}.strct-drawer--start{left:0;border-inline-end:1px solid var(--b2);animation:strct-drawer-in-start .18s ease}.strct-drawer--end{right:0;border-inline-start:1px solid var(--b2);animation:strct-drawer-in-end .18s ease}.strct-drawer--top,.strct-drawer--bottom{left:0;right:0}.strct-drawer--top{top:0;border-bottom:1px solid var(--b2);animation:strct-drawer-in-top .18s ease}.strct-drawer--bottom{bottom:0;border-top:1px solid var(--b2);animation:strct-drawer-in-bottom .18s ease}.strct-drawer--start.strct-drawer--sm,.strct-drawer--end.strct-drawer--sm{width:300px}.strct-drawer--start.strct-drawer--md,.strct-drawer--end.strct-drawer--md{width:420px}.strct-drawer--start.strct-drawer--lg,.strct-drawer--end.strct-drawer--lg{width:600px}.strct-drawer--top.strct-drawer--sm,.strct-drawer--bottom.strct-drawer--sm{height:220px}.strct-drawer--top.strct-drawer--md,.strct-drawer--bottom.strct-drawer--md{height:340px}.strct-drawer--top.strct-drawer--lg,.strct-drawer--bottom.strct-drawer--lg{height:480px}.strct-drawer{max-width:100vw;max-height:100vh}.strct-drawer__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--b1);flex-shrink:0}.strct-drawer__title{font-size:15px;font-weight:600;color:var(--t1)}.strct-drawer__close{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-drawer__close:hover{color:var(--t1);background:var(--bg-3)}.strct-drawer__body{flex:1;min-height:0;overflow-y:auto;padding:16px 18px;font-size:13px;color:var(--t1)}.strct-drawer__foot{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-top:1px solid var(--b1);flex-shrink:0}@keyframes strct-drawer-fade{0%{opacity:0}}@keyframes strct-drawer-in-start{0%{transform:translate(-100%)}}@keyframes strct-drawer-in-end{0%{transform:translate(100%)}}@keyframes strct-drawer-in-top{0%{transform:translateY(-100%)}}@keyframes strct-drawer-in-bottom{0%{transform:translateY(100%)}}@media(prefers-reduced-motion:reduce){.strct-drawer__backdrop,.strct-drawer{animation:none}}\n"] }]
3249
+ `, host: { class: 'strct-drawer-host' }, styles: [".strct-drawer__backdrop{position:fixed;inset:0;z-index:1100;background:#00000070;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px);animation:strct-drawer-fade .16s ease}.strct-drawer__backdrop--bare{background:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.strct-drawer{position:fixed;z-index:1101;display:flex;flex-direction:column;background:var(--bg-1);box-shadow:var(--shh)}.strct-drawer--start,.strct-drawer--end{top:0;bottom:0;border-radius:0}.strct-drawer--start{inset-inline-start:0;border-inline-end:1px solid var(--b2);animation:strct-drawer-in-start .18s ease}.strct-drawer--end{inset-inline-end:0;border-inline-start:1px solid var(--b2);animation:strct-drawer-in-end .18s ease}[dir=rtl] .strct-drawer--start{animation-name:strct-drawer-in-end}[dir=rtl] .strct-drawer--end{animation-name:strct-drawer-in-start}.strct-drawer--top,.strct-drawer--bottom{left:0;right:0}.strct-drawer--top{top:0;border-bottom:1px solid var(--b2);animation:strct-drawer-in-top .18s ease}.strct-drawer--bottom{bottom:0;border-top:1px solid var(--b2);animation:strct-drawer-in-bottom .18s ease}.strct-drawer--start.strct-drawer--sm,.strct-drawer--end.strct-drawer--sm{width:300px}.strct-drawer--start.strct-drawer--md,.strct-drawer--end.strct-drawer--md{width:420px}.strct-drawer--start.strct-drawer--lg,.strct-drawer--end.strct-drawer--lg{width:600px}.strct-drawer--top.strct-drawer--sm,.strct-drawer--bottom.strct-drawer--sm{height:220px}.strct-drawer--top.strct-drawer--md,.strct-drawer--bottom.strct-drawer--md{height:340px}.strct-drawer--top.strct-drawer--lg,.strct-drawer--bottom.strct-drawer--lg{height:480px}.strct-drawer{max-width:100vw;max-height:100vh}.strct-drawer__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--b1);flex-shrink:0}.strct-drawer__title{font-size:15px;font-weight:600;color:var(--t1)}.strct-drawer__close{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-drawer__close:hover{color:var(--t1);background:var(--bg-3)}.strct-drawer__body{flex:1;min-height:0;overflow-y:auto;padding:16px 18px;font-size:13px;color:var(--t1)}.strct-drawer__foot{display:flex;align-items:center;justify-content:flex-end;gap:8px;padding:12px 18px;border-top:1px solid var(--b1);flex-shrink:0}@keyframes strct-drawer-fade{0%{opacity:0}}@keyframes strct-drawer-in-start{0%{transform:translate(-100%)}}@keyframes strct-drawer-in-end{0%{transform:translate(100%)}}@keyframes strct-drawer-in-top{0%{transform:translateY(-100%)}}@keyframes strct-drawer-in-bottom{0%{transform:translateY(100%)}}@media(prefers-reduced-motion:reduce){.strct-drawer__backdrop,.strct-drawer{animation:none}}\n"] }]
3118
3250
  }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], dismissable: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissable", required: false }] }], footerDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDrawerFooter), { isSignal: true }] }] } });
3119
3251
 
3120
3252
  /**
@@ -3859,6 +3991,25 @@ class StrctSectionMenu {
3859
3991
  />
3860
3992
  }
3861
3993
  <span class="strct-sm__item-label">{{ item.label }}</span>
3994
+ @if (item.trailingIcon) {
3995
+ <strct-icon
3996
+ class="strct-sm__trailing"
3997
+ [name]="item.trailingIcon"
3998
+ [size]="14"
3999
+ [strokeWidth]="1.5"
4000
+ />
4001
+ }
4002
+ @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
4003
+ <span
4004
+ class="strct-sm__badge strct-sm__badge--{{ item.badgeStatus ?? 'accent' }}"
4005
+ >
4006
+ {{ item.badge }}
4007
+ </span>
4008
+ } @else if (item.dot) {
4009
+ <span
4010
+ class="strct-sm__dot strct-sm__dot--{{ item.dotStatus ?? 'accent' }}"
4011
+ ></span>
4012
+ }
3862
4013
  </button>
3863
4014
  }
3864
4015
  </div>
@@ -3866,7 +4017,7 @@ class StrctSectionMenu {
3866
4017
  </div>
3867
4018
  }
3868
4019
  </nav>
3869
- `, isInline: true, styles: [".strct-sm{display:flex;flex-direction:column;gap:1px}.strct-sm__section--collapsible+.strct-sm__section--collapsible{margin-top:1px}.strct-sm__cat{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--t2);font-size:13px;font-weight:500;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease,color .13s ease}.strct-sm__cat:hover{background:var(--bg-3);color:var(--t1)}.strct-sm__cat:focus-visible,.strct-sm__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-sm__chevron{display:inline-flex;color:var(--t3);flex-shrink:0;transition:transform .15s ease}.strct-sm__chevron--open{transform:rotate(90deg)}.strct-sm__cat-icon{color:var(--t3);flex-shrink:0}.strct-sm__cat-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__cat-static{padding:11px 10px 5px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--t3)}.strct-sm__items{display:flex;flex-direction:column}.strct-sm__item{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease}.strct-sm__section--collapsible .strct-sm__item{padding-inline-start:31px}.strct-sm__item:hover:not(:disabled){background:var(--bg-3)}.strct-sm__item--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-sm__item--active .strct-sm__item-icon{color:var(--acc)}.strct-sm__item:disabled{opacity:.5;cursor:not-allowed}.strct-sm__item-icon{color:var(--t2);flex-shrink:0}.strct-sm__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
4020
+ `, isInline: true, styles: [".strct-sm{display:flex;flex-direction:column;gap:1px}.strct-sm__section--collapsible+.strct-sm__section--collapsible{margin-top:1px}.strct-sm__cat{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--t2);font-size:13px;font-weight:500;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease,color .13s ease}.strct-sm__cat:hover{background:var(--bg-3);color:var(--t1)}.strct-sm__cat:focus-visible,.strct-sm__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-sm__chevron{display:inline-flex;color:var(--t3);flex-shrink:0;transition:transform .15s ease}.strct-sm__chevron--open{transform:rotate(90deg)}.strct-sm__cat-icon{color:var(--t3);flex-shrink:0}.strct-sm__cat-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__cat-static{padding:11px 10px 5px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--t3)}.strct-sm__items{display:flex;flex-direction:column}.strct-sm__item{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease}.strct-sm__section--collapsible .strct-sm__item{padding-inline-start:31px}.strct-sm__item:hover:not(:disabled){background:var(--bg-3)}.strct-sm__item--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-sm__item--active .strct-sm__item-icon{color:var(--acc)}.strct-sm__item:disabled{opacity:.5;cursor:not-allowed}.strct-sm__item-icon{color:var(--t2);flex-shrink:0}.strct-sm__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__trailing{flex-shrink:0;color:var(--t3)}.strct-sm__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-sm__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-sm__badge--success{background:var(--success-bg);color:var(--success)}.strct-sm__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-sm__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-sm__dot{flex-shrink:0;width:7px;height:7px;border-radius:50%}.strct-sm__dot--accent{background:var(--acc)}.strct-sm__dot--success{background:var(--success)}.strct-sm__dot--warning{background:var(--warning)}.strct-sm__dot--critical{background:var(--critical)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3870
4021
  }
3871
4022
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctSectionMenu, decorators: [{
3872
4023
  type: Component,
@@ -3918,6 +4069,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
3918
4069
  />
3919
4070
  }
3920
4071
  <span class="strct-sm__item-label">{{ item.label }}</span>
4072
+ @if (item.trailingIcon) {
4073
+ <strct-icon
4074
+ class="strct-sm__trailing"
4075
+ [name]="item.trailingIcon"
4076
+ [size]="14"
4077
+ [strokeWidth]="1.5"
4078
+ />
4079
+ }
4080
+ @if (item.badge !== undefined && item.badge !== null && item.badge !== '') {
4081
+ <span
4082
+ class="strct-sm__badge strct-sm__badge--{{ item.badgeStatus ?? 'accent' }}"
4083
+ >
4084
+ {{ item.badge }}
4085
+ </span>
4086
+ } @else if (item.dot) {
4087
+ <span
4088
+ class="strct-sm__dot strct-sm__dot--{{ item.dotStatus ?? 'accent' }}"
4089
+ ></span>
4090
+ }
3921
4091
  </button>
3922
4092
  }
3923
4093
  </div>
@@ -3925,7 +4095,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
3925
4095
  </div>
3926
4096
  }
3927
4097
  </nav>
3928
- `, host: { class: 'strct-sm-host' }, styles: [".strct-sm{display:flex;flex-direction:column;gap:1px}.strct-sm__section--collapsible+.strct-sm__section--collapsible{margin-top:1px}.strct-sm__cat{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--t2);font-size:13px;font-weight:500;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease,color .13s ease}.strct-sm__cat:hover{background:var(--bg-3);color:var(--t1)}.strct-sm__cat:focus-visible,.strct-sm__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-sm__chevron{display:inline-flex;color:var(--t3);flex-shrink:0;transition:transform .15s ease}.strct-sm__chevron--open{transform:rotate(90deg)}.strct-sm__cat-icon{color:var(--t3);flex-shrink:0}.strct-sm__cat-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__cat-static{padding:11px 10px 5px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--t3)}.strct-sm__items{display:flex;flex-direction:column}.strct-sm__item{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease}.strct-sm__section--collapsible .strct-sm__item{padding-inline-start:31px}.strct-sm__item:hover:not(:disabled){background:var(--bg-3)}.strct-sm__item--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-sm__item--active .strct-sm__item-icon{color:var(--acc)}.strct-sm__item:disabled{opacity:.5;cursor:not-allowed}.strct-sm__item-icon{color:var(--t2);flex-shrink:0}.strct-sm__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
4098
+ `, host: { class: 'strct-sm-host' }, styles: [".strct-sm{display:flex;flex-direction:column;gap:1px}.strct-sm__section--collapsible+.strct-sm__section--collapsible{margin-top:1px}.strct-sm__cat{display:flex;align-items:center;gap:8px;width:100%;padding:7px 9px;border:0;border-radius:6px;background:transparent;color:var(--t2);font-size:13px;font-weight:500;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease,color .13s ease}.strct-sm__cat:hover{background:var(--bg-3);color:var(--t1)}.strct-sm__cat:focus-visible,.strct-sm__item:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-sm__chevron{display:inline-flex;color:var(--t3);flex-shrink:0;transition:transform .15s ease}.strct-sm__chevron--open{transform:rotate(90deg)}.strct-sm__cat-icon{color:var(--t3);flex-shrink:0}.strct-sm__cat-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__cat-static{padding:11px 10px 5px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--t3)}.strct-sm__items{display:flex;flex-direction:column}.strct-sm__item{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;border-radius:6px;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);cursor:pointer;text-align:start;transition:background .13s ease}.strct-sm__section--collapsible .strct-sm__item{padding-inline-start:31px}.strct-sm__item:hover:not(:disabled){background:var(--bg-3)}.strct-sm__item--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-sm__item--active .strct-sm__item-icon{color:var(--acc)}.strct-sm__item:disabled{opacity:.5;cursor:not-allowed}.strct-sm__item-icon{color:var(--t2);flex-shrink:0}.strct-sm__item-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-sm__trailing{flex-shrink:0;color:var(--t3)}.strct-sm__badge{flex-shrink:0;min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;line-height:1;border-radius:9px;font-variant-numeric:tabular-nums}.strct-sm__badge--accent{background:var(--acc-m);color:var(--acc)}.strct-sm__badge--success{background:var(--success-bg);color:var(--success)}.strct-sm__badge--warning{background:var(--warning-bg);color:var(--warning)}.strct-sm__badge--critical{background:var(--critical-bg);color:var(--critical)}.strct-sm__dot{flex-shrink:0;width:7px;height:7px;border-radius:50%}.strct-sm__dot--accent{background:var(--acc)}.strct-sm__dot--success{background:var(--success)}.strct-sm__dot--warning{background:var(--warning)}.strct-sm__dot--critical{background:var(--critical)}\n"] }]
3929
4099
  }], propDecorators: { sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: true }] }], activeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeId", required: false }] }, { type: i0.Output, args: ["activeIdChange"] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], showIcons: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcons", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], select: [{ type: i0.Output, args: ["select"] }] } });
3930
4100
 
3931
4101
  /**
@@ -4592,7 +4762,7 @@ class StrctToggle {
4592
4762
  <span class="strct-tg__track"><span class="strct-tg__thumb"></span></span>
4593
4763
  <span class="strct-tg__label"><ng-content /></span>
4594
4764
  </label>
4595
- `, isInline: true, styles: [".strct-tg{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tg--disabled{opacity:.5;cursor:not-allowed}.strct-tg__native{position:absolute;opacity:0;width:0;height:0}.strct-tg__track{position:relative;width:34px;height:19px;border-radius:11px;flex-shrink:0;background:var(--bg-3);border:1px solid var(--b3);transition:background .16s ease,border-color .16s ease}.strct-tg__thumb{position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:var(--t2);transition:transform .16s ease,background .16s ease}.strct-tg__native:checked+.strct-tg__track{background:var(--acc);border-color:transparent}.strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(15px);background:var(--inv)}.strct-tg__native:focus-visible+.strct-tg__track{box-shadow:0 0 0 3px var(--acc18)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4765
+ `, isInline: true, styles: [".strct-tg{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tg--disabled{opacity:.5;cursor:not-allowed}.strct-tg__native{position:absolute;opacity:0;width:0;height:0}.strct-tg__track{position:relative;width:34px;height:19px;border-radius:11px;flex-shrink:0;background:var(--bg-3);border:1px solid var(--b3);transition:background .16s ease,border-color .16s ease}.strct-tg__thumb{position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:var(--t2);transition:transform .16s ease,background .16s ease}.strct-tg__native:checked+.strct-tg__track{background:var(--acc);border-color:transparent}.strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(15px);background:var(--inv)}:host-context([dir=\"rtl\"]) .strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(-15px)}.strct-tg__native:focus-visible+.strct-tg__track{box-shadow:0 0 0 3px var(--acc18)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4596
4766
  }
4597
4767
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctToggle, decorators: [{
4598
4768
  type: Component,
@@ -4612,7 +4782,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4612
4782
  <span class="strct-tg__track"><span class="strct-tg__thumb"></span></span>
4613
4783
  <span class="strct-tg__label"><ng-content /></span>
4614
4784
  </label>
4615
- `, styles: [".strct-tg{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tg--disabled{opacity:.5;cursor:not-allowed}.strct-tg__native{position:absolute;opacity:0;width:0;height:0}.strct-tg__track{position:relative;width:34px;height:19px;border-radius:11px;flex-shrink:0;background:var(--bg-3);border:1px solid var(--b3);transition:background .16s ease,border-color .16s ease}.strct-tg__thumb{position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:var(--t2);transition:transform .16s ease,background .16s ease}.strct-tg__native:checked+.strct-tg__track{background:var(--acc);border-color:transparent}.strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(15px);background:var(--inv)}.strct-tg__native:focus-visible+.strct-tg__track{box-shadow:0 0 0 3px var(--acc18)}\n"] }]
4785
+ `, styles: [".strct-tg{display:inline-flex;align-items:center;gap:9px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tg--disabled{opacity:.5;cursor:not-allowed}.strct-tg__native{position:absolute;opacity:0;width:0;height:0}.strct-tg__track{position:relative;width:34px;height:19px;border-radius:11px;flex-shrink:0;background:var(--bg-3);border:1px solid var(--b3);transition:background .16s ease,border-color .16s ease}.strct-tg__thumb{position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;background:var(--t2);transition:transform .16s ease,background .16s ease}.strct-tg__native:checked+.strct-tg__track{background:var(--acc);border-color:transparent}.strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(15px);background:var(--inv)}:host-context([dir=\"rtl\"]) .strct-tg__native:checked+.strct-tg__track .strct-tg__thumb{transform:translate(-15px)}.strct-tg__native:focus-visible+.strct-tg__track{box-shadow:0 0 0 3px var(--acc18)}\n"] }]
4616
4786
  }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
4617
4787
 
4618
4788
  let groupCounter = 0;
@@ -7216,6 +7386,10 @@ const DG_LABELS = {
7216
7386
  refresh: 'Refresh data',
7217
7387
  actions: 'Actions',
7218
7388
  };
7389
+ /** Utility-column widths used when sticky columns are active (px). */
7390
+ const UTIL_W = { detail: 36, expand: 36, sel: 40 };
7391
+ /** Fallback width for a sticky column without an explicit px width. */
7392
+ const STICKY_FALLBACK_W = 120;
7219
7393
  /**
7220
7394
  * Marks the expandable-row detail template. The row is the template's implicit
7221
7395
  * context: `<ng-template strctRowDetail let-row> … {{ row['name'] }} … </ng-template>`.
@@ -7302,12 +7476,42 @@ class StrctDatagrid {
7302
7476
  labels = input({}, ...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
7303
7477
  /** Effective labels (defaults + overrides). */
7304
7478
  L = computed(() => ({ ...DG_LABELS, ...this.labels() }), ...(ngDevMode ? [{ debugName: "L" }] : /* istanbul ignore next */ []));
7479
+ /**
7480
+ * Virtual scrolling for large data sets: only the rows inside the viewport
7481
+ * (plus a small overscan) are in the DOM — 20k+ rows stay smooth. The scroll
7482
+ * area gets `viewportHeight` and a sticky header. Assumes uniform
7483
+ * `rowHeight`; combine with `expandable` is unsupported.
7484
+ */
7485
+ virtual = input(false, { ...(ngDevMode ? { debugName: "virtual" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
7486
+ /** Scroll viewport height in px (virtual mode). */
7487
+ viewportHeight = input(360, ...(ngDevMode ? [{ debugName: "viewportHeight" }] : /* istanbul ignore next */ []));
7488
+ /** Uniform row height in px (virtual mode; tune when `compact`). */
7489
+ rowHeight = input(38, ...(ngDevMode ? [{ debugName: "rowHeight" }] : /* istanbul ignore next */ []));
7490
+ /**
7491
+ * Server-side mode: the grid never sorts or slices `rows` itself — it shows
7492
+ * them as given and emits `(lazyLoad)` with `{ page, pageSize, sortKey,
7493
+ * sortDir }` whenever the user pages or sorts (and once on init). Provide
7494
+ * `total` so the pager and count reflect the full server-side set.
7495
+ */
7496
+ lazy = input(false, { ...(ngDevMode ? { debugName: "lazy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
7497
+ /** Total row count on the server (lazy mode). */
7498
+ total = input(null, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
7499
+ /**
7500
+ * Persist the user's column widths + hidden columns under this key in
7501
+ * localStorage (`strct-dg:<key>`), restoring them on init. For full control
7502
+ * use the two-way `columnState` instead (both may be combined).
7503
+ */
7504
+ stateKey = input(null, ...(ngDevMode ? [{ debugName: "stateKey" }] : /* istanbul ignore next */ []));
7505
+ /** User column preferences (two-way): widths from resize, hidden from the chooser. */
7506
+ columnState = model(null, ...(ngDevMode ? [{ debugName: "columnState" }] : /* istanbul ignore next */ []));
7305
7507
  /** Emitted when the selection changes. */
7306
7508
  selectionChange = output();
7307
7509
  /** Emitted when the refresh button is clicked. */
7308
7510
  syncChange = output();
7309
7511
  /** Emitted when a row's action-menu item is chosen. */
7310
7512
  rowAction = output();
7513
+ /** Server-side data request (lazy mode): load this page / sort and set `rows`. */
7514
+ lazyLoad = output();
7311
7515
  menu = inject(StrctMenuService);
7312
7516
  detailDef = contentChild(StrctRowDetailDef, ...(ngDevMode ? [{ debugName: "detailDef" }] : /* istanbul ignore next */ []));
7313
7517
  actionBarDef = contentChild(StrctDatagridActionBar, ...(ngDevMode ? [{ debugName: "actionBarDef" }] : /* istanbul ignore next */ []));
@@ -7379,6 +7583,9 @@ class StrctDatagrid {
7379
7583
  return this.columns().filter((c) => !hidden.has(c.key));
7380
7584
  }, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : /* istanbul ignore next */ []));
7381
7585
  sorted = computed(() => {
7586
+ // Server-side mode: rows arrive already ordered / sliced — never touch them.
7587
+ if (this.lazy())
7588
+ return this.rows();
7382
7589
  const { key, dir } = this.sort();
7383
7590
  const data = [...this.rows()];
7384
7591
  if (!key)
@@ -7388,11 +7595,87 @@ class StrctDatagrid {
7388
7595
  }, ...(ngDevMode ? [{ debugName: "sorted" }] : /* istanbul ignore next */ []));
7389
7596
  paged = computed(() => {
7390
7597
  const size = this.pageSize();
7391
- if (size <= 0)
7598
+ if (this.lazy() || size <= 0)
7392
7599
  return this.sorted();
7393
7600
  const start = (this.page() - 1) * size;
7394
7601
  return this.sorted().slice(start, start + size);
7395
7602
  }, ...(ngDevMode ? [{ debugName: "paged" }] : /* istanbul ignore next */ []));
7603
+ /** Full count for the pager / footer (server total in lazy mode). */
7604
+ totalCount = computed(() => this.lazy() ? (this.total() ?? this.rows().length) : this.sorted().length, ...(ngDevMode ? [{ debugName: "totalCount" }] : /* istanbul ignore next */ []));
7605
+ // ── Virtual scrolling ──────────────────────────────────────────
7606
+ scrollTop = signal(0, ...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
7607
+ onScroll(event) {
7608
+ this.scrollTop.set(event.target.scrollTop);
7609
+ }
7610
+ /** Visible index window incl. overscan (virtual mode). */
7611
+ vRange = computed(() => {
7612
+ const rh = Math.max(1, this.rowHeight());
7613
+ const n = this.sorted().length;
7614
+ const start = Math.max(0, Math.floor(this.scrollTop() / rh) - 6);
7615
+ const count = Math.ceil(this.viewportHeight() / rh) + 12;
7616
+ return { start, end: Math.min(n, start + count) };
7617
+ }, ...(ngDevMode ? [{ debugName: "vRange" }] : /* istanbul ignore next */ []));
7618
+ /** The rows actually rendered: the virtual window, or the current page. */
7619
+ renderRows = computed(() => this.virtual() ? this.sorted().slice(this.vRange().start, this.vRange().end) : this.paged(), ...(ngDevMode ? [{ debugName: "renderRows" }] : /* istanbul ignore next */ []));
7620
+ topPad = computed(() => this.virtual() ? this.vRange().start * this.rowHeight() : 0, ...(ngDevMode ? [{ debugName: "topPad" }] : /* istanbul ignore next */ []));
7621
+ bottomPad = computed(() => this.virtual() ? Math.max(0, (this.sorted().length - this.vRange().end) * this.rowHeight()) : 0, ...(ngDevMode ? [{ debugName: "bottomPad" }] : /* istanbul ignore next */ []));
7622
+ // ── Sticky (frozen) columns ────────────────────────────────────
7623
+ stickyActive = computed(() => !this.paneOpen() && this.visibleColumns().some((c) => c.sticky), ...(ngDevMode ? [{ debugName: "stickyActive" }] : /* istanbul ignore next */ []));
7624
+ /** Total width of the frozen utility columns (detail / expand / select). */
7625
+ utilityWidth = computed(() => (this.canDetail() ? UTIL_W.detail : 0) +
7626
+ (this.canExpand() ? UTIL_W.expand : 0) +
7627
+ (this.selectable() ? UTIL_W.sel : 0), ...(ngDevMode ? [{ debugName: "utilityWidth" }] : /* istanbul ignore next */ []));
7628
+ /** Left offset of a frozen utility column, or null when sticky is off. */
7629
+ utilLeft(which) {
7630
+ if (!this.stickyActive())
7631
+ return null;
7632
+ let x = 0;
7633
+ if (which === 'detail')
7634
+ return x;
7635
+ if (this.canDetail())
7636
+ x += UTIL_W.detail;
7637
+ if (which === 'expand')
7638
+ return x;
7639
+ if (this.canExpand())
7640
+ x += UTIL_W.expand;
7641
+ return x;
7642
+ }
7643
+ /** Effective px width of a column (resize override → declared px → fallback). */
7644
+ colPx(col) {
7645
+ const resized = this.columnWidths().get(col.key);
7646
+ if (resized != null)
7647
+ return resized;
7648
+ const m = /^(\d+(?:\.\d+)?)px$/.exec(col.width ?? '');
7649
+ return m ? parseFloat(m[1]) : STICKY_FALLBACK_W;
7650
+ }
7651
+ /** key → left px for sticky data columns. */
7652
+ stickyOffsets = computed(() => {
7653
+ const map = new Map();
7654
+ if (!this.stickyActive())
7655
+ return map;
7656
+ let x = this.utilityWidth();
7657
+ for (const col of this.visibleColumns()) {
7658
+ if (!col.sticky)
7659
+ continue;
7660
+ map.set(col.key, x);
7661
+ x += this.colPx(col);
7662
+ }
7663
+ return map;
7664
+ }, ...(ngDevMode ? [{ debugName: "stickyOffsets" }] : /* istanbul ignore next */ []));
7665
+ stickyLeft(key) {
7666
+ return this.stickyOffsets().get(key) ?? null;
7667
+ }
7668
+ isSticky(col) {
7669
+ return !!col.sticky && this.stickyActive();
7670
+ }
7671
+ /** The last frozen column carries the edge shadow. */
7672
+ lastStickyKey = computed(() => {
7673
+ let last = null;
7674
+ for (const col of this.visibleColumns())
7675
+ if (col.sticky)
7676
+ last = col.key;
7677
+ return this.stickyActive() ? last : null;
7678
+ }, ...(ngDevMode ? [{ debugName: "lastStickyKey" }] : /* istanbul ignore next */ []));
7396
7679
  allPageSelected = computed(() => {
7397
7680
  const rows = this.paged();
7398
7681
  return rows.length > 0 && rows.every((r) => this.selected().has(this.idOf(r)));
@@ -7411,13 +7694,18 @@ class StrctDatagrid {
7411
7694
  cellTemplate(key) {
7412
7695
  return this.cellMap().get(key) ?? null;
7413
7696
  }
7697
+ /** Last emitted lazy request (dedupe key). */
7698
+ lastLazyKey = '';
7699
+ /** Last serialized column state pushed outward (loop guard). */
7700
+ lastStateKey = '';
7414
7701
  constructor() {
7415
- // Keep the page in range when the data set shrinks.
7702
+ // Keep the page in range when the data set shrinks (server total in lazy mode).
7416
7703
  effect(() => {
7417
7704
  const size = this.pageSize();
7418
7705
  if (size <= 0)
7419
7706
  return;
7420
- const pageCount = Math.max(1, Math.ceil(this.sorted().length / size));
7707
+ const count = this.totalCount();
7708
+ const pageCount = Math.max(1, Math.ceil(count / size));
7421
7709
  if (this.page() > pageCount)
7422
7710
  this.page.set(pageCount);
7423
7711
  });
@@ -7429,6 +7717,106 @@ class StrctDatagrid {
7429
7717
  return;
7430
7718
  untracked(() => this.selected.set(new Set(init)));
7431
7719
  });
7720
+ // Server-side mode: announce what to load whenever page / sort / pageSize
7721
+ // change (and once on init, so the consumer fetches the first page).
7722
+ effect(() => {
7723
+ if (!this.lazy())
7724
+ return;
7725
+ const state = {
7726
+ page: this.page(),
7727
+ pageSize: this.pageSize(),
7728
+ sortKey: this.sort().key,
7729
+ sortDir: this.sort().dir,
7730
+ };
7731
+ const key = JSON.stringify(state);
7732
+ untracked(() => {
7733
+ if (key === this.lastLazyKey)
7734
+ return;
7735
+ this.lastLazyKey = key;
7736
+ this.lazyLoad.emit(state);
7737
+ });
7738
+ });
7739
+ // Restore persisted column preferences once, before the first render.
7740
+ const persisted = this.readPersistedState();
7741
+ if (persisted)
7742
+ this.applyColumnState(persisted);
7743
+ // Apply externally-driven columnState.
7744
+ effect(() => {
7745
+ const st = this.columnState();
7746
+ untracked(() => {
7747
+ if (!st || JSON.stringify(st) === this.lastStateKey)
7748
+ return;
7749
+ this.lastStateKey = JSON.stringify(st);
7750
+ this.applyColumnState(st);
7751
+ });
7752
+ });
7753
+ // Push user changes (resize / chooser) outward + persist under stateKey.
7754
+ effect(() => {
7755
+ const widths = Object.fromEntries(this.columnWidths());
7756
+ const hidden = [...this.hiddenColumns()];
7757
+ const st = { widths, hidden };
7758
+ const key = JSON.stringify(st);
7759
+ untracked(() => {
7760
+ if (key === this.lastStateKey)
7761
+ return;
7762
+ this.lastStateKey = key;
7763
+ this.columnState.set(st);
7764
+ const sk = this.stateKey();
7765
+ if (sk && typeof localStorage !== 'undefined') {
7766
+ try {
7767
+ localStorage.setItem(`strct-dg:${sk}`, key);
7768
+ }
7769
+ catch {
7770
+ /* storage full / denied — persistence is best-effort */
7771
+ }
7772
+ }
7773
+ });
7774
+ });
7775
+ }
7776
+ readPersistedState() {
7777
+ const sk = this.stateKey();
7778
+ if (!sk || typeof localStorage === 'undefined')
7779
+ return null;
7780
+ try {
7781
+ const raw = localStorage.getItem(`strct-dg:${sk}`);
7782
+ return raw ? JSON.parse(raw) : null;
7783
+ }
7784
+ catch {
7785
+ return null;
7786
+ }
7787
+ }
7788
+ applyColumnState(st) {
7789
+ this.columnWidths.set(new Map(Object.entries(st.widths ?? {}).map(([k, v]) => [k, Number(v)])));
7790
+ this.hiddenColumns.set(new Set(st.hidden ?? []));
7791
+ }
7792
+ /**
7793
+ * The grid as CSV: header labels + every non-hidden column, all rows in the
7794
+ * current order (client mode: the full sorted set, not just the page).
7795
+ */
7796
+ toCSV() {
7797
+ const hidden = this.hiddenColumns();
7798
+ const cols = this.columns().filter((c) => !hidden.has(c.key));
7799
+ const esc = (v) => {
7800
+ const s = v == null ? '' : String(v);
7801
+ return /[",\n;]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
7802
+ };
7803
+ const data = this.lazy() ? this.rows() : this.sorted();
7804
+ return [
7805
+ cols.map((c) => esc(c.label)).join(','),
7806
+ ...data.map((r) => cols.map((c) => esc(r[c.key])).join(',')),
7807
+ ].join('\n');
7808
+ }
7809
+ /** Download the grid as a CSV file. */
7810
+ downloadCSV(filename = 'datagrid.csv') {
7811
+ if (typeof document === 'undefined')
7812
+ return;
7813
+ const blob = new Blob(['\uFEFF' + this.toCSV()], { type: 'text/csv;charset=utf-8' });
7814
+ const url = URL.createObjectURL(blob);
7815
+ const a = document.createElement('a');
7816
+ a.href = url;
7817
+ a.download = filename;
7818
+ a.click();
7819
+ URL.revokeObjectURL(url);
7432
7820
  }
7433
7821
  colspan() {
7434
7822
  return (this.visibleColumns().length +
@@ -7556,24 +7944,43 @@ class StrctDatagrid {
7556
7944
  return String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true });
7557
7945
  }
7558
7946
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDatagrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
7559
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPane: { classPropertyName: "detailPane", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, sync: { classPropertyName: "sync", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction" }, host: { properties: { "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
7947
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPane: { classPropertyName: "detailPane", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, sync: { classPropertyName: "sync", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, stateKey: { classPropertyName: "stateKey", publicName: "stateKey", isSignal: true, isRequired: false, transformFunction: null }, columnState: { classPropertyName: "columnState", publicName: "columnState", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { columnState: "columnStateChange", selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction", lazyLoad: "lazyLoad" }, host: { properties: { "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()", "class.strct-dg-host--virtual": "virtual()", "class.strct-dg-host--sticky": "stickyActive()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
7560
7948
  @if (actionBarDef()) {
7561
7949
  <div class="strct-dg__toolbar"><ng-content select="[strctDatagridActionBar]" /></div>
7562
7950
  }
7563
7951
 
7564
7952
  <div class="strct-dg__layout" [class.strct-dg__layout--paned]="paneOpen()">
7565
- <div class="strct-dg__scroll" tabindex="0" role="region" [attr.aria-label]="L().rows">
7953
+ <div
7954
+ class="strct-dg__scroll"
7955
+ tabindex="0"
7956
+ role="region"
7957
+ [attr.aria-label]="L().rows"
7958
+ [style.max-height.px]="virtual() ? viewportHeight() : null"
7959
+ (scroll)="virtual() && onScroll($event)"
7960
+ >
7566
7961
  <table class="strct-dg">
7567
7962
  <thead>
7568
7963
  <tr>
7569
7964
  @if (canDetail()) {
7570
- <th class="strct-dg__expandcol"></th>
7965
+ <th
7966
+ class="strct-dg__expandcol"
7967
+ [class.strct-dg__cell--sticky]="stickyActive()"
7968
+ [style.left.px]="utilLeft('detail')"
7969
+ ></th>
7571
7970
  }
7572
7971
  @if (canExpand()) {
7573
- <th class="strct-dg__expandcol"></th>
7972
+ <th
7973
+ class="strct-dg__expandcol"
7974
+ [class.strct-dg__cell--sticky]="stickyActive()"
7975
+ [style.left.px]="utilLeft('expand')"
7976
+ ></th>
7574
7977
  }
7575
7978
  @if (selectable()) {
7576
- <th class="strct-dg__sel">
7979
+ <th
7980
+ class="strct-dg__sel"
7981
+ [class.strct-dg__cell--sticky]="stickyActive()"
7982
+ [style.left.px]="utilLeft('sel')"
7983
+ >
7577
7984
  <strct-checkbox
7578
7985
  [ariaLabel]="L().selectAll"
7579
7986
  [checked]="allPageSelected()"
@@ -7587,6 +7994,9 @@ class StrctDatagrid {
7587
7994
  [style.text-align]="col.align ?? 'start'"
7588
7995
  [style.width]="colWidth(col.key) ?? col.width ?? null"
7589
7996
  [class.strct-dg__th--sortable]="col.sortable"
7997
+ [class.strct-dg__cell--sticky]="isSticky(col)"
7998
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
7999
+ [style.left.px]="stickyLeft(col.key)"
7590
8000
  [attr.tabindex]="col.sortable ? 0 : null"
7591
8001
  [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
7592
8002
  (click)="col.sortable && sortBy(col.key)"
@@ -7640,13 +8050,22 @@ class StrctDatagrid {
7640
8050
  </tr>
7641
8051
  }
7642
8052
  } @else {
7643
- @for (row of paged(); track rowKey(row)) {
8053
+ @if (virtual() && topPad() > 0) {
8054
+ <tr class="strct-dg__vspacer" aria-hidden="true">
8055
+ <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
8056
+ </tr>
8057
+ }
8058
+ @for (row of renderRows(); track rowKey(row)) {
7644
8059
  <tr
7645
8060
  [class.strct-dg__row--selected]="isSelected(row)"
7646
8061
  [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
7647
8062
  >
7648
8063
  @if (canDetail()) {
7649
- <td class="strct-dg__expandcell">
8064
+ <td
8065
+ class="strct-dg__expandcell"
8066
+ [class.strct-dg__cell--sticky]="stickyActive()"
8067
+ [style.left.px]="utilLeft('detail')"
8068
+ >
7650
8069
  <button
7651
8070
  type="button"
7652
8071
  class="strct-dg__detailbtn"
@@ -7660,7 +8079,11 @@ class StrctDatagrid {
7660
8079
  </td>
7661
8080
  }
7662
8081
  @if (canExpand()) {
7663
- <td class="strct-dg__expandcell">
8082
+ <td
8083
+ class="strct-dg__expandcell"
8084
+ [class.strct-dg__cell--sticky]="stickyActive()"
8085
+ [style.left.px]="utilLeft('expand')"
8086
+ >
7664
8087
  <button
7665
8088
  type="button"
7666
8089
  class="strct-dg__expandbtn"
@@ -7674,7 +8097,11 @@ class StrctDatagrid {
7674
8097
  </td>
7675
8098
  }
7676
8099
  @if (selectable()) {
7677
- <td class="strct-dg__sel">
8100
+ <td
8101
+ class="strct-dg__sel"
8102
+ [class.strct-dg__cell--sticky]="stickyActive()"
8103
+ [style.left.px]="utilLeft('sel')"
8104
+ >
7678
8105
  <strct-checkbox
7679
8106
  [ariaLabel]="L().selectRow"
7680
8107
  [checked]="isSelected(row)"
@@ -7683,7 +8110,12 @@ class StrctDatagrid {
7683
8110
  </td>
7684
8111
  }
7685
8112
  @for (col of visibleColumns(); track col.key) {
7686
- <td [style.text-align]="col.align ?? 'start'">
8113
+ <td
8114
+ [style.text-align]="col.align ?? 'start'"
8115
+ [class.strct-dg__cell--sticky]="isSticky(col)"
8116
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8117
+ [style.left.px]="stickyLeft(col.key)"
8118
+ >
7687
8119
  @if (cellTemplate(col.key); as tpl) {
7688
8120
  <ng-container
7689
8121
  [ngTemplateOutlet]="tpl"
@@ -7728,6 +8160,11 @@ class StrctDatagrid {
7728
8160
  <td class="strct-dg__empty" [attr.colspan]="colspan()">{{ emptyText() }}</td>
7729
8161
  </tr>
7730
8162
  }
8163
+ @if (virtual() && bottomPad() > 0) {
8164
+ <tr class="strct-dg__vspacer" aria-hidden="true">
8165
+ <td [attr.colspan]="colspan()" [style.height.px]="bottomPad()"></td>
8166
+ </tr>
8167
+ }
7731
8168
  }
7732
8169
  </tbody>
7733
8170
  </table>
@@ -7756,7 +8193,7 @@ class StrctDatagrid {
7756
8193
  }
7757
8194
  </div>
7758
8195
 
7759
- @if (pageSize() > 0 && !loading()) {
8196
+ @if ((pageSize() > 0 || virtual()) && !loading()) {
7760
8197
  <div class="strct-dg__foot">
7761
8198
  <div class="strct-dg__foot-left">
7762
8199
  @if (columnChooser() || sync()) {
@@ -7802,7 +8239,7 @@ class StrctDatagrid {
7802
8239
  </div>
7803
8240
  }
7804
8241
  <span class="strct-dg__count">
7805
- {{ sorted().length }} {{ sorted().length === 1 ? L().row : L().rows }}
8242
+ {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}
7806
8243
  @if (selectedCount()) {
7807
8244
  <span class="strct-dg__count-sep">|</span>
7808
8245
  <span class="strct-dg__count-sel">{{ selectedCount() }} {{ L().selected }}</span>
@@ -7810,11 +8247,13 @@ class StrctDatagrid {
7810
8247
  </span>
7811
8248
  </div>
7812
8249
  <div class="strct-dg__foot-right">
7813
- <strct-pagination [total]="sorted().length" [pageSize]="pageSize()" [(page)]="page" />
8250
+ @if (pageSize() > 0) {
8251
+ <strct-pagination [total]="totalCount()" [pageSize]="pageSize()" [(page)]="page" />
8252
+ }
7814
8253
  </div>
7815
8254
  </div>
7816
8255
  }
7817
- `, isInline: true, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{left:auto;right:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "isDisabled", "disabled", "indeterminate", "ariaLabel"], outputs: ["checkedChange", "isDisabledChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8256
+ `, isInline: true, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1px;width:7px;pointer-events:none;background:linear-gradient(90deg,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "isDisabled", "disabled", "indeterminate", "ariaLabel"], outputs: ["checkedChange", "isDisabledChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7818
8257
  }
7819
8258
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDatagrid, decorators: [{
7820
8259
  type: Component,
@@ -7831,18 +8270,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7831
8270
  }
7832
8271
 
7833
8272
  <div class="strct-dg__layout" [class.strct-dg__layout--paned]="paneOpen()">
7834
- <div class="strct-dg__scroll" tabindex="0" role="region" [attr.aria-label]="L().rows">
8273
+ <div
8274
+ class="strct-dg__scroll"
8275
+ tabindex="0"
8276
+ role="region"
8277
+ [attr.aria-label]="L().rows"
8278
+ [style.max-height.px]="virtual() ? viewportHeight() : null"
8279
+ (scroll)="virtual() && onScroll($event)"
8280
+ >
7835
8281
  <table class="strct-dg">
7836
8282
  <thead>
7837
8283
  <tr>
7838
8284
  @if (canDetail()) {
7839
- <th class="strct-dg__expandcol"></th>
8285
+ <th
8286
+ class="strct-dg__expandcol"
8287
+ [class.strct-dg__cell--sticky]="stickyActive()"
8288
+ [style.left.px]="utilLeft('detail')"
8289
+ ></th>
7840
8290
  }
7841
8291
  @if (canExpand()) {
7842
- <th class="strct-dg__expandcol"></th>
8292
+ <th
8293
+ class="strct-dg__expandcol"
8294
+ [class.strct-dg__cell--sticky]="stickyActive()"
8295
+ [style.left.px]="utilLeft('expand')"
8296
+ ></th>
7843
8297
  }
7844
8298
  @if (selectable()) {
7845
- <th class="strct-dg__sel">
8299
+ <th
8300
+ class="strct-dg__sel"
8301
+ [class.strct-dg__cell--sticky]="stickyActive()"
8302
+ [style.left.px]="utilLeft('sel')"
8303
+ >
7846
8304
  <strct-checkbox
7847
8305
  [ariaLabel]="L().selectAll"
7848
8306
  [checked]="allPageSelected()"
@@ -7856,6 +8314,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7856
8314
  [style.text-align]="col.align ?? 'start'"
7857
8315
  [style.width]="colWidth(col.key) ?? col.width ?? null"
7858
8316
  [class.strct-dg__th--sortable]="col.sortable"
8317
+ [class.strct-dg__cell--sticky]="isSticky(col)"
8318
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8319
+ [style.left.px]="stickyLeft(col.key)"
7859
8320
  [attr.tabindex]="col.sortable ? 0 : null"
7860
8321
  [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
7861
8322
  (click)="col.sortable && sortBy(col.key)"
@@ -7909,13 +8370,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7909
8370
  </tr>
7910
8371
  }
7911
8372
  } @else {
7912
- @for (row of paged(); track rowKey(row)) {
8373
+ @if (virtual() && topPad() > 0) {
8374
+ <tr class="strct-dg__vspacer" aria-hidden="true">
8375
+ <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
8376
+ </tr>
8377
+ }
8378
+ @for (row of renderRows(); track rowKey(row)) {
7913
8379
  <tr
7914
8380
  [class.strct-dg__row--selected]="isSelected(row)"
7915
8381
  [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
7916
8382
  >
7917
8383
  @if (canDetail()) {
7918
- <td class="strct-dg__expandcell">
8384
+ <td
8385
+ class="strct-dg__expandcell"
8386
+ [class.strct-dg__cell--sticky]="stickyActive()"
8387
+ [style.left.px]="utilLeft('detail')"
8388
+ >
7919
8389
  <button
7920
8390
  type="button"
7921
8391
  class="strct-dg__detailbtn"
@@ -7929,7 +8399,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7929
8399
  </td>
7930
8400
  }
7931
8401
  @if (canExpand()) {
7932
- <td class="strct-dg__expandcell">
8402
+ <td
8403
+ class="strct-dg__expandcell"
8404
+ [class.strct-dg__cell--sticky]="stickyActive()"
8405
+ [style.left.px]="utilLeft('expand')"
8406
+ >
7933
8407
  <button
7934
8408
  type="button"
7935
8409
  class="strct-dg__expandbtn"
@@ -7943,7 +8417,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7943
8417
  </td>
7944
8418
  }
7945
8419
  @if (selectable()) {
7946
- <td class="strct-dg__sel">
8420
+ <td
8421
+ class="strct-dg__sel"
8422
+ [class.strct-dg__cell--sticky]="stickyActive()"
8423
+ [style.left.px]="utilLeft('sel')"
8424
+ >
7947
8425
  <strct-checkbox
7948
8426
  [ariaLabel]="L().selectRow"
7949
8427
  [checked]="isSelected(row)"
@@ -7952,7 +8430,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7952
8430
  </td>
7953
8431
  }
7954
8432
  @for (col of visibleColumns(); track col.key) {
7955
- <td [style.text-align]="col.align ?? 'start'">
8433
+ <td
8434
+ [style.text-align]="col.align ?? 'start'"
8435
+ [class.strct-dg__cell--sticky]="isSticky(col)"
8436
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8437
+ [style.left.px]="stickyLeft(col.key)"
8438
+ >
7956
8439
  @if (cellTemplate(col.key); as tpl) {
7957
8440
  <ng-container
7958
8441
  [ngTemplateOutlet]="tpl"
@@ -7997,6 +8480,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7997
8480
  <td class="strct-dg__empty" [attr.colspan]="colspan()">{{ emptyText() }}</td>
7998
8481
  </tr>
7999
8482
  }
8483
+ @if (virtual() && bottomPad() > 0) {
8484
+ <tr class="strct-dg__vspacer" aria-hidden="true">
8485
+ <td [attr.colspan]="colspan()" [style.height.px]="bottomPad()"></td>
8486
+ </tr>
8487
+ }
8000
8488
  }
8001
8489
  </tbody>
8002
8490
  </table>
@@ -8025,7 +8513,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8025
8513
  }
8026
8514
  </div>
8027
8515
 
8028
- @if (pageSize() > 0 && !loading()) {
8516
+ @if ((pageSize() > 0 || virtual()) && !loading()) {
8029
8517
  <div class="strct-dg__foot">
8030
8518
  <div class="strct-dg__foot-left">
8031
8519
  @if (columnChooser() || sync()) {
@@ -8071,7 +8559,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8071
8559
  </div>
8072
8560
  }
8073
8561
  <span class="strct-dg__count">
8074
- {{ sorted().length }} {{ sorted().length === 1 ? L().row : L().rows }}
8562
+ {{ totalCount() }} {{ totalCount() === 1 ? L().row : L().rows }}
8075
8563
  @if (selectedCount()) {
8076
8564
  <span class="strct-dg__count-sep">|</span>
8077
8565
  <span class="strct-dg__count-sel">{{ selectedCount() }} {{ L().selected }}</span>
@@ -8079,7 +8567,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8079
8567
  </span>
8080
8568
  </div>
8081
8569
  <div class="strct-dg__foot-right">
8082
- <strct-pagination [total]="sorted().length" [pageSize]="pageSize()" [(page)]="page" />
8570
+ @if (pageSize() > 0) {
8571
+ <strct-pagination [total]="totalCount()" [pageSize]="pageSize()" [(page)]="page" />
8572
+ }
8083
8573
  </div>
8084
8574
  </div>
8085
8575
  }
@@ -8087,8 +8577,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8087
8577
  class: 'strct-dg-host',
8088
8578
  '[class.strct-dg-host--compact]': 'compact()',
8089
8579
  '[class.strct-dg-host--singleline]': 'singleLine()',
8090
- }, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{left:auto;right:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"] }]
8091
- }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPane: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], sync: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
8580
+ '[class.strct-dg-host--virtual]': 'virtual()',
8581
+ '[class.strct-dg-host--sticky]': 'stickyActive()',
8582
+ }, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1px;width:7px;pointer-events:none;background:linear-gradient(90deg,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"] }]
8583
+ }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPane: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], sync: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], columnState: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnState", required: false }] }, { type: i0.Output, args: ["columnStateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
8092
8584
 
8093
8585
  /** Vertical timeline container. Wraps `<strct-timeline-item>` children. */
8094
8586
  class StrctTimeline {