@akcelik/strct 0.1.1 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akcelik/strct",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "UIStruct — a standalone Angular component library with a tokenised, multi-palette theme system, built for datacenter / infrastructure management UIs.",
5
5
  "author": "Serkan Akcelik <serkan.akcelik@gmail.com>",
6
6
  "license": "MIT",
@@ -0,0 +1,49 @@
1
+ // Self-hosted fonts bundled with the library so it renders in its intended
2
+ // typography without any external request. Both are OFL-licensed:
3
+ // DM Sans — https://fonts.google.com/specimen/DM+Sans (OFL)
4
+ // JetBrains Mono — https://www.jetbrains.com/lp/mono/ (OFL)
5
+ // Imported by theme.scss; the woff2 files ship under `styles/fonts/`.
6
+
7
+ @font-face {
8
+ font-family: 'DM Sans';
9
+ font-style: normal;
10
+ font-weight: 400;
11
+ font-display: swap;
12
+ src: url('./fonts/dm-sans-400.woff2') format('woff2');
13
+ }
14
+ @font-face {
15
+ font-family: 'DM Sans';
16
+ font-style: normal;
17
+ font-weight: 500;
18
+ font-display: swap;
19
+ src: url('./fonts/dm-sans-500.woff2') format('woff2');
20
+ }
21
+ @font-face {
22
+ font-family: 'DM Sans';
23
+ font-style: normal;
24
+ font-weight: 600;
25
+ font-display: swap;
26
+ src: url('./fonts/dm-sans-600.woff2') format('woff2');
27
+ }
28
+ @font-face {
29
+ font-family: 'DM Sans';
30
+ font-style: normal;
31
+ font-weight: 700;
32
+ font-display: swap;
33
+ src: url('./fonts/dm-sans-700.woff2') format('woff2');
34
+ }
35
+
36
+ @font-face {
37
+ font-family: 'JetBrains Mono';
38
+ font-style: normal;
39
+ font-weight: 400;
40
+ font-display: swap;
41
+ src: url('./fonts/jetbrains-mono-400.woff2') format('woff2');
42
+ }
43
+ @font-face {
44
+ font-family: 'JetBrains Mono';
45
+ font-style: normal;
46
+ font-weight: 500;
47
+ font-display: swap;
48
+ src: url('./fonts/jetbrains-mono-500.woff2') format('woff2');
49
+ }
Binary file
Binary file
Binary file
Binary file
package/styles/theme.scss CHANGED
@@ -5,6 +5,7 @@
5
5
  // `data-theme` ('dark' | 'light') on the <html> element (StrctThemeService
6
6
  // does this for you).
7
7
 
8
+ @use 'fonts';
8
9
  @use 'tokens';
9
10
  @use 'base';
10
11
  @use 'forms';
@@ -366,28 +366,69 @@ declare class StrctTabs {
366
366
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTabs, "strct-tabs", never, {}, {}, ["tabs"], ["*"], true, never>;
367
367
  }
368
368
 
