cats-charts 0.0.76 → 0.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -637,3 +637,76 @@ Feel free to submit a pull request.
637
637
  # 📄 License
638
638
 
639
639
  MIT License
640
+
641
+ ---
642
+
643
+ # Status Timeline
644
+
645
+ `StatusTimelineComponent` renders generic categorical intervals in chronological order. Segment
646
+ widths are proportional to duration, and dense datasets scroll horizontally without aggregation.
647
+
648
+ ## Import and usage
649
+
650
+ ```ts
651
+ import { StatusTimelineComponent, type StatusTimelineConfig } from 'cats-charts';
652
+
653
+ @Component({
654
+ standalone: true,
655
+ imports: [StatusTimelineComponent],
656
+ template: `
657
+ <lib-status-timeline
658
+ [config]="timelineConfig"
659
+ (chartEvent)="onTimelineEvent($event)"
660
+ />
661
+ `,
662
+ })
663
+ export class ExampleComponent {
664
+ timelineConfig: StatusTimelineConfig = {
665
+ startLabel: 'Monday',
666
+ endLabel: 'Today',
667
+ statuses: [
668
+ { value: 'ready', label: 'Ready', color: '#2f8f3a' },
669
+ { value: 'deploying', label: 'Deploying', color: '#d0a020' },
670
+ { value: 'blocked', label: 'Blocked', color: '#a43b35' },
671
+ ],
672
+ data: [
673
+ { start: '2026-08-24T00:00:00Z', end: '2026-08-25T00:00:00Z', status: 'ready' },
674
+ {
675
+ start: '2026-08-25T00:00:00Z',
676
+ end: '2026-08-25T06:00:00Z',
677
+ status: 'deploying',
678
+ label: 'Release deployment',
679
+ },
680
+ { start: '2026-08-25T06:00:00Z', end: '2026-08-26T00:00:00Z', status: 'ready' },
681
+ ],
682
+ };
683
+ }
684
+ ```
685
+
686
+ ## Configuration
687
+
688
+ | Option | Description |
689
+ | --- | --- |
690
+ | `data` | Dynamic intervals with `start`, `end`, `status`, and optional `label`/`metadata` |
691
+ | `statuses` | Text and optional color for each status value |
692
+ | `showLegend`, `showTooltip` | Toggle supporting UI (both default to `true`) |
693
+ | `legendPosition` | Places the legend at `top`, `bottom`, `left`, or `right` |
694
+ | `legendLayout` | Arranges legend items `horizontal` or `vertical` |
695
+ | `legendAlign` | Aligns the legend at `start`/`left`, `center`, `end`/`right`, or `space-between` |
696
+ | `legendLabelAlign` | Aligns text within vertical legend items to `left`, `center`, or `right` |
697
+ | `legendInteractive` | Enables clicking legend labels to dim/restore matching intervals; defaults to `true` |
698
+ | `legendDimmedOpacity` | Opacity used for inactive legend items and intervals; defaults to `0.3` |
699
+ | `legendScrollable` | Enables bounded legend overflow scrolling; defaults to `true` |
700
+ | `legendMaxWidth`, `legendMaxHeight` | Optional scroll viewport limits |
701
+ | `legendNote` | Optional explanatory text displayed beside the legend |
702
+ | `startLabel`, `endLabel` | Endpoint text; dates are derived when omitted |
703
+ | `showEndpointLabels` | Shows or hides the endpoint-label row; defaults to `true` |
704
+ | `endpointLabelsAlign` | Aligns endpoint labels at `start`/`left`, `center`, `end`/`right`, or `space-between` |
705
+ | `loading`, `loadingLabel`, `emptyLabel`, `error` | Timeline state presentation |
706
+ | `minSegmentWidth`, `segmentGap` | Dense-data and spacing controls in pixels |
707
+ | `dateFormatter`, `durationFormatter`, `tooltipFormatter` | Display customization hooks |
708
+ | `unknownStatus` | Fallback label/color for unconfigured status values |
709
+ | `ariaLabel` | Accessible name for the timeline |
710
+
711
+ The component emits `chartEvent` with `{ type: 'itemClick', item, index }`. Its appearance can be
712
+ themed with the `--status-timeline-*` CSS custom properties defined in the component stylesheet.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, Output, Input, Component, Injectable, PLATFORM_ID, ViewChild, Inject, ChangeDetectionStrategy, HostListener } from '@angular/core';
2
+ import { EventEmitter, Output, Input, Component, Injectable, PLATFORM_ID, ViewChild, Inject, ChangeDetectionStrategy, HostListener, inject, APP_ID } from '@angular/core';
3
3
  import * as echarts from 'echarts/core';
