@akcelik/strct 0.31.1 → 1.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, model, output, booleanAttribute, HostListener, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, DestroyRef, contentChild, viewChild, viewChildren, TemplateRef, untracked, Renderer2 } from '@angular/core';
2
+ import { inject, DOCUMENT, signal, computed, Injectable, input, ViewEncapsulation, ChangeDetectionStrategy, Component, ElementRef, NgZone, afterNextRender, Directive, model, output, booleanAttribute, HostListener, contentChildren, effect, ApplicationRef, EnvironmentInjector, createComponent, forwardRef, DestroyRef, contentChild, viewChild, viewChildren, untracked, TemplateRef, Renderer2 } from '@angular/core';
3
3
  import { DomSanitizer } from '@angular/platform-browser';
4
4
  import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1 } from '@angular/common';
5
5
  import { RouterLink, RouterLinkActive } from '@angular/router';
@@ -6413,8 +6413,10 @@ const MONTHS = [
6413
6413
  'December',
6414
6414
  ];
6415
6415
  const DOW = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
6416
+ const DOW_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
6416
6417
  const pad = (n) => String(n).padStart(2, '0');
6417
6418
  const toIso = (y, m, d) => `${y}-${pad(m + 1)}-${pad(d)}`;
6419
+ let dpUid = 0;
6418
6420
  /**
6419
6421
  * Calendar date picker. Value is an ISO `yyyy-mm-dd` string. CVA-compatible.
6420
6422
  * <strct-datepicker [(ngModel)]="date" />
@@ -6424,6 +6426,8 @@ class StrctDatepicker {
6424
6426
  /** Placeholder text when empty. */
6425
6427
  placeholder = input('Select a date', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
6426
6428
  dow = DOW;
6429
+ dowFull = DOW_FULL;
6430
+ titleId = `strct-dp-title-${++dpUid}`;
6427
6431
  value = signal('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
6428
6432
  open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
6429
6433
  isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
@@ -6458,6 +6462,17 @@ class StrctDatepicker {
6458
6462
  };
6459
6463
  });
6460
6464
  }, ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
6465
+ /** The 42 cells chunked into ARIA grid rows (weeks). */
6466
+ weeks = computed(() => {
6467
+ const all = this.cells();
6468
+ return Array.from({ length: 6 }, (_, w) => all.slice(w * 7, w * 7 + 7));
6469
+ }, ...(ngDevMode ? [{ debugName: "weeks" }] : /* istanbul ignore next */ []));
6470
+ /** Accessible name of one day cell: "Tuesday, March 4, 2026". */
6471
+ cellAria(cell) {
6472
+ const [y, m, d] = cell.iso.split('-').map(Number);
6473
+ const dow = DOW_FULL[new Date(y, m - 1, d).getDay()];
6474
+ return `${dow}, ${MONTHS[m - 1]} ${d}, ${y}`;
6475
+ }
6461
6476
  onChange = () => { };
6462
6477
  onTouched = () => { };
