@akcelik/strct 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, booleanAttribute, output, HostListener, model, contentChildren, effect, forwardRef, TemplateRef, contentChild, Renderer2 } from '@angular/core';
2
+ import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, booleanAttribute, output, HostListener, model, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, TemplateRef, contentChild, Renderer2 } from '@angular/core';
3
3
  import { DomSanitizer } from '@angular/platform-browser';
4
+ import { DOCUMENT as DOCUMENT$1, NgTemplateOutlet } from '@angular/common';
4
5
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
- import { NgTemplateOutlet } from '@angular/common';
6
6
 
7
7
  /** All palettes the token system ships with, in display order. */
8
8
  const STRCT_PALETTES = [
@@ -1116,45 +1116,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1116
1116
  `, host: { class: 'strct-tabs' }, styles: [".strct-tabs{display:block}.strct-tabs__bar{display:flex;gap:2px;border-bottom:1px solid var(--b2)}.strct-tabs__btn{appearance:none;border:0;background:transparent;cursor:pointer;font-family:var(--font);font-size:13px;font-weight:500;color:var(--t2);padding:9px 14px;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .14s ease,border-color .14s ease}.strct-tabs__btn:hover{color:var(--t1)}.strct-tabs__btn--active{color:var(--acc);border-bottom-color:var(--acc)}.strct-tabs__btn:disabled{color:var(--t4);cursor:not-allowed}.strct-tabs__panels{padding-top:16px}\n"] }]
1117
1117
  }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctTab), { isSignal: true }] }] } });
1118
1118
 
1119
- /** Root container for a tree of `<strct-tree-node>` items. */
1120
- class StrctTree {
1121
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
1122
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctTree, isStandalone: true, selector: "strct-tree", host: { attributes: { "role": "tree" }, classAttribute: "strct-tree" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [".strct-tree{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1123
- }
1124
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, decorators: [{
1125
- type: Component,
1126
- args: [{ selector: 'strct-tree', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `<ng-content />`, host: { class: 'strct-tree', role: 'tree' }, styles: [".strct-tree{display:block}\n"] }]
1127
- }] });
1128
1119
  /**
1129
- * Tree node. Nest `<strct-tree-node>` children to build hierarchy:
1130
- * <strct-tree-node label="Group" [(expanded)]="open">
1131
- * <strct-tree-node label="Leaf" icon="grid" [active]="true" />
1120
+ * Tree node. Two modes:
1121
+ * - **Content:** nest `<strct-tree-node>` children manually.
1122
+ * - **Data:** pass a `[node]` object that recurses over its `children` —
1123
+ * used internally by `<strct-tree [nodes]>`.
1124
+ *
1125
+ * <strct-tree-node label="Group" icon="layers" badge="ok" [(expanded)]="open">
1126
+ * <strct-tree-node label="Leaf" icon="vm" [active]="true" />
1132
1127
  * </strct-tree-node>
1133
1128
  */
1134
1129
  class StrctTreeNode {
1135
- label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1130
+ /** Data-driven node; when set, label/icon/children come from it. */
1131
+ node = input(null, ...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
1132
+ label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1136
1133
  icon = input(undefined, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
1134
+ badge = input('none', ...(ngDevMode ? [{ debugName: "badge" }] : /* istanbul ignore next */ []));
1137
1135
  active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
1138
1136
  expanded = model(false, ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
1137
+ /** Content-mode click. */
1139
1138
  activated = output();
1139
+ /** Data-mode click — carries the activated node (bubbles to the tree). */
1140
+ nodeActivated = output();
1140
1141
  childNodes = contentChildren(StrctTreeNode, ...(ngDevMode ? [{ debugName: "childNodes" }] : /* istanbul ignore next */ []));
1141
- hasChildren = computed(() => this.childNodes().length > 0, ...(ngDevMode ? [{ debugName: "hasChildren" }] : /* istanbul ignore next */ []));
1142
+ /** Data-mode expansion (seeded from node.expanded on first toggle). */
1143
+ dataExpanded = signal(null, ...(ngDevMode ? [{ debugName: "dataExpanded" }] : /* istanbul ignore next */ []));
1144
+ displayLabel = computed(() => this.node()?.label ?? this.label(), ...(ngDevMode ? [{ debugName: "displayLabel" }] : /* istanbul ignore next */ []));
1145
+ displayIcon = computed(() => this.node()?.icon ?? this.icon(), ...(ngDevMode ? [{ debugName: "displayIcon" }] : /* istanbul ignore next */ []));
1146
+ displayBadge = computed(() => this.node()?.badge ?? this.badge(), ...(ngDevMode ? [{ debugName: "displayBadge" }] : /* istanbul ignore next */ []));
1147
+ displayActive = computed(() => this.node()?.active ?? this.active(), ...(ngDevMode ? [{ debugName: "displayActive" }] : /* istanbul ignore next */ []));
1148
+ hasChildren = computed(() => {
1149
+ const n = this.node();
1150
+ return n ? (n.children?.length ?? 0) > 0 : this.childNodes().length > 0;
1151
+ }, ...(ngDevMode ? [{ debugName: "hasChildren" }] : /* istanbul ignore next */ []));
1152
+ isOpen = computed(() => {
1153
+ if (this.node())
1154
+ return this.dataExpanded() ?? this.node().expanded ?? false;
1155
+ return this.expanded();
1156
+ }, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
1142
1157
  toggle() {
1143
- this.expanded.update((v) => !v);
1158
+ if (this.node()) {
1159
+ this.dataExpanded.set(!this.isOpen());
1160
+ }
1161
+ else {
1162
+ this.expanded.update((v) => !v);
1163
+ }
1164
+ }
1165
+ onActivate() {
1166
+ const n = this.node();
1167
+ if (n)
1168
+ this.nodeActivated.emit(n);
1169
+ else
1170
+ this.activated.emit();
1144
1171
  }
1145
1172
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTreeNode, deps: [], target: i0.ɵɵFactoryTarget.Component });
1146
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTreeNode, isStandalone: true, selector: "strct-tree-node", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", activated: "activated" }, host: { classAttribute: "strct-tnode" }, queries: [{ propertyName: "childNodes", predicate: StrctTreeNode, isSignal: true }], ngImport: i0, template: `
1173
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTreeNode, isStandalone: true, selector: "strct-tree-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, badge: { classPropertyName: "badge", publicName: "badge", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { expanded: "expandedChange", activated: "activated", nodeActivated: "nodeActivated" }, host: { classAttribute: "strct-tnode" }, queries: [{ propertyName: "childNodes", predicate: StrctTreeNode, isSignal: true }], ngImport: i0, template: `
1147
1174
  <div
1148
1175
  class="strct-tnode__row"
1149
- [class.strct-tnode__row--active]="active()"
1176
+ [class.strct-tnode__row--active]="displayActive()"
1150
1177
  role="treeitem"
1151
- [attr.aria-expanded]="hasChildren() ? expanded() : null"
1152
- (click)="activated.emit()"
1178
+ [attr.aria-expanded]="hasChildren() ? isOpen() : null"
1179
+ (click)="onActivate()"
1153
1180
  >
1154
1181
  @if (hasChildren()) {
1155
1182
  <span
1156
1183
  class="strct-tnode__chevron"
1157
- [class.strct-tnode__chevron--open]="expanded()"
1184
+ [class.strct-tnode__chevron--open]="isOpen()"
1158
1185
  (click)="$event.stopPropagation(); toggle()"
1159
1186
  >
1160
1187
  <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
@@ -1162,33 +1189,45 @@ class StrctTreeNode {
1162
1189
  } @else {
1163
1190
  <span class="strct-tnode__spacer"></span>
1164
1191
  }
1165
- @if (icon()) {
1166
- <strct-icon class="strct-tnode__icon" [name]="icon()!" [size]="14" [strokeWidth]="1.3" />
1192
+ @if (displayIcon()) {
1193
+ <strct-icon
1194
+ class="strct-tnode__icon"
1195
+ [name]="displayIcon()!"
1196
+ [size]="14"
1197
+ [strokeWidth]="1.3"
1198
+ [badge]="displayBadge()"
1199
+ />
1167
1200
  }
1168
- <span class="strct-tnode__label">{{ label() }}</span>
1201
+ <span class="strct-tnode__label">{{ displayLabel() }}</span>
1169
1202
  <ng-content select="[strctTreeTrailing]" />
1170
1203
  </div>
1171
- @if (hasChildren() && expanded()) {
1204
+ @if (hasChildren() && isOpen()) {
1172
1205
  <div class="strct-tnode__children" role="group">
1173
- <ng-content />
1206
+ @if (node()) {
1207
+ @for (child of node()!.children ?? []; track $index) {
1208
+ <strct-tree-node [node]="child" (nodeActivated)="nodeActivated.emit($event)" />
1209
+ }
1210
+ } @else {
1211
+ <ng-content />
1212
+ }
1174
1213
  </div>
1175
1214
  }
1176
- `, isInline: true, styles: [".strct-tnode{display:block}.strct-tnode__row{display:flex;align-items:center;gap:7px;padding:7px 10px;border-radius:5px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tnode__row:hover{background:var(--bg-3)}.strct-tnode__row--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-tnode__row--active .strct-tnode__icon,.strct-tnode__row--active .strct-tnode__chevron{color:var(--acc)}.strct-tnode__chevron{display:inline-flex;color:var(--t3);transition:transform .15s ease;width:14px;justify-content:center}.strct-tnode__chevron--open{transform:rotate(90deg)}.strct-tnode__spacer{width:14px;flex-shrink:0}.strct-tnode__icon{color:var(--t2);flex-shrink:0}.strct-tnode__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-tnode__children{margin-left:16px}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1215
+ `, isInline: true, styles: [".strct-tnode{display:block}.strct-tnode__row{display:flex;align-items:center;gap:7px;padding:7px 10px;border-radius:5px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tnode__row:hover{background:var(--bg-3)}.strct-tnode__row--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-tnode__row--active .strct-tnode__icon,.strct-tnode__row--active .strct-tnode__chevron{color:var(--acc)}.strct-tnode__chevron{display:inline-flex;color:var(--t3);transition:transform .15s ease;width:14px;justify-content:center}.strct-tnode__chevron--open{transform:rotate(90deg)}.strct-tnode__spacer{width:14px;flex-shrink:0}.strct-tnode__icon{color:var(--t2);flex-shrink:0}.strct-tnode__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-tnode__children{margin-left:16px}\n"], dependencies: [{ kind: "component", type: StrctTreeNode, selector: "strct-tree-node", inputs: ["node", "label", "icon", "badge", "active", "expanded"], outputs: ["expandedChange", "activated", "nodeActivated"] }, { kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1177
1216
  }
1178
1217
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTreeNode, decorators: [{
1179
1218
  type: Component,
1180
1219
  args: [{ selector: 'strct-tree-node', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
1181
1220
  <div
1182
1221
  class="strct-tnode__row"
1183
- [class.strct-tnode__row--active]="active()"
1222
+ [class.strct-tnode__row--active]="displayActive()"
1184
1223
  role="treeitem"
1185
- [attr.aria-expanded]="hasChildren() ? expanded() : null"
1186
- (click)="activated.emit()"
1224
+ [attr.aria-expanded]="hasChildren() ? isOpen() : null"
1225
+ (click)="onActivate()"
1187
1226
  >
1188
1227
  @if (hasChildren()) {
1189
1228
  <span
1190
1229
  class="strct-tnode__chevron"
1191
- [class.strct-tnode__chevron--open]="expanded()"
1230
+ [class.strct-tnode__chevron--open]="isOpen()"
1192
1231
  (click)="$event.stopPropagation(); toggle()"
1193
1232
  >
1194
1233
  <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
@@ -1196,19 +1235,64 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1196
1235
  } @else {
1197
1236
  <span class="strct-tnode__spacer"></span>
1198
1237
  }
1199
- @if (icon()) {
1200
- <strct-icon class="strct-tnode__icon" [name]="icon()!" [size]="14" [strokeWidth]="1.3" />
1238
+ @if (displayIcon()) {
1239
+ <strct-icon
1240
+ class="strct-tnode__icon"
1241
+ [name]="displayIcon()!"
1242
+ [size]="14"
1243
+ [strokeWidth]="1.3"
1244
+ [badge]="displayBadge()"
1245
+ />
1201
1246
  }
1202
- <span class="strct-tnode__label">{{ label() }}</span>
1247
+ <span class="strct-tnode__label">{{ displayLabel() }}</span>
1203
1248
  <ng-content select="[strctTreeTrailing]" />
1204
1249
  </div>
1205
- @if (hasChildren() && expanded()) {
1250
+ @if (hasChildren() && isOpen()) {
1206
1251
  <div class="strct-tnode__children" role="group">
1207
- <ng-content />
1252
+ @if (node()) {
1253
+ @for (child of node()!.children ?? []; track $index) {
1254
+ <strct-tree-node [node]="child" (nodeActivated)="nodeActivated.emit($event)" />
1255
+ }
1256
+ } @else {
1257
+ <ng-content />
1258
+ }
1208
1259
  </div>
1209
1260
  }
1210
1261
  `, host: { class: 'strct-tnode' }, styles: [".strct-tnode{display:block}.strct-tnode__row{display:flex;align-items:center;gap:7px;padding:7px 10px;border-radius:5px;cursor:pointer;font-size:13px;color:var(--t1);-webkit-user-select:none;user-select:none}.strct-tnode__row:hover{background:var(--bg-3)}.strct-tnode__row--active{background:var(--acc-m);color:var(--acc);font-weight:500}.strct-tnode__row--active .strct-tnode__icon,.strct-tnode__row--active .strct-tnode__chevron{color:var(--acc)}.strct-tnode__chevron{display:inline-flex;color:var(--t3);transition:transform .15s ease;width:14px;justify-content:center}.strct-tnode__chevron--open{transform:rotate(90deg)}.strct-tnode__spacer{width:14px;flex-shrink:0}.strct-tnode__icon{color:var(--t2);flex-shrink:0}.strct-tnode__label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.strct-tnode__children{margin-left:16px}\n"] }]
1211
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], activated: [{ type: i0.Output, args: ["activated"] }], childNodes: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctTreeNode), { isSignal: true }] }] } });
1262
+ }], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], badge: [{ type: i0.Input, args: [{ isSignal: true, alias: "badge", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }], activated: [{ type: i0.Output, args: ["activated"] }], nodeActivated: [{ type: i0.Output, args: ["nodeActivated"] }], childNodes: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctTreeNode), { isSignal: true }] }] } });
1263
+ /**
1264
+ * Root container for a tree. Either project `<strct-tree-node>` children, or
1265
+ * pass `[nodes]` for a fully data-driven, self-recursing tree:
1266
+ * <strct-tree [nodes]="roots" (nodeActivated)="select($event)" />
1267
+ */
1268
+ class StrctTree {
1269
+ /** Data-driven node list; when set, projected content is ignored. */
1270
+ nodes = input(null, ...(ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []));
1271
+ /** Emitted when any data-driven node is clicked. */
1272
+ nodeActivated = output();
1273
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
1274
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTree, isStandalone: true, selector: "strct-tree", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeActivated: "nodeActivated" }, host: { attributes: { "role": "tree" }, classAttribute: "strct-tree" }, ngImport: i0, template: `
1275
+ @if (nodes(); as ns) {
1276
+ @for (n of ns; track $index) {
1277
+ <strct-tree-node [node]="n" (nodeActivated)="nodeActivated.emit($event)" />
1278
+ }
1279
+ } @else {
1280
+ <ng-content />
1281
+ }
1282
+ `, isInline: true, styles: [".strct-tree{display:block}\n"], dependencies: [{ kind: "component", type: StrctTreeNode, selector: "strct-tree-node", inputs: ["node", "label", "icon", "badge", "active", "expanded"], outputs: ["expandedChange", "activated", "nodeActivated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1283
+ }
1284
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, decorators: [{
1285
+ type: Component,
1286
+ args: [{ selector: 'strct-tree', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctTreeNode], template: `
1287
+ @if (nodes(); as ns) {
1288
+ @for (n of ns; track $index) {
1289
+ <strct-tree-node [node]="n" (nodeActivated)="nodeActivated.emit($event)" />
1290
+ }
1291
+ } @else {
1292
+ <ng-content />
1293
+ }
1294
+ `, host: { class: 'strct-tree', role: 'tree' }, styles: [".strct-tree{display:block}\n"] }]
1295
+ }], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], nodeActivated: [{ type: i0.Output, args: ["nodeActivated"] }] } });
1212
1296
 
1213
1297
  let modalCounter = 0;
1214
1298
  /**
@@ -1606,9 +1690,341 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1606
1690
  `, host: { class: 'strct-submenu-host' }, styles: [".strct-submenu{position:relative}.strct-submenu__trigger{display:flex;align-items:center;gap:8px;padding:7px 8px 7px 10px;border-radius:5px;cursor:default;font-size:13px;color:var(--t1)}.strct-submenu__trigger:hover{background:var(--bg-3)}.strct-submenu__icon{color:var(--t2);flex-shrink:0}.strct-submenu__icon-spacer{width:14px;flex-shrink:0}.strct-submenu__label{flex:1;display:inline-flex;align-items:center;gap:8px}.strct-submenu__arrow{color:var(--t3)}.strct-submenu__panel{position:absolute;top:-5px;left:100%;z-index:1;min-width:170px;margin-left:2px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-submenu-in .1s ease}@keyframes strct-submenu-in{0%{opacity:0;transform:translate(-4px)}}\n"] }]
1607
1691
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
1608
1692
 
1693
+ /**
1694
+ * Floating menu panel — portaled into `<body>` (so it escapes overflow /
1695
+ * transform clipping), positioned by its real measured size, with full keyboard
1696
+ * navigation and recursive submenus. Usually created by `[strctContextMenu]`,
1697
+ * but can be embedded directly with `submenu`.
1698
+ */
1699
+ class StrctMenuPanel {
1700
+ host = inject(ElementRef);
1701
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1702
+ data = input(undefined, ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
1703
+ x = input(0, ...(ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []));
1704
+ y = input(0, ...(ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []));
1705
+ submenu = input(false, { ...(ngDevMode ? { debugName: "submenu" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1706
+ select = output();
1707
+ close = output();
1708
+ /** ArrowLeft inside a submenu — asks the parent to close it. */
1709
+ back = output();
1710
+ posX = signal(0, ...(ngDevMode ? [{ debugName: "posX" }] : /* istanbul ignore next */ []));
1711
+ posY = signal(0, ...(ngDevMode ? [{ debugName: "posY" }] : /* istanbul ignore next */ []));
1712
+ flipLeft = signal(false, ...(ngDevMode ? [{ debugName: "flipLeft" }] : /* istanbul ignore next */ []));
1713
+ activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
1714
+ openSubIndex = signal(null, ...(ngDevMode ? [{ debugName: "openSubIndex" }] : /* istanbul ignore next */ []));
1715
+ navIndices = computed(() => this.items()
1716
+ .map((it, i) => (it.divider ? -1 : i))
1717
+ .filter((i) => i >= 0), ...(ngDevMode ? [{ debugName: "navIndices" }] : /* istanbul ignore next */ []));
1718
+ constructor() {
1719
+ this.posX.set(this.x());
1720
+ this.posY.set(this.y());
1721
+ afterNextRender(() => {
1722
+ this.activeIndex.set(this.navIndices()[0] ?? 0);
1723
+ if (!this.submenu())
1724
+ this.clampToViewport();
1725
+ this.focusItem(this.activeIndex());
1726
+ });
1727
+ }
1728
+ clampToViewport() {
1729
+ const host = this.host.nativeElement;
1730
+ const w = host.offsetWidth;
1731
+ const h = host.offsetHeight;
1732
+ const vw = window.innerWidth;
1733
+ const vh = window.innerHeight;
1734
+ const m = 6;
1735
+ let nx = this.x();
1736
+ let ny = this.y();
1737
+ if (nx + w > vw - m)
1738
+ nx = Math.max(m, Math.min(this.x() - w, vw - w - m));
1739
+ if (ny + h > vh - m)
1740
+ ny = Math.max(m, vh - h - m);
1741
+ this.posX.set(nx);
1742
+ this.posY.set(ny);
1743
+ // Submenus of a panel near the right edge open to the left.
1744
+ this.flipLeft.set(nx + w > vw - 220);
1745
+ }
1746
+ focusItem(i) {
1747
+ this.activeIndex.set(i);
1748
+ this.host.nativeElement
1749
+ .querySelector(`.strct-menu__item[data-idx="${i}"]`)
1750
+ ?.focus();
1751
+ }
1752
+ move(dir) {
1753
+ const nav = this.navIndices();
1754
+ if (!nav.length)
1755
+ return;
1756
+ const pos = nav.indexOf(this.activeIndex());
1757
+ const next = nav[(pos + dir + nav.length) % nav.length];
1758
+ this.openSubIndex.set(null);
1759
+ this.focusItem(next);
1760
+ }
1761
+ onHover(i) {
1762
+ this.activeIndex.set(i);
1763
+ const it = this.items()[i];
1764
+ this.openSubIndex.set(it?.children?.length ? i : null);
1765
+ }
1766
+ onLeave(i) {
1767
+ if (this.openSubIndex() === i)
1768
+ this.openSubIndex.set(null);
1769
+ }
1770
+ onItemClick(item, i, event) {
1771
+ event.stopPropagation();
1772
+ if (item.disabled)
1773
+ return;
1774
+ if (item.children?.length) {
1775
+ this.openSubIndex.set(this.openSubIndex() === i ? null : i);
1776
+ this.focusItem(i);
1777
+ }
1778
+ else {
1779
+ this.select.emit(item);
1780
+ }
1781
+ }
1782
+ closeSub() {
1783
+ this.openSubIndex.set(null);
1784
+ }
1785
+ onKeydown(event) {
1786
+ const key = event.key;
1787
+ const item = this.items()[this.activeIndex()];
1788
+ switch (key) {
1789
+ case 'ArrowDown':
1790
+ event.preventDefault();
1791
+ event.stopPropagation();
1792
+ this.move(1);
1793
+ break;
1794
+ case 'ArrowUp':
1795
+ event.preventDefault();
1796
+ event.stopPropagation();
1797
+ this.move(-1);
1798
+ break;
1799
+ case 'Home':
1800
+ event.preventDefault();
1801
+ event.stopPropagation();
1802
+ this.focusItem(this.navIndices()[0] ?? 0);
1803
+ break;
1804
+ case 'End':
1805
+ event.preventDefault();
1806
+ event.stopPropagation();
1807
+ this.focusItem(this.navIndices().at(-1) ?? 0);
1808
+ break;
1809
+ case 'ArrowRight':
1810
+ if (item?.children?.length) {
1811
+ event.preventDefault();
1812
+ event.stopPropagation();
1813
+ this.openSubIndex.set(this.activeIndex());
1814
+ }
1815
+ break;
1816
+ case 'ArrowLeft':
1817
+ event.preventDefault();
1818
+ event.stopPropagation();
1819
+ if (this.openSubIndex() != null)
1820
+ this.closeSub();
1821
+ else if (this.submenu())
1822
+ this.back.emit();
1823
+ break;
1824
+ case 'Enter':
1825
+ case ' ':
1826
+ event.preventDefault();
1827
+ event.stopPropagation();
1828
+ if (item && !item.disabled) {
1829
+ if (item.children?.length)
1830
+ this.openSubIndex.set(this.activeIndex());
1831
+ else
1832
+ this.select.emit(item);
1833
+ }
1834
+ break;
1835
+ case 'Escape':
1836
+ event.preventDefault();
1837
+ event.stopPropagation();
1838
+ this.close.emit();
1839
+ break;
1840
+ }
1841
+ }
1842
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctMenuPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
1843
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctMenuPanel, isStandalone: true, selector: "strct-menu-panel", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, x: { classPropertyName: "x", publicName: "x", isSignal: true, isRequired: false, transformFunction: null }, y: { classPropertyName: "y", publicName: "y", isSignal: true, isRequired: false, transformFunction: null }, submenu: { classPropertyName: "submenu", publicName: "submenu", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { select: "select", close: "close", back: "back" }, host: { properties: { "style.position": "submenu() ? null : 'fixed'", "style.left.px": "submenu() ? null : posX()", "style.top.px": "submenu() ? null : posY()", "style.zIndex": "submenu() ? null : 1100" }, classAttribute: "strct-menu-host" }, ngImport: i0, template: `
1844
+ <div class="strct-menu" role="menu" tabindex="-1" (keydown)="onKeydown($event)">
1845
+ @for (item of items(); track $index; let i = $index) {
1846
+ @if (item.divider) {
1847
+ <div class="strct-menu__sep" role="separator"></div>
1848
+ } @else {
1849
+ <div class="strct-menu__wrap" (mouseenter)="onHover(i)" (mouseleave)="onLeave(i)">
1850
+ <button
1851
+ type="button"
1852
+ class="strct-menu__item"
1853
+ [attr.data-idx]="i"
1854
+ [class.strct-menu__item--danger]="item.danger"
1855
+ [class.strct-menu__item--active]="i === activeIndex()"
1856
+ [disabled]="item.disabled"
1857
+ role="menuitem"
1858
+ [attr.aria-haspopup]="item.children?.length ? 'menu' : null"
1859
+ [attr.aria-expanded]="item.children?.length ? openSubIndex() === i : null"
1860
+ [attr.tabindex]="i === activeIndex() ? 0 : -1"
1861
+ (click)="onItemClick(item, i, $event)"
1862
+ >
1863
+ @if (item.icon) {
1864
+ <strct-icon class="strct-menu__icon" [name]="item.icon" [size]="14" [strokeWidth]="1.3" />
1865
+ } @else {
1866
+ <span class="strct-menu__icon-spacer" aria-hidden="true"></span>
1867
+ }
1868
+ <span class="strct-menu__label">{{ item.label }}</span>
1869
+ @if (item.children?.length) {
1870
+ <strct-icon class="strct-menu__arrow" name="chevronRight" [size]="12" [strokeWidth]="1.6" />
1871
+ }
1872
+ </button>
1873
+ @if (openSubIndex() === i && item.children?.length) {
1874
+ <strct-menu-panel
1875
+ submenu
1876
+ class="strct-menu__subpanel"
1877
+ [class.strct-menu__subpanel--flip]="flipLeft()"
1878
+ [items]="item.children!"
1879
+ [data]="data()"
1880
+ (select)="select.emit($event)"
1881
+ (close)="close.emit()"
1882
+ (back)="closeSub(); focusItem(i)"
1883
+ />
1884
+ }
1885
+ </div>
1886
+ }
1887
+ }
1888
+ </div>
1889
+ `, isInline: true, styles: [".strct-menu-host{display:block}.strct-menu{min-width:180px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-menu-in .1s ease}.strct-menu:focus{outline:none}.strct-menu__wrap{position:relative}.strct-menu__item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 8px 7px 10px;border:0;border-radius:5px;cursor:pointer;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);text-align:left}.strct-menu__item:hover:not(:disabled),.strct-menu__item--active:not(:disabled){background:var(--bg-3)}.strct-menu__item:focus-visible{outline:none;background:var(--bg-3)}.strct-menu__item--danger{color:var(--crt)}.strct-menu__item--danger:hover:not(:disabled),.strct-menu__item--danger.strct-menu__item--active:not(:disabled){background:var(--crt-bg)}.strct-menu__item:disabled{opacity:.45;cursor:not-allowed}.strct-menu__icon{color:var(--t2);flex-shrink:0}.strct-menu__item--danger .strct-menu__icon{color:var(--crt)}.strct-menu__icon-spacer{width:14px;flex-shrink:0}.strct-menu__label{flex:1;white-space:nowrap}.strct-menu__arrow{color:var(--t3);flex-shrink:0}.strct-menu__sep{height:1px;margin:4px 6px;background:var(--b1)}.strct-menu__subpanel{position:absolute;top:-5px;left:100%;margin-left:2px;z-index:1}.strct-menu__subpanel--flip{left:auto;right:100%;margin-left:0;margin-right:2px}@keyframes strct-menu-in{0%{opacity:0;transform:scale(.97)}}\n"], dependencies: [{ kind: "component", type: StrctMenuPanel, selector: "strct-menu-panel", inputs: ["items", "data", "x", "y", "submenu"], outputs: ["select", "close", "back"] }, { kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1890
+ }
1891
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctMenuPanel, decorators: [{
1892
+ type: Component,
1893
+ args: [{ selector: 'strct-menu-panel', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
1894
+ <div class="strct-menu" role="menu" tabindex="-1" (keydown)="onKeydown($event)">
1895
+ @for (item of items(); track $index; let i = $index) {
1896
+ @if (item.divider) {
1897
+ <div class="strct-menu__sep" role="separator"></div>
1898
+ } @else {
1899
+ <div class="strct-menu__wrap" (mouseenter)="onHover(i)" (mouseleave)="onLeave(i)">
1900
+ <button
1901
+ type="button"
1902
+ class="strct-menu__item"
1903
+ [attr.data-idx]="i"
1904
+ [class.strct-menu__item--danger]="item.danger"
1905
+ [class.strct-menu__item--active]="i === activeIndex()"
1906
+ [disabled]="item.disabled"
1907
+ role="menuitem"
1908
+ [attr.aria-haspopup]="item.children?.length ? 'menu' : null"
1909
+ [attr.aria-expanded]="item.children?.length ? openSubIndex() === i : null"
1910
+ [attr.tabindex]="i === activeIndex() ? 0 : -1"
1911
+ (click)="onItemClick(item, i, $event)"
1912
+ >
1913
+ @if (item.icon) {
1914
+ <strct-icon class="strct-menu__icon" [name]="item.icon" [size]="14" [strokeWidth]="1.3" />
1915
+ } @else {
1916
+ <span class="strct-menu__icon-spacer" aria-hidden="true"></span>
1917
+ }
1918
+ <span class="strct-menu__label">{{ item.label }}</span>
1919
+ @if (item.children?.length) {
1920
+ <strct-icon class="strct-menu__arrow" name="chevronRight" [size]="12" [strokeWidth]="1.6" />
1921
+ }
1922
+ </button>
1923
+ @if (openSubIndex() === i && item.children?.length) {
1924
+ <strct-menu-panel
1925
+ submenu
1926
+ class="strct-menu__subpanel"
1927
+ [class.strct-menu__subpanel--flip]="flipLeft()"
1928
+ [items]="item.children!"
1929
+ [data]="data()"
1930
+ (select)="select.emit($event)"
1931
+ (close)="close.emit()"
1932
+ (back)="closeSub(); focusItem(i)"
1933
+ />
1934
+ }
1935
+ </div>
1936
+ }
1937
+ }
1938
+ </div>
1939
+ `, host: {
1940
+ class: 'strct-menu-host',
1941
+ '[style.position]': "submenu() ? null : 'fixed'",
1942
+ '[style.left.px]': 'submenu() ? null : posX()',
1943
+ '[style.top.px]': 'submenu() ? null : posY()',
1944
+ '[style.zIndex]': 'submenu() ? null : 1100',
1945
+ }, styles: [".strct-menu-host{display:block}.strct-menu{min-width:180px;padding:4px;background:var(--bg-1);border:1px solid var(--b2);border-radius:7px;box-shadow:var(--shh);animation:strct-menu-in .1s ease}.strct-menu:focus{outline:none}.strct-menu__wrap{position:relative}.strct-menu__item{display:flex;align-items:center;gap:8px;width:100%;padding:7px 8px 7px 10px;border:0;border-radius:5px;cursor:pointer;background:transparent;color:var(--t1);font-size:13px;font-family:var(--font);text-align:left}.strct-menu__item:hover:not(:disabled),.strct-menu__item--active:not(:disabled){background:var(--bg-3)}.strct-menu__item:focus-visible{outline:none;background:var(--bg-3)}.strct-menu__item--danger{color:var(--crt)}.strct-menu__item--danger:hover:not(:disabled),.strct-menu__item--danger.strct-menu__item--active:not(:disabled){background:var(--crt-bg)}.strct-menu__item:disabled{opacity:.45;cursor:not-allowed}.strct-menu__icon{color:var(--t2);flex-shrink:0}.strct-menu__item--danger .strct-menu__icon{color:var(--crt)}.strct-menu__icon-spacer{width:14px;flex-shrink:0}.strct-menu__label{flex:1;white-space:nowrap}.strct-menu__arrow{color:var(--t3);flex-shrink:0}.strct-menu__sep{height:1px;margin:4px 6px;background:var(--b1)}.strct-menu__subpanel{position:absolute;top:-5px;left:100%;margin-left:2px;z-index:1}.strct-menu__subpanel--flip{left:auto;right:100%;margin-left:0;margin-right:2px}@keyframes strct-menu-in{0%{opacity:0;transform:scale(.97)}}\n"] }]
1946
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: false }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: false }] }], submenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "submenu", required: false }] }], select: [{ type: i0.Output, args: ["select"] }], close: [{ type: i0.Output, args: ["close"] }], back: [{ type: i0.Output, args: ["back"] }] } });
1947
+ /**
1948
+ * Right-click (context) menu driven by a data array. Attach to any trigger; the
1949
+ * menu portals into `<body>` and runs each item's `action` on selection.
1950
+ * <div [strctContextMenu]="menuFor(host)" [strctContextMenuData]="host"
1951
+ * (menuSelect)="onPick($event)">…</div>
1952
+ */
1953
+ class StrctContextMenuTrigger {
1954
+ appRef = inject(ApplicationRef);
1955
+ envInjector = inject(EnvironmentInjector);
1956
+ zone = inject(NgZone);
1957
+ doc = inject(DOCUMENT$1);
1958
+ items = input.required({ ...(ngDevMode ? { debugName: "items" } : /* istanbul ignore next */ {}), alias: 'strctContextMenu' });
1959
+ data = input(undefined, { ...(ngDevMode ? { debugName: "data" } : /* istanbul ignore next */ {}), alias: 'strctContextMenuData' });
1960
+ menuSelect = output();
1961
+ ref = null;
1962
+ onClose = () => this.zone.run(() => this.closeMenu());
1963
+ onContextMenu(event) {
1964
+ if (!this.items()?.length)
1965
+ return;
1966
+ event.preventDefault();
1967
+ this.openAt(event.clientX, event.clientY);
1968
+ }
1969
+ openAt(x, y) {
1970
+ this.closeMenu();
1971
+ const ref = createComponent(StrctMenuPanel, { environmentInjector: this.envInjector });
1972
+ ref.setInput('items', this.items());
1973
+ ref.setInput('data', this.data());
1974
+ ref.setInput('x', x);
1975
+ ref.setInput('y', y);
1976
+ ref.instance.select.subscribe((item) => {
1977
+ item.action?.(this.data());
1978
+ this.menuSelect.emit(item);
1979
+ this.closeMenu();
1980
+ });
1981
+ ref.instance.close.subscribe(() => this.closeMenu());
1982
+ this.appRef.attachView(ref.hostView);
1983
+ this.doc.body.appendChild(ref.location.nativeElement);
1984
+ this.ref = ref;
1985
+ // Defer global listeners so the opening right-click doesn't immediately close.
1986
+ setTimeout(() => {
1987
+ this.zone.runOutsideAngular(() => {
1988
+ this.doc.addEventListener('mousedown', this.onOutside, true);
1989
+ window.addEventListener('scroll', this.onClose, true);
1990
+ window.addEventListener('resize', this.onClose);
1991
+ });
1992
+ });
1993
+ }
1994
+ onOutside = (event) => {
1995
+ if (this.ref && !this.ref.location.nativeElement.contains(event.target)) {
1996
+ this.onClose();
1997
+ }
1998
+ };
1999
+ closeMenu() {
2000
+ if (!this.ref)
2001
+ return;
2002
+ this.doc.removeEventListener('mousedown', this.onOutside, true);
2003
+ window.removeEventListener('scroll', this.onClose, true);
2004
+ window.removeEventListener('resize', this.onClose);
2005
+ this.appRef.detachView(this.ref.hostView);
2006
+ this.ref.destroy();
2007
+ this.ref = null;
2008
+ }
2009
+ ngOnDestroy() {
2010
+ this.closeMenu();
2011
+ }
2012
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctContextMenuTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2013
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.16", type: StrctContextMenuTrigger, isStandalone: true, selector: "[strctContextMenu]", inputs: { items: { classPropertyName: "items", publicName: "strctContextMenu", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "strctContextMenuData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuSelect: "menuSelect" }, host: { listeners: { "contextmenu": "onContextMenu($event)" } }, ngImport: i0 });
2014
+ }
2015
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctContextMenuTrigger, decorators: [{
2016
+ type: Directive,
2017
+ args: [{ selector: '[strctContextMenu]' }]
2018
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "strctContextMenu", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "strctContextMenuData", required: false }] }], menuSelect: [{ type: i0.Output, args: ["menuSelect"] }], onContextMenu: [{
2019
+ type: HostListener,
2020
+ args: ['contextmenu', ['$event']]
2021
+ }] } });
2022
+
1609
2023
  /** A single wizard step. `label` names it in the step header. */
1610
2024
  class StrctStep {
1611
2025
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2026
+ /** When false, the wizard's Next / Finish is disabled on this step. */
2027
+ canAdvance = input(true, { ...(ngDevMode ? { debugName: "canAdvance" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1612
2028
  _active = signal(false, ...(ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []));
1613
2029
  active = this._active.asReadonly();
1614
2030
  /** @internal */
@@ -1616,7 +2032,7 @@ class StrctStep {
1616
2032
  this._active.set(value);
1617
2033
  }
1618
2034
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctStep, deps: [], target: i0.ɵɵFactoryTarget.Component });
1619
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctStep, isStandalone: true, selector: "strct-step", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "hidden": "!active()" }, classAttribute: "strct-step" }, ngImport: i0, template: `@if (active()) {
2035
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctStep, isStandalone: true, selector: "strct-step", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, canAdvance: { classPropertyName: "canAdvance", publicName: "canAdvance", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!active()" }, classAttribute: "strct-step" }, ngImport: i0, template: `@if (active()) {
1620
2036
  <ng-content />
1621
2037
  }`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1622
2038
  }
@@ -1630,13 +2046,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1630
2046
  }`,
1631
2047
  host: { class: 'strct-step', '[hidden]': '!active()' },
1632
2048
  }]
1633
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
2049
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], canAdvance: [{ type: i0.Input, args: [{ isSignal: true, alias: "canAdvance", required: false }] }] } });
1634
2050
  /** Multi-step flow with a numbered header and Back / Next / Finish controls. */
1635
2051
  class StrctWizard {
1636
2052
  steps = contentChildren(StrctStep, ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
1637
2053
  current = signal(0, ...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
2054
+ /** Label for the final-step button (default "Finish"). */
2055
+ finishLabel = input('Finish', ...(ngDevMode ? [{ debugName: "finishLabel" }] : /* istanbul ignore next */ []));
2056
+ /** Disable Finish and show a busy label while an async submit is in flight. */
2057
+ submitting = input(false, { ...(ngDevMode ? { debugName: "submitting" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2058
+ /** Show a Cancel button on the left. */
2059
+ cancelable = input(false, { ...(ngDevMode ? { debugName: "cancelable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1638
2060
  finished = output();
2061
+ cancelled = output();
2062
+ /** Emits the new step index after a Back / Next move. */
2063
+ stepChange = output();
1639
2064
  isLast = computed(() => this.current() >= this.steps().length - 1, ...(ngDevMode ? [{ debugName: "isLast" }] : /* istanbul ignore next */ []));
2065
+ /** Whether the current step permits advancing (its `canAdvance`). */
2066
+ canAdvance = computed(() => this.steps()[this.current()]?.canAdvance() ?? true, ...(ngDevMode ? [{ debugName: "canAdvance" }] : /* istanbul ignore next */ []));
1640
2067
  constructor() {
1641
2068
  effect(() => {
1642
2069
  const idx = this.current();
@@ -1644,18 +2071,23 @@ class StrctWizard {
1644
2071
  });
1645
2072
  }
1646
2073
  next() {
1647
- if (!this.isLast())
2074
+ if (!this.isLast() && this.canAdvance()) {
1648
2075
  this.current.update((i) => i + 1);
2076
+ this.stepChange.emit(this.current());
2077
+ }
1649
2078
  }
1650
2079
  back() {
1651
- if (this.current() > 0)
2080
+ if (this.current() > 0) {
1652
2081
  this.current.update((i) => i - 1);
2082
+ this.stepChange.emit(this.current());
2083
+ }
1653
2084
  }
1654
2085
  finish() {
1655
- this.finished.emit();
2086
+ if (this.canAdvance() && !this.submitting())
2087
+ this.finished.emit();
1656
2088
  }
1657
2089
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctWizard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1658
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctWizard, isStandalone: true, selector: "strct-wizard", outputs: { finished: "finished" }, host: { classAttribute: "strct-wiz" }, queries: [{ propertyName: "steps", predicate: StrctStep, isSignal: true }], ngImport: i0, template: `
2090
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctWizard, isStandalone: true, selector: "strct-wizard", inputs: { finishLabel: { classPropertyName: "finishLabel", publicName: "finishLabel", isSignal: true, isRequired: false, transformFunction: null }, submitting: { classPropertyName: "submitting", publicName: "submitting", isSignal: true, isRequired: false, transformFunction: null }, cancelable: { classPropertyName: "cancelable", publicName: "cancelable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { finished: "finished", cancelled: "cancelled", stepChange: "stepChange" }, host: { classAttribute: "strct-wiz" }, queries: [{ propertyName: "steps", predicate: StrctStep, isSignal: true }], ngImport: i0, template: `
1659
2091
  <div class="strct-wiz__steps">
1660
2092
  @for (step of steps(); track step; let i = $index; let last = $last) {
1661
2093
  <div
@@ -1675,14 +2107,26 @@ class StrctWizard {
1675
2107
  <div class="strct-wiz__content"><ng-content /></div>
1676
2108
 
1677
2109
  <div class="strct-wiz__foot">
2110
+ @if (cancelable()) {
2111
+ <button strct-button variant="flat" class="strct-wiz__cancel" (click)="cancelled.emit()">
2112
+ Cancel
2113
+ </button>
2114
+ }
1678
2115
  <button strct-button variant="flat" [disabled]="current() === 0" (click)="back()">Back</button>
1679
2116
  @if (isLast()) {
1680
- <button strct-button variant="primary" (click)="finish()">Finish</button>
2117
+ <button
2118
+ strct-button
2119
+ variant="primary"
2120
+ [disabled]="submitting() || !canAdvance()"
2121
+ (click)="finish()"
2122
+ >
2123
+ {{ submitting() ? 'Submitting…' : finishLabel() }}
2124
+ </button>
1681
2125
  } @else {
1682
- <button strct-button variant="primary" (click)="next()">Next</button>
2126
+ <button strct-button variant="primary" [disabled]="!canAdvance()" (click)="next()">Next</button>
1683
2127
  }
1684
2128
  </div>
1685
- `, isInline: true, styles: [".strct-wiz{display:block}.strct-wiz__steps{display:flex;align-items:center;gap:6px}.strct-wiz__step{display:flex;align-items:center;gap:8px}.strct-wiz__dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:600;color:var(--t2);background:var(--bg-3);border:1px solid var(--b2)}.strct-wiz__label{font-size:12px;color:var(--t2)}.strct-wiz__step--active .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc)}.strct-wiz__step--active .strct-wiz__label{color:var(--t1);font-weight:600}.strct-wiz__step--done .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc30)}.strct-wiz__sep{flex:1;height:1px;background:var(--b2);min-width:18px}.strct-wiz__content{margin:18px 0;padding:16px;min-height:80px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;color:var(--t2);font-size:13px}.strct-wiz__foot{display:flex;justify-content:flex-end;gap:8px}\n"], dependencies: [{ kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2129
+ `, isInline: true, styles: [".strct-wiz{display:block}.strct-wiz__steps{display:flex;align-items:center;gap:6px}.strct-wiz__step{display:flex;align-items:center;gap:8px}.strct-wiz__dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:600;color:var(--t2);background:var(--bg-3);border:1px solid var(--b2)}.strct-wiz__label{font-size:12px;color:var(--t2)}.strct-wiz__step--active .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc)}.strct-wiz__step--active .strct-wiz__label{color:var(--t1);font-weight:600}.strct-wiz__step--done .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc30)}.strct-wiz__sep{flex:1;height:1px;background:var(--b2);min-width:18px}.strct-wiz__content{margin:18px 0;padding:16px;min-height:80px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;color:var(--t2);font-size:13px}.strct-wiz__foot{display:flex;justify-content:flex-end;gap:8px}.strct-wiz__cancel{margin-right:auto}\n"], dependencies: [{ kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1686
2130
  }
1687
2131
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctWizard, decorators: [{
1688
2132
  type: Component,
@@ -1706,15 +2150,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1706
2150
  <div class="strct-wiz__content"><ng-content /></div>
1707
2151
 
1708
2152
  <div class="strct-wiz__foot">
2153
+ @if (cancelable()) {
2154
+ <button strct-button variant="flat" class="strct-wiz__cancel" (click)="cancelled.emit()">
2155
+ Cancel
2156
+ </button>
2157
+ }
1709
2158
  <button strct-button variant="flat" [disabled]="current() === 0" (click)="back()">Back</button>
1710
2159
  @if (isLast()) {
1711
- <button strct-button variant="primary" (click)="finish()">Finish</button>
2160
+ <button
2161
+ strct-button
2162
+ variant="primary"
2163
+ [disabled]="submitting() || !canAdvance()"
2164
+ (click)="finish()"
2165
+ >
2166
+ {{ submitting() ? 'Submitting…' : finishLabel() }}
2167
+ </button>
1712
2168
  } @else {
1713
- <button strct-button variant="primary" (click)="next()">Next</button>
2169
+ <button strct-button variant="primary" [disabled]="!canAdvance()" (click)="next()">Next</button>
1714
2170
  }
1715
2171
  </div>
1716
- `, host: { class: 'strct-wiz' }, styles: [".strct-wiz{display:block}.strct-wiz__steps{display:flex;align-items:center;gap:6px}.strct-wiz__step{display:flex;align-items:center;gap:8px}.strct-wiz__dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:600;color:var(--t2);background:var(--bg-3);border:1px solid var(--b2)}.strct-wiz__label{font-size:12px;color:var(--t2)}.strct-wiz__step--active .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc)}.strct-wiz__step--active .strct-wiz__label{color:var(--t1);font-weight:600}.strct-wiz__step--done .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc30)}.strct-wiz__sep{flex:1;height:1px;background:var(--b2);min-width:18px}.strct-wiz__content{margin:18px 0;padding:16px;min-height:80px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;color:var(--t2);font-size:13px}.strct-wiz__foot{display:flex;justify-content:flex-end;gap:8px}\n"] }]
1717
- }], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctStep), { isSignal: true }] }], finished: [{ type: i0.Output, args: ["finished"] }] } });
2172
+ `, host: { class: 'strct-wiz' }, styles: [".strct-wiz{display:block}.strct-wiz__steps{display:flex;align-items:center;gap:6px}.strct-wiz__step{display:flex;align-items:center;gap:8px}.strct-wiz__dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;font-size:11px;font-weight:600;color:var(--t2);background:var(--bg-3);border:1px solid var(--b2)}.strct-wiz__label{font-size:12px;color:var(--t2)}.strct-wiz__step--active .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc)}.strct-wiz__step--active .strct-wiz__label{color:var(--t1);font-weight:600}.strct-wiz__step--done .strct-wiz__dot{background:var(--acc-m);color:var(--acc);border-color:var(--acc30)}.strct-wiz__sep{flex:1;height:1px;background:var(--b2);min-width:18px}.strct-wiz__content{margin:18px 0;padding:16px;min-height:80px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;color:var(--t2);font-size:13px}.strct-wiz__foot{display:flex;justify-content:flex-end;gap:8px}.strct-wiz__cancel{margin-right:auto}\n"] }]
2173
+ }], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctStep), { isSignal: true }] }], finishLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLabel", required: false }] }], submitting: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitting", required: false }] }], cancelable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelable", required: false }] }], finished: [{ type: i0.Output, args: ["finished"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }] } });
1718
2174
 
1719
2175
  /**
1720
2176
  * Separator rule. Horizontal by default; pass `vertical` for an inline rule.
@@ -4014,7 +4470,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4014
4470
  }] } });
4015
4471
 
4016
4472
  /**
4017
- * Declarative data table.
4473
+ * Per-column cell template for `strct-table` / `strct-datagrid`. The column key
4474
+ * is the directive value; the row, value and column are the template context:
4475
+ *
4476
+ * <ng-template strctCell="status" let-row let-value="value">
4477
+ * <strct-badge [status]="row['ok'] ? 'success' : 'danger'">{{ value }}</strct-badge>
4478
+ * </ng-template>
4479
+ */
4480
+ class StrctCellDef {
4481
+ key = input.required({ ...(ngDevMode ? { debugName: "key" } : /* istanbul ignore next */ {}), alias: 'strctCell' });
4482
+ template = inject(TemplateRef);
4483
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCellDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4484
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.16", type: StrctCellDef, isStandalone: true, selector: "[strctCell]", inputs: { key: { classPropertyName: "key", publicName: "strctCell", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
4485
+ }
4486
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCellDef, decorators: [{
4487
+ type: Directive,
4488
+ args: [{ selector: '[strctCell]' }]
4489
+ }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "strctCell", required: true }] }] } });
4490
+ /**
4491
+ * Declarative data table. Cells render `row[col.key]` as text by default; supply
4492
+ * a `*strctCell` template per column for custom content.
4018
4493
  * <strct-table [columns]="cols" [rows]="data" hover />
4019
4494
  */
4020
4495
  class StrctTable {
@@ -4023,8 +4498,18 @@ class StrctTable {
4023
4498
  striped = input(false, { ...(ngDevMode ? { debugName: "striped" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4024
4499
  hover = input(false, { ...(ngDevMode ? { debugName: "hover" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4025
4500
  emptyText = input('No data', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
4501
+ cellDefs = contentChildren(StrctCellDef, ...(ngDevMode ? [{ debugName: "cellDefs" }] : /* istanbul ignore next */ []));
4502
+ cellMap = computed(() => {
4503
+ const m = new Map();
4504
+ for (const d of this.cellDefs())
4505
+ m.set(d.key(), d.template);
4506
+ return m;
4507
+ }, ...(ngDevMode ? [{ debugName: "cellMap" }] : /* istanbul ignore next */ []));
4508
+ cellTemplate(key) {
4509
+ return this.cellMap().get(key) ?? null;
4510
+ }
4026
4511
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
4027
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTable, isStandalone: true, selector: "strct-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, hover: { classPropertyName: "hover", publicName: "hover", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.strct-table-host--striped": "striped()", "class.strct-table-host--hover": "hover()" }, classAttribute: "strct-table-host" }, ngImport: i0, template: `
4512
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTable, isStandalone: true, selector: "strct-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, hover: { classPropertyName: "hover", publicName: "hover", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.strct-table-host--striped": "striped()", "class.strct-table-host--hover": "hover()" }, classAttribute: "strct-table-host" }, queries: [{ propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
4028
4513
  <table class="strct-table">
4029
4514
  <thead>
4030
4515
  <tr>
@@ -4037,7 +4522,16 @@ class StrctTable {
4037
4522
  @for (row of rows(); track $index) {
4038
4523
  <tr>
4039
4524
  @for (col of columns(); track col.key) {
4040
- <td [style.text-align]="col.align ?? 'start'">{{ row[col.key] }}</td>
4525
+ <td [style.text-align]="col.align ?? 'start'">
4526
+ @if (cellTemplate(col.key); as tpl) {
4527
+ <ng-container
4528
+ [ngTemplateOutlet]="tpl"
4529
+ [ngTemplateOutletContext]="{ $implicit: row, value: row[col.key], column: col }"
4530
+ />
4531
+ } @else {
4532
+ {{ row[col.key] }}
4533
+ }
4534
+ </td>
4041
4535
  }
4042
4536
  </tr>
4043
4537
  } @empty {
@@ -4047,11 +4541,11 @@ class StrctTable {
4047
4541
  }
4048
4542
  </tbody>
4049
4543
  </table>
4050
- `, isInline: true, styles: [".strct-table-host{display:block;overflow-x:auto}.strct-table{width:100%;border-collapse:collapse;font-size:13px;border:1px solid var(--b2);border-radius:8px;overflow:hidden}.strct-table th,.strct-table td{padding:9px 13px;text-align:left;border-bottom:1px solid var(--b1)}.strct-table th{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2)}.strct-table td{color:var(--t1)}.strct-table tbody tr:last-child td{border-bottom:0}.strct-table-host--striped tbody tr:nth-child(2n) td{background:var(--bg-2)}.strct-table-host--hover tbody tr:hover td{background:var(--acc-s)}.strct-table__empty{text-align:center;color:var(--t3);padding:22px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4544
+ `, isInline: true, styles: [".strct-table-host{display:block;overflow-x:auto}.strct-table{width:100%;border-collapse:collapse;font-size:13px;border:1px solid var(--b2);border-radius:8px;overflow:hidden}.strct-table th,.strct-table td{padding:9px 13px;text-align:left;border-bottom:1px solid var(--b1)}.strct-table th{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2)}.strct-table td{color:var(--t1)}.strct-table tbody tr:last-child td{border-bottom:0}.strct-table-host--striped tbody tr:nth-child(2n) td{background:var(--bg-2)}.strct-table-host--hover tbody tr:hover td{background:var(--acc-s)}.strct-table__empty{text-align:center;color:var(--t3);padding:22px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4051
4545
  }
4052
4546
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTable, decorators: [{
4053
4547
  type: Component,
4054
- args: [{ selector: 'strct-table', changeDetection: ChangeDetectionStrategy.OnPush, template: `
4548
+ args: [{ selector: 'strct-table', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], template: `
4055
4549
  <table class="strct-table">
4056
4550
  <thead>
4057
4551
  <tr>
@@ -4064,7 +4558,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4064
4558
  @for (row of rows(); track $index) {
4065
4559
  <tr>
4066
4560
  @for (col of columns(); track col.key) {
4067
- <td [style.text-align]="col.align ?? 'start'">{{ row[col.key] }}</td>
4561
+ <td [style.text-align]="col.align ?? 'start'">
4562
+ @if (cellTemplate(col.key); as tpl) {
4563
+ <ng-container
4564
+ [ngTemplateOutlet]="tpl"
4565
+ [ngTemplateOutletContext]="{ $implicit: row, value: row[col.key], column: col }"
4566
+ />
4567
+ } @else {
4568
+ {{ row[col.key] }}
4569
+ }
4570
+ </td>
4068
4571
  }
4069
4572
  </tr>
4070
4573
  } @empty {
@@ -4079,7 +4582,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4079
4582
  '[class.strct-table-host--striped]': 'striped()',
4080
4583
  '[class.strct-table-host--hover]': 'hover()',
4081
4584
  }, styles: [".strct-table-host{display:block;overflow-x:auto}.strct-table{width:100%;border-collapse:collapse;font-size:13px;border:1px solid var(--b2);border-radius:8px;overflow:hidden}.strct-table th,.strct-table td{padding:9px 13px;text-align:left;border-bottom:1px solid var(--b1)}.strct-table th{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2)}.strct-table td{color:var(--t1)}.strct-table tbody tr:last-child td{border-bottom:0}.strct-table-host--striped tbody tr:nth-child(2n) td{background:var(--bg-2)}.strct-table-host--hover tbody tr:hover td{background:var(--acc-s)}.strct-table__empty{text-align:center;color:var(--t3);padding:22px}\n"] }]
4082
- }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], hover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hover", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }] } });
4585
+ }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], hover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hover", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
4083
4586
 
4084
4587
  /**
4085
4588
  * Marks the expandable-row detail template. The row is the template's implicit
@@ -4123,14 +4626,34 @@ class StrctDatagrid {
4123
4626
  detailPane = input(false, { ...(ngDevMode ? { debugName: "detailPane" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4124
4627
  compact = input(false, { ...(ngDevMode ? { debugName: "compact" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4125
4628
  emptyText = input('No data', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
4629
+ /**
4630
+ * Stable row identity (property key or function). Set this for live-refreshing
4631
+ * data so selection, expansion and the active detail row survive re-fetches
4632
+ * that replace the row objects. Defaults to object identity.
4633
+ */
4634
+ rowId = input(null, ...(ngDevMode ? [{ debugName: "rowId" }] : /* istanbul ignore next */ []));
4126
4635
  selectionChange = output();
4127
4636
  detailDef = contentChild(StrctRowDetailDef, ...(ngDevMode ? [{ debugName: "detailDef" }] : /* istanbul ignore next */ []));
4128
4637
  actionBarDef = contentChild(StrctDatagridActionBar, ...(ngDevMode ? [{ debugName: "actionBarDef" }] : /* istanbul ignore next */ []));
4638
+ cellDefs = contentChildren(StrctCellDef, ...(ngDevMode ? [{ debugName: "cellDefs" }] : /* istanbul ignore next */ []));
4639
+ cellMap = computed(() => {
4640
+ const m = new Map();
4641
+ for (const d of this.cellDefs())
4642
+ m.set(d.key(), d.template);
4643
+ return m;
4644
+ }, ...(ngDevMode ? [{ debugName: "cellMap" }] : /* istanbul ignore next */ []));
4129
4645
  page = signal(1, ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
4130
4646
  sort = signal({ key: null, dir: 'asc' }, ...(ngDevMode ? [{ debugName: "sort" }] : /* istanbul ignore next */ []));
4647
+ /** Selection / expansion are tracked by row id (see {@link rowId}). */
4131
4648
  selected = signal(new Set(), ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
4132
4649
  expandedRows = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRows" }] : /* istanbul ignore next */ []));
4133
- activeRow = signal(null, ...(ngDevMode ? [{ debugName: "activeRow" }] : /* istanbul ignore next */ []));
4650
+ activeId = signal(null, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
4651
+ activeRow = computed(() => {
4652
+ const id = this.activeId();
4653
+ if (id == null)
4654
+ return null;
4655
+ return this.rows().find((r) => this.idOf(r) === id) ?? null;
4656
+ }, ...(ngDevMode ? [{ debugName: "activeRow" }] : /* istanbul ignore next */ []));
4134
4657
  canExpand = computed(() => this.expandable() && !this.detailPane() && !!this.detailDef(), ...(ngDevMode ? [{ debugName: "canExpand" }] : /* istanbul ignore next */ []));
4135
4658
  canDetail = computed(() => this.detailPane() && !!this.detailDef(), ...(ngDevMode ? [{ debugName: "canDetail" }] : /* istanbul ignore next */ []));
4136
4659
  paneOpen = computed(() => this.detailPane() && !!this.detailDef() && !!this.activeRow(), ...(ngDevMode ? [{ debugName: "paneOpen" }] : /* istanbul ignore next */ []));
@@ -4154,9 +4677,22 @@ class StrctDatagrid {
4154
4677
  selectedCount = computed(() => this.selected().size, ...(ngDevMode ? [{ debugName: "selectedCount" }] : /* istanbul ignore next */ []));
4155
4678
  allPageSelected = computed(() => {
4156
4679
  const rows = this.paged();
4157
- return rows.length > 0 && rows.every((r) => this.selected().has(r));
4680
+ return rows.length > 0 && rows.every((r) => this.selected().has(this.idOf(r)));
4158
4681
  }, ...(ngDevMode ? [{ debugName: "allPageSelected" }] : /* istanbul ignore next */ []));
4159
- somePageSelected = computed(() => !this.allPageSelected() && this.paged().some((r) => this.selected().has(r)), ...(ngDevMode ? [{ debugName: "somePageSelected" }] : /* istanbul ignore next */ []));
4682
+ somePageSelected = computed(() => !this.allPageSelected() && this.paged().some((r) => this.selected().has(this.idOf(r))), ...(ngDevMode ? [{ debugName: "somePageSelected" }] : /* istanbul ignore next */ []));
4683
+ /** Resolve a row's stable identity (defaults to the row object itself). */
4684
+ idOf(row) {
4685
+ const id = this.rowId();
4686
+ if (id == null)
4687
+ return row;
4688
+ return typeof id === 'function' ? id(row) : row[id];
4689
+ }
4690
+ rowKey(row) {
4691
+ return this.idOf(row);
4692
+ }
4693
+ cellTemplate(key) {
4694
+ return this.cellMap().get(key) ?? null;
4695
+ }
4160
4696
  constructor() {
4161
4697
  // Keep the page in range when the data set shrinks.
4162
4698
  effect(() => {
@@ -4177,10 +4713,11 @@ class StrctDatagrid {
4177
4713
  /** Toggle the detail pane for a row (triggered by its » button, not the row,
4178
4714
  * so row cells remain selectable for copy). */
4179
4715
  openDetail(row) {
4180
- this.activeRow.set(this.activeRow() === row ? null : row);
4716
+ const id = this.idOf(row);
4717
+ this.activeId.set(this.activeId() === id ? null : id);
4181
4718
  }
4182
4719
  closePane() {
4183
- this.activeRow.set(null);
4720
+ this.activeId.set(null);
4184
4721
  }
4185
4722
  sortBy(key) {
4186
4723
  const current = this.sort();
@@ -4211,38 +4748,42 @@ class StrctDatagrid {
4211
4748
  this.sortBy(key);
4212
4749
  }
4213
4750
  isSelected(row) {
4214
- return this.selected().has(row);
4751
+ return this.selected().has(this.idOf(row));
4215
4752
  }
4216
4753
  isExpanded(row) {
4217
- return this.expandedRows().has(row);
4754
+ return this.expandedRows().has(this.idOf(row));
4218
4755
  }
4219
4756
  toggleExpand(row) {
4757
+ const id = this.idOf(row);
4220
4758
  const next = new Set(this.expandedRows());
4221
- next.has(row) ? next.delete(row) : next.add(row);
4759
+ next.has(id) ? next.delete(id) : next.add(id);
4222
4760
  this.expandedRows.set(next);
4223
4761
  }
4224
4762
  toggleRow(row) {
4763
+ const id = this.idOf(row);
4225
4764
  const next = new Set(this.selected());
4226
- next.has(row) ? next.delete(row) : next.add(row);
4765
+ next.has(id) ? next.delete(id) : next.add(id);
4227
4766
  this.commitSelection(next);
4228
4767
  }
4229
4768
  toggleAll() {
4230
4769
  const next = new Set(this.selected());
4231
4770
  const rows = this.paged();
4232
4771
  if (this.allPageSelected()) {
4233
- rows.forEach((r) => next.delete(r));
4772
+ rows.forEach((r) => next.delete(this.idOf(r)));
4234
4773
  }
4235
4774
  else {
4236
- rows.forEach((r) => next.add(r));
4775
+ rows.forEach((r) => next.add(this.idOf(r)));
4237
4776
  }
4238
4777
  this.commitSelection(next);
4239
4778
  }
4240
4779
  clearSelection() {
4241
4780
  this.commitSelection(new Set());
4242
4781
  }
4782
+ /** Emit the current row objects whose id is selected (resolved against the
4783
+ * latest data, so consumers always get fresh references). */
4243
4784
  commitSelection(next) {
4244
4785
  this.selected.set(next);
4245
- this.selectionChange.emit([...next]);
4786
+ this.selectionChange.emit(this.rows().filter((r) => next.has(this.idOf(r))));
4246
4787
  }
4247
4788
  compare(a, b) {
4248
4789
  if (typeof a === 'number' && typeof b === 'number')
@@ -4250,7 +4791,7 @@ class StrctDatagrid {
4250
4791
  return String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true });
4251
4792
  }
4252
4793
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDatagrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
4253
- 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 }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { properties: { "class.strct-dg-host--compact": "compact()" }, 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 }], ngImport: i0, template: `
4794
+ 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 }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, host: { properties: { "class.strct-dg-host--compact": "compact()" }, 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: `
4254
4795
  @if (actionBarDef()) {
4255
4796
  <div class="strct-dg__toolbar"><ng-content select="[strctDatagridActionBar]" /></div>
4256
4797
  }
@@ -4306,7 +4847,7 @@ class StrctDatagrid {
4306
4847
  </tr>
4307
4848
  </thead>
4308
4849
  <tbody>
4309
- @for (row of paged(); track $index) {
4850
+ @for (row of paged(); track rowKey(row)) {
4310
4851
  <tr
4311
4852
  [class.strct-dg__row--selected]="isSelected(row)"
4312
4853
  [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
@@ -4350,7 +4891,16 @@ class StrctDatagrid {
4350
4891
  </td>
4351
4892
  }
4352
4893
  @for (col of visibleColumns(); track col.key) {
4353
- <td [style.text-align]="col.align ?? 'start'">{{ row[col.key] }}</td>
4894
+ <td [style.text-align]="col.align ?? 'start'">
4895
+ @if (cellTemplate(col.key); as tpl) {
4896
+ <ng-container
4897
+ [ngTemplateOutlet]="tpl"
4898
+ [ngTemplateOutletContext]="{ $implicit: row, value: row[col.key], column: col }"
4899
+ />
4900
+ } @else {
4901
+ {{ row[col.key] }}
4902
+ }
4903
+ </td>
4354
4904
  }
4355
4905
  </tr>
4356
4906
  @if (canExpand() && isExpanded(row)) {
@@ -4462,7 +5012,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4462
5012
  </tr>
4463
5013
  </thead>
4464
5014
  <tbody>
4465
- @for (row of paged(); track $index) {
5015
+ @for (row of paged(); track rowKey(row)) {
4466
5016
  <tr
4467
5017
  [class.strct-dg__row--selected]="isSelected(row)"
4468
5018
  [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
@@ -4506,7 +5056,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4506
5056
  </td>
4507
5057
  }
4508
5058
  @for (col of visibleColumns(); track col.key) {
4509
- <td [style.text-align]="col.align ?? 'start'">{{ row[col.key] }}</td>
5059
+ <td [style.text-align]="col.align ?? 'start'">
5060
+ @if (cellTemplate(col.key); as tpl) {
5061
+ <ng-container
5062
+ [ngTemplateOutlet]="tpl"
5063
+ [ngTemplateOutletContext]="{ $implicit: row, value: row[col.key], column: col }"
5064
+ />
5065
+ } @else {
5066
+ {{ row[col.key] }}
5067
+ }
5068
+ </td>
4510
5069
  }
4511
5070
  </tr>
4512
5071
  @if (canExpand() && isExpanded(row)) {
@@ -4562,7 +5121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
4562
5121
  class: 'strct-dg-host',
4563
5122
  '[class.strct-dg-host--compact]': 'compact()',
4564
5123
  }, styles: [".strct-dg-host{display:block}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;border:1px solid var(--b2);border-radius:8px;overflow:hidden}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:left;border-bottom:1px solid var(--b1)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg th{font-size:11px;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 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__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;gap:14px;align-items:flex-start}.strct-dg__layout--paned .strct-dg{width:auto;min-width:180px;max-width:260px;flex-shrink: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-right: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-right: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-left:2px solid var(--acc);border-radius:8px;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)}}.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:8px 10px;margin-bottom:10px;background:var(--bg-2);border:1px solid var(--b2);border-radius:8px}.strct-dg__actionbar{display:flex;align-items:center;gap:14px;padding:8px 12px;margin-bottom:10px;background:var(--acc-m);border:1px solid var(--acc30);border-radius:7px;font-size:13px;animation:strct-dg-bar-in .12s ease}.strct-dg__actionbar-count{color:var(--acc);font-weight:600}.strct-dg__actionbar-clear{border:0;background:transparent;color:var(--t2);cursor:pointer;font-size:12px;padding:2px 4px}.strct-dg__actionbar-clear:hover{color:var(--t1)}.strct-dg__actionbar-actions{display:flex;gap:8px;margin-left:auto}@keyframes strct-dg-bar-in{0%{opacity:0;transform:translateY(-4px)}}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:12px;flex-wrap:wrap}.strct-dg__count{font-size:12px;color:var(--t2)}\n"] }]
4565
- }], 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 }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }] } });
5124
+ }], 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 }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], 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 }] }] } });
4566
5125
 
4567
5126
  /** Vertical timeline container. Wraps `<strct-timeline-item>` children. */
4568
5127
  class StrctTimeline {
@@ -5371,5 +5930,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
5371
5930
  * Generated bundle index. Do not edit.
5372
5931
  */
5373
5932
 
5374
- export { STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_MASKS, STRCT_PALETTES, STRCT_RAW_ICONS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCascadeHost, StrctCascadeNode, StrctCascadeSelect, StrctChart, StrctCheckbox, StrctChips, StrctColorPicker, StrctCombobox, StrctContextMenu, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDivider, StrctDonut, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctFile, StrctFooter, StrctGauge, StrctHeader, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKnob, StrctLogin, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRange, StrctRating, StrctRowDetailDef, StrctShell, StrctSignpost, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctStack, StrctStackItem, StrctStep, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctThemeService, StrctThemeSwitcher, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctTooltip, StrctTree, StrctTreeNode, StrctVerticalNav, StrctWizard, registerStrctIcon };
5933
+ export { STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_MASKS, STRCT_PALETTES, STRCT_RAW_ICONS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCascadeHost, StrctCascadeNode, StrctCascadeSelect, StrctCellDef, StrctChart, StrctCheckbox, StrctChips, StrctColorPicker, StrctCombobox, StrctContextMenu, StrctContextMenuTrigger, StrctDatagrid, StrctDatagridActionBar, StrctDatepicker, StrctDivider, StrctDonut, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctFile, StrctFooter, StrctGauge, StrctHeader, StrctIcon, StrctInput, StrctInputMask, StrctInputOtp, StrctKnob, StrctLogin, StrctMenuPanel, StrctModal, StrctNav, StrctNavItem, StrctOverlay, StrctPagination, StrctPassword, StrctProgress, StrctRadio, StrctRadioGroup, StrctRange, StrctRating, StrctRowDetailDef, StrctShell, StrctSignpost, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctStack, StrctStackItem, StrctStep, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctThemeService, StrctThemeSwitcher, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctTooltip, StrctTree, StrctTreeNode, StrctVerticalNav, StrctWizard, registerStrctIcon };
5375
5934
  //# sourceMappingURL=akcelik-strct.mjs.map