4
4
  import { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';
5
5
  import { LineChart, BarChart as BarChart$1, PieChart as PieChart$1, CustomChart, SankeyChart as SankeyChart$1, HeatmapChart, ScatterChart } from 'echarts/charts';
@@ -1564,59 +1564,83 @@ class LinesChart {
1564
1564
  type: 'category',
1565
1565
  // boundaryGap: false,
1566
1566
  data: this.config?.xAxisData,
1567
+ name: this.config?.xAxisName || '',
1568
+ nameLocation: 'center',
1569
+ ...(this.config?.xAxis || {}),
1567
1570
  axisLine: {
1568
1571
  show: this.config?.showXAxisLine ?? true,
1572
+ ...(this.config?.xAxis?.axisLine || {}),
1569
1573
  lineStyle: {
1570
1574
  color: '#C0C2C5',
1571
1575
  width: 1,
1576
+ ...(this.config?.xAxis?.axisLine?.lineStyle || {}),
1572
1577
  },
1573
1578
  },
1574
1579
  axisTick: {
1575
1580
  show: false,
1581
+ ...(this.config?.xAxis?.axisTick || {}),
1576
1582
  },
1577
1583
  axisLabel: {
1578
1584
  show: this.config?.showXAxisLabel ?? true,
1579
1585
  color: '#434A51',
1580
1586
  fontSize: 12,
1587
+ alignMinLabel: 'left',
1588
+ alignMaxLabel: 'right',
1589
+ ...(this.config?.xAxis?.axisLabel || {}),
1581
1590
  },
1582
- name: this.config?.xAxisName || '',
1583
- nameLocation: 'center',
1584
1591
  nameTextStyle: {
1585
1592
  color: '#434A51',
1586
1593
  fontSize: 14,
1587
1594
  fontWeight: 400,
1595
+ ...(this.config?.xAxis?.nameTextStyle || {}),
1588
1596
  },
1589
1597
  splitLine: {
1590
1598
  show: this.config?.showXAxisSplitLine ?? true,
1599
+ ...(this.config?.xAxis?.splitLine || {}),
1591
1600
  lineStyle: {
1592
1601
  type: 'dashed',
1593
1602
  color: '#C0C2C5',
1594
1603
  width: 1,
1604
+ ...(this.config?.xAxis?.splitLine?.lineStyle || {}),
1595
1605
  },
1596
1606
  },
1597
- ...(this.config?.xAxis || {}),
1598
1607
  },
1599
1608
  yAxis: [
1600
1609
  {
1601
1610
  show: !this.config?.isTrendChart,
1602
1611
  type: 'value',
1603
1612
  boundaryGap: this.config?.isTrendChart ? [0.2] : undefined,
1613
+ name: this.config?.yAxisName || '',
1614
+ nameLocation: 'center',
1615
+ ...(this.config?.yAxis || {}),
1604
1616
  axisLine: {
1605
1617
  show: this.config?.showYAxisLine ?? true,
1618
+ ...(this.config?.yAxis?.axisLine || {}),
1606
1619
  lineStyle: {
1607
1620
  color: '#C0C2C5',
1608
1621
  width: 1,
1622
+ ...(this.config?.yAxis?.axisLine?.lineStyle || {}),
1609
1623
  },
1610
1624
  },
1611
1625
  axisTick: {
1612
1626
  show: false,
1627
+ ...(this.config?.yAxis?.axisTick || {}),
1613
1628
  },
1614
- name: this.config?.yAxisName || '',
1615
- nameLocation: 'center',
1616
1629
  nameTextStyle: {
1617
1630
  color: '#434A51',
1618
1631
  fontSize: 14,
1619
1632
  fontWeight: 400,
1633
+ ...(this.config?.yAxis?.nameTextStyle || {}),
1634
+ },
1635
+ splitLine: {
1636
+ show: this.config?.showYAxisSplitLine ?? true,
1637
+ ...(this.config?.yAxis?.splitLine || {}),
1638
+ lineStyle: {
1639
+ type: 'dashed',
1640
+ color: '#C0C2C5',
1641
+ width: 1,
1642
+ ...(this.config?.yAxis?.splitLine?.lineStyle || {}),
1643
+ },
1620
1644
  },
1621
1645
  axisLabel: {
1622
1646
  show: this.config?.showYAxisLabel ?? true,
@@ -1653,16 +1677,8 @@ class LinesChart {
1653
1677
  }
1654
1678
  return value;
1655
1679
  },
1680
+ ...(this.config?.yAxis?.axisLabel || {}),
1656
1681
  },
1657
- splitLine: {
1658
- show: this.config?.showYAxisSplitLine ?? true,
1659
- lineStyle: {
1660
- type: 'dashed',
1661
- color: '#C0C2C5',
1662
- width: 1,
1663
- },
1664
- },
1665
- ...(this.config?.yAxis || {}),
1666
1682
  },
1667
1683
  ...(this.config?.secondaryYAxis
1668
1684
  ? [
@@ -1670,15 +1686,21 @@ class LinesChart {
1670
1686
  show: !this.config?.isTrendChart,
1671
1687
  type: 'value',
1672
1688
  boundaryGap: this.config?.isTrendChart ? [0.2] : undefined,
1689
+ name: this.config?.secondaryYAxisName || '',
1690
+ nameLocation: 'center',
1691
+ ...(this.config?.secondaryYAxisConfig || {}),
1673
1692
  axisLine: {
1674
1693
  show: this.config?.showYAxisLine ?? true,
1694
+ ...(this.config?.secondaryYAxisConfig?.axisLine || {}),
1675
1695
  lineStyle: {
1676
1696
  color: '#C0C2C5',
1677
1697
  width: 1,
1698
+ ...(this.config?.secondaryYAxisConfig?.axisLine?.lineStyle || {}),
1678
1699
  },
1679
1700
  },
1680
1701
  axisTick: {
1681
1702
  show: false,
1703
+ ...(this.config?.secondaryYAxisConfig?.axisTick || {}),
1682
1704
  },
1683
1705
  axisLabel: {
1684
1706
  show: this.config?.showSecondaryYAxisLabel ?? true,
@@ -1715,23 +1737,24 @@ class LinesChart {
1715
1737
  }
1716
1738
  return value;
1717
1739
  },
1740
+ ...(this.config?.secondaryYAxisConfig?.axisLabel || {}),
1718
1741
  },
1719
- name: this.config?.secondaryYAxisName || '',
1720
- nameLocation: 'center',
1721
1742
  nameTextStyle: {
1722
1743
  color: '#434A51',
1723
1744
  fontSize: 14,
1724
1745
  fontWeight: 400,
1746
+ ...(this.config?.secondaryYAxisConfig?.nameTextStyle || {}),
1725
1747
  },
1726
1748
  splitLine: {
1727
1749
  show: this.config?.showSecondaryYAxisSplitLine ?? true,
1750
+ ...(this.config?.secondaryYAxisConfig?.splitLine || {}),
1728
1751
  lineStyle: {
1729
1752
  type: 'dashed',
1730
1753
  color: '#C0C2C5',
1731
1754
  width: 1,
1755
+ ...(this.config?.secondaryYAxisConfig?.splitLine?.lineStyle || {}),
1732
1756
  },
1733
1757
  },
1734
- ...(this.config?.secondaryYAxisConfig || {}),
1735
1758
  },
1736
1759
  ]
1737
1760
  : []),