369
- /** Root container for a tree of `<strct-tree-node>` items. */
370
- declare class StrctTree {
371
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctTree, never>;
372
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTree, "strct-tree", never, {}, {}, never, ["*"], true, never>;
369
+ /** A node in the data-driven tree (`<strct-tree [nodes]="...">`). */
370
+ interface StrctTreeNodeData {
371
+ label: string;
372
+ /** Optional leading icon name. */
373
+ icon?: string;
374
+ /** Status dot on the icon (running / maintenance / off …). */
375
+ badge?: StrctIconBadge;
376
+ /** Highlight as the selected node. */
377
+ active?: boolean;
378
+ /** Initial expanded state. */
379
+ expanded?: boolean;
380
+ children?: StrctTreeNodeData[];
381
+ /** Arbitrary payload returned with (nodeActivated). */
382
+ data?: unknown;
373
383
  }
374
384
  /**
375
- * Tree node. Nest `<strct-tree-node>` children to build hierarchy:
376
- * <strct-tree-node label="Group" [(expanded)]="open">
377
- * <strct-tree-node label="Leaf" icon="grid" [active]="true" />
385
+ * Tree node. Two modes:
386
+ * - **Content:** nest `<strct-tree-node>` children manually.
387
+ * - **Data:** pass a `[node]` object that recurses over its `children` —
388
+ * used internally by `<strct-tree [nodes]>`.
389
+ *
390
+ * <strct-tree-node label="Group" icon="layers" badge="ok" [(expanded)]="open">
391
+ * <strct-tree-node label="Leaf" icon="vm" [active]="true" />
378
392
  * </strct-tree-node>
379
393
  */
380
394
  declare class StrctTreeNode {
395
+ /** Data-driven node; when set, label/icon/children come from it. */
396
+ readonly node: _angular_core.InputSignal<StrctTreeNodeData | null>;
381
397
  readonly label: _angular_core.InputSignal<string>;
382
398
  readonly icon: _angular_core.InputSignal<string | undefined>;
399
+ readonly badge: _angular_core.InputSignal<StrctIconBadge>;
383
400
  readonly active: _angular_core.InputSignal<boolean>;
384
401
  readonly expanded: _angular_core.ModelSignal<boolean>;
402
+ /** Content-mode click. */
385
403
  readonly activated: _angular_core.OutputEmitterRef<void>;
404
+ /** Data-mode click — carries the activated node (bubbles to the tree). */
405
+ readonly nodeActivated: _angular_core.OutputEmitterRef<StrctTreeNodeData>;
386
406
  private readonly childNodes;
407
+ /** Data-mode expansion (seeded from node.expanded on first toggle). */
408
+ private readonly dataExpanded;
409
+ protected readonly displayLabel: _angular_core.Signal<string>;
410
+ protected readonly displayIcon: _angular_core.Signal<string | undefined>;
411
+ protected readonly displayBadge: _angular_core.Signal<StrctIconBadge>;
412
+ protected readonly displayActive: _angular_core.Signal<boolean>;
387
413
  protected readonly hasChildren: _angular_core.Signal<boolean>;
414
+ protected readonly isOpen: _angular_core.Signal<boolean>;
388
415
  toggle(): void;
416
+ protected onActivate(): void;
389
417
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctTreeNode, never>;
390
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTreeNode, "strct-tree-node", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; "activated": "activated"; }, ["childNodes"], ["[strctTreeTrailing]", "*"], true, never>;
418
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTreeNode, "strct-tree-node", never, { "node": { "alias": "node"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; "activated": "activated"; "nodeActivated": "nodeActivated"; }, ["childNodes"], ["[strctTreeTrailing]", "*"], true, never>;
419
+ }
420
+ /**
421
+ * Root container for a tree. Either project `<strct-tree-node>` children, or
422
+ * pass `[nodes]` for a fully data-driven, self-recursing tree:
423
+ * <strct-tree [nodes]="roots" (nodeActivated)="select($event)" />
424
+ */
425
+ declare class StrctTree {
426
+ /** Data-driven node list; when set, projected content is ignored. */
427
+ readonly nodes: _angular_core.InputSignal<StrctTreeNodeData[] | null>;
428
+ /** Emitted when any data-driven node is clicked. */
429
+ readonly nodeActivated: _angular_core.OutputEmitterRef<StrctTreeNodeData>;
430
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctTree, never>;
431
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTree, "strct-tree", never, { "nodes": { "alias": "nodes"; "required": false; "isSignal": true; }; }, { "nodeActivated": "nodeActivated"; }, never, ["*"], true, never>;
391
432
  }
392
433
 
393
434
  type StrctModalSize = 'sm' | 'md' | 'lg';
@@ -413,6 +454,8 @@ declare class StrctModal {
413
454
  protected readonly titleId: string;
414
455
  /** Element that had focus before the dialog opened, restored on close. */
415
456
  private previousActive;
457
+ /** Whether this instance currently holds a scroll lock. */
458
+ private locked;
416
459
  constructor();
417
460
  close(): void;
418
461
  protected onBackdrop(): void;
@@ -486,45 +529,139 @@ declare class StrctContextMenu {
486
529
  }
487
530
 
488
531
  /**
489
- * A nested fly-out inside a `strct-context-menu` or `strct-dropdown`. Reuse
490
- * `strct-dropdown-item` for the nested entries.
532
+ * A nested fly-out inside a `strct-context-menu` or `strct-dropdown`. Opens on
533
+ * hover, click/tap, or the keyboard (Enter / Space / →), and flips to the left
534
+ * near the right edge of the viewport. Reuse `strct-dropdown-item` for entries.
491
535
  * <strct-submenu label="Power">
492
536
  * <strct-dropdown-item>Power on</strct-dropdown-item>
493
537
  * <strct-dropdown-item>Power off</strct-dropdown-item>
494
538
  * </strct-submenu>
495
539
  */
496
540
  declare class StrctSubmenu {
541
+ private readonly host;
497
542
  readonly label: _angular_core.InputSignal<string>;
498
543
  /** Optional leading icon; when omitted the icon column is still reserved so
499
544
  * the label stays aligned with sibling items that do have icons. */
500
545
  readonly icon: _angular_core.InputSignal<string>;
501
546
  readonly open: _angular_core.WritableSignal<boolean>;
547
+ /** Open to the left when the fly-out would overflow the right edge. */
548
+ protected readonly flip: _angular_core.WritableSignal<boolean>;
549
+ protected setOpen(value: boolean): void;
502
550
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctSubmenu, never>;
503
551
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctSubmenu, "strct-submenu", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; }, {}, never, ["[strctSubmenuLabel]", "*"], true, never>;
504
552
  }
505
553
 
554
+ /** A single entry in a data-driven menu. */
555
+ interface StrctMenuItem {
556
+ label: string;
557
+ icon?: string;
558
+ /** Destructive styling. */
559
+ danger?: boolean;
560
+ disabled?: boolean;
561
+ /** Render a separator instead of an entry (label is ignored). */
562
+ divider?: boolean;
563
+ /** Nested submenu. */
564
+ children?: StrctMenuItem[];
565
+ /** Invoked on selection, with the trigger's `strctContextMenuData`. */
566
+ action?: (data?: unknown) => void;
567
+ /** Arbitrary payload. */
568
+ data?: unknown;
569
+ }
570
+ /**
571
+ * Floating menu panel — portaled into `<body>` (so it escapes overflow /
572
+ * transform clipping), positioned by its real measured size, with full keyboard
573
+ * navigation and recursive submenus. Usually created by `[strctContextMenu]`,
574
+ * but can be embedded directly with `submenu`.
575
+ */
576
+ declare class StrctMenuPanel {
577
+ private readonly host;
578
+ readonly items: _angular_core.InputSignal<StrctMenuItem[]>;
579
+ readonly data: _angular_core.InputSignal<unknown>;
580
+ readonly x: _angular_core.InputSignal<number>;
581
+ readonly y: _angular_core.InputSignal<number>;
582
+ readonly submenu: _angular_core.InputSignalWithTransform<boolean, unknown>;
583
+ readonly select: _angular_core.OutputEmitterRef<StrctMenuItem>;
584
+ readonly close: _angular_core.OutputEmitterRef<void>;
585
+ /** ArrowLeft inside a submenu — asks the parent to close it. */
586
+ readonly back: _angular_core.OutputEmitterRef<void>;
587
+ protected readonly posX: _angular_core.WritableSignal<number>;
588
+ protected readonly posY: _angular_core.WritableSignal<number>;
589
+ protected readonly flipLeft: _angular_core.WritableSignal<boolean>;
590
+ protected readonly activeIndex: _angular_core.WritableSignal<number>;
591
+ protected readonly openSubIndex: _angular_core.WritableSignal<number | null>;
592
+ private readonly navIndices;
593
+ constructor();
594
+ private clampToViewport;
595
+ protected focusItem(i: number): void;
596
+ private move;
597
+ protected onHover(i: number): void;
598
+ protected onLeave(i: number): void;
599
+ protected onItemClick(item: StrctMenuItem, i: number, event: Event): void;
600
+ protected closeSub(): void;
601
+ protected onKeydown(event: KeyboardEvent): void;
602
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctMenuPanel, never>;
603
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctMenuPanel, "strct-menu-panel", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "x": { "alias": "x"; "required": false; "isSignal": true; }; "y": { "alias": "y"; "required": false; "isSignal": true; }; "submenu": { "alias": "submenu"; "required": false; "isSignal": true; }; }, { "select": "select"; "close": "close"; "back": "back"; }, never, never, true, never>;
604
+ }
605
+ /**
606
+ * Right-click (context) menu driven by a data array. Attach to any trigger; the
607
+ * menu portals into `<body>` and runs each item's `action` on selection.
608
+ * <div [strctContextMenu]="menuFor(host)" [strctContextMenuData]="host"
609
+ * (menuSelect)="onPick($event)">…</div>
610
+ */
611
+ declare class StrctContextMenuTrigger implements OnDestroy {
612
+ private readonly appRef;
613
+ private readonly envInjector;
614
+ private readonly zone;
615
+ private readonly doc;
616
+ readonly items: _angular_core.InputSignal<StrctMenuItem[]>;
617
+ readonly data: _angular_core.InputSignal<unknown>;
618
+ readonly menuSelect: _angular_core.OutputEmitterRef<StrctMenuItem>;
619
+ private ref;
620
+ private readonly onClose;
621
+ protected onContextMenu(event: MouseEvent): void;
622
+ private openAt;
623
+ private readonly onOutside;
624
+ private closeMenu;
625
+ ngOnDestroy(): void;
626
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctContextMenuTrigger, never>;
627
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<StrctContextMenuTrigger, "[strctContextMenu]", never, { "items": { "alias": "strctContextMenu"; "required": true; "isSignal": true; }; "data": { "alias": "strctContextMenuData"; "required": false; "isSignal": true; }; }, { "menuSelect": "menuSelect"; }, never, never, true, never>;
628
+ }
629
+
506
630
  /** A single wizard step. `label` names it in the step header. */
