cats-charts 0.0.77 → 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 +73 -0
- package/fesm2022/cats-charts.mjs +206 -2
- package/fesm2022/cats-charts.mjs.map +1 -1
- package/index.d.ts +113 -2
- package/package.json +1 -1
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.
|
package/fesm2022/cats-charts.mjs
CHANGED
|
@@ -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';
|
|
@@ -4105,9 +4105,213 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
4105
4105
|
type: Output
|
|
4106
4106
|
}] } });
|
|
4107
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
|
+
|
|
4108
4312
|
/**
|
|
4109
4313
|
* Generated bundle index. Do not edit.
|
|
4110
4314
|
*/
|
|
4111
4315
|
|
|
4112
|
-
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 };
|
|
4113
4317
|
//# sourceMappingURL=cats-charts.mjs.map
|