@tolle_/tolle-ui 18.3.0 → 18.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10059,6 +10059,140 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
10059
10059
  type: Input
10060
10060
  }] } });
10061
10061
 
10062
+ /**
10063
+ * Radial counterpart to `tolle-progress`: a ring that fills clockwise from
10064
+ * 12 o'clock. Project content to label the centre — a percentage, an icon,
10065
+ * whatever the caller wants; the ring renders nothing there on its own.
10066
+ * @new
10067
+ */
10068
+ class ProgressCircleComponent {
10069
+ /**
10070
+ * Angular writes a bound `class` input through its styling path, which does
10071
+ * not mark an OnPush component dirty — without this hook the component keeps
10072
+ * rendering the class it was born with.
10073
+ */
10074
+ ngOnChanges() {
10075
+ this.cdr.markForCheck();
10076
+ }
10077
+ /** Percent complete, 0-100. @default 0 */
10078
+ value = 0;
10079
+ /** Diameter of the ring in px. @default 120 */
10080
+ size = 120;
10081
+ /** Thickness of the ring in px. @default 8 */
10082
+ strokeWidth = 8;
10083
+ /** Extra Tailwind classes merged onto the ring via `cn()` (last-wins). */
10084
+ class = '';
10085
+ cdr = inject(ChangeDetectorRef);
10086
+ get clampedValue() {
10087
+ return Math.min(100, Math.max(0, this.value ?? 0));
10088
+ }
10089
+ get viewBox() {
10090
+ return '0 0 ' + this.size + ' ' + this.size;
10091
+ }
10092
+ get center() {
10093
+ return this.size / 2;
10094
+ }
10095
+ /** Leaves half the stroke width as margin so the ring never clips the frame. */
10096
+ get radius() {
10097
+ return Math.max(0, (this.size - this.strokeWidth) / 2);
10098
+ }
10099
+ get circumference() {
10100
+ return 2 * Math.PI * this.radius;
10101
+ }
10102
+ get dashOffset() {
10103
+ return this.circumference * (1 - this.clampedValue / 100);
10104
+ }
10105
+ get computedClass() {
10106
+ return cn('relative inline-flex shrink-0 items-center justify-center', this.class);
10107
+ }
10108
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ProgressCircleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10109
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ProgressCircleComponent, isStandalone: true, selector: "tolle-progress-circle", inputs: { value: "value", size: "size", strokeWidth: "strokeWidth", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
10110
+ <div
10111
+ [class]="computedClass"
10112
+ [style.width.px]="size"
10113
+ [style.height.px]="size"
10114
+ role="progressbar"
10115
+ [attr.aria-valuemin]="0"
10116
+ [attr.aria-valuemax]="100"
10117
+ [attr.aria-valuenow]="clampedValue"
10118
+ >
10119
+ <svg [attr.width]="size" [attr.height]="size" [attr.viewBox]="viewBox" class="block">
10120
+ <svg:circle
10121
+ [attr.cx]="center"
10122
+ [attr.cy]="center"
10123
+ [attr.r]="radius"
10124
+ fill="none"
10125
+ [attr.stroke-width]="strokeWidth"
10126
+ class="stroke-primary/20"
10127
+ ></svg:circle>
10128
+ <svg:circle
10129
+ [attr.cx]="center"
10130
+ [attr.cy]="center"
10131
+ [attr.r]="radius"
10132
+ fill="none"
10133
+ [attr.stroke-width]="strokeWidth"
10134
+ stroke-linecap="round"
10135
+ [attr.stroke-dasharray]="circumference"
10136
+ [attr.stroke-dashoffset]="dashOffset"
10137
+ [attr.transform]="'rotate(-90 ' + center + ' ' + center + ')'"
10138
+ class="stroke-primary transition-[stroke-dashoffset] duration-300 ease-in-out"
10139
+ ></svg:circle>
10140
+ </svg>
10141
+ <div class="absolute inset-0 flex items-center justify-center text-sm font-medium tabular-nums text-foreground">
10142
+ <ng-content></ng-content>
10143
+ </div>
10144
+ </div>
10145
+ `, isInline: true, styles: [":host{display:inline-flex}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10146
+ }
10147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ProgressCircleComponent, decorators: [{
10148
+ type: Component,
10149
+ args: [{ selector: 'tolle-progress-circle', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
10150
+ <div
10151
+ [class]="computedClass"
10152
+ [style.width.px]="size"
10153
+ [style.height.px]="size"
10154
+ role="progressbar"
10155
+ [attr.aria-valuemin]="0"
10156
+ [attr.aria-valuemax]="100"
10157
+ [attr.aria-valuenow]="clampedValue"
10158
+ >
10159
+ <svg [attr.width]="size" [attr.height]="size" [attr.viewBox]="viewBox" class="block">
10160
+ <svg:circle
10161
+ [attr.cx]="center"
10162
+ [attr.cy]="center"
10163
+ [attr.r]="radius"
10164
+ fill="none"
10165
+ [attr.stroke-width]="strokeWidth"
10166
+ class="stroke-primary/20"
10167
+ ></svg:circle>
10168
+ <svg:circle
10169
+ [attr.cx]="center"
10170
+ [attr.cy]="center"
10171
+ [attr.r]="radius"
10172
+ fill="none"
10173
+ [attr.stroke-width]="strokeWidth"
10174
+ stroke-linecap="round"
10175
+ [attr.stroke-dasharray]="circumference"
10176
+ [attr.stroke-dashoffset]="dashOffset"
10177
+ [attr.transform]="'rotate(-90 ' + center + ' ' + center + ')'"
10178
+ class="stroke-primary transition-[stroke-dashoffset] duration-300 ease-in-out"
10179
+ ></svg:circle>
10180
+ </svg>
10181
+ <div class="absolute inset-0 flex items-center justify-center text-sm font-medium tabular-nums text-foreground">
10182
+ <ng-content></ng-content>
10183
+ </div>
10184
+ </div>
10185
+ `, styles: [":host{display:inline-flex}\n"] }]
10186
+ }], propDecorators: { value: [{
10187
+ type: Input
10188
+ }], size: [{
10189
+ type: Input
10190
+ }], strokeWidth: [{
10191
+ type: Input
10192
+ }], class: [{
10193
+ type: Input
10194
+ }] } });
10195
+
10062
10196
  class SliderComponent {
10063
10197
  min = 0;
10064
10198
  max = 100;
@@ -28285,6 +28419,12 @@ class ChartService {
28285
28419
  margin = { top: 8, right: 8, bottom: 24, left: 40 };
28286
28420
  /** Fraction of a band's step spent on padding, split either side. */
28287
28421
  bandPadding = 0.2;
28422
+ /**
28423
+ * Which physical axis is the category axis. Bars flip this via `configure()`;
28424
+ * every other geometry getter branches on it internally, defaulting to the
28425
+ * exact vertical formula when unset, so existing vertical charts are unaffected.
28426
+ */
28427
+ orientation = 'vertical';
28288
28428
  scaleMode = 'point';
28289
28429
  zeroRequired = false;
28290
28430
  domainMin = 0;
@@ -28417,21 +28557,36 @@ class ChartService {
28417
28557
  return total;
28418
28558
  }
28419
28559
  /**
28420
- * Maps a data value to an svg y. SVG y grows downward, so the domain maximum
28421
- * lands on `plotTop` and the minimum on `plotBottom`.
28560
+ * Maps a data value to a position along the *value* axis svg y when
28561
+ * vertical (svg y grows downward, so the domain maximum lands on `plotTop`),
28562
+ * svg x when horizontal (the domain maximum lands on `plotRight`).
28422
28563
  */
28423
28564
  yFor(value) {
28424
28565
  const span = this.domainMax - this.domainMin;
28566
+ if (this.orientation === 'horizontal') {
28567
+ if (span <= 0)
28568
+ return this.plotLeft;
28569
+ const ratio = (value - this.domainMin) / span;
28570
+ return this.plotLeft + ratio * this.plotWidth;
28571
+ }
28425
28572
  if (span <= 0)
28426
28573
  return this.plotBottom;
28427
28574
  const ratio = (value - this.domainMin) / span;
28428
28575
  return this.plotBottom - ratio * this.plotHeight;
28429
28576
  }
28430
- /** The svg y of the zero line, clamped into the plot. */
28577
+ /** The value-axis position of the zero line, clamped into the plot. */
28431
28578
  get baselineY() {
28432
28579
  const zero = Math.min(Math.max(0, this.domainMin), this.domainMax);
28433
28580
  return this.yFor(zero);
28434
28581
  }
28582
+ /** Length of the plot along the category axis: `plotHeight` when horizontal, else `plotWidth`. */
28583
+ get categoryAxisLength() {
28584
+ return this.orientation === 'horizontal' ? this.plotHeight : this.plotWidth;
28585
+ }
28586
+ /** Origin of the plot along the category axis: `plotTop` when horizontal, else `plotLeft`. */
28587
+ get categoryAxisOrigin() {
28588
+ return this.orientation === 'horizontal' ? this.plotTop : this.plotLeft;
28589
+ }
28435
28590
  /** Width of one band, excluding its padding. Meaningful in band mode. */
28436
28591
  get bandWidth() {
28437
28592
  if (this.count === 0)
@@ -28442,13 +28597,16 @@ class ChartService {
28442
28597
  get bandStep() {
28443
28598
  if (this.count === 0)
28444
28599
  return 0;
28445
- return this.plotWidth / this.count;
28600
+ return this.categoryAxisLength / this.count;
28446
28601
  }
28447
- /** Geometry of the band at `index`. */
28602
+ /**
28603
+ * Geometry of the band at `index`, along the category axis — x when
28604
+ * vertical (today's meaning), y when horizontal.
28605
+ */
28448
28606
  bandFor(index) {
28449
28607
  const step = this.bandStep;
28450
28608
  const width = this.bandWidth;
28451
- const start = this.plotLeft + index * step + (step - width) / 2;
28609
+ const start = this.categoryAxisOrigin + index * step + (step - width) / 2;
28452
28610
  return { start, centre: start + width / 2, width };
28453
28611
  }
28454
28612
  /** x of the data point at `index` on the point scale. */
@@ -28471,7 +28629,7 @@ class ChartService {
28471
28629
  hitBandFor(index) {
28472
28630
  if (this.scaleMode === 'band') {
28473
28631
  const step = this.bandStep;
28474
- const start = this.plotLeft + index * step;
28632
+ const start = this.categoryAxisOrigin + index * step;
28475
28633
  return { start, centre: start + step / 2, width: step };
28476
28634
  }
28477
28635
  const x = this.pointX(index);
@@ -28545,6 +28703,7 @@ class ChartService {
28545
28703
  this.height,
28546
28704
  this.bandPadding,
28547
28705
  this.scaleMode,
28706
+ this.orientation,
28548
28707
  this.zeroRequired,
28549
28708
  this.margin.top, this.margin.right, this.margin.bottom, this.margin.left,
28550
28709
  this.domainMin, this.domainMax, this.tickStep,
@@ -29191,6 +29350,11 @@ class ChartComponent {
29191
29350
  description = '';
29192
29351
  /** Stacks marks on a shared baseline instead of grouping them. @default false */
29193
29352
  stacked = false;
29353
+ /**
29354
+ * Which physical axis carries the category vs the value. Only bars support
29355
+ * 'horizontal' — line/area marks stay vertical regardless. @default 'vertical'
29356
+ */
29357
+ orientation = 'vertical';
29194
29358
  /** Renders the crosshair, hit layer and shared tooltip. @default true */
29195
29359
  hover = true;
29196
29360
  /** Shows the data table instead of leaving it visually hidden. @default false */
@@ -29263,6 +29427,7 @@ class ChartComponent {
29263
29427
  series: this.series ?? [],
29264
29428
  xKey: this.xKey,
29265
29429
  stacked: this.stacked,
29430
+ orientation: this.orientation,
29266
29431
  height: this.height,
29267
29432
  margin: this.margin,
29268
29433
  width: this.chart.width || ChartComponent.fallbackWidth,
@@ -29274,11 +29439,19 @@ class ChartComponent {
29274
29439
  /**
29275
29440
  * One catchment rect per x position, deliberately wider than the marks it
29276
29441
  * covers — readers aim at a category, never at a 2px line or an 8px dot.
29442
+ * Spans the full plot in the cross direction: full height in a vertical
29443
+ * chart, full width in a horizontal one.
29277
29444
  */
29278
- get hitBands() {
29445
+ get hitRects() {
29279
29446
  if (!this.hover)
29280
29447
  return [];
29281
- return this.data.map((_, i) => this.chart.hitBandFor(i));
29448
+ const horizontal = this.chart.orientation === 'horizontal';
29449
+ return this.data.map((_, i) => {
29450
+ const band = this.chart.hitBandFor(i);
29451
+ return horizontal
29452
+ ? { x: this.chart.plotLeft, y: band.start, width: this.chart.plotWidth, height: band.width }
29453
+ : { x: band.start, y: this.chart.plotTop, width: band.width, height: this.chart.plotHeight };
29454
+ });
29282
29455
  }
29283
29456
  onEnterBand(index) {
29284
29457
  this.chart.setActiveIndex(index);
@@ -29290,7 +29463,7 @@ class ChartComponent {
29290
29463
  return cn(chartVariants({ variant: this.variant, density: this.density }), this.class);
29291
29464
  }
29292
29465
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
29293
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChartComponent, isStandalone: true, selector: "tolle-chart", inputs: { data: "data", series: "series", xKey: "xKey", height: "height", margin: "margin", ariaLabel: "ariaLabel", description: "description", stacked: "stacked", hover: "hover", showTable: "showTable", xHeader: "xHeader", variant: "variant", density: "density", class: "class" }, outputs: { activeIndexChange: "activeIndexChange" }, providers: [ChartService], viewQueries: [{ propertyName: "plotRef", first: true, predicate: ["plot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
29466
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChartComponent, isStandalone: true, selector: "tolle-chart", inputs: { data: "data", series: "series", xKey: "xKey", height: "height", margin: "margin", ariaLabel: "ariaLabel", description: "description", stacked: "stacked", orientation: "orientation", hover: "hover", showTable: "showTable", xHeader: "xHeader", variant: "variant", density: "density", class: "class" }, outputs: { activeIndexChange: "activeIndexChange" }, providers: [ChartService], viewQueries: [{ propertyName: "plotRef", first: true, predicate: ["plot"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
29294
29467
  <div [class]="computedClass">
29295
29468
  <div class="relative w-full" #plot>
29296
29469
  <svg
@@ -29321,11 +29494,11 @@ class ChartComponent {
29321
29494
  ></svg:line>
29322
29495
 
29323
29496
  <svg:rect
29324
- *ngFor="let band of hitBands; let i = index; trackBy: trackByIndex"
29325
- [attr.x]="band.start"
29326
- [attr.y]="chart.plotTop"
29327
- [attr.width]="band.width"
29328
- [attr.height]="chart.plotHeight"
29497
+ *ngFor="let rect of hitRects; let i = index; trackBy: trackByIndex"
29498
+ [attr.x]="rect.x"
29499
+ [attr.y]="rect.y"
29500
+ [attr.width]="rect.width"
29501
+ [attr.height]="rect.height"
29329
29502
  fill="transparent"
29330
29503
  (pointerenter)="onEnterBand(i)"
29331
29504
  ></svg:rect>
@@ -29373,11 +29546,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29373
29546
  ></svg:line>
29374
29547
 
29375
29548
  <svg:rect
29376
- *ngFor="let band of hitBands; let i = index; trackBy: trackByIndex"
29377
- [attr.x]="band.start"
29378
- [attr.y]="chart.plotTop"
29379
- [attr.width]="band.width"
29380
- [attr.height]="chart.plotHeight"
29549
+ *ngFor="let rect of hitRects; let i = index; trackBy: trackByIndex"
29550
+ [attr.x]="rect.x"
29551
+ [attr.y]="rect.y"
29552
+ [attr.width]="rect.width"
29553
+ [attr.height]="rect.height"
29381
29554
  fill="transparent"
29382
29555
  (pointerenter)="onEnterBand(i)"
29383
29556
  ></svg:rect>
@@ -29407,6 +29580,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29407
29580
  type: Input
29408
29581
  }], stacked: [{
29409
29582
  type: Input
29583
+ }], orientation: [{
29584
+ type: Input
29410
29585
  }], hover: [{
29411
29586
  type: Input
29412
29587
  }], showTable: [{
@@ -29425,16 +29600,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29425
29600
  type: ViewChild,
29426
29601
  args: ['plot']
29427
29602
  }] } });
29428
- /** Horizontal rules at the y ticks, with optional vertical rules at the x positions. */
29603
+ /**
29604
+ * Rules at the value ticks, with optional rules at the category positions.
29605
+ * Orientation-aware: in a horizontal chart the value rules run vertically and
29606
+ * the (optional) category rules run horizontally — the transpose of a
29607
+ * vertical chart's grid.
29608
+ */
29429
29609
  class ChartGridComponent extends ChartChild {
29430
- /** Also draws a rule at every x position. @default false */
29610
+ /** Also draws a rule at every category position. @default false */
29431
29611
  vertical = false;
29432
29612
  /** Extra Tailwind classes merged onto each rule via `cn()` (last-wins). */
29433
29613
  class = '';
29434
- get verticalXs() {
29614
+ /** Rules at each value tick, spanning the plot in the cross direction. */
29615
+ get valueLines() {
29616
+ const horizontal = this.chart.orientation === 'horizontal';
29617
+ return this.chart.ticks.map((tick) => horizontal
29618
+ ? { x1: tick.y, x2: tick.y, y1: this.chart.plotTop, y2: this.chart.plotBottom }
29619
+ : { x1: this.chart.plotLeft, x2: this.chart.plotRight, y1: tick.y, y2: tick.y });
29620
+ }
29621
+ /** Rules at each category position, only when `vertical` is set. */
29622
+ get categoryLines() {
29435
29623
  if (!this.vertical)
29436
29624
  return [];
29437
- return this.chart.xLabels.map((_, i) => this.chart.xFor(i));
29625
+ const horizontal = this.chart.orientation === 'horizontal';
29626
+ return this.chart.xLabels.map((_, i) => {
29627
+ const pos = this.chart.xFor(i);
29628
+ return horizontal
29629
+ ? { x1: this.chart.plotLeft, x2: this.chart.plotRight, y1: pos, y2: pos }
29630
+ : { x1: pos, x2: pos, y1: this.chart.plotTop, y2: this.chart.plotBottom };
29631
+ });
29438
29632
  }
29439
29633
  get lineClass() {
29440
29634
  // Recessive by design: a grid is a reading aid, never a mark.
@@ -29443,20 +29637,20 @@ class ChartGridComponent extends ChartChild {
29443
29637
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChartGridComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
29444
29638
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChartGridComponent, isStandalone: true, selector: "svg:g[tolle-chart-grid]", inputs: { vertical: "vertical", class: "class" }, usesInheritance: true, ngImport: i0, template: `
29445
29639
  <svg:line
29446
- *ngFor="let tick of chart.ticks; trackBy: trackByIndex"
29447
- [attr.x1]="chart.plotLeft"
29448
- [attr.x2]="chart.plotRight"
29449
- [attr.y1]="tick.y"
29450
- [attr.y2]="tick.y"
29640
+ *ngFor="let line of valueLines; trackBy: trackByIndex"
29641
+ [attr.x1]="line.x1"
29642
+ [attr.x2]="line.x2"
29643
+ [attr.y1]="line.y1"
29644
+ [attr.y2]="line.y2"
29451
29645
  [class]="lineClass"
29452
29646
  stroke-width="1"
29453
29647
  ></svg:line>
29454
29648
  <svg:line
29455
- *ngFor="let x of verticalXs; trackBy: trackByIndex"
29456
- [attr.x1]="x"
29457
- [attr.x2]="x"
29458
- [attr.y1]="chart.plotTop"
29459
- [attr.y2]="chart.plotBottom"
29649
+ *ngFor="let line of categoryLines; trackBy: trackByIndex"
29650
+ [attr.x1]="line.x1"
29651
+ [attr.x2]="line.x2"
29652
+ [attr.y1]="line.y1"
29653
+ [attr.y2]="line.y2"
29460
29654
  [class]="lineClass"
29461
29655
  stroke-width="1"
29462
29656
  ></svg:line>
@@ -29471,20 +29665,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29471
29665
  changeDetection: ChangeDetectionStrategy.OnPush,
29472
29666
  template: `
29473
29667
  <svg:line
29474
- *ngFor="let tick of chart.ticks; trackBy: trackByIndex"
29475
- [attr.x1]="chart.plotLeft"
29476
- [attr.x2]="chart.plotRight"
29477
- [attr.y1]="tick.y"
29478
- [attr.y2]="tick.y"
29668
+ *ngFor="let line of valueLines; trackBy: trackByIndex"
29669
+ [attr.x1]="line.x1"
29670
+ [attr.x2]="line.x2"
29671
+ [attr.y1]="line.y1"
29672
+ [attr.y2]="line.y2"
29479
29673
  [class]="lineClass"
29480
29674
  stroke-width="1"
29481
29675
  ></svg:line>
29482
29676
  <svg:line
29483
- *ngFor="let x of verticalXs; trackBy: trackByIndex"
29484
- [attr.x1]="x"
29485
- [attr.x2]="x"
29486
- [attr.y1]="chart.plotTop"
29487
- [attr.y2]="chart.plotBottom"
29677
+ *ngFor="let line of categoryLines; trackBy: trackByIndex"
29678
+ [attr.x1]="line.x1"
29679
+ [attr.x2]="line.x2"
29680
+ [attr.y1]="line.y1"
29681
+ [attr.y2]="line.y2"
29488
29682
  [class]="lineClass"
29489
29683
  stroke-width="1"
29490
29684
  ></svg:line>
@@ -29497,7 +29691,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29497
29691
  }] } });
29498
29692
  /** X-axis tick labels, thinned so they never collide. */
29499
29693
  class ChartXAxisComponent extends ChartChild {
29500
- /** Approximate px per character, used to decide when labels would collide. @default 7 */
29694
+ /**
29695
+ * Approximate px per character, used to decide when category labels would
29696
+ * collide. Only consulted in the vertical orientation. @default 7
29697
+ */
29501
29698
  charWidth = 7;
29502
29699
  /** Extra Tailwind classes merged onto each label via `cn()` (last-wins). */
29503
29700
  class = '';
@@ -29516,7 +29713,15 @@ class ChartXAxisComponent extends ChartChild {
29516
29713
  return 1;
29517
29714
  return Math.max(1, Math.ceil(needed / perLabel));
29518
29715
  }
29716
+ /**
29717
+ * Vertical orientation: category labels, thinned so they never collide
29718
+ * (today's behaviour). Horizontal orientation: this is the bottom axis'
29719
+ * physical position, which a horizontal chart uses for its value ticks.
29720
+ */
29519
29721
  get visibleTicks() {
29722
+ if (this.chart.orientation === 'horizontal') {
29723
+ return this.chart.ticks.map((tick) => ({ x: tick.y, label: tick.label }));
29724
+ }
29520
29725
  const stride = this.stride;
29521
29726
  return this.chart.xLabels
29522
29727
  .map((label, i) => ({ label, x: this.chart.xFor(i), i }))
@@ -29573,13 +29778,24 @@ class ChartYAxisComponent extends ChartChild {
29573
29778
  get labelX() {
29574
29779
  return this.chart.plotLeft - this.offset;
29575
29780
  }
29781
+ /**
29782
+ * Vertical orientation: value ticks (today's behaviour). Horizontal
29783
+ * orientation: this is the left axis' physical position, which a
29784
+ * horizontal chart uses for its category labels.
29785
+ */
29786
+ get visibleTicks() {
29787
+ if (this.chart.orientation === 'horizontal') {
29788
+ return this.chart.xLabels.map((label, i) => ({ label, y: this.chart.xFor(i) }));
29789
+ }
29790
+ return this.chart.ticks;
29791
+ }
29576
29792
  get labelClass() {
29577
29793
  return cn('fill-muted-foreground text-xs tabular-nums', this.class);
29578
29794
  }
29579
29795
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChartYAxisComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
29580
29796
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChartYAxisComponent, isStandalone: true, selector: "svg:g[tolle-chart-y-axis]", inputs: { offset: "offset", class: "class" }, usesInheritance: true, ngImport: i0, template: `
29581
29797
  <svg:text
29582
- *ngFor="let tick of chart.ticks; trackBy: trackByIndex"
29798
+ *ngFor="let tick of visibleTicks; trackBy: trackByIndex"
29583
29799
  [attr.x]="labelX"
29584
29800
  [attr.y]="tick.y"
29585
29801
  text-anchor="end"
@@ -29597,7 +29813,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
29597
29813
  changeDetection: ChangeDetectionStrategy.OnPush,
29598
29814
  template: `
29599
29815
  <svg:text
29600
- *ngFor="let tick of chart.ticks; trackBy: trackByIndex"
29816
+ *ngFor="let tick of visibleTicks; trackBy: trackByIndex"
29601
29817
  [attr.x]="labelX"
29602
29818
  [attr.y]="tick.y"
29603
29819
  text-anchor="end"
@@ -29791,6 +30007,24 @@ function barPath(x, width, yBase, yValue, radius) {
29791
30007
  ' Q ' + right + ' ' + yValue + ' ' + right + ' ' + corner +
29792
30008
  ' L ' + right + ' ' + yBase + ' Z');
29793
30009
  }
30010
+ /**
30011
+ * `barPath()` transposed for horizontal bars: fixed vertical extent
30012
+ * `[y, y+height]`, variable horizontal extent `[xBase, xValue]`, rounded at
30013
+ * the `xValue` (data) end.
30014
+ */
30015
+ function barPathHorizontal(y, height, xBase, xValue, radius) {
30016
+ const width = Math.abs(xBase - xValue);
30017
+ const r = Math.max(0, Math.min(radius, height / 2, width));
30018
+ const bottom = y + height;
30019
+ const sign = xValue >= xBase ? 1 : -1;
30020
+ const corner = xValue - sign * r;
30021
+ return ('M ' + xBase + ' ' + y +
30022
+ ' L ' + corner + ' ' + y +
30023
+ ' Q ' + xValue + ' ' + y + ' ' + xValue + ' ' + (y + r) +
30024
+ ' L ' + xValue + ' ' + (bottom - r) +
30025
+ ' Q ' + xValue + ' ' + bottom + ' ' + corner + ' ' + bottom +
30026
+ ' L ' + xBase + ' ' + bottom + ' Z');
30027
+ }
29794
30028
  /**
29795
30029
  * A bar mark for one series. Grouped beside its siblings by default, stacked
29796
30030
  * when the container's `stacked` is set. The data end carries a 4px radius, the
@@ -29823,12 +30057,19 @@ class ChartBarComponent extends ChartChild {
29823
30057
  get color() {
29824
30058
  return this.chart.colorFor(this.seriesKey);
29825
30059
  }
29826
- /** Geometry for every bar this series contributes. */
30060
+ /**
30061
+ * Geometry for every bar this series contributes. `x`/`width` are the bar's
30062
+ * footprint on the category axis and `yBase`/`yValue` its extent on the
30063
+ * value axis — physical x/y when vertical (the original meaning), but
30064
+ * transposed (x holds a vertical footprint, yBase/yValue hold horizontal
30065
+ * positions) when the chart is horizontal, same as `ChartService.yFor()`.
30066
+ */
29827
30067
  get bars() {
29828
30068
  const out = [];
29829
30069
  const seriesCount = Math.max(1, this.chart.series.length);
29830
30070
  const found = this.chart.series.findIndex((item) => item.key === this.seriesKey);
29831
30071
  const slotIndex = found < 0 ? 0 : found;
30072
+ const horizontal = this.chart.orientation === 'horizontal';
29832
30073
  for (let i = 0; i < this.chart.count; i++) {
29833
30074
  const value = this.chart.valueAt(this.seriesKey, i);
29834
30075
  if (value == null)
@@ -29849,12 +30090,16 @@ class ChartBarComponent extends ChartChild {
29849
30090
  const yBase = this.chart.yFor(base);
29850
30091
  let yValue = this.chart.yFor(base + value);
29851
30092
  // The gap between stacked segments comes off the data end, so what
29852
- // separates them is surface showing through rather than a stroke.
30093
+ // separates them is surface showing through rather than a stroke. Signed
30094
+ // from the resolved positions (not the raw value) so it holds under
30095
+ // both orientations, whose value axis runs in opposite directions.
29853
30096
  if (this.chart.stacked && Math.abs(yBase - yValue) > this.gap) {
29854
- yValue += value >= 0 ? this.gap : -this.gap;
30097
+ yValue += Math.sign(yBase - yValue) * this.gap;
29855
30098
  }
29856
30099
  out.push({
29857
- d: barPath(x, width, yBase, yValue, this.radius),
30100
+ d: horizontal
30101
+ ? barPathHorizontal(x, width, yBase, yValue, this.radius)
30102
+ : barPath(x, width, yBase, yValue, this.radius),
29858
30103
  active: this.activeIndex === i,
29859
30104
  x,
29860
30105
  width,
@@ -30280,6 +30525,414 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
30280
30525
  type: Output
30281
30526
  }] } });
30282
30527
 
30528
+ /**
30529
+ * A minimal inline chart: one series, no grid, no axes, no legend — sized to
30530
+ * sit next to a number in a table cell or a stat card. A thin composition
30531
+ * over `tolle-chart`; it draws nothing of its own and inherits `--chart-1`
30532
+ * the same way any other single-series chart in this library does.
30533
+ * @new
30534
+ */
30535
+ class ChartSparkComponent {
30536
+ /**
30537
+ * Angular writes a bound `class` input through its styling path, which does
30538
+ * not mark an OnPush component dirty — without this hook the component keeps
30539
+ * rendering the class it was born with.
30540
+ */
30541
+ ngOnChanges() {
30542
+ this.cdr.markForCheck();
30543
+ }
30544
+ /** Rows to plot, in x order. @default [] */
30545
+ data = [];
30546
+ /** Row key holding each row's numeric value. @default 'value' */
30547
+ valueKey = 'value';
30548
+ /** Row key holding each row's x position. Only matters once `hover` is on. @default '' */
30549
+ xKey = '';
30550
+ /** Which mark to draw. @default 'line' */
30551
+ type = 'line';
30552
+ /** Interpolation for line/area. @default 'linear' */
30553
+ curve = 'linear';
30554
+ /** Largest bar thickness in px, used when `type` is 'bar'. @default 6 */
30555
+ maxBarWidth = 6;
30556
+ /** Height in px. @default 32 */
30557
+ height = 32;
30558
+ /** Renders the crosshair, hit layer and tooltip. @default false */
30559
+ hover = false;
30560
+ /** Shows the data table instead of leaving it visually hidden. @default false */
30561
+ showTable = false;
30562
+ /** Accessible name, used as the svg <title>. @default '' */
30563
+ ariaLabel = '';
30564
+ /** Longer summary used as the svg <desc>. Falls back to `ariaLabel`. @default '' */
30565
+ description = '';
30566
+ /** Space reserved around the plot — a sparkline has no axis labels to fit. @default { top: 2, right: 2, bottom: 2, left: 2 } */
30567
+ margin = { top: 2, right: 2, bottom: 2, left: 2 };
30568
+ /** Extra Tailwind classes merged onto the chart via `cn()` (last-wins). */
30569
+ class = '';
30570
+ cdr = inject(ChangeDetectorRef);
30571
+ /** Always exactly one series, reading `valueKey` off each row. */
30572
+ get series() {
30573
+ return [{ key: this.valueKey, label: this.ariaLabel || this.valueKey }];
30574
+ }
30575
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChartSparkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
30576
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ChartSparkComponent, isStandalone: true, selector: "tolle-chart-spark", inputs: { data: "data", valueKey: "valueKey", xKey: "xKey", type: "type", curve: "curve", maxBarWidth: "maxBarWidth", height: "height", hover: "hover", showTable: "showTable", ariaLabel: "ariaLabel", description: "description", margin: "margin", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
30577
+ <tolle-chart
30578
+ [class]="class"
30579
+ [data]="data"
30580
+ [series]="series"
30581
+ [xKey]="xKey"
30582
+ [height]="height"
30583
+ [margin]="margin"
30584
+ [hover]="hover"
30585
+ [showTable]="showTable"
30586
+ [ariaLabel]="ariaLabel"
30587
+ [description]="description"
30588
+ >
30589
+ <svg:g *ngIf="type === 'line'" tolle-chart-line [seriesKey]="valueKey" [curve]="curve"></svg:g>
30590
+ <svg:g *ngIf="type === 'area'" tolle-chart-area [seriesKey]="valueKey" [curve]="curve"></svg:g>
30591
+ <svg:g *ngIf="type === 'bar'" tolle-chart-bar [seriesKey]="valueKey" [maxWidth]="maxBarWidth"></svg:g>
30592
+ </tolle-chart>
30593
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ChartComponent, selector: "tolle-chart", inputs: ["data", "series", "xKey", "height", "margin", "ariaLabel", "description", "stacked", "orientation", "hover", "showTable", "xHeader", "variant", "density", "class"], outputs: ["activeIndexChange"] }, { kind: "component", type: ChartLineComponent, selector: "svg:g[tolle-chart-line]", inputs: ["seriesKey", "curve", "showDots", "dotRadius", "class"] }, { kind: "component", type: ChartAreaComponent, selector: "svg:g[tolle-chart-area]", inputs: ["seriesKey", "curve", "fillOpacity", "class"] }, { kind: "component", type: ChartBarComponent, selector: "svg:g[tolle-chart-bar]", inputs: ["seriesKey", "radius", "maxWidth", "gap", "class"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
30594
+ }
30595
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChartSparkComponent, decorators: [{
30596
+ type: Component,
30597
+ args: [{ selector: 'tolle-chart-spark', standalone: true, imports: [CommonModule, ChartComponent, ChartLineComponent, ChartAreaComponent, ChartBarComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
30598
+ <tolle-chart
30599
+ [class]="class"
30600
+ [data]="data"
30601
+ [series]="series"
30602
+ [xKey]="xKey"
30603
+ [height]="height"
30604
+ [margin]="margin"
30605
+ [hover]="hover"
30606
+ [showTable]="showTable"
30607
+ [ariaLabel]="ariaLabel"
30608
+ [description]="description"
30609
+ >
30610
+ <svg:g *ngIf="type === 'line'" tolle-chart-line [seriesKey]="valueKey" [curve]="curve"></svg:g>
30611
+ <svg:g *ngIf="type === 'area'" tolle-chart-area [seriesKey]="valueKey" [curve]="curve"></svg:g>
30612
+ <svg:g *ngIf="type === 'bar'" tolle-chart-bar [seriesKey]="valueKey" [maxWidth]="maxBarWidth"></svg:g>
30613
+ </tolle-chart>
30614
+ `, styles: [":host{display:block}\n"] }]
30615
+ }], propDecorators: { data: [{
30616
+ type: Input
30617
+ }], valueKey: [{
30618
+ type: Input
30619
+ }], xKey: [{
30620
+ type: Input
30621
+ }], type: [{
30622
+ type: Input
30623
+ }], curve: [{
30624
+ type: Input
30625
+ }], maxBarWidth: [{
30626
+ type: Input
30627
+ }], height: [{
30628
+ type: Input
30629
+ }], hover: [{
30630
+ type: Input
30631
+ }], showTable: [{
30632
+ type: Input
30633
+ }], ariaLabel: [{
30634
+ type: Input
30635
+ }], description: [{
30636
+ type: Input
30637
+ }], margin: [{
30638
+ type: Input
30639
+ }], class: [{
30640
+ type: Input
30641
+ }] } });
30642
+
30643
+ /**
30644
+ * A single bar split into proportional, labelled ranges — for showing where
30645
+ * one value falls against a scale made of categories (a credit score inside
30646
+ * "Poor / Fair / Good / Excellent", latency inside "Fast / OK / Slow").
30647
+ *
30648
+ * Segments take the chart palette by position, since a category bar has no
30649
+ * stable per-render identity to key colour off the way a chart series does —
30650
+ * pass `colors` to override.
30651
+ * @new
30652
+ */
30653
+ class CategoryBarComponent {
30654
+ /**
30655
+ * Angular writes a bound `class` input through its styling path, which does
30656
+ * not mark an OnPush component dirty — without this hook the component keeps
30657
+ * rendering the class it was born with.
30658
+ */
30659
+ ngOnChanges() {
30660
+ this.cdr.markForCheck();
30661
+ }
30662
+ /** Identity for `*ngFor`, so a redraw patches nodes instead of replacing them. */
30663
+ trackByIndex = (index) => index;
30664
+ /** Width of each segment. Values that are not finite and greater than zero are dropped. @default [] */
30665
+ values = [];
30666
+ /** Name for each segment, used in the accessible summary and as a title tooltip. @default [] */
30667
+ labels = [];
30668
+ /** Paint for each segment, by index. Falls back to the chart palette --chart-1 through --chart-5. @default [] */
30669
+ colors = [];
30670
+ /** Position of the pointer, 0-100 on the same scale the segments sum to. Omit to draw no pointer. @default null */
30671
+ markerValue = null;
30672
+ /** Accessible name, prefixed onto the sr-only summary of segments and marker. @default '' */
30673
+ ariaLabel = '';
30674
+ /** Extra Tailwind classes merged onto the bar via `cn()` (last-wins). */
30675
+ class = '';
30676
+ cdr = inject(ChangeDetectorRef);
30677
+ get total() {
30678
+ return this.values.reduce((sum, v) => (Number.isFinite(v) && v > 0 ? sum + v : sum), 0);
30679
+ }
30680
+ get segments() {
30681
+ const total = this.total;
30682
+ if (total <= 0)
30683
+ return [];
30684
+ return this.values
30685
+ .map((value, i) => ({ value, i }))
30686
+ .filter(({ value }) => Number.isFinite(value) && value > 0)
30687
+ .map(({ value, i }) => ({
30688
+ label: this.labels[i] ?? '',
30689
+ value,
30690
+ percent: (value / total) * 100,
30691
+ color: this.colors[i] || 'rgb(var(--chart-' + ((i % 5) + 1) + '))',
30692
+ }));
30693
+ }
30694
+ get clampedMarker() {
30695
+ if (this.markerValue == null || !Number.isFinite(this.markerValue))
30696
+ return null;
30697
+ return Math.min(100, Math.max(0, this.markerValue));
30698
+ }
30699
+ get summary() {
30700
+ const parts = this.segments.map((s) => (s.label ? s.label + ': ' : '') + s.percent.toFixed(0) + '%');
30701
+ const marker = this.clampedMarker !== null ? ', current position ' + this.clampedMarker.toFixed(0) + '%' : '';
30702
+ const prefix = this.ariaLabel ? this.ariaLabel + '. ' : '';
30703
+ return prefix + parts.join(', ') + marker;
30704
+ }
30705
+ get computedClass() {
30706
+ return cn('w-full', this.class);
30707
+ }
30708
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CategoryBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
30709
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: CategoryBarComponent, isStandalone: true, selector: "tolle-category-bar", inputs: { values: "values", labels: "labels", colors: "colors", markerValue: "markerValue", ariaLabel: "ariaLabel", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
30710
+ <div [class]="computedClass">
30711
+ <div class="relative flex w-full gap-0.5">
30712
+ <div
30713
+ *ngFor="let segment of segments; let i = index; trackBy: trackByIndex"
30714
+ class="h-2 first:rounded-l-full last:rounded-r-full"
30715
+ [style.width.%]="segment.percent"
30716
+ [style.background]="segment.color"
30717
+ [attr.title]="segment.label || null"
30718
+ ></div>
30719
+
30720
+ <span
30721
+ *ngIf="clampedMarker !== null"
30722
+ class="absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-foreground shadow"
30723
+ [style.left.%]="clampedMarker"
30724
+ ></span>
30725
+ </div>
30726
+
30727
+ <p class="sr-only">{{ summary }}</p>
30728
+ </div>
30729
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
30730
+ }
30731
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CategoryBarComponent, decorators: [{
30732
+ type: Component,
30733
+ args: [{ selector: 'tolle-category-bar', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
30734
+ <div [class]="computedClass">
30735
+ <div class="relative flex w-full gap-0.5">
30736
+ <div
30737
+ *ngFor="let segment of segments; let i = index; trackBy: trackByIndex"
30738
+ class="h-2 first:rounded-l-full last:rounded-r-full"
30739
+ [style.width.%]="segment.percent"
30740
+ [style.background]="segment.color"
30741
+ [attr.title]="segment.label || null"
30742
+ ></div>
30743
+
30744
+ <span
30745
+ *ngIf="clampedMarker !== null"
30746
+ class="absolute top-1/2 h-3 w-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-background bg-foreground shadow"
30747
+ [style.left.%]="clampedMarker"
30748
+ ></span>
30749
+ </div>
30750
+
30751
+ <p class="sr-only">{{ summary }}</p>
30752
+ </div>
30753
+ `, styles: [":host{display:block}\n"] }]
30754
+ }], propDecorators: { values: [{
30755
+ type: Input
30756
+ }], labels: [{
30757
+ type: Input
30758
+ }], colors: [{
30759
+ type: Input
30760
+ }], markerValue: [{
30761
+ type: Input
30762
+ }], ariaLabel: [{
30763
+ type: Input
30764
+ }], class: [{
30765
+ type: Input
30766
+ }] } });
30767
+
30768
+ const STATUS_COLOR = {
30769
+ success: 'rgb(var(--success))',
30770
+ warning: 'rgb(var(--warning))',
30771
+ error: 'rgb(var(--destructive))',
30772
+ neutral: 'rgb(var(--muted))',
30773
+ };
30774
+ /**
30775
+ * A row of equal-width blocks, one per period — an uptime or activity strip.
30776
+ * Each block is a real `<button>` so its tooltip is reachable by keyboard and
30777
+ * has a screen-reader name of its own, not just a hover affordance.
30778
+ * @new
30779
+ */
30780
+ class TrackerComponent {
30781
+ /**
30782
+ * Angular writes a bound `class` input through its styling path, which does
30783
+ * not mark an OnPush component dirty — without this hook the component keeps
30784
+ * rendering the class it was born with.
30785
+ */
30786
+ ngOnChanges() {
30787
+ this.cdr.markForCheck();
30788
+ }
30789
+ /** Identity for `*ngFor`, so a redraw patches nodes instead of replacing them. */
30790
+ trackByIndex = (index) => index;
30791
+ /** One block per period, in chronological order. @default [] */
30792
+ data = [];
30793
+ /** Height of each block in px. @default 32 */
30794
+ blockHeight = 32;
30795
+ /** Accessible name for the whole strip. @default '' */
30796
+ ariaLabel = '';
30797
+ /** Extra Tailwind classes merged onto the strip via `cn()` (last-wins). */
30798
+ class = '';
30799
+ cdr = inject(ChangeDetectorRef);
30800
+ colorFor(block) {
30801
+ return block.color || STATUS_COLOR[block.status ?? 'neutral'];
30802
+ }
30803
+ get computedClass() {
30804
+ return cn('flex w-full items-stretch gap-0.5', this.class);
30805
+ }
30806
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TrackerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
30807
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: TrackerComponent, isStandalone: true, selector: "tolle-tracker", inputs: { data: "data", blockHeight: "blockHeight", ariaLabel: "ariaLabel", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
30808
+ <div [class]="computedClass" role="group" [attr.aria-label]="ariaLabel || null">
30809
+ <button
30810
+ *ngFor="let block of data; let i = index; trackBy: trackByIndex"
30811
+ type="button"
30812
+ class="flex-1 rounded-[2px] transition-opacity hover:opacity-80 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
30813
+ [style.height.px]="blockHeight"
30814
+ [style.background]="colorFor(block)"
30815
+ [tolleTooltip]="block.tooltip || ''"
30816
+ [attr.aria-label]="block.tooltip || 'Period ' + (i + 1)"
30817
+ ></button>
30818
+ </div>
30819
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: TooltipDirective, selector: "[tolleTooltip]", inputs: ["tolleTooltip", "placement"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
30820
+ }
30821
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: TrackerComponent, decorators: [{
30822
+ type: Component,
30823
+ args: [{ selector: 'tolle-tracker', standalone: true, imports: [CommonModule, TooltipDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: `
30824
+ <div [class]="computedClass" role="group" [attr.aria-label]="ariaLabel || null">
30825
+ <button
30826
+ *ngFor="let block of data; let i = index; trackBy: trackByIndex"
30827
+ type="button"
30828
+ class="flex-1 rounded-[2px] transition-opacity hover:opacity-80 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
30829
+ [style.height.px]="blockHeight"
30830
+ [style.background]="colorFor(block)"
30831
+ [tolleTooltip]="block.tooltip || ''"
30832
+ [attr.aria-label]="block.tooltip || 'Period ' + (i + 1)"
30833
+ ></button>
30834
+ </div>
30835
+ `, styles: [":host{display:block}\n"] }]
30836
+ }], propDecorators: { data: [{
30837
+ type: Input
30838
+ }], blockHeight: [{
30839
+ type: Input
30840
+ }], ariaLabel: [{
30841
+ type: Input
30842
+ }], class: [{
30843
+ type: Input
30844
+ }] } });
30845
+
30846
+ /**
30847
+ * A ranked list of horizontal bars — label left, value right, bar width
30848
+ * scaled to each row's share of the list's largest value. Unlike a chart
30849
+ * series or a category bar's segments, every bar is the same accent: rank is
30850
+ * carried by length, not by a categorical palette.
30851
+ * @new
30852
+ */
30853
+ class BarListComponent {
30854
+ /**
30855
+ * Angular writes a bound `class` input through its styling path, which does
30856
+ * not mark an OnPush component dirty — without this hook the component keeps
30857
+ * rendering the class it was born with.
30858
+ */
30859
+ ngOnChanges() {
30860
+ this.cdr.markForCheck();
30861
+ }
30862
+ /** Identity for `*ngFor`, so a redraw patches nodes instead of replacing them. */
30863
+ trackByIndex = (index) => index;
30864
+ /** Rows to rank, in the order they should be drawn. @default [] */
30865
+ data = [];
30866
+ /** Row key holding each row's label. @default 'label' */
30867
+ labelKey = 'label';
30868
+ /** Row key holding each row's numeric value. @default 'value' */
30869
+ valueKey = 'value';
30870
+ /** Extra Tailwind classes merged onto the list via `cn()` (last-wins). */
30871
+ class = '';
30872
+ cdr = inject(ChangeDetectorRef);
30873
+ get rows() {
30874
+ const rows = this.data ?? [];
30875
+ const values = rows.map((row) => {
30876
+ const num = Number(row?.[this.valueKey]);
30877
+ return Number.isFinite(num) ? num : 0;
30878
+ });
30879
+ const max = values.reduce((m, v) => Math.max(m, v), 0);
30880
+ return rows.map((row, i) => {
30881
+ const raw = row?.[this.labelKey];
30882
+ const value = values[i];
30883
+ return {
30884
+ label: raw == null ? '' : String(raw),
30885
+ value,
30886
+ percent: max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0,
30887
+ formattedValue: value.toLocaleString('en-US'),
30888
+ };
30889
+ });
30890
+ }
30891
+ get computedClass() {
30892
+ return cn('flex w-full flex-col gap-1', this.class);
30893
+ }
30894
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BarListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
30895
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: BarListComponent, isStandalone: true, selector: "tolle-bar-list", inputs: { data: "data", labelKey: "labelKey", valueKey: "valueKey", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
30896
+ <div [class]="computedClass">
30897
+ <div
30898
+ *ngFor="let row of rows; trackBy: trackByIndex"
30899
+ class="relative flex items-center overflow-hidden rounded-sm"
30900
+ >
30901
+ <div class="absolute inset-y-0 left-0 rounded-sm bg-primary/15" [style.width.%]="row.percent"></div>
30902
+ <span class="relative z-10 truncate px-2 py-1.5 text-sm text-foreground">{{ row.label }}</span>
30903
+ <span class="relative z-10 ml-auto shrink-0 px-2 py-1.5 text-sm font-medium tabular-nums text-foreground">
30904
+ {{ row.formattedValue }}
30905
+ </span>
30906
+ </div>
30907
+ </div>
30908
+ `, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
30909
+ }
30910
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BarListComponent, decorators: [{
30911
+ type: Component,
30912
+ args: [{ selector: 'tolle-bar-list', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
30913
+ <div [class]="computedClass">
30914
+ <div
30915
+ *ngFor="let row of rows; trackBy: trackByIndex"
30916
+ class="relative flex items-center overflow-hidden rounded-sm"
30917
+ >
30918
+ <div class="absolute inset-y-0 left-0 rounded-sm bg-primary/15" [style.width.%]="row.percent"></div>
30919
+ <span class="relative z-10 truncate px-2 py-1.5 text-sm text-foreground">{{ row.label }}</span>
30920
+ <span class="relative z-10 ml-auto shrink-0 px-2 py-1.5 text-sm font-medium tabular-nums text-foreground">
30921
+ {{ row.formattedValue }}
30922
+ </span>
30923
+ </div>
30924
+ </div>
30925
+ `, styles: [":host{display:block}\n"] }]
30926
+ }], propDecorators: { data: [{
30927
+ type: Input
30928
+ }], labelKey: [{
30929
+ type: Input
30930
+ }], valueKey: [{
30931
+ type: Input
30932
+ }], class: [{
30933
+ type: Input
30934
+ }] } });
30935
+
30283
30936
  /**
30284
30937
  * Publishes the application's current writing direction so components can adapt
30285
30938
  * without reading the DOM.
@@ -31773,5 +32426,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
31773
32426
  * Generated bundle index. Do not edit.
31774
32427
  */
31775
32428
 
31776
- export { AccordionComponent, AccordionItemComponent, AlertComponent, AlertDialogActionComponent, AlertDialogCancelComponent, AlertDialogComponent, AlertDialogContentComponent, AlertDialogDescriptionComponent, AlertDialogDynamicComponent, AlertDialogFooterComponent, AlertDialogHeaderComponent, AlertDialogPortalComponent, AlertDialogRef, AlertDialogService, AlertDialogTitleComponent, AlertDialogTriggerComponent, AspectRatioComponent, AttachmentActionsComponent, AttachmentComponent, AttachmentGroupComponent, AvatarComponent, AvatarFallbackComponent, BASE_PALETTES, BadgeComponent, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbLinkComponent, BreadcrumbSeparatorComponent, BubbleActionsComponent, BubbleComponent, BubbleReactionsComponent, ButtonComponent, ButtonGroupComponent, CHART_COLOR_LIMIT, CHART_OVERFLOW_COLOR, CLOSE_FALLBACK_MS, CalendarComponent, CardComponent, CardContentComponent, CardDescriptionComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CarouselComponent, CarouselContainerDirective, CarouselContentDirective, CarouselContext, CarouselItemDirective, CarouselNextDirective, CarouselPreviousDirective, ChainOfThoughtComponent, ChainOfThoughtContentComponent, ChainOfThoughtHeaderComponent, ChainOfThoughtSearchResultComponent, ChainOfThoughtSearchResultsComponent, ChainOfThoughtService, ChainOfThoughtStepComponent, ChartAreaComponent, ChartBarComponent, ChartChild, ChartColorScale, ChartComponent, ChartGridComponent, ChartLegendComponent, ChartLineComponent, ChartPieComponent, ChartService, ChartTableComponent, ChartTooltipComponent, ChartXAxisComponent, ChartYAxisComponent, CheckboxComponent, CheckpointComponent, CollapsibleComponent, CollapsibleContentComponent, CollapsibleTriggerComponent, ComboboxComponent, CommandComponent, CommandDialogComponent, CommandEmptyComponent, CommandGroupComponent, CommandInputComponent, CommandItemComponent, CommandListComponent, CommandSeparatorComponent, CommandService, CommandShortcutComponent, ConfirmationComponent, ContextComponent, ContextContentComponent, ContextMenuComponent, ContextMenuService, ContextMenuTriggerDirective, ContextService, ContextTriggerComponent, ConversationComponent, ConversationContentComponent, ConversationEmptyStateComponent, ConversationScrollButtonComponent, ConversationService, CountryCodesService, CountrySelectorComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DateTimePickerComponent, DirectionDirective, DirectionService, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, EmptyStateComponent, FieldComponent, FieldContentComponent, FieldDescriptionComponent, FieldErrorComponent, FieldGroupComponent, FieldLabelComponent, FieldLegendComponent, FieldSeparatorComponent, FieldSetComponent, FieldTitleComponent, HoverCardComponent, HoverCardContentComponent, HoverCardTriggerComponent, InlineCitationCardComponent, InlineCitationComponent, InlineCitationQuoteComponent, InputComponent, InputGroupAddonComponent, InputGroupButtonComponent, InputGroupComponent, InputGroupInputComponent, InputGroupTextComponent, InputGroupTextareaComponent, ItemActionsComponent, ItemComponent, ItemContentComponent, ItemDescriptionComponent, ItemFooterComponent, ItemGroupComponent, ItemHeaderComponent, ItemMediaComponent, ItemTitleComponent, KbdComponent, KbdGroupComponent, LabelComponent, MarkerComponent, MarkerGroupComponent, MaskedInputComponent, MenubarComponent, MenubarContentComponent, MenubarItemComponent, MenubarLabelComponent, MenubarMenuComponent, MenubarSeparatorComponent, MenubarService, MenubarTriggerComponent, MessageAlignService, MessageAvatarComponent, MessageComponent, MessageContentComponent, MessageFooterComponent, MessageGroupComponent, MessageHeaderComponent, MessageScrollerButtonComponent, MessageScrollerComponent, MessageScrollerContentComponent, MessageScrollerItemDirective, MessageScrollerService, MessageScrollerViewportComponent, ModalComponent, ModalRef, ModalService, ModalStackService, ModelSelectorComponent, MultiSelectComponent, NativeSelectComponent, NavigationMenuComponent, NavigationMenuContentComponent, NavigationMenuItemComponent, NavigationMenuLinkComponent, NavigationMenuListComponent, NavigationMenuService, NavigationMenuTriggerComponent, OtpComponent, OtpGroupComponent, OtpSlotComponent, PaginationComponent, PhoneNumberInputComponent, PhoneNumberService, PlanComponent, PlanService, PlanStepComponent, PopoverComponent, PopoverContentComponent, ProgressComponent, PromptInputComponent, PromptInputService, PromptInputSubmitComponent, PromptInputToolbarComponent, PromptInputToolsComponent, QueueComponent, QueueItemComponent, QueueService, RadioGroupComponent, RadioItemComponent, RadioService, RangeCalendarComponent, ReasoningComponent, ReasoningContentComponent, ReasoningService, ReasoningTriggerComponent, ResizableComponent, ResizablePanelComponent, ResizablePanelItemComponent, ScrollAreaComponent, SegmentComponent, SegmentComponent as SegmentedComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SelectService, SeparatorComponent, SheetComponent, SheetContentComponent, SheetDescriptionComponent, SheetFooterComponent, SheetHeaderComponent, SheetRef, SheetService, SheetTitleComponent, SheetTriggerComponent, SheetWrapperComponent, ShimmerComponent, SidebarComponent, SkeletonComponent, SliderComponent, SourceComponent, SourcesComponent, SourcesContentComponent, SourcesService, SourcesTriggerComponent, SpinnerComponent, SuggestionComponent, SuggestionsComponent, SuggestionsService, SwitchComponent, TOLLE_CONFIG, TableBodyDirective, TableCaptionDirective, TableCellDirective, TableComponent, TableFooterDirective, TableHeadDirective, TableHeaderDirective, TableRowDirective, TabsComponent, TabsContentComponent, TabsListComponent, TabsTriggerComponent, TagInputComponent, TaskComponent, TaskContentComponent, TaskItemComponent, TaskItemFileComponent, TaskService, TaskTriggerComponent, TextareaComponent, ThemeService, TimeColumnsComponent, TimePickerComponent, ToastContainerComponent, ToastService, ToggleComponent, ToggleGroupComponent, ToggleGroupItemComponent, TolleCellDirective, ToolComponent, ToolHeaderComponent, ToolInputComponent, ToolOutputComponent, ToolPayloadBase, ToolService, TooltipDirective, TypographyComponent, arcPath, barPath, buildPath, cn, contrastTriplet, darkenColor, decimalsFor, formatTimeString, formatTokens, from12Hour, generateBaseCss, generateChartCss, generateChartRamp, generateThemeCss, getContrastColor, hexToHsl, hexToRgb, hslToHex, lightenColor, monotoneTangents, niceScale, niceStep, parseTimeString, polarPoint, provideTolleConfig, radiusScale, rgbTriplet, timePartsToSeconds, to12Hour };
32429
+ export { AccordionComponent, AccordionItemComponent, AlertComponent, AlertDialogActionComponent, AlertDialogCancelComponent, AlertDialogComponent, AlertDialogContentComponent, AlertDialogDescriptionComponent, AlertDialogDynamicComponent, AlertDialogFooterComponent, AlertDialogHeaderComponent, AlertDialogPortalComponent, AlertDialogRef, AlertDialogService, AlertDialogTitleComponent, AlertDialogTriggerComponent, AspectRatioComponent, AttachmentActionsComponent, AttachmentComponent, AttachmentGroupComponent, AvatarComponent, AvatarFallbackComponent, BASE_PALETTES, BadgeComponent, BarListComponent, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbLinkComponent, BreadcrumbSeparatorComponent, BubbleActionsComponent, BubbleComponent, BubbleReactionsComponent, ButtonComponent, ButtonGroupComponent, CHART_COLOR_LIMIT, CHART_OVERFLOW_COLOR, CLOSE_FALLBACK_MS, CalendarComponent, CardComponent, CardContentComponent, CardDescriptionComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CarouselComponent, CarouselContainerDirective, CarouselContentDirective, CarouselContext, CarouselItemDirective, CarouselNextDirective, CarouselPreviousDirective, CategoryBarComponent, ChainOfThoughtComponent, ChainOfThoughtContentComponent, ChainOfThoughtHeaderComponent, ChainOfThoughtSearchResultComponent, ChainOfThoughtSearchResultsComponent, ChainOfThoughtService, ChainOfThoughtStepComponent, ChartAreaComponent, ChartBarComponent, ChartChild, ChartColorScale, ChartComponent, ChartGridComponent, ChartLegendComponent, ChartLineComponent, ChartPieComponent, ChartService, ChartSparkComponent, ChartTableComponent, ChartTooltipComponent, ChartXAxisComponent, ChartYAxisComponent, CheckboxComponent, CheckpointComponent, CollapsibleComponent, CollapsibleContentComponent, CollapsibleTriggerComponent, ComboboxComponent, CommandComponent, CommandDialogComponent, CommandEmptyComponent, CommandGroupComponent, CommandInputComponent, CommandItemComponent, CommandListComponent, CommandSeparatorComponent, CommandService, CommandShortcutComponent, ConfirmationComponent, ContextComponent, ContextContentComponent, ContextMenuComponent, ContextMenuService, ContextMenuTriggerDirective, ContextService, ContextTriggerComponent, ConversationComponent, ConversationContentComponent, ConversationEmptyStateComponent, ConversationScrollButtonComponent, ConversationService, CountryCodesService, CountrySelectorComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DateTimePickerComponent, DirectionDirective, DirectionService, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, EmptyStateComponent, FieldComponent, FieldContentComponent, FieldDescriptionComponent, FieldErrorComponent, FieldGroupComponent, FieldLabelComponent, FieldLegendComponent, FieldSeparatorComponent, FieldSetComponent, FieldTitleComponent, HoverCardComponent, HoverCardContentComponent, HoverCardTriggerComponent, InlineCitationCardComponent, InlineCitationComponent, InlineCitationQuoteComponent, InputComponent, InputGroupAddonComponent, InputGroupButtonComponent, InputGroupComponent, InputGroupInputComponent, InputGroupTextComponent, InputGroupTextareaComponent, ItemActionsComponent, ItemComponent, ItemContentComponent, ItemDescriptionComponent, ItemFooterComponent, ItemGroupComponent, ItemHeaderComponent, ItemMediaComponent, ItemTitleComponent, KbdComponent, KbdGroupComponent, LabelComponent, MarkerComponent, MarkerGroupComponent, MaskedInputComponent, MenubarComponent, MenubarContentComponent, MenubarItemComponent, MenubarLabelComponent, MenubarMenuComponent, MenubarSeparatorComponent, MenubarService, MenubarTriggerComponent, MessageAlignService, MessageAvatarComponent, MessageComponent, MessageContentComponent, MessageFooterComponent, MessageGroupComponent, MessageHeaderComponent, MessageScrollerButtonComponent, MessageScrollerComponent, MessageScrollerContentComponent, MessageScrollerItemDirective, MessageScrollerService, MessageScrollerViewportComponent, ModalComponent, ModalRef, ModalService, ModalStackService, ModelSelectorComponent, MultiSelectComponent, NativeSelectComponent, NavigationMenuComponent, NavigationMenuContentComponent, NavigationMenuItemComponent, NavigationMenuLinkComponent, NavigationMenuListComponent, NavigationMenuService, NavigationMenuTriggerComponent, OtpComponent, OtpGroupComponent, OtpSlotComponent, PaginationComponent, PhoneNumberInputComponent, PhoneNumberService, PlanComponent, PlanService, PlanStepComponent, PopoverComponent, PopoverContentComponent, ProgressCircleComponent, ProgressComponent, PromptInputComponent, PromptInputService, PromptInputSubmitComponent, PromptInputToolbarComponent, PromptInputToolsComponent, QueueComponent, QueueItemComponent, QueueService, RadioGroupComponent, RadioItemComponent, RadioService, RangeCalendarComponent, ReasoningComponent, ReasoningContentComponent, ReasoningService, ReasoningTriggerComponent, ResizableComponent, ResizablePanelComponent, ResizablePanelItemComponent, ScrollAreaComponent, SegmentComponent, SegmentComponent as SegmentedComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SelectService, SeparatorComponent, SheetComponent, SheetContentComponent, SheetDescriptionComponent, SheetFooterComponent, SheetHeaderComponent, SheetRef, SheetService, SheetTitleComponent, SheetTriggerComponent, SheetWrapperComponent, ShimmerComponent, SidebarComponent, SkeletonComponent, SliderComponent, SourceComponent, SourcesComponent, SourcesContentComponent, SourcesService, SourcesTriggerComponent, SpinnerComponent, SuggestionComponent, SuggestionsComponent, SuggestionsService, SwitchComponent, TOLLE_CONFIG, TableBodyDirective, TableCaptionDirective, TableCellDirective, TableComponent, TableFooterDirective, TableHeadDirective, TableHeaderDirective, TableRowDirective, TabsComponent, TabsContentComponent, TabsListComponent, TabsTriggerComponent, TagInputComponent, TaskComponent, TaskContentComponent, TaskItemComponent, TaskItemFileComponent, TaskService, TaskTriggerComponent, TextareaComponent, ThemeService, TimeColumnsComponent, TimePickerComponent, ToastContainerComponent, ToastService, ToggleComponent, ToggleGroupComponent, ToggleGroupItemComponent, TolleCellDirective, ToolComponent, ToolHeaderComponent, ToolInputComponent, ToolOutputComponent, ToolPayloadBase, ToolService, TooltipDirective, TrackerComponent, TypographyComponent, arcPath, barPath, barPathHorizontal, buildPath, cn, contrastTriplet, darkenColor, decimalsFor, formatTimeString, formatTokens, from12Hour, generateBaseCss, generateChartCss, generateChartRamp, generateThemeCss, getContrastColor, hexToHsl, hexToRgb, hslToHex, lightenColor, monotoneTangents, niceScale, niceStep, parseTimeString, polarPoint, provideTolleConfig, radiusScale, rgbTriplet, timePartsToSeconds, to12Hour };
31777
32430
  //# sourceMappingURL=tolle-ui.mjs.map