507
631
  declare class StrctStep {
508
632
  readonly label: _angular_core.InputSignal<string>;
633
+ /** When false, the wizard's Next / Finish is disabled on this step. */
634
+ readonly canAdvance: _angular_core.InputSignalWithTransform<boolean, unknown>;
509
635
  private readonly _active;
510
636
  readonly active: _angular_core.Signal<boolean>;
511
637
  /** @internal */
512
638
  setActive(value: boolean): void;
513
639
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctStep, never>;
514
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctStep, "strct-step", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
640
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctStep, "strct-step", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "canAdvance": { "alias": "canAdvance"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
515
641
  }
516
642
  /** Multi-step flow with a numbered header and Back / Next / Finish controls. */
517
643
  declare class StrctWizard {
518
644
  readonly steps: _angular_core.Signal<readonly StrctStep[]>;
519
645
  readonly current: _angular_core.WritableSignal<number>;
646
+ /** Label for the final-step button (default "Finish"). */
647
+ readonly finishLabel: _angular_core.InputSignal<string>;
648
+ /** Disable Finish and show a busy label while an async submit is in flight. */
649
+ readonly submitting: _angular_core.InputSignalWithTransform<boolean, unknown>;
650
+ /** Show a Cancel button on the left. */
651
+ readonly cancelable: _angular_core.InputSignalWithTransform<boolean, unknown>;
520
652
  readonly finished: _angular_core.OutputEmitterRef<void>;
653
+ readonly cancelled: _angular_core.OutputEmitterRef<void>;
654
+ /** Emits the new step index after a Back / Next move. */
655
+ readonly stepChange: _angular_core.OutputEmitterRef<number>;
521
656
  protected readonly isLast: _angular_core.Signal<boolean>;
657
+ /** Whether the current step permits advancing (its `canAdvance`). */
658
+ protected readonly canAdvance: _angular_core.Signal<boolean>;
522
659
  constructor();
523
660
  next(): void;
524
661
  back(): void;
525
662
  finish(): void;
526
663
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctWizard, never>;
527
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctWizard, "strct-wizard", never, {}, { "finished": "finished"; }, ["steps"], ["*"], true, never>;
664
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctWizard, "strct-wizard", never, { "finishLabel": { "alias": "finishLabel"; "required": false; "isSignal": true; }; "submitting": { "alias": "submitting"; "required": false; "isSignal": true; }; "cancelable": { "alias": "cancelable"; "required": false; "isSignal": true; }; }, { "finished": "finished"; "cancelled": "cancelled"; "stepChange": "stepChange"; }, ["steps"], ["*"], true, never>;
528
665
  }
529
666
 
530
667
  /**
@@ -567,6 +704,35 @@ declare class StrctPagination {
567
704
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctPagination, "strct-pagination", never, { "total": { "alias": "total"; "required": true; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; }, { "page": "pageChange"; }, never, never, true, never>;
568
705
  }
569
706
 
707
+ /**
708
+ * Form-field wrapper: a label (with optional required marker), the projected
709
+ * control, and a hint or error message. It auto-links the control via
710
+ * `aria-describedby` and sets `aria-invalid` when an error is present.
711
+ *
712
+ * <strct-field label="Email" required hint="We never share it." [error]="emailError()">
713
+ * <input strctInput type="email" [(ngModel)]="email" />
714
+ * </strct-field>
715
+ */
716
+ declare class StrctField {
717
+ readonly label: _angular_core.InputSignal<string>;
718
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
719
+ readonly hint: _angular_core.InputSignal<string>;
720
+ /** Error message (string or first-of array); falsy clears the error state. */
721
+ readonly error: _angular_core.InputSignal<string | string[] | null | undefined>;
722
+ private readonly host;
723
+ private readonly n;
724
+ protected readonly hintId: string;
725
+ protected readonly errorId: string;
726
+ protected readonly controlId: _angular_core.WritableSignal<string>;
727
+ protected readonly errorText: _angular_core.Signal<string>;
728
+ constructor();
729
+ private control;
730
+ private link;
731
+ private applyAria;
732
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctField, never>;
733
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctField, "strct-field", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
734
+ }
735
+
570
736
  /**
571
737
  * Applies the shared `.strct-control` look to a native input / textarea / select.
572
738
  * <input strctInput placeholder="Name" />
@@ -1051,8 +1217,32 @@ interface StrctColumn {
1051
1217
  width?: string;
1052
1218
  }
1053
1219
  type StrctRow = Record<string, unknown>;
1220
+ /** Context passed to a per-column cell template. */
1221
+ interface StrctCellContext {
1222
+ /** The row object. */
1223
+ $implicit: StrctRow;
1224
+ /** The raw value for this column (`row[column.key]`). */
1225
+ value: unknown;
1226
+ /** The column definition. */
1227
+ column: StrctColumn;
1228
+ }
1229
+ /**
1230
+ * Per-column cell template for `strct-table` / `strct-datagrid`. The column key
1231
+ * is the directive value; the row, value and column are the template context:
1232
+ *
1233
+ * <ng-template strctCell="status" let-row let-value="value">
1234
+ * <strct-badge [status]="row['ok'] ? 'success' : 'danger'">{{ value }}</strct-badge>
1235
+ * </ng-template>
1236
+ */
1237
+ declare class StrctCellDef {
1238
+ readonly key: _angular_core.InputSignal<string>;
1239
+ readonly template: TemplateRef<StrctCellContext>;
1240
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctCellDef, never>;
1241
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<StrctCellDef, "[strctCell]", never, { "key": { "alias": "strctCell"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
1242
+ }
1054
1243
  /**
1055
- * Declarative data table.
1244
+ * Declarative data table. Cells render `row[col.key]` as text by default; supply
1245
+ * a `*strctCell` template per column for custom content.
1056
1246
  * <strct-table [columns]="cols" [rows]="data" hover />
1057
1247
  */
1058
1248
  declare class StrctTable {
@@ -1061,10 +1251,15 @@ declare class StrctTable {
1061
1251
  readonly striped: _angular_core.InputSignalWithTransform<boolean, unknown>;
1062
1252
  readonly hover: _angular_core.InputSignalWithTransform<boolean, unknown>;
1063
1253
  readonly emptyText: _angular_core.InputSignal<string>;
1254
+ private readonly cellDefs;
1255
+ private readonly cellMap;
1256
+ protected cellTemplate(key: string): TemplateRef<StrctCellContext> | null;
1064
1257
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctTable, never>;
1065
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTable, "strct-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "striped": { "alias": "striped"; "required": false; "isSignal": true; }; "hover": { "alias": "hover"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1258
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctTable, "strct-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "striped": { "alias": "striped"; "required": false; "isSignal": true; }; "hover": { "alias": "hover"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; }, {}, ["cellDefs"], never, true, never>;
1066
1259
  }
1067
1260
 
1261
+ /** Resolves a stable identity for a row: a property key, or a function. */
1262
+ type StrctRowId = string | ((row: StrctRow) => unknown);
1068
1263
  interface StrctDatagridColumn {
1069
1264
  key: string;
1070
1265
  label: string;
@@ -1106,14 +1301,24 @@ declare class StrctDatagrid {
1106
1301
  readonly detailPane: _angular_core.InputSignalWithTransform<boolean, unknown>;
1107
1302
  readonly compact: _angular_core.InputSignalWithTransform<boolean, unknown>;
1108
1303
  readonly emptyText: _angular_core.InputSignal<string>;
1304
+ /**
1305
+ * Stable row identity (property key or function). Set this for live-refreshing
1306
+ * data so selection, expansion and the active detail row survive re-fetches
1307
+ * that replace the row objects. Defaults to object identity.
1308
+ */
1309
+ readonly rowId: _angular_core.InputSignal<StrctRowId | null>;
1109
1310
  readonly selectionChange: _angular_core.OutputEmitterRef<StrctRow[]>;
1110
1311
  protected readonly detailDef: _angular_core.Signal<StrctRowDetailDef | undefined>;
1111
1312
  protected readonly actionBarDef: _angular_core.Signal<StrctDatagridActionBar | undefined>;
1313
+ private readonly cellDefs;
1314
+ private readonly cellMap;
1112
1315
  readonly page: _angular_core.WritableSignal<number>;
1113
1316
  private readonly sort;
1317
+ /** Selection / expansion are tracked by row id (see {@link rowId}). */
1114
1318
  private readonly selected;
1115
1319
  private readonly expandedRows;
1116
- protected readonly activeRow: _angular_core.WritableSignal<StrctRow | null>;
1320
+ private readonly activeId;
1321
+ protected readonly activeRow: _angular_core.Signal<StrctRow | null>;
1117
1322
  protected readonly canExpand: _angular_core.Signal<boolean>;
1118
1323
  protected readonly canDetail: _angular_core.Signal<boolean>;
1119
1324
  protected readonly paneOpen: _angular_core.Signal<boolean>;
@@ -1124,6 +1329,10 @@ declare class StrctDatagrid {
1124
1329
  protected readonly selectedCount: _angular_core.Signal<number>;
1125
1330
  protected readonly allPageSelected: _angular_core.Signal<boolean>;
1126
1331
  protected readonly somePageSelected: _angular_core.Signal<boolean>;
1332
+ /** Resolve a row's stable identity (defaults to the row object itself). */
1333
+ private idOf;
1334
+ protected rowKey(row: StrctRow): unknown;
1335
+ protected cellTemplate(key: string): TemplateRef<StrctCellContext> | null;
1127
1336
  constructor();
1128
1337
  protected colspan(): number;
1129
1338
  /** Toggle the detail pane for a row (triggered by its » button, not the row,
@@ -1140,10 +1349,12 @@ declare class StrctDatagrid {
1140
1349
  toggleRow(row: StrctRow): void;
1141
1350
  toggleAll(): void;
1142
1351
  clearSelection(): void;
1352
+ /** Emit the current row objects whose id is selected (resolved against the
1353
+ * latest data, so consumers always get fresh references). */
1143
1354
  private commitSelection;
1144
1355
  private compare;
1145
1356
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StrctDatagrid, never>;
1146
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctDatagrid, "strct-datagrid", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "detailPane": { "alias": "detailPane"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; }, ["detailDef", "actionBarDef"], ["[strctDatagridActionBar]", "[strctDatagridActions]"], true, never>;
1357
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctDatagrid, "strct-datagrid", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "detailPane": { "alias": "detailPane"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "rowId": { "alias": "rowId"; "required": false; "isSignal": true; }; }, { "selectionChange": "selectionChange"; }, ["detailDef", "actionBarDef", "cellDefs"], ["[strctDatagridActionBar]", "[strctDatagridActions]"], true, never>;
1147
1358
  }
1148
1359
 
1149
1360
  type StrctTimelineState = 'default' | 'current' | 'success' | 'warning' | 'danger';
@@ -1371,5 +1582,5 @@ declare class StrctToastOutlet {
1371
1582
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<StrctToastOutlet, "strct-toast-outlet", never, {}, {}, never, never, true, never>;
1372
1583
  }
1373
1584
 
1374
- 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 };
1375
- export type { StrctAlertType, StrctAvatarSize, StrctAvatarStatus, StrctBadgeStatus, StrctButtonSize, StrctButtonVariant, StrctCascadeOption, StrctChartStatus, StrctChartType, StrctColumn, StrctDatagridColumn, StrctDonutSegment, StrctIconBadge, StrctKnobStatus, StrctModalSize, StrctMode, StrctOption, StrctOverlayPlacement, StrctPalette, StrctPaletteInfo, StrctProgressStatus, StrctRow, StrctSignpostPosition, StrctSpeedDialDirection, StrctSpinnerSize, StrctTagStatus, StrctTimelineState, StrctToast, StrctToastOptions, StrctToastType, StrctTooltipPosition };
1585
+ 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, StrctField, 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 };
1586
+ export type { StrctAlertType, StrctAvatarSize, StrctAvatarStatus, StrctBadgeStatus, StrctButtonSize, StrctButtonVariant, StrctCascadeOption, StrctCellContext, StrctChartStatus, StrctChartType, StrctColumn, StrctDatagridColumn, StrctDonutSegment, StrctIconBadge, StrctKnobStatus, StrctMenuItem, StrctModalSize, StrctMode, StrctOption, StrctOverlayPlacement, StrctPalette, StrctPaletteInfo, StrctProgressStatus, StrctRow, StrctRowId, StrctSignpostPosition, StrctSpeedDialDirection, StrctSpinnerSize, StrctTagStatus, StrctTimelineState, StrctToast, StrctToastOptions, StrctToastType, StrctTooltipPosition, StrctTreeNodeData };