@akcelik/strct 4.2.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21612,6 +21612,34 @@ class StrctHeatmap {
21612
21612
  /** Scale ceiling — the value that maps to full intensity. Auto (data max) when null. */
21613
21613
  max = input(null, /* @ts-ignore */
21614
21614
  ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
21615
+ /**
21616
+ * Label every n-th column (1 = every column, today's behaviour). 24 hourly
21617
+ * columns in a narrow card cannot all carry a readable label; labelling every
21618
+ * third keeps "14:00" legible instead of forcing it down to "14".
21619
+ */
21620
+ colLabelEvery = input(1, /* @ts-ignore */
21621
+ ...(ngDevMode ? [{ debugName: "colLabelEvery" }] : /* istanbul ignore next */ []));
21622
+ /**
21623
+ * Column label text, so a column can be keyed by an ISO time and shown as
21624
+ * "14:00". Return '' to drop a label. Applied after `colLabelEvery`.
21625
+ */
21626
+ colLabel = input(null, /* @ts-ignore */
21627
+ ...(ngDevMode ? [{ debugName: "colLabel" }] : /* istanbul ignore next */ []));
21628
+ /**
21629
+ * Cell tooltip text. Without it a cell reads `row × col: value`, which for a
21630
+ * utilisation grid has no unit — `hv-05 · 14:00 — 47% CPU` is the readable form.
21631
+ */
21632
+ valueFormat = input(null, /* @ts-ignore */
21633
+ ...(ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []));
21634
+ /**
21635
+ * Colour by band instead of one hue: `status` (accent by default) below
21636
+ * `warning`, the warning hue from there, the critical hue from `critical`.
21637
+ * Intensity is scaled by the value's position WITHIN its band, so a 96% cell
21638
+ * reads darker than an 81% one and both read as critical/warning at a glance.
21639
+ * Unset: today's single-hue ramp across the whole range.
21640
+ */
21641
+ thresholds = input(null, /* @ts-ignore */
21642
+ ...(ngDevMode ? [{ debugName: "thresholds" }] : /* istanbul ignore next */ []));
21615
21643
  /** Base color of the intensity ramp. */
21616
21644
  status = input('accent', /* @ts-ignore */
21617
21645
  ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
@@ -21710,7 +21738,7 @@ class StrctHeatmap {
21710
21738
  w: round(w),
21711
21739
  h: ch,
21712
21740
  fill: value === undefined ? 'var(--bg-2)' : this.fillFor(value),
21713
- tip: value === undefined ? null : `${row} × ${col}: ${value}`,
21741
+ tip: value === undefined ? null : this.tipFor(value, row, col),
21714
21742
  });
21715
21743
  });
21716
21744
  });
@@ -21732,9 +21760,24 @@ class StrctHeatmap {
21732
21760
  const gap = this.gap();
21733
21761
  const x0 = this.rowLabelWidth();
21734
21762
  const y = this.height() - 4;
21735
- return this.colOrder().map((text, c) => ({ text, x: round(x0 + c * (w + gap) + w / 2), y }));
21763
+ const every = Math.max(1, Math.trunc(this.colLabelEvery()));
21764
+ const format = this.colLabel();
21765
+ const out = [];
21766
+ this.colOrder().forEach((col, c) => {
21767
+ if (c % every !== 0)
21768
+ return;
21769
+ const text = format ? format(col, c) : col;
21770
+ if (text === '')
21771
+ return;
21772
+ out.push({ text, x: round(x0 + c * (w + gap) + w / 2), y });
21773
+ });
21774
+ return out;
21736
21775
  }, /* @ts-ignore */
21737
21776
  ...(ngDevMode ? [{ debugName: "colLabels" }] : /* istanbul ignore next */ []));
21777
+ tipFor(value, row, col) {
21778
+ const format = this.valueFormat();
21779
+ return format ? format(value, row, col) : `${row} × ${col}: ${value}`;
21780
+ }
21738
21781
  /** Screen-reader summary of the whole grid (role="img" name). */