@@ -2115,57 +2138,71 @@ class BarChart {
2115
2138
  xAxis: {
2116
2139
  type: 'category',
2117
2140
  data: this.config?.xAxisData,
2141
+ name: this.config?.xAxisName || '',
2142
+ nameLocation: 'center',
2143
+ ...(this.config?.xAxis || {}),
2118
2144
  axisLine: {
2119
2145
  show: this.config?.showXAxisLine ?? true,
2146
+ ...(this.config?.xAxis?.axisLine || {}),
2120
2147
  lineStyle: {
2121
2148
  color: '#C0C2C5',
2122
2149
  width: 1,
2150
+ ...(this.config?.xAxis?.axisLine?.lineStyle || {}),
2123
2151
  },
2124
2152
  },
2125
2153
  axisTick: {
2126
2154
  show: false,
2155
+ ...(this.config?.xAxis?.axisTick || {}),
2127
2156
  },
2128
2157
  axisLabel: {
2129
2158
  show: this.config?.showXAxisLabel ?? true,
2130
2159
  color: '#434A51',
2131
2160
  fontSize: 12,
2161
+ alignMinLabel: 'left',
2162
+ alignMaxLabel: 'right',
2163
+ ...(this.config?.xAxis?.axisLabel || {}),
2132
2164
  },
2133
- name: this.config?.xAxisName || '',
2134
- nameLocation: 'center',
2135
2165
  nameTextStyle: {
2136
2166
  color: '#434A51',
2137
2167
  fontSize: 14,
2138
2168
  fontWeight: 400,
2169
+ ...(this.config?.xAxis?.nameTextStyle || {}),
2139
2170
  },
2140
2171
  splitLine: {
2141
2172
  show: this.config?.showXAxisSplitLine ?? true,
2173
+ ...(this.config?.xAxis?.splitLine || {}),
2142
2174
  lineStyle: {
2143
2175
  type: 'dashed',
2144
2176
  color: '#C0C2C5',
2145
2177
  width: 1,
2178
+ ...(this.config?.xAxis?.splitLine?.lineStyle || {}),
2146
2179
  },
2147
2180
  },
2148
- ...(this.config?.xAxis || {}),
2149
2181
  },
2150
2182
  yAxis: [
2151
2183
  {
2152
2184
  type: 'value',
2185
+ name: this.config?.yAxisName || '',
2186
+ nameLocation: 'center',
2187
+ ...(this.config?.yAxis || {}),
2153
2188
  axisLine: {
2154
2189
  show: this.config?.showYAxisLine ?? true,
2190
+ ...(this.config?.yAxis?.axisLine || {}),
2155
2191
  lineStyle: {
2156
2192
  color: '#C0C2C5',
2157
2193
  width: 1,
2194
+ ...(this.config?.yAxis?.lineStyle || {}),
2158
2195
  },
2159
2196
  },
2160
2197
  axisTick: {
2161
2198
  show: false,
2199
+ ...(this.config?.yAxis?.axisTick || {}),
2162
2200
  },
2163
- name: this.config?.yAxisName || '',
2164
- nameLocation: 'center',
2165
2201
  nameTextStyle: {
2166
2202
  color: '#434A51',
2167
2203
  fontSize: 14,
2168
2204
  fontWeight: 400,
2205
+ ...(this.config?.yAxis?.nameTextStyle || {}),
2169
2206
  },
2170
2207
  axisLabel: {
2171
2208
  show: this.config?.showYAxisLabel ?? true,
@@ -2202,30 +2239,38 @@ class BarChart {
2202
2239
  }
2203
2240
  return value;
2204
2241
  },
2242
+ ...(this.config?.yAxis?.axisLabel || {}),
2205
2243
  },
2206
2244
  splitLine: {
2207
2245
  show: this.config?.showYAxisSplitLine ?? true,
2246
+ ...(this.config?.yAxis?.splitLine || {}),
2208
2247
  lineStyle: {
2209
2248
  type: 'dashed',
2210
2249
  color: '#C0C2C5',
2211
2250
  width: 1,
2251
+ ...(this.config?.yAxis?.splitLine?.lineStyle || {}),
2212
2252
  },
2213
2253
  },
2214
- ...(this.config?.yAxis || {}),
2215
2254
  },
2216
2255
  ...(this.config?.secondaryYAxis
2217
2256
  ? [
2218
2257
  {
2219
2258
  type: 'value',
2259
+ name: this.config?.secondaryYAxisName || '',
2260
+ nameLocation: 'center',
2261
+ ...(this.config?.secondaryYAxisConfig || {}),
2220
2262
  axisLine: {
2221
2263
  show: this.config?.showYAxisLine ?? true,
2264
+ ...(this.config?.secondaryYAxisConfig?.axisLine || {}),
2222
2265
  lineStyle: {
2223
2266
  color: '#C0C2C5',
2224
2267
  width: 1,
2268
+ ...(this.config?.secondaryYAxisConfig?.axisLine?.lineStyle || {}),
2225
2269
  },
2226
2270
  },
2227
2271
  axisTick: {
2228
2272
  show: false,
2273
+ ...(this.config?.secondaryYAxisConfig?.axisTick || {}),
2229
2274
  },
2230
2275
  axisLabel: {
2231
2276
  show: this.config?.showSecondaryYAxisLabel ?? true,
@@ -2262,23 +2307,24 @@ class BarChart {
2262
2307
  }
2263
2308
  return value;
2264
2309
  },
2310
+ ...(this.config?.secondaryYAxisConfig?.axisLabel || {}),
2265
2311
  },
2266
- name: this.config?.secondaryYAxisName || '',
2267
- nameLocation: 'center',
2268
2312
  nameTextStyle: {
2269
2313
  color: '#434A51',
2270
2314
  fontSize: 14,
2271
2315
  fontWeight: 400,
2316
+ ...(this.config?.secondaryYAxisConfig?.nameTextStyle || {}),
2272
2317
  },
2273
2318
  splitLine: {
2274
2319
  show: this.config?.showSecondaryYAxisSplitLine ?? true,
2320
+ ...(this.config?.secondaryYAxisConfig?.splitLine || {}),
2275
2321
  lineStyle: {
2276
2322
  type: 'dashed',
2277
2323
  color: '#C0C2C5',
2278
2324
  width: 1,
2325
+ ...(this.config?.secondaryYAxisConfig?.splitLine?.lineStyle || {}),
2279
2326
  },
2280
2327
  },
2281
- ...(this.config?.secondaryYAxisConfig || {}),
2282
2328
  },
2283
2329
  ]
2284
2330
  : []),
@@ -4059,9 +4105,213 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
4059
4105
  type: Output
4060
4106
  }] } });
4061
4107
 