6463
6478
  toggle() {
@@ -6477,10 +6492,26 @@ class StrctDatepicker {
6477
6492
  this.focusedIso.set(iso);
6478
6493
  const [y, m] = iso.split('-').map(Number);
6479
6494
  this.view.set({ y, m: m - 1 });
6480
- this.open.set(false);
6495
+ this.close();
6481
6496
  this.onChange(iso);
6482
6497
  this.onTouched();
6483
6498
  }
6499
+ constructor() {
6500
+ // Roving focus: the keyboard cursor's day cell takes real focus while the
6501
+ // calendar is open (APG date-picker dialog pattern); the input gets it back
6502
+ // on close via close().
6503
+ effect(() => {
6504
+ const open = this.open();
6505
+ const iso = this.focusedIso();
6506
+ untracked(() => {
6507
+ if (!open || !iso)
6508
+ return;
6509
+ setTimeout(() => this.host.nativeElement
6510
+ .querySelector(`.strct-dp__day[data-iso="${iso}"]`)
6511
+ ?.focus());
6512
+ });
6513
+ });
6514
+ }
6484
6515
  onKeydown(event) {
6485
6516
  if (this.isDisabled())
6486
6517
  return;
@@ -6508,13 +6539,24 @@ class StrctDatepicker {
6508
6539
  event.preventDefault();
6509
6540
  this.shiftFocus(7);
6510
6541
  break;
6542
+ case 'Home': {
6543
+ // Start of the focused week (APG grid pattern).
6544
+ event.preventDefault();
6545
+ this.shiftFocus(-this.focusedDow());
6546
+ break;
6547
+ }
6548
+ case 'End': {
6549
+ event.preventDefault();
6550
+ this.shiftFocus(6 - this.focusedDow());
6551
+ break;
6552
+ }
6511
6553
  case 'PageUp':
6512
6554
  event.preventDefault();
6513
- this.shiftFocus(0, -1);
6555
+ this.shiftFocus(0, event.shiftKey ? -12 : -1); // Shift = previous year
6514
6556
  break;
6515
6557
  case 'PageDown':
6516
6558
  event.preventDefault();
6517
- this.shiftFocus(0, 1);
6559
+ this.shiftFocus(0, event.shiftKey ? 12 : 1); // Shift = next year
6518
6560
  break;
6519
6561
  case 'Enter':
6520
6562
  case ' ':
@@ -6524,10 +6566,22 @@ class StrctDatepicker {
6524
6566
  break;
6525
6567
  case 'Escape':
6526
6568
  event.preventDefault();
6527
- this.open.set(false);
6569
+ this.close();
6528
6570
  break;
6529
6571
  }
6530
6572
  }
6573
+ /** Day-of-week (0–6) of the keyboard cursor. */
6574
+ focusedDow() {
6575
+ const [y, m, d] = (this.focusedIso() || this.today).split('-').map(Number);
6576
+ return new Date(y, m - 1, d).getDay();
6577
+ }
6578
+ /** Close the panel and hand focus back to the input. */
6579
+ close() {
6580
+ this.open.set(false);
6581
+ this.host.nativeElement
6582
+ .querySelector('.strct-dp__input')
6583
+ ?.focus();
6584
+ }
6531
6585
  syncFocus() {
6532
6586
  const base = this.value() || this.today;
6533
6587
  this.focusedIso.set(base);
@@ -6598,6 +6652,7 @@ class StrctDatepicker {
6598
6652
  <div
6599
6653
  class="strct-dp__panel"
6600
6654
  role="dialog"
6655
+ [attr.aria-labelledby]="titleId"
6601
6656
  [strctOverlay]="field"
6602
6657
  strctOverlayPlacement="bottom-start"
6603
6658
  >
@@ -6610,7 +6665,7 @@ class StrctDatepicker {
6610
6665
  >
6611
6666
  <strct-icon name="chevronLeft" [size]="14" [strokeWidth]="1.7" />
6612
6667
  </button>
6613
- <span class="strct-dp__title">{{ monthLabel() }}</span>
6668
+ <span class="strct-dp__title" [id]="titleId" aria-live="polite">{{ monthLabel() }}</span>
6614
6669
  <button
6615
6670
  type="button"
6616
6671
  class="strct-dp__nav"
@@ -6620,24 +6675,39 @@ class StrctDatepicker {
6620
6675
  <strct-icon name="chevronRight" [size]="14" [strokeWidth]="1.7" />
6621
6676
  </button>
6622
6677
  </div>
6623
- <div class="strct-dp__dow">
6624
- @for (d of dow; track d) {
6625
- <span>{{ d }}</span>
6626
- }
6627
- </div>
6628
- <div class="strct-dp__grid">
6629
- @for (cell of cells(); track cell.iso) {
6630
- <button
6631
- type="button"
6632
- class="strct-dp__day"
6633
- [class.strct-dp__day--muted]="!cell.inMonth"
6634
- [class.strct-dp__day--today]="cell.iso === today"
6635
- [class.strct-dp__day--focused]="cell.iso === focusedIso()"
6636
- [class.strct-dp__day--selected]="cell.iso === value()"
6637
- (click)="pick(cell.iso)"
6638
- >
6639
- {{ cell.day }}
6640
- </button>
6678
+ <!-- ARIA grid (APG date-picker dialog pattern): focus roves the day
6679
+ cells; the input regains focus on close. -->
6680
+ <div class="strct-dp__gridwrap" role="grid" [attr.aria-labelledby]="titleId">
6681
+ <div class="strct-dp__dow" role="row">
6682
+ @for (d of dow; track d; let i = $index) {
6683
+ <span role="columnheader" [attr.aria-label]="dowFull[i]" [attr.abbr]="dowFull[i]">{{
6684
+ d
6685
+ }}</span>
6686
+ }
6687
+ </div>
6688
+ @for (week of weeks(); track $index) {
6689
+ <div class="strct-dp__grid" role="row">
6690
+ @for (cell of week; track cell.iso) {
6691
+ <button
6692
+ type="button"
6693
+ role="gridcell"
6694
+ class="strct-dp__day"
6695
+ [class.strct-dp__day--muted]="!cell.inMonth"
6696
+ [class.strct-dp__day--today]="cell.iso === today"
6697
+ [class.strct-dp__day--focused]="cell.iso === focusedIso()"
6698
+ [class.strct-dp__day--selected]="cell.iso === value()"
6699
+ [attr.data-iso]="cell.iso"
6700
+ [attr.tabindex]="cell.iso === focusedIso() ? 0 : -1"
6701
+ [attr.aria-selected]="cell.iso === value()"
6702
+ [attr.aria-current]="cell.iso === today ? 'date' : null"
6703
+ [attr.aria-label]="cellAria(cell)"
6704
+ (click)="pick(cell.iso)"
6705
+ (keydown)="onKeydown($event)"
6706
+ >
6707
+ {{ cell.day }}
6708
+ </button>
6709
+ }
6710
+ </div>
6641
6711
  }
6642
6712
  </div>
6643
6713
  </div>
@@ -6675,6 +6745,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
6675
6745
  <div
6676
6746
  class="strct-dp__panel"
6677
6747
  role="dialog"
6748
+ [attr.aria-labelledby]="titleId"
6678
6749
  [strctOverlay]="field"
6679
6750
  strctOverlayPlacement="bottom-start"
6680
6751
  >
@@ -6687,7 +6758,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
6687
6758
  >
6688
6759
  <strct-icon name="chevronLeft" [size]="14" [strokeWidth]="1.7" />
6689
6760
  </button>
6690
- <span class="strct-dp__title">{{ monthLabel() }}</span>
6761
+ <span class="strct-dp__title" [id]="titleId" aria-live="polite">{{ monthLabel() }}</span>
6691
6762
  <button
6692
6763
  type="button"
6693
6764
  class="strct-dp__nav"
@@ -6697,30 +6768,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
6697
6768
  <strct-icon name="chevronRight" [size]="14" [strokeWidth]="1.7" />
6698
6769
  </button>
6699
6770
  </div>
6700
- <div class="strct-dp__dow">
6701
- @for (d of dow; track d) {
6702
- <span>{{ d }}</span>
6703
- }
6704
- </div>
6705
- <div class="strct-dp__grid">
6706
- @for (cell of cells(); track cell.iso) {
6707
- <button
6708
- type="button"
6709
- class="strct-dp__day"
6710
- [class.strct-dp__day--muted]="!cell.inMonth"
6711
- [class.strct-dp__day--today]="cell.iso === today"
6712
- [class.strct-dp__day--focused]="cell.iso === focusedIso()"
6713
- [class.strct-dp__day--selected]="cell.iso === value()"
6714
- (click)="pick(cell.iso)"
6715
- >
6716
- {{ cell.day }}
6717
- </button>
6771
+ <!-- ARIA grid (APG date-picker dialog pattern): focus roves the day
6772
+ cells; the input regains focus on close. -->
6773
+ <div class="strct-dp__gridwrap" role="grid" [attr.aria-labelledby]="titleId">
6774
+ <div class="strct-dp__dow" role="row">
6775
+ @for (d of dow; track d; let i = $index) {
6776
+ <span role="columnheader" [attr.aria-label]="dowFull[i]" [attr.abbr]="dowFull[i]">{{
6777
+ d
6778
+ }}</span>
6779
+ }
6780
+ </div>
6781
+ @for (week of weeks(); track $index) {
6782
+ <div class="strct-dp__grid" role="row">
6783
+ @for (cell of week; track cell.iso) {
6784
+ <button
6785
+ type="button"
6786
+ role="gridcell"
6787
+ class="strct-dp__day"
6788
+ [class.strct-dp__day--muted]="!cell.inMonth"
6789
+ [class.strct-dp__day--today]="cell.iso === today"
6790
+ [class.strct-dp__day--focused]="cell.iso === focusedIso()"
6791
+ [class.strct-dp__day--selected]="cell.iso === value()"
6792
+ [attr.data-iso]="cell.iso"
6793
+ [attr.tabindex]="cell.iso === focusedIso() ? 0 : -1"
6794
+ [attr.aria-selected]="cell.iso === value()"
6795
+ [attr.aria-current]="cell.iso === today ? 'date' : null"
6796
+ [attr.aria-label]="cellAria(cell)"
6797
+ (click)="pick(cell.iso)"
6798
+ (keydown)="onKeydown($event)"
6799
+ >
6800
+ {{ cell.day }}
6801
+ </button>
6802
+ }
6803
+ </div>
6718
6804
  }
6719
6805
  </div>
6720
6806
  </div>
6721
6807
  }
6722
6808
  `, host: { class: 'strct-dp' }, styles: [".strct-dp{position:relative;display:inline-block;width:100%;max-width:240px}.strct-dp__field{position:relative}.strct-dp__input{padding-inline-end:36px;cursor:pointer}.strct-dp__icon{position:absolute;right:4px;top:50%;transform:translateY(-50%);display:inline-flex;padding:5px;border:0;border-radius:5px;background:transparent;color:var(--t2);cursor:pointer}.strct-dp__icon:hover{color:var(--acc);background:var(--bg-3)}.strct-dp__panel{position:absolute;top:calc(100% + 5px);left:0;z-index:250;width:250px;padding:10px;background:var(--bg-1);border:1px solid var(--b2);border-radius:9px;box-shadow:var(--shh)}.strct-dp__head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.strct-dp__title{font-size:13px;font-weight:600;color:var(--t1)}.strct-dp__nav{display:inline-flex;padding:5px;border:0;border-radius:5px;background:transparent;color:var(--t2);cursor:pointer}.strct-dp__nav:hover{color:var(--t1);background:var(--bg-3)}.strct-dp__dow{display:grid;grid-template-columns:repeat(7,1fr);margin-bottom:4px}.strct-dp__dow span{text-align:center;font-size:12px;font-weight:600;color:var(--t3);padding:4px 0}.strct-dp__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.strct-dp__day{aspect-ratio:1;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:6px;background:transparent;cursor:pointer;font-family:var(--font);font-size:12px;color:var(--t1)}.strct-dp__day:hover{background:var(--bg-3)}.strct-dp__day--muted{color:var(--t3)}.strct-dp__day--today{box-shadow:inset 0 0 0 1px var(--acc30)}.strct-dp__day--focused{box-shadow:inset 0 0 0 2px var(--acc)}.strct-dp__day--selected{background:var(--acc);color:var(--inv)}.strct-dp__day--selected:hover{background:var(--acc)}\n"] }]
6723
- }], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], onDocClick: [{
6809
+ }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], onDocClick: [{
6724
6810
  type: HostListener,
6725
6811
  args: ['document:click', ['$event']]
6726
6812
  }], onEscape: [{
@@ -7372,6 +7458,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
7372
7458
  }, 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:start;border-bottom:1px solid var(--b1)}.strct-table th{font-size:12px;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)}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-table__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-table__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-table__empty{text-align:center;color:var(--t3);padding:22px}\n"] }]
7373
7459
  }], 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 }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
7374
7460
 
7461
+ /**
7462
+ * Minimal, dependency-free XLSX writer for the datagrid's Excel export.
7463
+ *
7464
+ * An .xlsx file is a ZIP of SpreadsheetML parts. This builds the five
7465
+ * required parts with inline strings and zips them with STORED (uncompressed)
7466
+ * entries — no runtime dependency, byte-accurate CRC32, opens in Excel,
7467
+ * LibreOffice and Google Sheets.
7468
+ */
7469
+ const CRC_TABLE = (() => {
7470
+ const t = new Uint32Array(256);
7471
+ for (let n = 0; n < 256; n++) {
7472
+ let c = n;
7473
+ for (let k = 0; k < 8; k++)
7474
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
7475
+ t[n] = c >>> 0;
7476
+ }
7477
+ return t;
7478
+ })();
7479
+ function crc32(data) {
7480
+ let c = 0xffffffff;
7481
+ for (const byte of data)
7482
+ c = CRC_TABLE[(c ^ byte) & 0xff] ^ (c >>> 8);
7483
+ return (c ^ 0xffffffff) >>> 0;
7484
+ }
7485
+ const enc = new TextEncoder();
7486
+ /** ZIP with STORED entries (no compression — spreadsheets are small). */
7487
+ function zip(entries) {
7488
+ const chunks = [];
7489
+ const central = [];
7490
+ let offset = 0;
7491
+ const u16 = (n) => new Uint8Array([n & 0xff, (n >> 8) & 0xff]);
7492
+ const u32 = (n) => new Uint8Array([n & 0xff, (n >> 8) & 0xff, (n >> 16) & 0xff, (n >> 24) & 0xff]);
7493
+ const cat = (...parts) => {
7494
+ const total = parts.reduce((s, p) => s + p.length, 0);
7495
+ const out = new Uint8Array(total);
7496
+ let o = 0;
7497
+ for (const p of parts) {
7498
+ out.set(p, o);
7499
+ o += p.length;
7500
+ }
7501
+ return out;
7502
+ };
7503
+ for (const { name, data } of entries) {
7504
+ const nameBytes = enc.encode(name);
7505
+ const crc = crc32(data);
7506
+ const local = cat(u32(0x04034b50), u16(20), // version needed
7507
+ u16(0), // flags
7508
+ u16(0), // method: stored
7509
+ u16(0), u16(0), // mod time/date
7510
+ u32(crc), u32(data.length), u32(data.length), u16(nameBytes.length), u16(0), nameBytes, data);
7511
+ central.push(cat(u32(0x02014b50), u16(20), u16(20), u16(0), u16(0), u16(0), u16(0), u32(crc), u32(data.length), u32(data.length), u16(nameBytes.length), u16(0), u16(0), u16(0), u16(0), u32(0), u32(offset), nameBytes));
7512
+ chunks.push(local);
7513
+ offset += local.length;
7514
+ }
7515
+ const centralStart = offset;
7516
+ const centralBytes = cat(...central);
7517
+ const end = cat(u32(0x06054b50), u16(0), u16(0), u16(entries.length), u16(entries.length), u32(centralBytes.length), u32(centralStart), u16(0));
7518
+ return cat(...chunks, centralBytes, end);
7519
+ }
7520
+ const xml = (s) => String(s ?? '')
7521
+ .replace(/&/g, '&amp;')
7522
+ .replace(/</g, '&lt;')
7523
+ .replace(/>/g, '&gt;')
7524
+ .replace(/"/g, '&quot;');
7525
+ /** A1-style column ref: 0 → A, 26 → AA … */
7526
+ function colRef(i) {
7527
+ let s = '';
7528
+ for (let n = i; n >= 0; n = Math.floor(n / 26) - 1)
7529
+ s = String.fromCharCode(65 + (n % 26)) + s;
7530
+ return s;
7531
+ }
7532
+ /**
7533
+ * Build a single-sheet workbook: `header` as a bold-free first row, then
7534
+ * `rows`. Numbers stay numeric; everything else exports as inline strings.
7535
+ */
7536
+ function buildXlsx(header, rows, sheetName = 'Data') {
7537
+ const rowXml = (cells, r) => `<row r="${r}">` +
7538
+ cells
7539
+ .map((v, c) => {
7540
+ const ref = `${colRef(c)}${r}`;
7541
+ if (typeof v === 'number' && Number.isFinite(v)) {
7542
+ return `<c r="${ref}"><v>${v}</v></c>`;
7543
+ }
7544
+ return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${xml(v)}</t></is></c>`;
7545
+ })
7546
+ .join('') +
7547
+ '</row>';
7548
+ const sheet = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
7549
+ `<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>` +
7550
+ rowXml(header, 1) +
7551
+ rows.map((cells, i) => rowXml(cells, i + 2)).join('') +
7552
+ `</sheetData></worksheet>`;
7553
+ const workbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
7554
+ `<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" ` +
7555
+ `xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">` +
7556
+ `<sheets><sheet name="${xml(sheetName)}" sheetId="1" r:id="rId1"/></sheets></workbook>`;
7557
+ const workbookRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
7558
+ `<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">` +
7559
+ `<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>` +
7560
+ `</Relationships>`;
7561
+ const rootRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
7562
+ `<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">` +
7563
+ `<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>` +
7564
+ `</Relationships>`;
7565
+ const contentTypes = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` +
7566
+ `<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">` +
7567
+ `<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>` +
7568
+ `<Default Extension="xml" ContentType="application/xml"/>` +
7569
+ `<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>` +
7570
+ `<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>` +
7571
+ `</Types>`;
7572
+ return zip([
7573
+ { name: '[Content_Types].xml', data: enc.encode(contentTypes) },
7574
+ { name: '_rels/.rels', data: enc.encode(rootRels) },
7575
+ { name: 'xl/workbook.xml', data: enc.encode(workbook) },
7576
+ { name: 'xl/_rels/workbook.xml.rels', data: enc.encode(workbookRels) },
7577
+ { name: 'xl/worksheets/sheet1.xml', data: enc.encode(sheet) },
7578
+ ]);
7579
+ }
7580
+
7375
7581
  const DG_LABELS = {
7376
7582
  row: 'row',
7377
7583
  rows: 'rows',
@@ -7504,6 +7710,14 @@ class StrctDatagrid {
7504
7710
  stateKey = input(null, ...(ngDevMode ? [{ debugName: "stateKey" }] : /* istanbul ignore next */ []));
7505
7711
  /** User column preferences (two-way): widths from resize, hidden from the chooser. */
7506
7712
  columnState = model(null, ...(ngDevMode ? [{ debugName: "columnState" }] : /* istanbul ignore next */ []));
7713
+ /** Let the user reorder data columns by dragging their headers. */
7714
+ reorderable = input(false, { ...(ngDevMode ? { debugName: "reorderable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
7715
+ /**
7716
+ * Group rows by this column key: the grid renders a collapsible header row
7717
+ * per distinct value (with a count), respecting the current sort within
7718
+ * groups. Paging is bypassed while grouped; not combinable with `virtual`.
7719
+ */
7720
+ groupBy = input(null, ...(ngDevMode ? [{ debugName: "groupBy" }] : /* istanbul ignore next */ []));
7507
7721
  /** Emitted when the selection changes. */
7508
7722
  selectionChange = output();
7509
7723
  /** Emitted when the refresh button is clicked. */
@@ -7575,13 +7789,108 @@ class StrctDatagrid {
7575
7789
  paneOpen = computed(() => this.detailPane() && !!this.detailDef() && !!this.activeRow(), ...(ngDevMode ? [{ debugName: "paneOpen" }] : /* istanbul ignore next */ []));
7576
7790
  /** Whether to render the trailing row-actions (kebab) column. */
7577
7791
  canActions = computed(() => !!this.rowActions() && !this.paneOpen(), ...(ngDevMode ? [{ debugName: "canActions" }] : /* istanbul ignore next */ []));
7792
+ /** User-chosen column order (keys); unknown keys keep declared positions. */
7793
+ columnOrder = signal(null, ...(ngDevMode ? [{ debugName: "columnOrder" }] : /* istanbul ignore next */ []));
7794
+ /** Declared columns re-arranged by the user's drag order. */
7795
+ orderedColumns = computed(() => {
7796
+ const cols = this.columns();
7797
+ const order = this.columnOrder();
7798
+ if (!order?.length)
7799
+ return cols;
7800
+ const byKey = new Map(cols.map((c) => [c.key, c]));
7801
+ const out = [];
7802
+ for (const key of order) {
7803
+ const c = byKey.get(key);
7804
+ if (c) {
7805
+ out.push(c);
7806
+ byKey.delete(key);
7807
+ }
7808
+ }
7809
+ for (const c of cols)
7810
+ if (byKey.has(c.key))
7811
+ out.push(c);
7812
+ return out;
7813
+ }, ...(ngDevMode ? [{ debugName: "orderedColumns" }] : /* istanbul ignore next */ []));
7578
7814
  /** Only the first column is shown while the detail pane is open. */
7579
7815
  visibleColumns = computed(() => {
7580
7816
  if (this.paneOpen())
7581
- return this.columns().slice(0, 1);
7817
+ return this.orderedColumns().slice(0, 1);
7582
7818
  const hidden = this.hiddenColumns();
7583
- return this.columns().filter((c) => !hidden.has(c.key));
7819
+ return this.orderedColumns().filter((c) => !hidden.has(c.key));
7584
7820
  }, ...(ngDevMode ? [{ debugName: "visibleColumns" }] : /* istanbul ignore next */ []));
7821
+ // ── Column drag-reorder ────────────────────────────────────────
7822
+ dragKey = signal(null, ...(ngDevMode ? [{ debugName: "dragKey" }] : /* istanbul ignore next */ []));
7823
+ dropKey = signal(null, ...(ngDevMode ? [{ debugName: "dropKey" }] : /* istanbul ignore next */ []));
7824
+ onColDragStart(key, event) {
7825
+ if (!this.reorderable())
7826
+ return;
7827
+ this.dragKey.set(key);
7828
+ event.dataTransfer?.setData('text/plain', key);
7829
+ if (event.dataTransfer)
7830
+ event.dataTransfer.effectAllowed = 'move';
7831
+ }
7832
+ onColDragOver(key, event) {
7833
+ if (!this.reorderable() || !this.dragKey() || key === this.dragKey())
7834
+ return;
7835
+ event.preventDefault();
7836
+ this.dropKey.set(key);
7837
+ }
7838
+ onColDrop(key) {
7839
+ const from = this.dragKey();
7840
+ this.dragKey.set(null);
7841
+ this.dropKey.set(null);
7842
+ if (!from || from === key)
7843
+ return;
7844
+ const keys = this.orderedColumns().map((c) => c.key);
7845
+ const fromIdx = keys.indexOf(from);
7846
+ const toIdx = keys.indexOf(key);
7847
+ if (fromIdx < 0 || toIdx < 0)
7848
+ return;
7849
+ keys.splice(toIdx, 0, ...keys.splice(fromIdx, 1));
7850
+ this.columnOrder.set(keys);
7851
+ }
7852
+ onColDragEnd() {
7853
+ this.dragKey.set(null);
7854
+ this.dropKey.set(null);
7855
+ }
7856
+ // ── Row grouping ───────────────────────────────────────────────
7857
+ collapsedGroups = signal(new Set(), ...(ngDevMode ? [{ debugName: "collapsedGroups" }] : /* istanbul ignore next */ []));
7858
+ /** Collapse / expand one group header. */
7859
+ toggleGroup(key) {
7860
+ const next = new Set(this.collapsedGroups());
7861
+ if (next.has(key))
7862
+ next.delete(key);
7863
+ else
7864
+ next.add(key);
7865
+ this.collapsedGroups.set(next);
7866
+ }
7867
+ /** What tbody renders: plain (virtual/paged) rows, or groups + their rows. */
7868
+ displayItems = computed(() => {
7869
+ const g = this.groupBy();
7870
+ if (!g || this.virtual())
7871
+ return this.renderRows().map((row) => ({ row }));
7872
+ const map = new Map();
7873
+ for (const row of this.sorted()) {
7874
+ const key = row[g];
7875
+ const bucket = map.get(key);
7876
+ if (bucket)
7877
+ bucket.push(row);
7878
+ else
7879
+ map.set(key, [row]);
7880
+ }
7881
+ const out = [];
7882
+ for (const [key, rows] of map) {
7883
+ const collapsed = this.collapsedGroups().has(key);
7884
+ out.push({ group: { key, label: String(key ?? '—'), count: rows.length, collapsed } });
7885
+ if (!collapsed)
7886
+ for (const row of rows)
7887
+ out.push({ row });
7888
+ }
7889
+ return out;
7890
+ }, ...(ngDevMode ? [{ debugName: "displayItems" }] : /* istanbul ignore next */ []));
7891
+ itemKey(it) {
7892
+ return it.group ? `strct-group:${String(it.group.key)}` : this.rowKey(it.row);
7893
+ }
7585
7894
  sorted = computed(() => {
7586
7895
  // Server-side mode: rows arrive already ordered / sliced — never touch them.
7587
7896
  if (this.lazy())
@@ -7750,11 +8059,12 @@ class StrctDatagrid {
7750
8059
  this.applyColumnState(st);
7751
8060
  });
7752
8061
  });
7753
- // Push user changes (resize / chooser) outward + persist under stateKey.
8062
+ // Push user changes (resize / chooser / reorder) outward + persist under stateKey.
7754
8063
  effect(() => {
7755
8064
  const widths = Object.fromEntries(this.columnWidths());
7756
8065
  const hidden = [...this.hiddenColumns()];
7757
- const st = { widths, hidden };
8066
+ const order = this.columnOrder() ?? undefined;
8067
+ const st = { widths, hidden, order };
7758
8068
  const key = JSON.stringify(st);
7759
8069
  untracked(() => {
7760
8070
  if (key === this.lastStateKey)
@@ -7788,6 +8098,7 @@ class StrctDatagrid {
7788
8098
  applyColumnState(st) {
7789
8099
  this.columnWidths.set(new Map(Object.entries(st.widths ?? {}).map(([k, v]) => [k, Number(v)])));
7790
8100
  this.hiddenColumns.set(new Set(st.hidden ?? []));
8101
+ this.columnOrder.set(st.order?.length ? [...st.order] : null);
7791
8102
  }
7792
8103
  /**
7793
8104
  * The grid as CSV: header labels + every non-hidden column, all rows in the
@@ -7806,6 +8117,33 @@ class StrctDatagrid {
7806
8117
  ...data.map((r) => cols.map((c) => esc(r[c.key])).join(',')),
7807
8118
  ].join('\n');
7808
8119
  }
8120
+ /**
8121
+ * The grid as a real .xlsx workbook (dependency-free SpreadsheetML):
8122
+ * header labels + every non-hidden column in the current order, all rows in
8123
+ * the current sort; numeric cells stay numeric.
8124
+ */
8125
+ toXLSX() {
8126
+ const hidden = this.hiddenColumns();
8127
+ const cols = this.orderedColumns().filter((c) => !hidden.has(c.key));
8128
+ const data = this.lazy() ? this.rows() : this.sorted();
8129
+ return buildXlsx(cols.map((c) => c.label), data.map((r) => cols.map((c) => r[c.key])));
8130
+ }
8131
+ /** Download the grid as an .xlsx file. */
8132
+ downloadXLSX(filename = 'datagrid.xlsx') {
8133
+ if (typeof document === 'undefined')
8134
+ return;
8135
+ const bytes = this.toXLSX();
8136
+ const copy = new Uint8Array(bytes);
8137
+ const blob = new Blob([copy.buffer], {
8138
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
8139
+ });
8140
+ const url = URL.createObjectURL(blob);
8141
+ const a = document.createElement('a');
8142
+ a.href = url;
8143
+ a.download = filename;
8144
+ a.click();
8145
+ URL.revokeObjectURL(url);
8146
+ }
7809
8147
  /** Download the grid as a CSV file. */
7810
8148
  downloadCSV(filename = 'datagrid.csv') {
7811
8149
  if (typeof document === 'undefined')
@@ -7944,7 +8282,7 @@ class StrctDatagrid {
7944
8282
  return String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true });
7945
8283
  }
7946
8284
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDatagrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
7947
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPane: { classPropertyName: "detailPane", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, sync: { classPropertyName: "sync", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, stateKey: { classPropertyName: "stateKey", publicName: "stateKey", isSignal: true, isRequired: false, transformFunction: null }, columnState: { classPropertyName: "columnState", publicName: "columnState", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { columnState: "columnStateChange", selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction", lazyLoad: "lazyLoad" }, host: { properties: { "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()", "class.strct-dg-host--virtual": "virtual()", "class.strct-dg-host--sticky": "stickyActive()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
8285
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctDatagrid, isStandalone: true, selector: "strct-datagrid", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, detailPane: { classPropertyName: "detailPane", publicName: "detailPane", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null }, singleLine: { classPropertyName: "singleLine", publicName: "singleLine", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, columnChooser: { classPropertyName: "columnChooser", publicName: "columnChooser", isSignal: true, isRequired: false, transformFunction: null }, sync: { classPropertyName: "sync", publicName: "sync", isSignal: true, isRequired: false, transformFunction: null }, footerActionsDisabled: { classPropertyName: "footerActionsDisabled", publicName: "footerActionsDisabled", isSignal: true, isRequired: false, transformFunction: null }, rowId: { classPropertyName: "rowId", publicName: "rowId", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, initialSelection: { classPropertyName: "initialSelection", publicName: "initialSelection", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, viewportHeight: { classPropertyName: "viewportHeight", publicName: "viewportHeight", isSignal: true, isRequired: false, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, lazy: { classPropertyName: "lazy", publicName: "lazy", isSignal: true, isRequired: false, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, stateKey: { classPropertyName: "stateKey", publicName: "stateKey", isSignal: true, isRequired: false, transformFunction: null }, columnState: { classPropertyName: "columnState", publicName: "columnState", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { columnState: "columnStateChange", selectionChange: "selectionChange", syncChange: "syncChange", rowAction: "rowAction", lazyLoad: "lazyLoad" }, host: { properties: { "class.strct-dg-host--compact": "compact()", "class.strct-dg-host--singleline": "singleLine()", "class.strct-dg-host--virtual": "virtual()", "class.strct-dg-host--sticky": "stickyActive()" }, classAttribute: "strct-dg-host" }, queries: [{ propertyName: "detailDef", first: true, predicate: StrctRowDetailDef, descendants: true, isSignal: true }, { propertyName: "actionBarDef", first: true, predicate: StrctDatagridActionBar, descendants: true, isSignal: true }, { propertyName: "cellDefs", predicate: StrctCellDef, isSignal: true }], ngImport: i0, template: `
7948
8286
  @if (actionBarDef()) {
7949
8287
  <div class="strct-dg__toolbar"><ng-content select="[strctDatagridActionBar]" /></div>
7950
8288
  }
@@ -7965,21 +8303,21 @@ class StrctDatagrid {
7965
8303
  <th
7966
8304
  class="strct-dg__expandcol"
7967
8305
  [class.strct-dg__cell--sticky]="stickyActive()"
7968
- [style.left.px]="utilLeft('detail')"
8306
+ [style.insetInlineStart.px]="utilLeft('detail')"
7969
8307
  ></th>
7970
8308
  }
7971
8309
  @if (canExpand()) {
7972
8310
  <th
7973
8311
  class="strct-dg__expandcol"
7974
8312
  [class.strct-dg__cell--sticky]="stickyActive()"
7975
- [style.left.px]="utilLeft('expand')"
8313
+ [style.insetInlineStart.px]="utilLeft('expand')"
7976
8314
  ></th>
7977
8315
  }
7978
8316
  @if (selectable()) {
7979
8317
  <th
7980
8318
  class="strct-dg__sel"
7981
8319
  [class.strct-dg__cell--sticky]="stickyActive()"
7982
- [style.left.px]="utilLeft('sel')"
8320
+ [style.insetInlineStart.px]="utilLeft('sel')"
7983
8321
  >
7984
8322
  <strct-checkbox
7985
8323
  [ariaLabel]="L().selectAll"
@@ -7994,11 +8332,17 @@ class StrctDatagrid {
7994
8332
  [style.text-align]="col.align ?? 'start'"
7995
8333
  [style.width]="colWidth(col.key) ?? col.width ?? null"
7996
8334
  [class.strct-dg__th--sortable]="col.sortable"
8335
+ [class.strct-dg__th--drop]="col.key === dropKey()"
7997
8336
  [class.strct-dg__cell--sticky]="isSticky(col)"
7998
8337
  [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
7999
- [style.left.px]="stickyLeft(col.key)"
8338
+ [style.insetInlineStart.px]="stickyLeft(col.key)"
8000
8339
  [attr.tabindex]="col.sortable ? 0 : null"
8001
8340
  [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
8341
+ [attr.draggable]="reorderable() ? true : null"
8342
+ (dragstart)="onColDragStart(col.key, $event)"
8343
+ (dragover)="onColDragOver(col.key, $event)"
8344
+ (drop)="onColDrop(col.key)"
8345
+ (dragend)="onColDragEnd()"
8002
8346
  (click)="col.sortable && sortBy(col.key)"
8003
8347
  (keydown.enter)="col.sortable && sortBy(col.key)"
8004
8348
  (keydown.space)="col.sortable && onHeaderSpace($event, col.key)"
@@ -8055,105 +8399,129 @@ class StrctDatagrid {
8055
8399
  <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
8056
8400
  </tr>
8057
8401
  }
8058
- @for (row of renderRows(); track rowKey(row)) {
8059
- <tr
8060
- [class.strct-dg__row--selected]="isSelected(row)"
8061
- [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
8062
- >
8063
- @if (canDetail()) {
8064
- <td
8065
- class="strct-dg__expandcell"
8066
- [class.strct-dg__cell--sticky]="stickyActive()"
8067
- [style.left.px]="utilLeft('detail')"
8068
- >
8402
+ @for (it of displayItems(); track itemKey(it)) {
8403
+ @if (it.group; as grp) {
8404
+ <!-- Group header: distinct value + count, collapsible. -->
8405
+ <tr class="strct-dg__grouprow">
8406
+ <td [attr.colspan]="colspan()">
8069
8407
  <button
8070
8408
  type="button"
8071
- class="strct-dg__detailbtn"
8072
- [class.strct-dg__detailbtn--active]="row === activeRow()"
8073
- [attr.aria-expanded]="row === activeRow()"
8074
- [attr.aria-label]="L().openDetail"
8075
- (click)="openDetail(row)"
8409
+ class="strct-dg__groupbtn"
8410
+ [attr.aria-expanded]="!grp.collapsed"
8411
+ (click)="toggleGroup(grp.key)"
8076
8412
  >
8077
- <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
8413
+ <span
8414
+ class="strct-dg__groupchev"
8415
+ [class.strct-dg__groupchev--open]="!grp.collapsed"
8416
+ >
8417
+ <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8418
+ </span>
8419
+ <span class="strct-dg__grouplabel">{{ grp.label }}</span>
8420
+ <span class="strct-dg__groupcount">{{ grp.count }}</span>
8078
8421
  </button>
8079
8422
  </td>
8080
- }
8081
- @if (canExpand()) {
8082
- <td
8083
- class="strct-dg__expandcell"
8084
- [class.strct-dg__cell--sticky]="stickyActive()"
8085
- [style.left.px]="utilLeft('expand')"
8086
- >
8087
- <button
8088
- type="button"
8089
- class="strct-dg__expandbtn"
8090
- [class.strct-dg__expandbtn--open]="isExpanded(row)"
8091
- [attr.aria-expanded]="isExpanded(row)"
8092
- [attr.aria-label]="L().toggleDetail"
8093
- (click)="toggleExpand(row)"
8423
+ </tr>
8424
+ } @else {
8425
+ @let row = it.row!;
8426
+ <tr
8427
+ [class.strct-dg__row--selected]="isSelected(row)"
8428
+ [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
8429
+ >
8430
+ @if (canDetail()) {
8431
+ <td
8432
+ class="strct-dg__expandcell"
8433
+ [class.strct-dg__cell--sticky]="stickyActive()"
8434
+ [style.insetInlineStart.px]="utilLeft('detail')"
8094
8435
  >
8095
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8096
- </button>
8097
- </td>
8098
- }
8099
- @if (selectable()) {
8100
- <td
8101
- class="strct-dg__sel"
8102
- [class.strct-dg__cell--sticky]="stickyActive()"
8103
- [style.left.px]="utilLeft('sel')"
8104
- >
8105
- <strct-checkbox
8106
- [ariaLabel]="L().selectRow"
8107
- [checked]="isSelected(row)"
8108
- (checkedChange)="toggleRow(row)"
8109
- />
8110
- </td>
8111
- }
8112
- @for (col of visibleColumns(); track col.key) {
8113
- <td
8114
- [style.text-align]="col.align ?? 'start'"
8115
- [class.strct-dg__cell--sticky]="isSticky(col)"
8116
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8117
- [style.left.px]="stickyLeft(col.key)"
8118
- >
8119
- @if (cellTemplate(col.key); as tpl) {
8120
- <ng-container
8121
- [ngTemplateOutlet]="tpl"
8122
- [ngTemplateOutletContext]="{
8123
- $implicit: row,
8124
- value: row[col.key],
8125
- column: col,
8126
- }"
8127
- />
8128
- } @else {
8129
- {{ row[col.key] }}
8130
- }
8131
- </td>
8132
- }
8133
- @if (canActions()) {
8134
- <td class="strct-dg__actioncell">
8135
- <button
8136
- type="button"
8137
- class="strct-dg__kebab"
8138
- [attr.aria-label]="L().rowActions"
8139
- (click)="openRowMenu(row, $event)"
8436
+ <button
8437
+ type="button"
8438
+ class="strct-dg__detailbtn"
8439
+ [class.strct-dg__detailbtn--active]="row === activeRow()"
8440
+ [attr.aria-expanded]="row === activeRow()"
8441
+ [attr.aria-label]="L().openDetail"
8442
+ (click)="openDetail(row)"
8443
+ >
8444
+ <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
8445
+ </button>
8446
+ </td>
8447
+ }
8448
+ @if (canExpand()) {
8449
+ <td
8450
+ class="strct-dg__expandcell"
8451
+ [class.strct-dg__cell--sticky]="stickyActive()"
8452
+ [style.insetInlineStart.px]="utilLeft('expand')"
8140
8453
  >
8141
- <strct-icon name="dots" [size]="16" />
8142
- </button>
8143
- </td>
8144
- }
8145
- </tr>
8146
- @if (canExpand() && isExpanded(row)) {
8147
- <tr class="strct-dg__detailrow">
8148
- <td [attr.colspan]="colspan()">
8149
- <div class="strct-dg__detail">
8150
- <ng-container
8151
- [ngTemplateOutlet]="detailDef()!.template"
8152
- [ngTemplateOutletContext]="{ $implicit: row }"
8454
+ <button
8455
+ type="button"
8456
+ class="strct-dg__expandbtn"
8457
+ [class.strct-dg__expandbtn--open]="isExpanded(row)"
8458
+ [attr.aria-expanded]="isExpanded(row)"
8459
+ [attr.aria-label]="L().toggleDetail"
8460
+ (click)="toggleExpand(row)"
8461
+ >
8462
+ <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8463
+ </button>
8464
+ </td>
8465
+ }
8466
+ @if (selectable()) {
8467
+ <td
8468
+ class="strct-dg__sel"
8469
+ [class.strct-dg__cell--sticky]="stickyActive()"
8470
+ [style.insetInlineStart.px]="utilLeft('sel')"
8471
+ >
8472
+ <strct-checkbox
8473
+ [ariaLabel]="L().selectRow"
8474
+ [checked]="isSelected(row)"
8475
+ (checkedChange)="toggleRow(row)"
8153
8476
  />
8154
- </div>
8155
- </td>
8477
+ </td>
8478
+ }
8479
+ @for (col of visibleColumns(); track col.key) {
8480
+ <td
8481
+ [style.text-align]="col.align ?? 'start'"
8482
+ [class.strct-dg__cell--sticky]="isSticky(col)"
8483
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8484
+ [style.insetInlineStart.px]="stickyLeft(col.key)"
8485
+ >
8486
+ @if (cellTemplate(col.key); as tpl) {
8487
+ <ng-container
8488
+ [ngTemplateOutlet]="tpl"
8489
+ [ngTemplateOutletContext]="{
8490
+ $implicit: row,
8491
+ value: row[col.key],
8492
+ column: col,
8493
+ }"
8494
+ />
8495
+ } @else {
8496
+ {{ row[col.key] }}
8497
+ }
8498
+ </td>
8499
+ }
8500
+ @if (canActions()) {
8501
+ <td class="strct-dg__actioncell">
8502
+ <button
8503
+ type="button"
8504
+ class="strct-dg__kebab"
8505
+ [attr.aria-label]="L().rowActions"
8506
+ (click)="openRowMenu(row, $event)"
8507
+ >
8508
+ <strct-icon name="dots" [size]="16" />
8509
+ </button>
8510
+ </td>
8511
+ }
8156
8512
  </tr>
8513
+ @if (canExpand() && isExpanded(row)) {
8514
+ <tr class="strct-dg__detailrow">
8515
+ <td [attr.colspan]="colspan()">
8516
+ <div class="strct-dg__detail">
8517
+ <ng-container
8518
+ [ngTemplateOutlet]="detailDef()!.template"
8519
+ [ngTemplateOutletContext]="{ $implicit: row }"
8520
+ />
8521
+ </div>
8522
+ </td>
8523
+ </tr>
8524
+ }
8157
8525
  }
8158
8526
  } @empty {
8159
8527
  <tr>
@@ -8247,13 +8615,13 @@ class StrctDatagrid {
8247
8615
  </span>
8248
8616
  </div>
8249
8617
  <div class="strct-dg__foot-right">
8250
- @if (pageSize() > 0) {
8618
+ @if (pageSize() > 0 && !groupBy()) {
8251
8619
  <strct-pagination [total]="totalCount()" [pageSize]="pageSize()" [(page)]="page" />
8252
8620
  }
8253
8621
  </div>
8254
8622
  </div>
8255
8623
  }
8256
- `, isInline: true, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1px;width:7px;pointer-events:none;background:linear-gradient(90deg,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "isDisabled", "disabled", "indeterminate", "ariaLabel"], outputs: ["checkedChange", "isDisabledChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8624
+ `, isInline: true, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:12.5px;font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:9px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }, { kind: "component", type: StrctPagination, selector: "strct-pagination", inputs: ["prevLabel", "nextLabel", "regionLabel", "total", "pageSize", "page"], outputs: ["pageChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: StrctCheckbox, selector: "strct-checkbox", inputs: ["checked", "isDisabled", "disabled", "indeterminate", "ariaLabel"], outputs: ["checkedChange", "isDisabledChange"] }, { kind: "component", type: StrctButton, selector: "button[strct-button], a[strct-button]", inputs: ["variant", "size", "solid", "block", "iconOnly"] }, { kind: "component", type: StrctButtonGroup, selector: "strct-button-group" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8257
8625
  }
8258
8626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctDatagrid, decorators: [{
8259
8627
  type: Component,
@@ -8285,21 +8653,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8285
8653
  <th
8286
8654
  class="strct-dg__expandcol"
8287
8655
  [class.strct-dg__cell--sticky]="stickyActive()"
8288
- [style.left.px]="utilLeft('detail')"
8656
+ [style.insetInlineStart.px]="utilLeft('detail')"
8289
8657
  ></th>
8290
8658
  }
8291
8659
  @if (canExpand()) {
8292
8660
  <th
8293
8661
  class="strct-dg__expandcol"
8294
8662
  [class.strct-dg__cell--sticky]="stickyActive()"
8295
- [style.left.px]="utilLeft('expand')"
8663
+ [style.insetInlineStart.px]="utilLeft('expand')"
8296
8664
  ></th>
8297
8665
  }
8298
8666
  @if (selectable()) {
8299
8667
  <th
8300
8668
  class="strct-dg__sel"
8301
8669
  [class.strct-dg__cell--sticky]="stickyActive()"
8302
- [style.left.px]="utilLeft('sel')"
8670
+ [style.insetInlineStart.px]="utilLeft('sel')"
8303
8671
  >
8304
8672
  <strct-checkbox
8305
8673
  [ariaLabel]="L().selectAll"
@@ -8314,11 +8682,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8314
8682
  [style.text-align]="col.align ?? 'start'"
8315
8683
  [style.width]="colWidth(col.key) ?? col.width ?? null"
8316
8684
  [class.strct-dg__th--sortable]="col.sortable"
8685
+ [class.strct-dg__th--drop]="col.key === dropKey()"
8317
8686
  [class.strct-dg__cell--sticky]="isSticky(col)"
8318
8687
  [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8319
- [style.left.px]="stickyLeft(col.key)"
8688
+ [style.insetInlineStart.px]="stickyLeft(col.key)"
8320
8689
  [attr.tabindex]="col.sortable ? 0 : null"
8321
8690
  [attr.aria-sort]="col.sortable ? ariaSort(col.key) : null"
8691
+ [attr.draggable]="reorderable() ? true : null"
8692
+ (dragstart)="onColDragStart(col.key, $event)"
8693
+ (dragover)="onColDragOver(col.key, $event)"
8694
+ (drop)="onColDrop(col.key)"
8695
+ (dragend)="onColDragEnd()"
8322
8696
  (click)="col.sortable && sortBy(col.key)"
8323
8697
  (keydown.enter)="col.sortable && sortBy(col.key)"
8324
8698
  (keydown.space)="col.sortable && onHeaderSpace($event, col.key)"
@@ -8375,105 +8749,129 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8375
8749
  <td [attr.colspan]="colspan()" [style.height.px]="topPad()"></td>
8376
8750
  </tr>
8377
8751
  }
8378
- @for (row of renderRows(); track rowKey(row)) {
8379
- <tr
8380
- [class.strct-dg__row--selected]="isSelected(row)"
8381
- [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
8382
- >
8383
- @if (canDetail()) {
8384
- <td
8385
- class="strct-dg__expandcell"
8386
- [class.strct-dg__cell--sticky]="stickyActive()"
8387
- [style.left.px]="utilLeft('detail')"
8388
- >
8752
+ @for (it of displayItems(); track itemKey(it)) {
8753
+ @if (it.group; as grp) {
8754
+ <!-- Group header: distinct value + count, collapsible. -->
8755
+ <tr class="strct-dg__grouprow">
8756
+ <td [attr.colspan]="colspan()">
8389
8757
  <button
8390
8758
  type="button"
8391
- class="strct-dg__detailbtn"
8392
- [class.strct-dg__detailbtn--active]="row === activeRow()"
8393
- [attr.aria-expanded]="row === activeRow()"
8394
- [attr.aria-label]="L().openDetail"
8395
- (click)="openDetail(row)"
8759
+ class="strct-dg__groupbtn"
8760
+ [attr.aria-expanded]="!grp.collapsed"
8761
+ (click)="toggleGroup(grp.key)"
8396
8762
  >
8397
- <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
8763
+ <span
8764
+ class="strct-dg__groupchev"
8765
+ [class.strct-dg__groupchev--open]="!grp.collapsed"
8766
+ >
8767
+ <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8768
+ </span>
8769
+ <span class="strct-dg__grouplabel">{{ grp.label }}</span>
8770
+ <span class="strct-dg__groupcount">{{ grp.count }}</span>
8398
8771
  </button>
8399
8772
  </td>
8400
- }
8401
- @if (canExpand()) {
8402
- <td
8403
- class="strct-dg__expandcell"
8404
- [class.strct-dg__cell--sticky]="stickyActive()"
8405
- [style.left.px]="utilLeft('expand')"
8406
- >
8407
- <button
8408
- type="button"
8409
- class="strct-dg__expandbtn"
8410
- [class.strct-dg__expandbtn--open]="isExpanded(row)"
8411
- [attr.aria-expanded]="isExpanded(row)"
8412
- [attr.aria-label]="L().toggleDetail"
8413
- (click)="toggleExpand(row)"
8773
+ </tr>
8774
+ } @else {
8775
+ @let row = it.row!;
8776
+ <tr
8777
+ [class.strct-dg__row--selected]="isSelected(row)"
8778
+ [class.strct-dg__row--active]="paneOpen() && row === activeRow()"
8779
+ >
8780
+ @if (canDetail()) {
8781
+ <td
8782
+ class="strct-dg__expandcell"
8783
+ [class.strct-dg__cell--sticky]="stickyActive()"
8784
+ [style.insetInlineStart.px]="utilLeft('detail')"
8414
8785
  >
8415
- <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8416
- </button>
8417
- </td>
8418
- }
8419
- @if (selectable()) {
8420
- <td
8421
- class="strct-dg__sel"
8422
- [class.strct-dg__cell--sticky]="stickyActive()"
8423
- [style.left.px]="utilLeft('sel')"
8424
- >
8425
- <strct-checkbox
8426
- [ariaLabel]="L().selectRow"
8427
- [checked]="isSelected(row)"
8428
- (checkedChange)="toggleRow(row)"
8429
- />
8430
- </td>
8431
- }
8432
- @for (col of visibleColumns(); track col.key) {
8433
- <td
8434
- [style.text-align]="col.align ?? 'start'"
8435
- [class.strct-dg__cell--sticky]="isSticky(col)"
8436
- [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8437
- [style.left.px]="stickyLeft(col.key)"
8438
- >
8439
- @if (cellTemplate(col.key); as tpl) {
8440
- <ng-container
8441
- [ngTemplateOutlet]="tpl"
8442
- [ngTemplateOutletContext]="{
8443
- $implicit: row,
8444
- value: row[col.key],
8445
- column: col,
8446
- }"
8447
- />
8448
- } @else {
8449
- {{ row[col.key] }}
8450
- }
8451
- </td>
8452
- }
8453
- @if (canActions()) {
8454
- <td class="strct-dg__actioncell">
8455
- <button
8456
- type="button"
8457
- class="strct-dg__kebab"
8458
- [attr.aria-label]="L().rowActions"
8459
- (click)="openRowMenu(row, $event)"
8786
+ <button
8787
+ type="button"
8788
+ class="strct-dg__detailbtn"
8789
+ [class.strct-dg__detailbtn--active]="row === activeRow()"
8790
+ [attr.aria-expanded]="row === activeRow()"
8791
+ [attr.aria-label]="L().openDetail"
8792
+ (click)="openDetail(row)"
8793
+ >
8794
+ <strct-icon name="chevronDoubleRight" [size]="13" [strokeWidth]="1.6" />
8795
+ </button>
8796
+ </td>
8797
+ }
8798
+ @if (canExpand()) {
8799
+ <td
8800
+ class="strct-dg__expandcell"
8801
+ [class.strct-dg__cell--sticky]="stickyActive()"
8802
+ [style.insetInlineStart.px]="utilLeft('expand')"
8460
8803
  >
8461
- <strct-icon name="dots" [size]="16" />
8462
- </button>
8463
- </td>
8464
- }
8465
- </tr>
8466
- @if (canExpand() && isExpanded(row)) {
8467
- <tr class="strct-dg__detailrow">
8468
- <td [attr.colspan]="colspan()">
8469
- <div class="strct-dg__detail">
8470
- <ng-container
8471
- [ngTemplateOutlet]="detailDef()!.template"
8472
- [ngTemplateOutletContext]="{ $implicit: row }"
8804
+ <button
8805
+ type="button"
8806
+ class="strct-dg__expandbtn"
8807
+ [class.strct-dg__expandbtn--open]="isExpanded(row)"
8808
+ [attr.aria-expanded]="isExpanded(row)"
8809
+ [attr.aria-label]="L().toggleDetail"
8810
+ (click)="toggleExpand(row)"
8811
+ >
8812
+ <strct-icon name="chevronRight" [size]="12" [strokeWidth]="1.7" />
8813
+ </button>
8814
+ </td>
8815
+ }
8816
+ @if (selectable()) {
8817
+ <td
8818
+ class="strct-dg__sel"
8819
+ [class.strct-dg__cell--sticky]="stickyActive()"
8820
+ [style.insetInlineStart.px]="utilLeft('sel')"
8821
+ >
8822
+ <strct-checkbox
8823
+ [ariaLabel]="L().selectRow"
8824
+ [checked]="isSelected(row)"
8825
+ (checkedChange)="toggleRow(row)"
8473
8826
  />
8474
- </div>
8475
- </td>
8827
+ </td>
8828
+ }
8829
+ @for (col of visibleColumns(); track col.key) {
8830
+ <td
8831
+ [style.text-align]="col.align ?? 'start'"
8832
+ [class.strct-dg__cell--sticky]="isSticky(col)"
8833
+ [class.strct-dg__cell--sticky-last]="col.key === lastStickyKey()"
8834
+ [style.insetInlineStart.px]="stickyLeft(col.key)"
8835
+ >
8836
+ @if (cellTemplate(col.key); as tpl) {
8837
+ <ng-container
8838
+ [ngTemplateOutlet]="tpl"
8839
+ [ngTemplateOutletContext]="{
8840
+ $implicit: row,
8841
+ value: row[col.key],
8842
+ column: col,
8843
+ }"
8844
+ />
8845
+ } @else {
8846
+ {{ row[col.key] }}
8847
+ }
8848
+ </td>
8849
+ }
8850
+ @if (canActions()) {
8851
+ <td class="strct-dg__actioncell">
8852
+ <button
8853
+ type="button"
8854
+ class="strct-dg__kebab"
8855
+ [attr.aria-label]="L().rowActions"
8856
+ (click)="openRowMenu(row, $event)"
8857
+ >
8858
+ <strct-icon name="dots" [size]="16" />
8859
+ </button>
8860
+ </td>
8861
+ }
8476
8862
  </tr>
8863
+ @if (canExpand() && isExpanded(row)) {
8864
+ <tr class="strct-dg__detailrow">
8865
+ <td [attr.colspan]="colspan()">
8866
+ <div class="strct-dg__detail">
8867
+ <ng-container
8868
+ [ngTemplateOutlet]="detailDef()!.template"
8869
+ [ngTemplateOutletContext]="{ $implicit: row }"
8870
+ />
8871
+ </div>
8872
+ </td>
8873
+ </tr>
8874
+ }
8477
8875
  }
8478
8876
  } @empty {
8479
8877
  <tr>
@@ -8567,7 +8965,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8567
8965
  </span>
8568
8966
  </div>
8569
8967
  <div class="strct-dg__foot-right">
8570
- @if (pageSize() > 0) {
8968
+ @if (pageSize() > 0 && !groupBy()) {
8571
8969
  <strct-pagination [total]="totalCount()" [pageSize]="pageSize()" [(page)]="page" />
8572
8970
  }
8573
8971
  </div>
@@ -8579,8 +8977,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
8579
8977
  '[class.strct-dg-host--singleline]': 'singleLine()',
8580
8978
  '[class.strct-dg-host--virtual]': 'virtual()',
8581
8979
  '[class.strct-dg-host--sticky]': 'stickyActive()',
8582
- }, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-1px;width:7px;pointer-events:none;background:linear-gradient(90deg,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"] }]
8583
- }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPane: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], sync: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], columnState: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnState", required: false }] }, { type: i0.Output, args: ["columnStateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
8980
+ }, styles: [".strct-dg-host{display:block;border:1px solid var(--b2);border-radius:10px;background:var(--bg-2);box-shadow:var(--shadow-rest)}.strct-dg__scroll{flex:1 1 auto;min-width:0;overflow-x:auto;-webkit-overflow-scrolling:touch}.strct-dg-host--virtual .strct-dg__scroll{overflow-y:auto}.strct-dg-host--virtual .strct-dg thead th{position:sticky;top:0;z-index:5}.strct-dg__vspacer td{padding:0;border:0;background:transparent}.strct-dg th[draggable]{cursor:grab}.strct-dg__th--drop{box-shadow:inset 3px 0 0 var(--acc)}.strct-dg__grouprow td{background:var(--bg-2)!important;padding:0}.strct-dg__groupbtn{display:flex;align-items:center;gap:8px;width:100%;padding:7px 13px;border:0;background:transparent;color:var(--t1);font-family:var(--font);font-size:12.5px;font-weight:600;cursor:pointer;text-align:start}.strct-dg__groupbtn:hover{background:var(--bg-3)}.strct-dg__groupbtn:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__groupchev{display:inline-flex;color:var(--t3);transition:transform .15s ease}.strct-dg__groupchev--open{transform:rotate(90deg)}@media(prefers-reduced-motion:reduce){.strct-dg__groupchev{transition:none}}.strct-dg__groupcount{min-width:18px;height:18px;padding:0 5px;display:inline-flex;align-items:center;justify-content:center;font-size:12px;border-radius:9px;background:var(--acc-m);color:var(--acc);font-variant-numeric:tabular-nums}.strct-dg-host--sticky .strct-dg,.strct-dg-host--virtual .strct-dg{border-collapse:separate;border-spacing:0;overflow:visible;border-radius:0}.strct-dg-host--sticky .strct-dg{width:max-content;min-width:100%}.strct-dg .strct-dg__cell--sticky{position:sticky;z-index:3}.strct-dg thead th.strct-dg__cell--sticky{z-index:6}.strct-dg .strct-dg__cell--sticky-last:after{content:\"\";position:absolute;top:0;bottom:0;inset-inline-end:-1px;width:7px;pointer-events:none;background:linear-gradient(to right,rgba(0,0,0,.14),transparent)}[dir=rtl] .strct-dg .strct-dg__cell--sticky-last:after{background:linear-gradient(to left,rgba(0,0,0,.14),transparent)}.strct-dg-host--sticky .strct-dg__expandcol,.strct-dg-host--sticky .strct-dg__expandcell{width:36px;min-width:36px;max-width:36px;box-sizing:border-box}.strct-dg-host--sticky .strct-dg__sel{width:40px;min-width:40px;max-width:40px;box-sizing:border-box}.strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__cell--sticky{background:var(--bg-3)}.strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody tr:hover .strct-dg__cell--sticky{background:linear-gradient(var(--acc-s),var(--acc-s)) var(--bg-3)}.strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-1)}[data-theme=dark] .strct-dg-host--sticky tbody .strct-dg__row--selected .strct-dg__cell--sticky{background:linear-gradient(var(--acc-m),var(--acc-m)) var(--bg-3)}.strct-dg{width:100%;border-collapse:collapse;font-size:13px;overflow:hidden;background:var(--bg-2)}.strct-dg-host:not(:has(.strct-dg__toolbar)) .strct-dg{border-start-start-radius:9px;border-start-end-radius:9px}.strct-dg-host:not(:has(.strct-dg__foot)) .strct-dg{border-end-start-radius:9px;border-end-end-radius:9px}.strct-dg th,.strct-dg td{padding:9px 13px;text-align:start;border-bottom:1px solid var(--b1)}.strct-dg tbody td{background:var(--bg-1)}[data-theme=dark] .strct-dg tbody td{background:var(--bg-3)}.strct-dg-host--compact .strct-dg th,.strct-dg-host--compact .strct-dg td{padding:5px 11px}.strct-dg-host--singleline .strct-dg tbody tr:not(.strct-dg__detailrow)>td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:360px}.strct-dg th{position:relative;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--t2);background:var(--bg-2);white-space:nowrap;-webkit-user-select:none;user-select:none}.strct-dg__th--sortable{cursor:pointer}.strct-dg__th--sortable:hover{color:var(--t1)}.strct-dg__th--sortable:focus-visible{outline:2px solid var(--acc50);outline-offset:-2px}.strct-dg__hd{display:inline-flex;align-items:center;gap:5px}.strct-dg__sorticon{color:var(--t3)}.strct-dg__th--sortable:hover .strct-dg__sorticon{color:var(--acc)}.strct-dg__resize{position:absolute;inset-inline-end:0;top:0;bottom:0;width:4px;cursor:col-resize;background:transparent;z-index:2}.strct-dg__resize:hover{background:var(--acc)}.strct-dg td{color:var(--t1)}.strct-dg tbody tr:last-child td{border-bottom:0}.strct-dg tbody tr:not(.strct-dg__detailrow):hover td{background:var(--acc-s)}.strct-dg__row--selected td{background:var(--acc-m)}.strct-dg__sel{width:1%;white-space:nowrap}.strct-dg__sel input{accent-color:var(--acc);width:15px;height:15px;cursor:pointer}.strct-dg__expandcol,.strct-dg__expandcell{width:1%;white-space:nowrap}.strct-dg__actioncol,.strct-dg__actioncell{width:1%;white-space:nowrap;text-align:end}.strct-dg__kebab{display:inline-flex;padding:4px;border:0;border-radius:5px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__kebab:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:transform .15s ease,color .15s ease}.strct-dg__expandbtn:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__expandbtn--open{transform:rotate(90deg);color:var(--acc)}.strct-dg__detailbtn{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer;transition:color .14s ease,background .14s ease}.strct-dg__detailbtn:hover{color:var(--acc);background:var(--bg-3)}.strct-dg__detailbtn--active{color:var(--acc);background:var(--acc-m)}.strct-dg__detailrow td{background:var(--bg-2);padding:0}.strct-dg__detail{padding:14px 16px;font-size:13px;color:var(--t2)}.strct-dg__layout{display:flex;align-items:flex-start;min-width:0}.strct-dg__layout--paned{gap:0}.strct-dg__layout--paned .strct-dg__scroll{flex:0 0 auto;width:260px;min-width:260px;max-width:260px}.strct-dg__layout--paned .strct-dg{width:260px;min-width:260px;max-width:260px;flex-shrink:0;border-top-right-radius:0;border-bottom-right-radius:0}.strct-dg__row--clickable{cursor:pointer}.strct-dg__row--active td{background:var(--acc-m)}.strct-dg__layout--paned .strct-dg__row--active td:last-child{position:relative;padding-inline-end:26px}.strct-dg__layout--paned .strct-dg__row--active td:last-child:after{content:\"\";position:absolute;right:11px;top:50%;width:6px;height:6px;border-top:1.6px solid var(--acc);border-inline-end:1.6px solid var(--acc);transform:translateY(-50%) rotate(45deg)}.strct-dg__pane{flex:1;min-width:0;align-self:stretch;background:var(--bg-1);border:1px solid var(--b2);border-inline-start:2px solid var(--acc);border-radius:0 8px 8px 0;overflow:hidden;animation:strct-dg-pane-in .14s ease}.strct-dg__pane-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px;border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-dg__pane-close{display:inline-flex;padding:3px;border:0;border-radius:4px;background:transparent;color:var(--t3);cursor:pointer}.strct-dg__pane-close:hover{color:var(--t1);background:var(--bg-3)}.strct-dg__pane-body{padding:14px 16px;font-size:13px;color:var(--t2)}@keyframes strct-dg-pane-in{0%{opacity:0;transform:translate(8px)}}@keyframes strct-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.strct-dg__skeleton-block{height:12px;background:var(--bg-3);border-radius:var(--radius-sm);animation:strct-skeleton-pulse 1.4s ease infinite}.strct-dg__skeleton-row td{border-bottom:1px solid var(--b1)}.strct-dg__empty{text-align:center;color:var(--t3);padding:22px}.strct-dg__toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:9px 14px;border-bottom:1px solid var(--b2)}.strct-dg__foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--b2);flex-wrap:wrap}.strct-dg__foot-left,.strct-dg__foot-right{display:flex;align-items:center;gap:12px}.strct-dg__actions{position:relative}.strct-dg__chooser-menu{position:absolute;bottom:calc(100% + 6px);left:0;z-index:10;min-width:180px;background:var(--bg-1);border:1px solid var(--b2);border-radius:8px;box-shadow:var(--shadow-pop);padding:6px;display:flex;flex-direction:column;gap:2px}.strct-dg__chooser-menu--right{inset-inline-start:auto;inset-inline-end:0}.strct-dg__chooser-item{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;color:var(--t1);transition:background .1s ease;-webkit-user-select:none;user-select:none}.strct-dg__chooser-item:hover{background:var(--bg-3)}.strct-dg__count{font-size:12px;color:var(--t2)}.strct-dg__count-sep{margin:0 8px;color:var(--b2)}.strct-dg__count-sel{color:var(--acc)}\n"] }]
8981
+ }], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], detailPane: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailPane", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], singleLine: [{ type: i0.Input, args: [{ isSignal: true, alias: "singleLine", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], columnChooser: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnChooser", required: false }] }], sync: [{ type: i0.Input, args: [{ isSignal: true, alias: "sync", required: false }] }], footerActionsDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerActionsDisabled", required: false }] }], rowId: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowId", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], initialSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSelection", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], viewportHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewportHeight", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], lazy: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazy", required: false }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], columnState: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnState", required: false }] }, { type: i0.Output, args: ["columnStateChange"] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], syncChange: [{ type: i0.Output, args: ["syncChange"] }], rowAction: [{ type: i0.Output, args: ["rowAction"] }], lazyLoad: [{ type: i0.Output, args: ["lazyLoad"] }], detailDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctRowDetailDef), { isSignal: true }] }], actionBarDef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => StrctDatagridActionBar), { isSignal: true }] }], cellDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => StrctCellDef), { isSignal: true }] }] } });
8584
8982
 
8585
8983
  /** Vertical timeline container. Wraps `<strct-timeline-item>` children. */
8586
8984
  class StrctTimeline {
@@ -8968,6 +9366,19 @@ class StrctChart {
8968
9366
  * Double-click, Escape or the reset chip zooms back out.
8969
9367
  */
8970
9368
  zoom = input(false, { ...(ngDevMode ? { debugName: "zoom" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9369
+ /**
9370
+ * Stack multi-series values cumulatively: each series draws its line at the
9371
+ * running total and fills the band down to the series below (nulls break the
9372
+ * stack at that slot). Tooltips keep the original per-series values.
9373
+ */
9374
+ stacked = input(false, { ...(ngDevMode ? { debugName: "stacked" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9375
+ /** Y-axis scale. `log` needs positive values; non-positives clamp to the floor. */
9376
+ scale = input('linear', ...(ngDevMode ? [{ debugName: "scale" }] : /* istanbul ignore next */ []));
9377
+ /**
9378
+ * Per-point timestamps (ms epoch or Date). When set, x positions map to real
9379
+ * time, so uneven sampling renders honestly instead of equally spaced.
9380
+ */
9381
+ times = input(null, ...(ngDevMode ? [{ debugName: "times" }] : /* istanbul ignore next */ []));
8971
9382
  /** Tooltip text for a gap (null) point. */
8972
9383
  gapText = input('no data', ...(ngDevMode ? [{ debugName: "gapText" }] : /* istanbul ignore next */ []));
8973
9384
  /** Accessible label of the reset-zoom chip (localizable). */
@@ -9090,7 +9501,8 @@ class StrctChart {
9090
9501
  const [s, e] = this.domain();
9091
9502
  return e > s ? this.chartW() / (e - s) : 0;
9092
9503
  }
9093
- /** Real values inside the visible window (multi-aware; bands included). */
9504
+ /** Real values inside the visible window (multi-aware; bands included;
9505
+ * per-slot totals when stacked). */
9094
9506
  visibleValues = computed(() => {
9095
9507
  const [s, e] = this.domain();
9096
9508
  const out = [];
@@ -9099,6 +9511,26 @@ class StrctChart {
9099
9511
  out.push(v);
9100
9512
  };
9101
9513
  const ser = this.seriesResolved();
9514
+ if (ser && this.stacked() && ser.length > 1) {
9515
+ const N = this.nx();
9516
+ const totals = new Array(N).fill(0);
9517
+ const has = new Array(N).fill(false);
9518
+ for (const x of ser) {
9519
+ const data = x.data ?? [];
9520
+ const offset = N - data.length;
9521
+ for (let li = 0; li < data.length; li++) {
9522
+ const v = data[li];
9523
+ if (isVal(v)) {
9524
+ totals[offset + li] += v;
9525
+ has[offset + li] = true;
9526
+ }
9527
+ }
9528
+ }
9529
+ for (let gi = s; gi <= Math.min(e, N - 1); gi++)
9530
+ if (has[gi])
9531
+ out.push(totals[gi]);
9532
+ return out;
9533
+ }
9102
9534
  if (ser) {
9103
9535
  const N = this.nx();
9104
9536
  for (const x of ser) {
@@ -9128,13 +9560,34 @@ class StrctChart {
9128
9560
  return m === 0 ? 1 : m * 1.1;
9129
9561
  }, ...(ngDevMode ? [{ debugName: "yMax" }] : /* istanbul ignore next */ []));
9130
9562
  yMin = computed(() => this.min() ?? 0, ...(ngDevMode ? [{ debugName: "yMin" }] : /* istanbul ignore next */ []));
9563
+ /** Smallest positive visible value — the floor of a log axis. */
9564
+ logFloor = computed(() => {
9565
+ const explicit = this.min();
9566
+ if (explicit != null && explicit > 0)
9567
+ return explicit;
9568
+ const positives = this.visibleValues().filter((v) => v > 0);
9569
+ return positives.length ? Math.min(...positives) : 1;
9570
+ }, ...(ngDevMode ? [{ debugName: "logFloor" }] : /* istanbul ignore next */ []));
9131
9571
  yOf(v) {
9572
+ if (this.scale() === 'log') {
9573
+ const lo = this.logFloor();
9574
+ const hi = Math.max(this.yMax(), lo * 10);
9575
+ const c = Math.max(lo, Math.min(hi, v));
9576
+ const span = Math.log10(hi) - Math.log10(lo) || 1;
9577
+ const f = (Math.log10(c) - Math.log10(lo)) / span;
9578
+ return PAD.t + (1 - f) * this.chartH();
9579
+ }
9132
9580
  const lo = this.yMin();
9133
9581
  const hi = this.yMax();
9134
9582
  const range = hi - lo || 1;
9135
9583
  const c = Math.max(lo, Math.min(hi, v));
9136
9584
  return PAD.t + (1 - (c - lo) / range) * this.chartH();
9137
9585
  }
9586
+ /** Timestamps normalized to ms (null when the axis is index-based). */
9587
+ timesMs = computed(() => {
9588
+ const t = this.times();
9589
+ return t?.length ? t.map((v) => +v) : null;
9590
+ }, ...(ngDevMode ? [{ debugName: "timesMs" }] : /* istanbul ignore next */ []));
9138
9591
  // ── Single-series geometry (gap-aware; null keeps its x-slot) ──
9139
9592
  points = computed(() => {
9140
9593
  const d = this.data();
@@ -9164,6 +9617,46 @@ class StrctChart {
9164
9617
  return [];
9165
9618
  const N = this.nx();
9166
9619
  const base = this.height() - PAD.b;
9620
+ // Stacked: each series rides on the running total of the ones before it,
9621
+ // filling the band down to that total; a null breaks the stack there.
9622
+ if (this.stacked() && s.length > 1) {
9623
+ const running = new Array(N).fill(0);
9624
+ return s.map((x) => {
9625
+ const data = x.data ?? [];
9626
+ const offset = N - data.length;
9627
+ const pts = [];
9628
+ const lowerPts = [];
9629
+ for (let li = 0; li < data.length; li++) {
9630
+ const v = data[li];
9631
+ const gi = offset + li;
9632
+ if (isVal(v)) {
9633
+ const lo = running[gi];
9634
+ const hi = lo + v;
9635
+ running[gi] = hi;
9636
+ const px = this.xOf(gi);
9637
+ lowerPts.push({ x: px, y: this.yOf(lo) });
9638
+ pts.push({ x: px, y: this.yOf(hi) });
9639
+ }
9640
+ else {
9641
+ lowerPts.push(null);
9642
+ pts.push(null);
9643
+ }
9644
+ }
9645
+ const curve = x.curve ?? this.curve();
9646
+ return {
9647
+ color: COLOR$1[x.status ?? this.status()],
9648
+ label: x.label ?? '',
9649
+ area: false,
9650
+ dash: x.dash ? (typeof x.dash === 'string' ? x.dash : '5 4') : null,
9651
+ pts,
9652
+ path: pathForSegs(pts, curve),
9653
+ areaPath: '',
9654
+ bandPath: bandPath(pts, lowerPts, curve),
9655
+ offset,
9656
+ data,
9657
+ };
9658
+ });
9659
+ }
9167
9660
  return s.map((x) => {
9168
9661
  const data = x.data ?? [];
9169
9662
  const offset = N - data.length; // right-align shorter series
@@ -9234,10 +9727,23 @@ class StrctChart {
9234
9727
  yAxisTicks = computed(() => {
9235
9728
  if (!this.yAxis())
9236
9729
  return [];
9730
+ const out = [];
9731
+ if (this.scale() === 'log') {
9732
+ // Decade ticks between the floor and the max, endpoints included.
9733
+ const lo = this.logFloor();
9734
+ const hi = Math.max(this.yMax(), lo * 10);
9735
+ const values = new Set([lo, hi]);
9736
+ for (let p = Math.ceil(Math.log10(lo)); p <= Math.floor(Math.log10(hi)); p++) {
9737
+ values.add(10 ** p);
9738
+ }
9739
+ for (const v of [...values].sort((a, b) => a - b)) {
9740
+ out.push({ value: v, y: this.yOf(v), text: this.fmtAxis(v) });
9741
+ }
9742
+ return out;
9743
+ }
9237
9744
  const n = Math.max(2, this.yTicks());
9238
9745
  const lo = this.yMin();
9239
9746
  const hi = this.yMax();
9240
- const out = [];
9241
9747
  for (let i = 0; i < n; i++) {
9242
9748
  const v = lo + (hi - lo) * (i / (n - 1));
9243
9749
  out.push({ value: v, y: this.yOf(v), text: this.fmtAxis(v) });
@@ -9449,9 +9955,17 @@ class StrctChart {
9449
9955
  const value = this.hoverGap() ? this.gapText() : this.hoverValueText();
9450
9956
  return `${meta ? meta + ': ' : ''}${value}`;
9451
9957
  }, ...(ngDevMode ? [{ debugName: "srText" }] : /* istanbul ignore next */ []));
9452
- /** Pixel x of a data index (shared by the plot, labels and annotations). */
9958
+ /** Pixel x of a data index (shared by the plot, labels and annotations).
9959
+ * With `times`, positions map to real timestamps — uneven sampling shows. */
9453
9960
  xOf(i) {
9454
- const [s] = this.domain();
9961
+ const [s, e] = this.domain();
9962
+ const t = this.timesMs();
9963
+ if (t && e > s) {
9964
+ const clamp = (k) => Math.min(Math.max(k, 0), t.length - 1);
9965
+ const t0 = t[clamp(s)];
9966
+ const span = t[clamp(e)] - t0 || 1;
9967
+ return this.pl() + ((t[clamp(i)] - t0) / span) * this.chartW();
9968
+ }
9455
9969
  return this.pl() + (i - s) * this.stepX();
9456
9970
  }
9457
9971
  /** Set the local hover index, emitting `hoverIndex` on change. */
@@ -9510,10 +10024,24 @@ class StrctChart {
9510
10024
  const rect = el.getBoundingClientRect();
9511
10025
  if (!rect.width)
9512
10026
  return null;
10027
+ const [s, e] = this.domain();
10028
+ if (this.timesMs()) {
10029
+ // Non-uniform x: nearest point by pixel distance.
10030
+ const svgX = ((event.clientX - rect.left) / rect.width) * this.width();
10031
+ let best = s;
10032
+ let bestDist = Infinity;
10033
+ for (let i = s; i <= e; i++) {
10034
+ const d = Math.abs(this.xOf(i) - svgX);
10035
+ if (d < bestDist) {
10036
+ bestDist = d;
10037
+ best = i;
10038
+ }
10039
+ }
10040
+ return best;
10041
+ }
9513
10042
  const plFrac = this.pl() / this.width();
9514
10043
  const rFrac = PAD.r / this.width();
9515
10044
  const fx = ((event.clientX - rect.left) / rect.width - plFrac) / (1 - plFrac - rFrac);
9516
- const [s, e] = this.domain();
9517
10045
  return Math.max(s, Math.min(e, s + Math.round(fx * (e - s))));
9518
10046
  }
9519
10047
  onDown(event) {
@@ -9660,7 +10188,7 @@ class StrctChart {
9660
10188
  });
9661
10189
  }
9662
10190
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctChart, deps: [], target: i0.ɵɵFactoryTarget.Component });
9663
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctChart, isStandalone: true, selector: "strct-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, curve: { classPropertyName: "curve", publicName: "curve", isSignal: true, isRequired: false, transformFunction: null }, area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, glow: { classPropertyName: "glow", publicName: "glow", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, interval: { classPropertyName: "interval", publicName: "interval", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, grid: { classPropertyName: "grid", publicName: "grid", isSignal: true, isRequired: false, transformFunction: null }, dots: { classPropertyName: "dots", publicName: "dots", isSignal: true, isRequired: false, transformFunction: null }, legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, xTicks: { classPropertyName: "xTicks", publicName: "xTicks", isSignal: true, isRequired: false, transformFunction: null }, xFormat: { classPropertyName: "xFormat", publicName: "xFormat", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, yAxis: { classPropertyName: "yAxis", publicName: "yAxis", isSignal: true, isRequired: false, transformFunction: null }, yTicks: { classPropertyName: "yTicks", publicName: "yTicks", isSignal: true, isRequired: false, transformFunction: null }, axisFormat: { classPropertyName: "axisFormat", publicName: "axisFormat", isSignal: true, isRequired: false, transformFunction: null }, thresholds: { classPropertyName: "thresholds", publicName: "thresholds", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, agoFormat: { classPropertyName: "agoFormat", publicName: "agoFormat", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, annotations: { classPropertyName: "annotations", publicName: "annotations", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, brush: { classPropertyName: "brush", publicName: "brush", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, gapText: { classPropertyName: "gapText", publicName: "gapText", isSignal: true, isRequired: false, transformFunction: null }, resetLabel: { classPropertyName: "resetLabel", publicName: "resetLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { hoverIndex: "hoverIndex", brushChange: "brushChange" }, host: { properties: { "class.strct-chart--glow": "glow()", "class.strct-chart--brush": "brush() || zoom()", "style.--strct-chart-c": "color()" }, classAttribute: "strct-chart" }, viewQueries: [{ propertyName: "svgRef", first: true, predicate: ["svg"], descendants: true, isSignal: true }], ngImport: i0, template: `
10191
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctChart, isStandalone: true, selector: "strct-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, series: { classPropertyName: "series", publicName: "series", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, curve: { classPropertyName: "curve", publicName: "curve", isSignal: true, isRequired: false, transformFunction: null }, area: { classPropertyName: "area", publicName: "area", isSignal: true, isRequired: false, transformFunction: null }, glow: { classPropertyName: "glow", publicName: "glow", isSignal: true, isRequired: false, transformFunction: null }, live: { classPropertyName: "live", publicName: "live", isSignal: true, isRequired: false, transformFunction: null }, interval: { classPropertyName: "interval", publicName: "interval", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, grid: { classPropertyName: "grid", publicName: "grid", isSignal: true, isRequired: false, transformFunction: null }, dots: { classPropertyName: "dots", publicName: "dots", isSignal: true, isRequired: false, transformFunction: null }, legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, xTicks: { classPropertyName: "xTicks", publicName: "xTicks", isSignal: true, isRequired: false, transformFunction: null }, xFormat: { classPropertyName: "xFormat", publicName: "xFormat", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, yAxis: { classPropertyName: "yAxis", publicName: "yAxis", isSignal: true, isRequired: false, transformFunction: null }, yTicks: { classPropertyName: "yTicks", publicName: "yTicks", isSignal: true, isRequired: false, transformFunction: null }, axisFormat: { classPropertyName: "axisFormat", publicName: "axisFormat", isSignal: true, isRequired: false, transformFunction: null }, thresholds: { classPropertyName: "thresholds", publicName: "thresholds", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, agoFormat: { classPropertyName: "agoFormat", publicName: "agoFormat", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, annotations: { classPropertyName: "annotations", publicName: "annotations", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, brush: { classPropertyName: "brush", publicName: "brush", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, stacked: { classPropertyName: "stacked", publicName: "stacked", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, times: { classPropertyName: "times", publicName: "times", isSignal: true, isRequired: false, transformFunction: null }, gapText: { classPropertyName: "gapText", publicName: "gapText", isSignal: true, isRequired: false, transformFunction: null }, resetLabel: { classPropertyName: "resetLabel", publicName: "resetLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { hoverIndex: "hoverIndex", brushChange: "brushChange" }, host: { properties: { "class.strct-chart--glow": "glow()", "class.strct-chart--brush": "brush() || zoom()", "style.--strct-chart-c": "color()" }, classAttribute: "strct-chart" }, viewQueries: [{ propertyName: "svgRef", first: true, predicate: ["svg"], descendants: true, isSignal: true }], ngImport: i0, template: `
9664
10192
  @if (isEmpty()) {
9665
10193
  <div class="strct-chart__empty" [style.height.px]="height()">{{ emptyText() }}</div>
9666
10194
  } @else {
@@ -9760,7 +10288,12 @@ class StrctChart {
9760
10288
  @if (isMulti()) {
9761
10289
  @for (s of multiSeries(); track $index) {
9762
10290
  @if (s.bandPath) {
9763
- <path class="strct-chart__band" [attr.d]="s.bandPath" [attr.fill]="s.color" />
10291
+ <path
10292
+ class="strct-chart__band"
10293
+ [class.strct-chart__band--stack]="stacked()"
10294
+ [attr.d]="s.bandPath"
10295
+ [attr.fill]="s.color"
10296
+ />
9764
10297
  }
9765
10298
  @if (s.area && s.areaPath) {
9766
10299
  <path
@@ -9979,7 +10512,7 @@ class StrctChart {
9979
10512
  </div>
9980
10513
  }
9981
10514
  }
9982
- `, isInline: true, styles: [".strct-chart{display:block;position:relative}.strct-chart__plot{position:relative}.strct-chart__svg{width:100%;display:block;touch-action:none}.strct-chart__svg:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-chart__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-chart__empty{display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--t3)}.strct-chart__grid{stroke:var(--b1);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__line{vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}.strct-chart__area{stroke:none}.strct-chart__area--flat{opacity:.14}.strct-chart__dot,.strct-chart__hoverdot,.strct-chart__head-dot{stroke:var(--bg-1);stroke-width:1.5}.strct-chart__cross{stroke:var(--strct-chart-c);stroke-width:1;opacity:.4;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}.strct-chart__threshold{stroke-width:1;opacity:.85;vector-effect:non-scaling-stroke}.strct-chart__threshold--dashed{stroke-dasharray:4 3}.strct-chart__bar{rx:1.5}.strct-chart__band{opacity:.13;stroke:none}.strct-chart__ann{stroke-width:1;opacity:.8;vector-effect:non-scaling-stroke}.strct-chart__ann--dashed{stroke-dasharray:4 3}.strct-chart__ann-label{position:absolute;top:0;transform:translate(-50%);pointer-events:none;font-size:12px;font-weight:600;background:var(--bg-1);padding:0 3px;border-radius:3px;white-space:nowrap}.strct-chart--brush .strct-chart__svg{cursor:crosshair}.strct-chart__brush{fill:var(--acc);opacity:.16;stroke:var(--acc50);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__reset{position:absolute;top:6px;right:8px;z-index:3;display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--bg-a);color:var(--t2);font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer}.strct-chart__reset:hover{color:var(--t1);border-color:var(--acc50)}.strct-chart__reset:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-chart__tip--gap{top:6px;transform:translate(-50%)}.strct-chart__tip-ann{font-size:12px;font-weight:600}.strct-chart--glow .strct-chart__line{filter:drop-shadow(0 0 1.5px var(--strct-chart-c)) drop-shadow(0 0 5px var(--strct-chart-c))}.strct-chart--glow .strct-chart__head-dot{filter:drop-shadow(0 0 2px var(--strct-chart-c)) drop-shadow(0 0 6px var(--strct-chart-c))}.strct-chart--glow .strct-chart__hoverdot{filter:drop-shadow(0 0 4px var(--strct-chart-c))}.strct-chart__ytick{position:absolute;left:0;width:34px;text-align:end;transform:translateY(-50%);pointer-events:none;font-family:var(--mono);font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__thr{position:absolute;right:2px;transform:translateY(-50%);pointer-events:none;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--bg-1);padding:0 3px;border-radius:3px}.strct-chart__axis-y{position:absolute;left:0;transform:translateY(-50%);pointer-events:none;padding:1px 5px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);font-family:var(--mono);font-size:12px;font-weight:600;color:var(--t2);font-variant-numeric:tabular-nums;z-index:2}.strct-chart__label--active{color:var(--t1);font-weight:700}.strct-chart__legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-bottom:8px;font-size:12px;color:var(--t2)}.strct-chart__leg{display:inline-flex;align-items:center;gap:6px}.strct-chart__leg-sw{width:9px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 8px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);box-shadow:var(--shadow-elevated);white-space:nowrap;z-index:2}.strct-chart__tip--multi{top:6px;transform:translate(-50%);align-items:stretch;gap:3px}.strct-chart__tip-v{font-size:12px;font-weight:700;color:var(--t1);font-variant-numeric:tabular-nums}.strct-chart__tip-meta{display:inline-flex;align-items:center;gap:5px}.strct-chart__tip-delta{font-size:12px;font-weight:600;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__tip-delta--up{color:var(--success)}.strct-chart__tip-delta--down{color:var(--critical)}.strct-chart__tip-l{font-size:12px;color:var(--t3)}.strct-chart__tip-row{display:inline-flex;align-items:center;gap:6px;font-size:12px}.strct-chart__tip-sw{width:8px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip-rl{color:var(--t3);margin-inline-end:auto}.strct-chart__tip-rv{color:var(--t1);font-weight:700;font-variant-numeric:tabular-nums}.strct-chart__labels{position:relative;height:15px;margin-top:6px;font-size:12px;color:var(--t3)}.strct-chart__labels span{position:absolute;transform:translate(-50%);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.strct-chart__line--draw{stroke-dasharray:1;stroke-dashoffset:1;animation:strct-chart-draw .9s ease forwards}.strct-chart__pulse{transform-box:fill-box;transform-origin:center;animation:strct-chart-pulse 2.4s ease-out infinite}}@keyframes strct-chart-draw{to{stroke-dashoffset:0}}@keyframes strct-chart-pulse{0%{transform:scale(1);opacity:.45}70%{opacity:0}to{transform:scale(2.5);opacity:0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10515
+ `, isInline: true, styles: [".strct-chart{display:block;position:relative}.strct-chart__plot{position:relative}.strct-chart__svg{width:100%;display:block;touch-action:none}.strct-chart__svg:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-chart__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-chart__empty{display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--t3)}.strct-chart__grid{stroke:var(--b1);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__line{vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}.strct-chart__area{stroke:none}.strct-chart__area--flat{opacity:.14}.strct-chart__dot,.strct-chart__hoverdot,.strct-chart__head-dot{stroke:var(--bg-1);stroke-width:1.5}.strct-chart__cross{stroke:var(--strct-chart-c);stroke-width:1;opacity:.4;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}.strct-chart__threshold{stroke-width:1;opacity:.85;vector-effect:non-scaling-stroke}.strct-chart__threshold--dashed{stroke-dasharray:4 3}.strct-chart__bar{rx:1.5}.strct-chart__band{opacity:.13;stroke:none}.strct-chart__band--stack{opacity:.32}.strct-chart__ann{stroke-width:1;opacity:.8;vector-effect:non-scaling-stroke}.strct-chart__ann--dashed{stroke-dasharray:4 3}.strct-chart__ann-label{position:absolute;top:0;transform:translate(-50%);pointer-events:none;font-size:12px;font-weight:600;background:var(--bg-1);padding:0 3px;border-radius:3px;white-space:nowrap}.strct-chart--brush .strct-chart__svg{cursor:crosshair}.strct-chart__brush{fill:var(--acc);opacity:.16;stroke:var(--acc50);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__reset{position:absolute;top:6px;right:8px;z-index:3;display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--bg-a);color:var(--t2);font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer}.strct-chart__reset:hover{color:var(--t1);border-color:var(--acc50)}.strct-chart__reset:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-chart__tip--gap{top:6px;transform:translate(-50%)}.strct-chart__tip-ann{font-size:12px;font-weight:600}.strct-chart--glow .strct-chart__line{filter:drop-shadow(0 0 1.5px var(--strct-chart-c)) drop-shadow(0 0 5px var(--strct-chart-c))}.strct-chart--glow .strct-chart__head-dot{filter:drop-shadow(0 0 2px var(--strct-chart-c)) drop-shadow(0 0 6px var(--strct-chart-c))}.strct-chart--glow .strct-chart__hoverdot{filter:drop-shadow(0 0 4px var(--strct-chart-c))}.strct-chart__ytick{position:absolute;left:0;width:34px;text-align:end;transform:translateY(-50%);pointer-events:none;font-family:var(--mono);font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__thr{position:absolute;right:2px;transform:translateY(-50%);pointer-events:none;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--bg-1);padding:0 3px;border-radius:3px}.strct-chart__axis-y{position:absolute;left:0;transform:translateY(-50%);pointer-events:none;padding:1px 5px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);font-family:var(--mono);font-size:12px;font-weight:600;color:var(--t2);font-variant-numeric:tabular-nums;z-index:2}.strct-chart__label--active{color:var(--t1);font-weight:700}.strct-chart__legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-bottom:8px;font-size:12px;color:var(--t2)}.strct-chart__leg{display:inline-flex;align-items:center;gap:6px}.strct-chart__leg-sw{width:9px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 8px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);box-shadow:var(--shadow-elevated);white-space:nowrap;z-index:2}.strct-chart__tip--multi{top:6px;transform:translate(-50%);align-items:stretch;gap:3px}.strct-chart__tip-v{font-size:12px;font-weight:700;color:var(--t1);font-variant-numeric:tabular-nums}.strct-chart__tip-meta{display:inline-flex;align-items:center;gap:5px}.strct-chart__tip-delta{font-size:12px;font-weight:600;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__tip-delta--up{color:var(--success)}.strct-chart__tip-delta--down{color:var(--critical)}.strct-chart__tip-l{font-size:12px;color:var(--t3)}.strct-chart__tip-row{display:inline-flex;align-items:center;gap:6px;font-size:12px}.strct-chart__tip-sw{width:8px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip-rl{color:var(--t3);margin-inline-end:auto}.strct-chart__tip-rv{color:var(--t1);font-weight:700;font-variant-numeric:tabular-nums}.strct-chart__labels{position:relative;height:15px;margin-top:6px;font-size:12px;color:var(--t3)}.strct-chart__labels span{position:absolute;transform:translate(-50%);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.strct-chart__line--draw{stroke-dasharray:1;stroke-dashoffset:1;animation:strct-chart-draw .9s ease forwards}.strct-chart__pulse{transform-box:fill-box;transform-origin:center;animation:strct-chart-pulse 2.4s ease-out infinite}}@keyframes strct-chart-draw{to{stroke-dashoffset:0}}@keyframes strct-chart-pulse{0%{transform:scale(1);opacity:.45}70%{opacity:0}to{transform:scale(2.5);opacity:0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
9983
10516
  }
9984
10517
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctChart, decorators: [{
9985
10518
  type: Component,
@@ -10083,7 +10616,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
10083
10616
  @if (isMulti()) {
10084
10617
  @for (s of multiSeries(); track $index) {
10085
10618
  @if (s.bandPath) {
10086
- <path class="strct-chart__band" [attr.d]="s.bandPath" [attr.fill]="s.color" />
10619
+ <path
10620
+ class="strct-chart__band"
10621
+ [class.strct-chart__band--stack]="stacked()"
10622
+ [attr.d]="s.bandPath"
10623
+ [attr.fill]="s.color"
10624
+ />
10087
10625
  }
10088
10626
  @if (s.area && s.areaPath) {
10089
10627
  <path
@@ -10307,8 +10845,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
10307
10845
  '[class.strct-chart--glow]': 'glow()',
10308
10846
  '[class.strct-chart--brush]': 'brush() || zoom()',
10309
10847
  '[style.--strct-chart-c]': 'color()',
10310
- }, styles: [".strct-chart{display:block;position:relative}.strct-chart__plot{position:relative}.strct-chart__svg{width:100%;display:block;touch-action:none}.strct-chart__svg:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-chart__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-chart__empty{display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--t3)}.strct-chart__grid{stroke:var(--b1);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__line{vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}.strct-chart__area{stroke:none}.strct-chart__area--flat{opacity:.14}.strct-chart__dot,.strct-chart__hoverdot,.strct-chart__head-dot{stroke:var(--bg-1);stroke-width:1.5}.strct-chart__cross{stroke:var(--strct-chart-c);stroke-width:1;opacity:.4;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}.strct-chart__threshold{stroke-width:1;opacity:.85;vector-effect:non-scaling-stroke}.strct-chart__threshold--dashed{stroke-dasharray:4 3}.strct-chart__bar{rx:1.5}.strct-chart__band{opacity:.13;stroke:none}.strct-chart__ann{stroke-width:1;opacity:.8;vector-effect:non-scaling-stroke}.strct-chart__ann--dashed{stroke-dasharray:4 3}.strct-chart__ann-label{position:absolute;top:0;transform:translate(-50%);pointer-events:none;font-size:12px;font-weight:600;background:var(--bg-1);padding:0 3px;border-radius:3px;white-space:nowrap}.strct-chart--brush .strct-chart__svg{cursor:crosshair}.strct-chart__brush{fill:var(--acc);opacity:.16;stroke:var(--acc50);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__reset{position:absolute;top:6px;right:8px;z-index:3;display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--bg-a);color:var(--t2);font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer}.strct-chart__reset:hover{color:var(--t1);border-color:var(--acc50)}.strct-chart__reset:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-chart__tip--gap{top:6px;transform:translate(-50%)}.strct-chart__tip-ann{font-size:12px;font-weight:600}.strct-chart--glow .strct-chart__line{filter:drop-shadow(0 0 1.5px var(--strct-chart-c)) drop-shadow(0 0 5px var(--strct-chart-c))}.strct-chart--glow .strct-chart__head-dot{filter:drop-shadow(0 0 2px var(--strct-chart-c)) drop-shadow(0 0 6px var(--strct-chart-c))}.strct-chart--glow .strct-chart__hoverdot{filter:drop-shadow(0 0 4px var(--strct-chart-c))}.strct-chart__ytick{position:absolute;left:0;width:34px;text-align:end;transform:translateY(-50%);pointer-events:none;font-family:var(--mono);font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__thr{position:absolute;right:2px;transform:translateY(-50%);pointer-events:none;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--bg-1);padding:0 3px;border-radius:3px}.strct-chart__axis-y{position:absolute;left:0;transform:translateY(-50%);pointer-events:none;padding:1px 5px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);font-family:var(--mono);font-size:12px;font-weight:600;color:var(--t2);font-variant-numeric:tabular-nums;z-index:2}.strct-chart__label--active{color:var(--t1);font-weight:700}.strct-chart__legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-bottom:8px;font-size:12px;color:var(--t2)}.strct-chart__leg{display:inline-flex;align-items:center;gap:6px}.strct-chart__leg-sw{width:9px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 8px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);box-shadow:var(--shadow-elevated);white-space:nowrap;z-index:2}.strct-chart__tip--multi{top:6px;transform:translate(-50%);align-items:stretch;gap:3px}.strct-chart__tip-v{font-size:12px;font-weight:700;color:var(--t1);font-variant-numeric:tabular-nums}.strct-chart__tip-meta{display:inline-flex;align-items:center;gap:5px}.strct-chart__tip-delta{font-size:12px;font-weight:600;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__tip-delta--up{color:var(--success)}.strct-chart__tip-delta--down{color:var(--critical)}.strct-chart__tip-l{font-size:12px;color:var(--t3)}.strct-chart__tip-row{display:inline-flex;align-items:center;gap:6px;font-size:12px}.strct-chart__tip-sw{width:8px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip-rl{color:var(--t3);margin-inline-end:auto}.strct-chart__tip-rv{color:var(--t1);font-weight:700;font-variant-numeric:tabular-nums}.strct-chart__labels{position:relative;height:15px;margin-top:6px;font-size:12px;color:var(--t3)}.strct-chart__labels span{position:absolute;transform:translate(-50%);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.strct-chart__line--draw{stroke-dasharray:1;stroke-dashoffset:1;animation:strct-chart-draw .9s ease forwards}.strct-chart__pulse{transform-box:fill-box;transform-origin:center;animation:strct-chart-pulse 2.4s ease-out infinite}}@keyframes strct-chart-draw{to{stroke-dashoffset:0}}@keyframes strct-chart-pulse{0%{transform:scale(1);opacity:.45}70%{opacity:0}to{transform:scale(2.5);opacity:0}}\n"] }]
10311
- }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], curve: [{ type: i0.Input, args: [{ isSignal: true, alias: "curve", required: false }] }], area: [{ type: i0.Input, args: [{ isSignal: true, alias: "area", required: false }] }], glow: [{ type: i0.Input, args: [{ isSignal: true, alias: "glow", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], grid: [{ type: i0.Input, args: [{ isSignal: true, alias: "grid", required: false }] }], dots: [{ type: i0.Input, args: [{ isSignal: true, alias: "dots", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], xTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "xTicks", required: false }] }], xFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "xFormat", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], yAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxis", required: false }] }], yTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "yTicks", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], thresholds: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholds", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], agoFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "agoFormat", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], brush: [{ type: i0.Input, args: [{ isSignal: true, alias: "brush", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], gapText: [{ type: i0.Input, args: [{ isSignal: true, alias: "gapText", required: false }] }], resetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetLabel", required: false }] }], hoverIndex: [{ type: i0.Output, args: ["hoverIndex"] }], brushChange: [{ type: i0.Output, args: ["brushChange"] }], svgRef: [{ type: i0.ViewChild, args: ['svg', { isSignal: true }] }] } });
10848
+ }, styles: [".strct-chart{display:block;position:relative}.strct-chart__plot{position:relative}.strct-chart__svg{width:100%;display:block;touch-action:none}.strct-chart__svg:focus-visible{outline:2px solid var(--acc50);outline-offset:2px;border-radius:var(--radius-sm)}.strct-chart__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-chart__empty{display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--t3)}.strct-chart__grid{stroke:var(--b1);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__line{vector-effect:non-scaling-stroke;stroke-linejoin:round;stroke-linecap:round}.strct-chart__area{stroke:none}.strct-chart__area--flat{opacity:.14}.strct-chart__dot,.strct-chart__hoverdot,.strct-chart__head-dot{stroke:var(--bg-1);stroke-width:1.5}.strct-chart__cross{stroke:var(--strct-chart-c);stroke-width:1;opacity:.4;stroke-dasharray:3 3;vector-effect:non-scaling-stroke}.strct-chart__threshold{stroke-width:1;opacity:.85;vector-effect:non-scaling-stroke}.strct-chart__threshold--dashed{stroke-dasharray:4 3}.strct-chart__bar{rx:1.5}.strct-chart__band{opacity:.13;stroke:none}.strct-chart__band--stack{opacity:.32}.strct-chart__ann{stroke-width:1;opacity:.8;vector-effect:non-scaling-stroke}.strct-chart__ann--dashed{stroke-dasharray:4 3}.strct-chart__ann-label{position:absolute;top:0;transform:translate(-50%);pointer-events:none;font-size:12px;font-weight:600;background:var(--bg-1);padding:0 3px;border-radius:3px;white-space:nowrap}.strct-chart--brush .strct-chart__svg{cursor:crosshair}.strct-chart__brush{fill:var(--acc);opacity:.16;stroke:var(--acc50);stroke-width:1;vector-effect:non-scaling-stroke}.strct-chart__reset{position:absolute;top:6px;right:8px;z-index:3;display:inline-flex;align-items:center;gap:5px;padding:2px 9px;border:1px solid var(--b2);border-radius:99px;background:var(--bg-a);color:var(--t2);font-family:var(--font);font-size:12px;font-weight:600;cursor:pointer}.strct-chart__reset:hover{color:var(--t1);border-color:var(--acc50)}.strct-chart__reset:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}.strct-chart__tip--gap{top:6px;transform:translate(-50%)}.strct-chart__tip-ann{font-size:12px;font-weight:600}.strct-chart--glow .strct-chart__line{filter:drop-shadow(0 0 1.5px var(--strct-chart-c)) drop-shadow(0 0 5px var(--strct-chart-c))}.strct-chart--glow .strct-chart__head-dot{filter:drop-shadow(0 0 2px var(--strct-chart-c)) drop-shadow(0 0 6px var(--strct-chart-c))}.strct-chart--glow .strct-chart__hoverdot{filter:drop-shadow(0 0 4px var(--strct-chart-c))}.strct-chart__ytick{position:absolute;left:0;width:34px;text-align:end;transform:translateY(-50%);pointer-events:none;font-family:var(--mono);font-size:12px;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__thr{position:absolute;right:2px;transform:translateY(-50%);pointer-events:none;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums;background:var(--bg-1);padding:0 3px;border-radius:3px}.strct-chart__axis-y{position:absolute;left:0;transform:translateY(-50%);pointer-events:none;padding:1px 5px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);font-family:var(--mono);font-size:12px;font-weight:600;color:var(--t2);font-variant-numeric:tabular-nums;z-index:2}.strct-chart__label--active{color:var(--t1);font-weight:700}.strct-chart__legend{display:flex;flex-wrap:wrap;gap:6px 14px;margin-bottom:8px;font-size:12px;color:var(--t2)}.strct-chart__leg{display:inline-flex;align-items:center;gap:6px}.strct-chart__leg-sw{width:9px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip{position:absolute;transform:translate(-50%,calc(-100% - 10px));pointer-events:none;display:flex;flex-direction:column;align-items:center;gap:1px;padding:4px 8px;border-radius:var(--radius-sm);background:var(--bg-a);border:1px solid var(--b2);box-shadow:var(--shadow-elevated);white-space:nowrap;z-index:2}.strct-chart__tip--multi{top:6px;transform:translate(-50%);align-items:stretch;gap:3px}.strct-chart__tip-v{font-size:12px;font-weight:700;color:var(--t1);font-variant-numeric:tabular-nums}.strct-chart__tip-meta{display:inline-flex;align-items:center;gap:5px}.strct-chart__tip-delta{font-size:12px;font-weight:600;color:var(--t3);font-variant-numeric:tabular-nums}.strct-chart__tip-delta--up{color:var(--success)}.strct-chart__tip-delta--down{color:var(--critical)}.strct-chart__tip-l{font-size:12px;color:var(--t3)}.strct-chart__tip-row{display:inline-flex;align-items:center;gap:6px;font-size:12px}.strct-chart__tip-sw{width:8px;height:3px;border-radius:2px;flex-shrink:0}.strct-chart__tip-rl{color:var(--t3);margin-inline-end:auto}.strct-chart__tip-rv{color:var(--t1);font-weight:700;font-variant-numeric:tabular-nums}.strct-chart__labels{position:relative;height:15px;margin-top:6px;font-size:12px;color:var(--t3)}.strct-chart__labels span{position:absolute;transform:translate(-50%);white-space:nowrap}@media(prefers-reduced-motion:no-preference){.strct-chart__line--draw{stroke-dasharray:1;stroke-dashoffset:1;animation:strct-chart-draw .9s ease forwards}.strct-chart__pulse{transform-box:fill-box;transform-origin:center;animation:strct-chart-pulse 2.4s ease-out infinite}}@keyframes strct-chart-draw{to{stroke-dashoffset:0}}@keyframes strct-chart-pulse{0%{transform:scale(1);opacity:.45}70%{opacity:0}to{transform:scale(2.5);opacity:0}}\n"] }]
10849
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], series: [{ type: i0.Input, args: [{ isSignal: true, alias: "series", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], curve: [{ type: i0.Input, args: [{ isSignal: true, alias: "curve", required: false }] }], area: [{ type: i0.Input, args: [{ isSignal: true, alias: "area", required: false }] }], glow: [{ type: i0.Input, args: [{ isSignal: true, alias: "glow", required: false }] }], live: [{ type: i0.Input, args: [{ isSignal: true, alias: "live", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], grid: [{ type: i0.Input, args: [{ isSignal: true, alias: "grid", required: false }] }], dots: [{ type: i0.Input, args: [{ isSignal: true, alias: "dots", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], xTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "xTicks", required: false }] }], xFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "xFormat", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], yAxis: [{ type: i0.Input, args: [{ isSignal: true, alias: "yAxis", required: false }] }], yTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "yTicks", required: false }] }], axisFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "axisFormat", required: false }] }], thresholds: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholds", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], agoFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "agoFormat", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], brush: [{ type: i0.Input, args: [{ isSignal: true, alias: "brush", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], stacked: [{ type: i0.Input, args: [{ isSignal: true, alias: "stacked", required: false }] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }], times: [{ type: i0.Input, args: [{ isSignal: true, alias: "times", required: false }] }], gapText: [{ type: i0.Input, args: [{ isSignal: true, alias: "gapText", required: false }] }], resetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetLabel", required: false }] }], hoverIndex: [{ type: i0.Output, args: ["hoverIndex"] }], brushChange: [{ type: i0.Output, args: ["brushChange"] }], svgRef: [{ type: i0.ViewChild, args: ['svg', { isSignal: true }] }] } });
10312
10850
 
10313
10851
  const PALETTE = [
10314
10852
  'var(--acc)',