21739
21782
  aria = computed(() => {
21740
21783
  const info = {
@@ -21752,17 +21795,41 @@ class StrctHeatmap {
21752
21795
  /**
21753
21796
  * Intensity ramp: 8% of the status hue for the smallest non-zero value up
21754
21797
  * to 100% at the ceiling; zero stays on the empty-cell surface.
21798
+ *
21799
+ * With `thresholds`, the hue comes from the value's band and the intensity
21800
+ * from its position inside that band, floored at 45% — a band has to be
21801
+ * recognisable as itself, and a pale cell must not read as "no data".
21755
21802
  */
21756
21803
  fillFor(value) {
21757
21804
  if (value <= 0)
21758
21805
  return 'var(--bg-2)';
21759
21806
  const maxV = this.maxVal();
21760
- const t = maxV > 0 ? Math.min(value / maxV, 1) : 0;
21761
- const pct = 8 + Math.round(t * 92);
21807
+ const t = this.thresholds();
21808
+ if (t) {
21809
+ const { color, from, to } = this.bandFor(value, t, maxV);
21810
+ const span = to - from;
21811
+ const pos = span > 0 ? Math.min(Math.max((value - from) / span, 0), 1) : 1;
21812
+ const pct = 45 + Math.round(pos * 55);
21813
+ return `color-mix(in srgb, ${color} ${pct}%, var(--bg-1))`;
21814
+ }
21815
+ const ratio = maxV > 0 ? Math.min(value / maxV, 1) : 0;
21816
+ const pct = 8 + Math.round(ratio * 92);
21762
21817
  return `color-mix(in srgb, ${this.color()} ${pct}%, var(--bg-1))`;
21763
21818
  }
21819
+ /** The band a value falls in: its hue and the range intensity scales over. */
21820
+ bandFor(value, t, maxV) {
21821
+ const ceiling = Math.max(maxV, value);
21822
+ if (t.critical != null && value >= t.critical) {
21823
+ return { color: COLOR.critical, from: t.critical, to: ceiling };
21824
+ }
21825
+ if (t.warning != null && value >= t.warning) {
21826
+ return { color: COLOR.warning, from: t.warning, to: t.critical ?? ceiling };
21827
+ }
21828
+ const upper = t.warning ?? t.critical ?? ceiling;
21829
+ return { color: this.color(), from: 0, to: upper };
21830
+ }
21764
21831
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctHeatmap, deps: [], target: i0.ɵɵFactoryTarget.Component });
21765
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctHeatmap, isStandalone: true, selector: "strct-heatmap", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, summaryFormat: { classPropertyName: "summaryFormat", publicName: "summaryFormat", isSignal: true, isRequired: false, transformFunction: null }, cellHeight: { classPropertyName: "cellHeight", publicName: "cellHeight", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, rowLabelWidth: { classPropertyName: "rowLabelWidth", publicName: "rowLabelWidth", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-heatmap" }, ngImport: i0, template: `
21832
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctHeatmap, isStandalone: true, selector: "strct-heatmap", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, colLabelEvery: { classPropertyName: "colLabelEvery", publicName: "colLabelEvery", isSignal: true, isRequired: false, transformFunction: null }, colLabel: { classPropertyName: "colLabel", publicName: "colLabel", isSignal: true, isRequired: false, transformFunction: null }, valueFormat: { classPropertyName: "valueFormat", publicName: "valueFormat", isSignal: true, isRequired: false, transformFunction: null }, thresholds: { classPropertyName: "thresholds", publicName: "thresholds", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, summaryFormat: { classPropertyName: "summaryFormat", publicName: "summaryFormat", isSignal: true, isRequired: false, transformFunction: null }, cellHeight: { classPropertyName: "cellHeight", publicName: "cellHeight", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, rowLabelWidth: { classPropertyName: "rowLabelWidth", publicName: "rowLabelWidth", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-heatmap" }, ngImport: i0, template: `
21766
21833
  @if (isEmpty()) {
21767
21834
  <div class="strct-heatmap__empty">{{ emptyText() }}</div>
21768
21835
  } @else {
@@ -21774,7 +21841,10 @@ class StrctHeatmap {
21774
21841
  [attr.width]="width()"
21775
21842
  [attr.height]="height()"
21776
21843
  >
21777
- @for (l of rowLabels(); track l.text) {
21844
+ <!-- Tracked by index: label text is data and repeats legitimately (two
21845
+ hosts with the same name, hour 02 twice at the DST fall-back), and
21846
+ a thinned column axis repeats the empty string. -->
21847
+ @for (l of rowLabels(); track $index) {
21778
21848
  <text
21779
21849
  class="strct-heatmap__label strct-heatmap__label--row"
21780
21850
  [attr.x]="l.x"
@@ -21785,7 +21855,7 @@ class StrctHeatmap {
21785
21855
  {{ l.text }}
21786
21856
  </text>
21787
21857
  }
21788
- @for (l of colLabels(); track l.text) {
21858
+ @for (l of colLabels(); track $index) {
21789
21859
  <text
21790
21860
  class="strct-heatmap__label strct-heatmap__label--col"
21791
21861
  [attr.x]="l.x"
@@ -21829,7 +21899,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
21829
21899
  [attr.width]="width()"
21830
21900
  [attr.height]="height()"
21831
21901
  >
21832
- @for (l of rowLabels(); track l.text) {
21902
+ <!-- Tracked by index: label text is data and repeats legitimately (two
21903
+ hosts with the same name, hour 02 twice at the DST fall-back), and
21904
+ a thinned column axis repeats the empty string. -->
21905
+ @for (l of rowLabels(); track $index) {
21833
21906
  <text
21834
21907
  class="strct-heatmap__label strct-heatmap__label--row"
21835
21908
  [attr.x]="l.x"
@@ -21840,7 +21913,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
21840
21913
  {{ l.text }}
21841
21914
  </text>
21842
21915
  }
21843
- @for (l of colLabels(); track l.text) {
21916
+ @for (l of colLabels(); track $index) {
21844
21917
  <text
21845
21918
  class="strct-heatmap__label strct-heatmap__label--col"
21846
21919
  [attr.x]="l.x"
@@ -21869,7 +21942,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
21869
21942
  </svg>
21870
21943
  }
21871
21944
  `, host: { class: 'strct-heatmap' }, styles: [".strct-heatmap{display:block}.strct-heatmap__svg{width:100%;display:block}.strct-heatmap__empty{display:flex;align-items:center;justify-content:center;min-height:60px;font-size:12px;color:var(--t3)}.strct-heatmap__label{fill:var(--t3);font-size:10px}.strct-heatmap__label--row{font-family:var(--font)}.strct-heatmap__label--col{font-family:var(--mono);font-variant-numeric:tabular-nums}.strct-heatmap__cell{stroke:var(--bg-1);stroke-width:1}@media(prefers-reduced-motion:no-preference){.strct-heatmap__cell{transition:opacity .12s ease}}.strct-heatmap__cell:hover{opacity:.75}\n"] }]
21872
- }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], cols: [{ type: i0.Input, args: [{ isSignal: true, alias: "cols", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], summaryFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryFormat", required: false }] }], cellHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellHeight", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], rowLabelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowLabelWidth", required: false }] }] } });
21945
+ }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], cols: [{ type: i0.Input, args: [{ isSignal: true, alias: "cols", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], colLabelEvery: [{ type: i0.Input, args: [{ isSignal: true, alias: "colLabelEvery", required: false }] }], colLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "colLabel", required: false }] }], valueFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormat", required: false }] }], thresholds: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholds", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], summaryFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "summaryFormat", required: false }] }], cellHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellHeight", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], rowLabelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowLabelWidth", required: false }] }] } });
21873
21946
 
21874
21947
  /**
21875
21948
  * Public API surface of the strct UI library.