4108
+ const DEFAULT_COLORS = ['#2f8f3a', '#d0a020', '#dc8a1f', '#a43b35', '#d9dde1'];
4109
+ const DEFAULT_UNKNOWN_STATUS = {
4110
+ value: '__unknown__',
4111
+ label: 'Unknown status',
4112
+ color: '#7b8491',
4113
+ };
4114
+ let statusTimelineInstanceSequence = 0;
4115
+ class StatusTimelineComponent {
4116
+ config;
4117
+ chartEvent = new EventEmitter();
4118
+ renderedItems = [];
4119
+ legendStatuses = [];
4120
+ activeIndex = null;
4121
+ timelineMinWidth = '100%';
4122
+ resolvedStartLabel = '';
4123
+ resolvedEndLabel = '';
4124
+ tooltipIdPrefix = `${inject(APP_ID).replace(/[^a-zA-Z0-9_-]/g, '-')}-status-timeline-${statusTimelineInstanceSequence++}-tooltip`;
4125
+ dimmedStatuses = new Set();
4126
+ get legendPosition() {
4127
+ return this.config.legendPosition ?? 'bottom';
4128
+ }
4129
+ get legendLayout() {
4130
+ return (this.config.legendLayout ??
4131
+ (this.legendPosition === 'left' || this.legendPosition === 'right' ? 'vertical' : 'horizontal'));
4132
+ }
4133
+ get legendScrollable() {
4134
+ return this.config.legendScrollable !== false;
4135
+ }
4136
+ get legendInteractive() {
4137
+ return this.config.legendInteractive !== false;
4138
+ }
4139
+ get legendAlign() {
4140
+ if (this.config.legendAlign === 'left')
4141
+ return 'start';
4142
+ if (this.config.legendAlign === 'right')
4143
+ return 'end';
4144
+ return this.config.legendAlign ?? 'start';
4145
+ }
4146
+ get legendLabelAlign() {
4147
+ return this.config.legendLabelAlign ?? 'left';
4148
+ }
4149
+ get legendLabelJustify() {
4150
+ if (this.legendLabelAlign === 'center')
4151
+ return 'center';
4152
+ if (this.legendLabelAlign === 'right')
4153
+ return 'flex-end';
4154
+ return 'flex-start';
4155
+ }
4156
+ get endpointLabelsAlign() {
4157
+ if (this.config.endpointLabelsAlign === 'left')
4158
+ return 'start';
4159
+ if (this.config.endpointLabelsAlign === 'right')
4160
+ return 'end';
4161
+ return this.config.endpointLabelsAlign ?? 'space-between';
4162
+ }
4163
+ get legendMaxWidth() {
4164
+ return this.config.legendMaxWidth ?? null;
4165
+ }
4166
+ get legendMaxHeight() {
4167
+ return this.config.legendMaxHeight ?? (this.legendLayout === 'vertical' ? '240px' : null);
4168
+ }
4169
+ ngOnChanges() {
4170
+ this.rebuild();
4171
+ }
4172
+ activate(index) {
4173
+ this.activeIndex = index;
4174
+ }
4175
+ deactivate(index) {
4176
+ if (this.activeIndex === index)
4177
+ this.activeIndex = null;
4178
+ }
4179
+ selectItem(item) {
4180
+ this.chartEvent.emit({
4181
+ type: 'itemClick',
4182
+ item: item.source,
4183
+ index: item.originalIndex,
4184
+ });
4185
+ }
4186
+ isStatusSelected(value) {
4187
+ return !this.dimmedStatuses.has(value);
4188
+ }
4189
+ toggleStatus(status) {
4190
+ if (!this.legendInteractive)
4191
+ return;
4192
+ if (this.dimmedStatuses.has(status.value)) {
4193
+ this.dimmedStatuses.delete(status.value);
4194
+ }
4195
+ else {
4196
+ this.dimmedStatuses.add(status.value);
4197
+ }
4198
+ this.chartEvent.emit({
4199
+ type: 'legendToggle',
4200
+ status,
4201
+ selected: this.isStatusSelected(status.value),
4202
+ });
4203
+ }
4204
+ tooltipId(index) {
4205
+ return `${this.tooltipIdPrefix}-${index}`;
4206
+ }
4207
+ trackItem(_, item) {
4208
+ return `${item.start.getTime()}-${item.end.getTime()}-${item.status.value}-${item.originalIndex}`;
4209
+ }
4210
+ trackStatus(_, status) {
4211
+ return status.value;
4212
+ }
4213
+ rebuild() {
4214
+ const config = this.config;
4215
+ if (!config) {
4216
+ this.renderedItems = [];
4217
+ this.legendStatuses = [];
4218
+ return;
4219
+ }
4220
+ const statusMap = new Map();
4221
+ this.legendStatuses = (config.statuses ?? []).map((status, index) => {
4222
+ const resolved = { ...status, color: status.color ?? DEFAULT_COLORS[index % DEFAULT_COLORS.length] };
4223
+ statusMap.set(resolved.value, resolved);
4224
+ return resolved;
4225
+ });
4226
+ const configuredStatusValues = new Set(this.legendStatuses.map((status) => status.value));
4227
+ this.dimmedStatuses = new Set([...this.dimmedStatuses].filter((value) => configuredStatusValues.has(value)));
4228
+ const unknownStatus = {
4229
+ ...DEFAULT_UNKNOWN_STATUS,
4230
+ ...config.unknownStatus,
4231
+ value: config.unknownStatus?.value ?? DEFAULT_UNKNOWN_STATUS.value,
4232
+ label: config.unknownStatus?.label ?? DEFAULT_UNKNOWN_STATUS.label,
4233
+ color: config.unknownStatus?.color ?? DEFAULT_UNKNOWN_STATUS.color,
4234
+ };
4235
+ this.renderedItems = (config.data ?? [])
4236
+ .map((item, originalIndex) => {
4237
+ const start = this.toDate(item.start);
4238
+ const end = this.toDate(item.end);
4239
+ if (!start || !end || end.getTime() <= start.getTime())
4240
+ return null;
4241
+ const status = statusMap.get(item.status) ?? {
4242
+ ...unknownStatus,
4243
+ value: item.status,
4244
+ label: config.unknownStatus?.label ?? `Unknown (${item.status})`,
4245
+ };
4246
+ const durationMs = end.getTime() - start.getTime();
4247
+ const context = { item, status, start, end, durationMs };
4248
+ const tooltip = config.tooltipFormatter?.(context) ?? this.defaultTooltip(context);
4249
+ const accessibleLabel = [item.label, status.label, tooltip].filter(Boolean).join('. ');
4250
+ const tooltipLines = tooltip
4251
+ .split(/\r?\n/)
4252
+ .map((line) => line.trim())
4253
+ .filter(Boolean);
4254
+ return {
4255
+ source: item,
4256
+ originalIndex,
4257
+ start,
4258
+ end,
4259
+ durationMs,
4260
+ status,
4261
+ tooltip,
4262
+ tooltipLines,
4263
+ accessibleLabel,
4264
+ };
4265
+ })
4266
+ .filter((item) => item !== null)
4267
+ .sort((left, right) => left.start.getTime() - right.start.getTime());
4268
+ const gap = Math.max(config.segmentGap ?? 2, 0);
4269
+ const minimum = Math.max(config.minSegmentWidth ?? 6, 1);
4270
+ const requiredWidth = this.renderedItems.length * minimum + Math.max(this.renderedItems.length - 1, 0) * gap;
4271
+ this.timelineMinWidth = `max(100%, ${requiredWidth}px)`;
4272
+ this.resolvedStartLabel = config.startLabel ?? this.formatDate(this.renderedItems[0]?.start);
4273
+ this.resolvedEndLabel = config.endLabel ?? this.formatDate(this.renderedItems.at(-1)?.end);
4274
+ this.activeIndex = null;
4275
+ }
4276
+ toDate(value) {
4277
+ const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
4278
+ return Number.isFinite(date.getTime()) ? date : null;
4279
+ }
4280
+ formatDate(date) {
4281
+ if (!date)
4282
+ return '';
4283
+ return (this.config.dateFormatter?.(date) ??
4284
+ new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' }).format(date));
4285
+ }
4286
+ defaultTooltip(context) {
4287
+ const duration = this.config.durationFormatter?.(context.durationMs) ?? this.formatDuration(context.durationMs);
4288
+ return `${context.status.label}. Start: ${this.formatDate(context.start)}. End: ${this.formatDate(context.end)}. Duration: ${duration}`;
4289
+ }
4290
+ formatDuration(durationMs) {
4291
+ const totalMinutes = Math.max(Math.round(durationMs / 60_000), 1);
4292
+ const days = Math.floor(totalMinutes / 1_440);
4293
+ const hours = Math.floor((totalMinutes % 1_440) / 60);
4294
+ const minutes = totalMinutes % 60;
4295
+ return [days ? `${days}d` : '', hours ? `${hours}h` : '', minutes || (!days && !hours) ? `${minutes}m` : '']
4296
+ .filter(Boolean)
4297
+ .join(' ');
4298
+ }
4299
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: StatusTimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4300
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: StatusTimelineComponent, isStandalone: true, selector: "lib-status-timeline", inputs: { config: "config" }, outputs: { chartEvent: "chartEvent" }, usesOnChanges: true, ngImport: i0, template: "<section\n class=\"status-timeline\"\n [style.--status-timeline-gap]=\"config.segmentGap ?? 2\"\n [style.--status-timeline-min-segment-width]=\"config.minSegmentWidth ?? 6\"\n [style.--status-timeline-dimmed-opacity]=\"config.legendDimmedOpacity ?? 0.3\"\n [class.status-timeline--legend-top]=\"legendPosition === 'top'\"\n [class.status-timeline--legend-bottom]=\"legendPosition === 'bottom'\"\n [class.status-timeline--legend-left]=\"legendPosition === 'left'\"\n [class.status-timeline--legend-right]=\"legendPosition === 'right'\"\n [attr.aria-label]=\"config.ariaLabel || 'Status timeline'\"\n>\n @if (config.loading) {\n <div class=\"status-timeline__state\" role=\"status\" aria-live=\"polite\">\n <span class=\"status-timeline__skeleton\" aria-hidden=\"true\"></span>\n <span>{{ config.loadingLabel || 'Loading timeline' }}</span>\n </div>\n } @else if (config.error) {\n <div class=\"status-timeline__state status-timeline__state--error\" role=\"alert\">\n {{ config.error }}\n </div>\n } @else if (!renderedItems.length) {\n <div class=\"status-timeline__state\" role=\"status\">\n {{ config.emptyLabel || 'No timeline data available' }}\n </div>\n } @else {\n <div class=\"status-timeline__visual\">\n <div class=\"status-timeline__scroll\" tabindex=\"0\" aria-label=\"Scrollable status intervals\">\n <div class=\"status-timeline__content\" [style.min-width]=\"timelineMinWidth\">\n <div class=\"status-timeline__segments\" role=\"list\">\n @for (item of renderedItems; track trackItem($index, item); let index = $index) {\n <button\n type=\"button\"\n role=\"listitem\"\n class=\"status-timeline__segment\"\n [class.status-timeline__segment--dimmed]=\"!isStatusSelected(item.status.value)\"\n [style.background-color]=\"item.status.color\"\n [style.flex-grow]=\"item.durationMs\"\n [attr.aria-label]=\"item.accessibleLabel\"\n [attr.aria-describedby]=\"config.showTooltip !== false ? tooltipId(index) : null\"\n (mouseenter)=\"activate(index)\"\n (mouseleave)=\"deactivate(index)\"\n (focus)=\"activate(index)\"\n (blur)=\"deactivate(index)\"\n (click)=\"selectItem(item)\"\n >\n <span class=\"status-timeline__sr-only\">{{ item.status.label }}</span>\n @if (config.showTooltip !== false && activeIndex === index) {\n <span\n class=\"status-timeline__tooltip\"\n [class.status-timeline__tooltip--start]=\"index === 0\"\n [class.status-timeline__tooltip--end]=\"index === renderedItems.length - 1\"\n [id]=\"tooltipId(index)\"\n role=\"tooltip\"\n >\n @for (line of item.tooltipLines; track $index) {\n <span\n class=\"status-timeline__tooltip-line\"\n [class.status-timeline__tooltip-title]=\"$index === 0\"\n >{{ line }}</span\n >\n }\n </span>\n }\n </button>\n }\n </div>\n\n @if (config.showEndpointLabels !== false) {\n <div\n class=\"status-timeline__labels\"\n [class.status-timeline__labels--start]=\"endpointLabelsAlign === 'start'\"\n [class.status-timeline__labels--center]=\"endpointLabelsAlign === 'center'\"\n [class.status-timeline__labels--end]=\"endpointLabelsAlign === 'end'\"\n aria-hidden=\"true\"\n >\n <span>{{ resolvedStartLabel }}</span>\n <span>{{ resolvedEndLabel }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n\n @if (config.showLegend !== false && legendStatuses.length) {\n <div\n class=\"status-timeline__legend-row\"\n [class.status-timeline__legend-row--vertical]=\"legendLayout === 'vertical'\"\n >\n <ul\n class=\"status-timeline__legend\"\n [class.status-timeline__legend--vertical]=\"legendLayout === 'vertical'\"\n [class.status-timeline__legend--scrollable]=\"legendScrollable\"\n [class.status-timeline__legend--align-center]=\"legendAlign === 'center'\"\n [class.status-timeline__legend--align-end]=\"legendAlign === 'end'\"\n [class.status-timeline__legend--align-space-between]=\"legendAlign === 'space-between'\"\n [style.max-width]=\"legendMaxWidth\"\n [style.max-height]=\"legendMaxHeight\"\n aria-label=\"Status legend\"\n tabindex=\"0\"\n >\n @for (status of legendStatuses; track trackStatus($index, status)) {\n <li\n class=\"status-timeline__legend-item\"\n [class.status-timeline__legend-item--dimmed]=\"!isStatusSelected(status.value)\"\n [style.justify-content]=\"legendLabelJustify\"\n [style.text-align]=\"legendLabelAlign\"\n >\n <button\n type=\"button\"\n class=\"status-timeline__legend-button\"\n role=\"switch\"\n [attr.aria-checked]=\"isStatusSelected(status.value)\"\n [attr.aria-label]=\"\n (isStatusSelected(status.value) ? 'Hide ' : 'Show ') + status.label + ' intervals'\n \"\n [disabled]=\"!legendInteractive\"\n (click)=\"toggleStatus(status)\"\n >\n <span\n class=\"status-timeline__legend-swatch\"\n [style.background-color]=\"status.color\"\n aria-hidden=\"true\"\n ></span>\n <span>{{ status.label }}</span>\n </button>\n </li>\n }\n </ul>\n @if (config.legendNote) {\n <p class=\"status-timeline__legend-note\">{{ config.legendNote }}</p>\n }\n </div>\n }\n }\n</section>\n", styles: [":host{display:block;width:100%;max-width:100%;min-width:0;box-sizing:border-box;color:var(--status-timeline-text-color, #344054);font-family:var(--status-timeline-font-family, inherit)}.status-timeline{display:flex;flex-direction:column;width:100%;min-width:0}.status-timeline__visual{flex:1 1 auto;width:100%;min-width:0}.status-timeline__scroll{display:block;max-width:100%;min-width:0;width:100%;overflow-x:auto;overflow-y:visible;overscroll-behavior-inline:contain;padding:var(--status-timeline-tooltip-space, 90px) 1px 4px;box-sizing:border-box;scrollbar-width:thin}.status-timeline__scroll:focus-visible{outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__content{box-sizing:border-box}.status-timeline__segments{display:flex;align-items:stretch;gap:calc(var(--status-timeline-gap, 2) * 1px);height:var(--status-timeline-height, 36px)}.status-timeline__segment{position:relative;flex-basis:0;min-width:calc(var(--status-timeline-min-segment-width, 6) * 1px);min-height:24px;padding:0;border:0;border-radius:var(--status-timeline-segment-radius, 2px);cursor:pointer;transition:opacity .14s ease,filter .14s ease,box-shadow .14s ease}.status-timeline__segment--dimmed{opacity:var(--status-timeline-dimmed-opacity, .3);filter:saturate(.35)}.status-timeline__segment:hover,.status-timeline__segment:focus-visible{z-index:2;box-shadow:0 0 0 2px var(--status-timeline-focus-ring, rgba(37, 99, 235, .45));outline:none}.status-timeline__tooltip{position:absolute;z-index:10;display:flex;flex-direction:column;gap:2px;left:50%;bottom:calc(100% + 8px);width:max-content;min-width:var(--status-timeline-tooltip-min-width, 166px);max-width:min(300px,80vw);transform:translate(-50%);padding:8px 10px;border-radius:var(--status-timeline-tooltip-radius, 4px);background:var(--status-timeline-tooltip-background, #101828);color:var(--status-timeline-tooltip-color, #fff);box-shadow:0 4px 12px #10182833;font-size:var(--status-timeline-tooltip-font-size, 12px);line-height:1.5;text-align:left;pointer-events:none}.status-timeline__tooltip-line{display:block;white-space:normal}.status-timeline__tooltip-title{font-weight:700;white-space:nowrap}.status-timeline__tooltip--start{left:0;transform:none}.status-timeline__tooltip--end{right:0;left:auto;transform:none}.status-timeline__labels{display:flex;justify-content:space-between;gap:16px;margin-top:8px;color:var(--status-timeline-muted-color, #667085);font-size:var(--status-timeline-label-font-size, 12px)}.status-timeline__labels--start{justify-content:flex-start}.status-timeline__labels--center{justify-content:center}.status-timeline__labels--end{justify-content:flex-end}.status-timeline__legend-row{display:flex;flex-wrap:wrap;align-items:center;gap:18px;margin:12px 0 0;padding:12px 0 0;border-top:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline__legend{display:flex;flex:1 1 520px;min-width:0;flex-wrap:wrap;align-items:center;gap:8px 18px;margin:0;padding:0;list-style:none;scrollbar-width:thin}.status-timeline__legend--vertical{flex:0 1 auto;flex-direction:column;flex-wrap:nowrap;align-items:flex-start}.status-timeline__legend--align-center{justify-content:safe center}.status-timeline__legend--align-end{justify-content:safe flex-end}.status-timeline__legend--align-space-between{justify-content:space-between}.status-timeline__legend--scrollable:not(.status-timeline__legend--vertical){flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;overscroll-behavior-inline:contain}.status-timeline__legend--scrollable.status-timeline__legend--vertical{overflow-x:hidden;overflow-y:auto;overscroll-behavior-block:contain}.status-timeline__legend:focus-visible{outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__legend-item{display:inline-flex;align-items:center;font-size:var(--status-timeline-legend-font-size, 12px);white-space:nowrap;transition:opacity .14s ease}.status-timeline__legend-item--dimmed{opacity:var(--status-timeline-dimmed-opacity, .3)}.status-timeline__legend-button{display:inline-flex;align-items:center;justify-content:inherit;width:100%;gap:6px;margin:0;padding:2px 0;border:0;background:transparent;color:inherit;font:inherit;text-align:inherit;white-space:inherit;cursor:pointer}.status-timeline__legend-button:disabled{opacity:1;cursor:default}.status-timeline__legend-button:focus-visible{border-radius:2px;outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__legend--vertical .status-timeline__legend-item{width:100%;box-sizing:border-box}.status-timeline__legend-swatch{width:10px;height:10px;flex:0 0 10px;border-radius:var(--status-timeline-legend-radius, 2px)}.status-timeline__legend-note{flex:1 1 300px;margin:0;color:var(--status-timeline-muted-color, #667085);font-size:var(--status-timeline-legend-font-size, 12px);text-align:right}.status-timeline--legend-top .status-timeline__legend-row{order:-1;margin:0 0 12px;padding:0 0 12px;border-top:0;border-bottom:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-left,.status-timeline--legend-right{flex-direction:row;align-items:stretch;gap:12px}.status-timeline--legend-left .status-timeline__legend-row,.status-timeline--legend-right .status-timeline__legend-row{flex:0 1 auto;flex-direction:column;align-items:flex-start;width:max-content;max-width:min(280px,40%);margin:0}.status-timeline--legend-left .status-timeline__legend-row{order:-1;padding:0 12px 0 0;border-top:0;border-right:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-right .status-timeline__legend-row{padding:0 0 0 12px;border-top:0;border-left:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline__legend-row--vertical .status-timeline__legend-note{text-align:left}.status-timeline__state{min-height:var(--status-timeline-state-height, 72px);display:flex;align-items:center;justify-content:center;gap:10px;padding:12px;box-sizing:border-box;color:var(--status-timeline-muted-color, #667085);border:1px dashed var(--status-timeline-divider-color, #d0d5dd);border-radius:4px;font-size:13px}.status-timeline__state--error{color:var(--status-timeline-error-color, #b42318);background:var(--status-timeline-error-background, #fef3f2)}.status-timeline__skeleton{width:min(180px,45%);height:12px;border-radius:3px;background:linear-gradient(90deg,#eaecf0 25%,#f9fafb,#eaecf0 75%);background-size:200% 100%;animation:status-timeline-shimmer 1.5s infinite}.status-timeline__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@keyframes status-timeline-shimmer{to{background-position-x:-200%}}@media (prefers-reduced-motion: reduce){.status-timeline__skeleton{animation:none}.status-timeline__segment,.status-timeline__legend-item{transition:none}}@media (max-width: 720px){.status-timeline--legend-left,.status-timeline--legend-right{flex-direction:column}.status-timeline--legend-left .status-timeline__legend-row,.status-timeline--legend-right .status-timeline__legend-row{width:100%;max-width:100%}.status-timeline--legend-left .status-timeline__legend-row{margin:0 0 12px;padding:0 0 12px;border-right:0;border-bottom:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-right .status-timeline__legend-row{margin:12px 0 0;padding:12px 0 0;border-top:1px solid var(--status-timeline-divider-color, #eaecf0);border-left:0}.status-timeline__legend-row{align-items:flex-start;flex-direction:column}.status-timeline__legend-note{text-align:left}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4301
+ }
4302
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: StatusTimelineComponent, decorators: [{
4303
+ type: Component,
4304
+ args: [{ selector: 'lib-status-timeline', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section\n class=\"status-timeline\"\n [style.--status-timeline-gap]=\"config.segmentGap ?? 2\"\n [style.--status-timeline-min-segment-width]=\"config.minSegmentWidth ?? 6\"\n [style.--status-timeline-dimmed-opacity]=\"config.legendDimmedOpacity ?? 0.3\"\n [class.status-timeline--legend-top]=\"legendPosition === 'top'\"\n [class.status-timeline--legend-bottom]=\"legendPosition === 'bottom'\"\n [class.status-timeline--legend-left]=\"legendPosition === 'left'\"\n [class.status-timeline--legend-right]=\"legendPosition === 'right'\"\n [attr.aria-label]=\"config.ariaLabel || 'Status timeline'\"\n>\n @if (config.loading) {\n <div class=\"status-timeline__state\" role=\"status\" aria-live=\"polite\">\n <span class=\"status-timeline__skeleton\" aria-hidden=\"true\"></span>\n <span>{{ config.loadingLabel || 'Loading timeline' }}</span>\n </div>\n } @else if (config.error) {\n <div class=\"status-timeline__state status-timeline__state--error\" role=\"alert\">\n {{ config.error }}\n </div>\n } @else if (!renderedItems.length) {\n <div class=\"status-timeline__state\" role=\"status\">\n {{ config.emptyLabel || 'No timeline data available' }}\n </div>\n } @else {\n <div class=\"status-timeline__visual\">\n <div class=\"status-timeline__scroll\" tabindex=\"0\" aria-label=\"Scrollable status intervals\">\n <div class=\"status-timeline__content\" [style.min-width]=\"timelineMinWidth\">\n <div class=\"status-timeline__segments\" role=\"list\">\n @for (item of renderedItems; track trackItem($index, item); let index = $index) {\n <button\n type=\"button\"\n role=\"listitem\"\n class=\"status-timeline__segment\"\n [class.status-timeline__segment--dimmed]=\"!isStatusSelected(item.status.value)\"\n [style.background-color]=\"item.status.color\"\n [style.flex-grow]=\"item.durationMs\"\n [attr.aria-label]=\"item.accessibleLabel\"\n [attr.aria-describedby]=\"config.showTooltip !== false ? tooltipId(index) : null\"\n (mouseenter)=\"activate(index)\"\n (mouseleave)=\"deactivate(index)\"\n (focus)=\"activate(index)\"\n (blur)=\"deactivate(index)\"\n (click)=\"selectItem(item)\"\n >\n <span class=\"status-timeline__sr-only\">{{ item.status.label }}</span>\n @if (config.showTooltip !== false && activeIndex === index) {\n <span\n class=\"status-timeline__tooltip\"\n [class.status-timeline__tooltip--start]=\"index === 0\"\n [class.status-timeline__tooltip--end]=\"index === renderedItems.length - 1\"\n [id]=\"tooltipId(index)\"\n role=\"tooltip\"\n >\n @for (line of item.tooltipLines; track $index) {\n <span\n class=\"status-timeline__tooltip-line\"\n [class.status-timeline__tooltip-title]=\"$index === 0\"\n >{{ line }}</span\n >\n }\n </span>\n }\n </button>\n }\n </div>\n\n @if (config.showEndpointLabels !== false) {\n <div\n class=\"status-timeline__labels\"\n [class.status-timeline__labels--start]=\"endpointLabelsAlign === 'start'\"\n [class.status-timeline__labels--center]=\"endpointLabelsAlign === 'center'\"\n [class.status-timeline__labels--end]=\"endpointLabelsAlign === 'end'\"\n aria-hidden=\"true\"\n >\n <span>{{ resolvedStartLabel }}</span>\n <span>{{ resolvedEndLabel }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n\n @if (config.showLegend !== false && legendStatuses.length) {\n <div\n class=\"status-timeline__legend-row\"\n [class.status-timeline__legend-row--vertical]=\"legendLayout === 'vertical'\"\n >\n <ul\n class=\"status-timeline__legend\"\n [class.status-timeline__legend--vertical]=\"legendLayout === 'vertical'\"\n [class.status-timeline__legend--scrollable]=\"legendScrollable\"\n [class.status-timeline__legend--align-center]=\"legendAlign === 'center'\"\n [class.status-timeline__legend--align-end]=\"legendAlign === 'end'\"\n [class.status-timeline__legend--align-space-between]=\"legendAlign === 'space-between'\"\n [style.max-width]=\"legendMaxWidth\"\n [style.max-height]=\"legendMaxHeight\"\n aria-label=\"Status legend\"\n tabindex=\"0\"\n >\n @for (status of legendStatuses; track trackStatus($index, status)) {\n <li\n class=\"status-timeline__legend-item\"\n [class.status-timeline__legend-item--dimmed]=\"!isStatusSelected(status.value)\"\n [style.justify-content]=\"legendLabelJustify\"\n [style.text-align]=\"legendLabelAlign\"\n >\n <button\n type=\"button\"\n class=\"status-timeline__legend-button\"\n role=\"switch\"\n [attr.aria-checked]=\"isStatusSelected(status.value)\"\n [attr.aria-label]=\"\n (isStatusSelected(status.value) ? 'Hide ' : 'Show ') + status.label + ' intervals'\n \"\n [disabled]=\"!legendInteractive\"\n (click)=\"toggleStatus(status)\"\n >\n <span\n class=\"status-timeline__legend-swatch\"\n [style.background-color]=\"status.color\"\n aria-hidden=\"true\"\n ></span>\n <span>{{ status.label }}</span>\n </button>\n </li>\n }\n </ul>\n @if (config.legendNote) {\n <p class=\"status-timeline__legend-note\">{{ config.legendNote }}</p>\n }\n </div>\n }\n }\n</section>\n", styles: [":host{display:block;width:100%;max-width:100%;min-width:0;box-sizing:border-box;color:var(--status-timeline-text-color, #344054);font-family:var(--status-timeline-font-family, inherit)}.status-timeline{display:flex;flex-direction:column;width:100%;min-width:0}.status-timeline__visual{flex:1 1 auto;width:100%;min-width:0}.status-timeline__scroll{display:block;max-width:100%;min-width:0;width:100%;overflow-x:auto;overflow-y:visible;overscroll-behavior-inline:contain;padding:var(--status-timeline-tooltip-space, 90px) 1px 4px;box-sizing:border-box;scrollbar-width:thin}.status-timeline__scroll:focus-visible{outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__content{box-sizing:border-box}.status-timeline__segments{display:flex;align-items:stretch;gap:calc(var(--status-timeline-gap, 2) * 1px);height:var(--status-timeline-height, 36px)}.status-timeline__segment{position:relative;flex-basis:0;min-width:calc(var(--status-timeline-min-segment-width, 6) * 1px);min-height:24px;padding:0;border:0;border-radius:var(--status-timeline-segment-radius, 2px);cursor:pointer;transition:opacity .14s ease,filter .14s ease,box-shadow .14s ease}.status-timeline__segment--dimmed{opacity:var(--status-timeline-dimmed-opacity, .3);filter:saturate(.35)}.status-timeline__segment:hover,.status-timeline__segment:focus-visible{z-index:2;box-shadow:0 0 0 2px var(--status-timeline-focus-ring, rgba(37, 99, 235, .45));outline:none}.status-timeline__tooltip{position:absolute;z-index:10;display:flex;flex-direction:column;gap:2px;left:50%;bottom:calc(100% + 8px);width:max-content;min-width:var(--status-timeline-tooltip-min-width, 166px);max-width:min(300px,80vw);transform:translate(-50%);padding:8px 10px;border-radius:var(--status-timeline-tooltip-radius, 4px);background:var(--status-timeline-tooltip-background, #101828);color:var(--status-timeline-tooltip-color, #fff);box-shadow:0 4px 12px #10182833;font-size:var(--status-timeline-tooltip-font-size, 12px);line-height:1.5;text-align:left;pointer-events:none}.status-timeline__tooltip-line{display:block;white-space:normal}.status-timeline__tooltip-title{font-weight:700;white-space:nowrap}.status-timeline__tooltip--start{left:0;transform:none}.status-timeline__tooltip--end{right:0;left:auto;transform:none}.status-timeline__labels{display:flex;justify-content:space-between;gap:16px;margin-top:8px;color:var(--status-timeline-muted-color, #667085);font-size:var(--status-timeline-label-font-size, 12px)}.status-timeline__labels--start{justify-content:flex-start}.status-timeline__labels--center{justify-content:center}.status-timeline__labels--end{justify-content:flex-end}.status-timeline__legend-row{display:flex;flex-wrap:wrap;align-items:center;gap:18px;margin:12px 0 0;padding:12px 0 0;border-top:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline__legend{display:flex;flex:1 1 520px;min-width:0;flex-wrap:wrap;align-items:center;gap:8px 18px;margin:0;padding:0;list-style:none;scrollbar-width:thin}.status-timeline__legend--vertical{flex:0 1 auto;flex-direction:column;flex-wrap:nowrap;align-items:flex-start}.status-timeline__legend--align-center{justify-content:safe center}.status-timeline__legend--align-end{justify-content:safe flex-end}.status-timeline__legend--align-space-between{justify-content:space-between}.status-timeline__legend--scrollable:not(.status-timeline__legend--vertical){flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;overscroll-behavior-inline:contain}.status-timeline__legend--scrollable.status-timeline__legend--vertical{overflow-x:hidden;overflow-y:auto;overscroll-behavior-block:contain}.status-timeline__legend:focus-visible{outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__legend-item{display:inline-flex;align-items:center;font-size:var(--status-timeline-legend-font-size, 12px);white-space:nowrap;transition:opacity .14s ease}.status-timeline__legend-item--dimmed{opacity:var(--status-timeline-dimmed-opacity, .3)}.status-timeline__legend-button{display:inline-flex;align-items:center;justify-content:inherit;width:100%;gap:6px;margin:0;padding:2px 0;border:0;background:transparent;color:inherit;font:inherit;text-align:inherit;white-space:inherit;cursor:pointer}.status-timeline__legend-button:disabled{opacity:1;cursor:default}.status-timeline__legend-button:focus-visible{border-radius:2px;outline:2px solid var(--status-timeline-focus-color, #2563eb);outline-offset:2px}.status-timeline__legend--vertical .status-timeline__legend-item{width:100%;box-sizing:border-box}.status-timeline__legend-swatch{width:10px;height:10px;flex:0 0 10px;border-radius:var(--status-timeline-legend-radius, 2px)}.status-timeline__legend-note{flex:1 1 300px;margin:0;color:var(--status-timeline-muted-color, #667085);font-size:var(--status-timeline-legend-font-size, 12px);text-align:right}.status-timeline--legend-top .status-timeline__legend-row{order:-1;margin:0 0 12px;padding:0 0 12px;border-top:0;border-bottom:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-left,.status-timeline--legend-right{flex-direction:row;align-items:stretch;gap:12px}.status-timeline--legend-left .status-timeline__legend-row,.status-timeline--legend-right .status-timeline__legend-row{flex:0 1 auto;flex-direction:column;align-items:flex-start;width:max-content;max-width:min(280px,40%);margin:0}.status-timeline--legend-left .status-timeline__legend-row{order:-1;padding:0 12px 0 0;border-top:0;border-right:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-right .status-timeline__legend-row{padding:0 0 0 12px;border-top:0;border-left:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline__legend-row--vertical .status-timeline__legend-note{text-align:left}.status-timeline__state{min-height:var(--status-timeline-state-height, 72px);display:flex;align-items:center;justify-content:center;gap:10px;padding:12px;box-sizing:border-box;color:var(--status-timeline-muted-color, #667085);border:1px dashed var(--status-timeline-divider-color, #d0d5dd);border-radius:4px;font-size:13px}.status-timeline__state--error{color:var(--status-timeline-error-color, #b42318);background:var(--status-timeline-error-background, #fef3f2)}.status-timeline__skeleton{width:min(180px,45%);height:12px;border-radius:3px;background:linear-gradient(90deg,#eaecf0 25%,#f9fafb,#eaecf0 75%);background-size:200% 100%;animation:status-timeline-shimmer 1.5s infinite}.status-timeline__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@keyframes status-timeline-shimmer{to{background-position-x:-200%}}@media (prefers-reduced-motion: reduce){.status-timeline__skeleton{animation:none}.status-timeline__segment,.status-timeline__legend-item{transition:none}}@media (max-width: 720px){.status-timeline--legend-left,.status-timeline--legend-right{flex-direction:column}.status-timeline--legend-left .status-timeline__legend-row,.status-timeline--legend-right .status-timeline__legend-row{width:100%;max-width:100%}.status-timeline--legend-left .status-timeline__legend-row{margin:0 0 12px;padding:0 0 12px;border-right:0;border-bottom:1px solid var(--status-timeline-divider-color, #eaecf0)}.status-timeline--legend-right .status-timeline__legend-row{margin:12px 0 0;padding:12px 0 0;border-top:1px solid var(--status-timeline-divider-color, #eaecf0);border-left:0}.status-timeline__legend-row{align-items:flex-start;flex-direction:column}.status-timeline__legend-note{text-align:left}}\n"] }]
4305
+ }], propDecorators: { config: [{
4306
+ type: Input,
4307
+ args: [{ required: true }]
4308
+ }], chartEvent: [{
4309
+ type: Output
4310
+ }] } });
4311
+
4062
4312
  /**
4063
4313
  * Generated bundle index. Do not edit.
4064
4314
  */
4065
4315
 
4066
- export { BarChart, ChartLegendAdapter, ChartLegendComponent, Charts, CustomChartComponent, DoughnutChart, Heatmap, LinesChart, PieChart, RightClickCard, SankeyChart, ScatterChartComp, StackedAreaChart };
4316
+ export { BarChart, ChartLegendAdapter, ChartLegendComponent, Charts, CustomChartComponent, DoughnutChart, Heatmap, LinesChart, PieChart, RightClickCard, SankeyChart, ScatterChartComp, StackedAreaChart, StatusTimelineComponent };
4067
4317
  //# sourceMappingURL=cats-charts.mjs.map