@weeehaoooz/talos-ui 0.1.0 → 0.1.2
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 +2 -0
- package/fesm2022/{weeehaoooz-talos-ui-button-button-group.mjs → weeehaoooz-talos-ui-button.mjs} +115 -18
- package/fesm2022/weeehaoooz-talos-ui-button.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-data-display.mjs +3110 -0
- package/fesm2022/weeehaoooz-talos-ui-data-display.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-data-viz.mjs +2414 -0
- package/fesm2022/weeehaoooz-talos-ui-data-viz.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-dropdown.mjs +1039 -0
- package/fesm2022/weeehaoooz-talos-ui-dropdown.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-feedback.mjs +2885 -0
- package/fesm2022/weeehaoooz-talos-ui-feedback.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-form.mjs +10799 -0
- package/fesm2022/weeehaoooz-talos-ui-form.mjs.map +1 -0
- package/fesm2022/weeehaoooz-talos-ui-layout.mjs +1320 -11
- package/fesm2022/weeehaoooz-talos-ui-layout.mjs.map +1 -1
- package/fesm2022/weeehaoooz-talos-ui-nav.mjs +119 -72
- package/fesm2022/weeehaoooz-talos-ui-nav.mjs.map +1 -1
- package/fesm2022/weeehaoooz-talos-ui.mjs +22190 -15213
- package/fesm2022/weeehaoooz-talos-ui.mjs.map +1 -1
- package/package.json +20 -135
- package/styles/_breadcrumb.scss +16 -0
- package/styles/_button-group.scss +156 -7
- package/styles/_buttons.scss +156 -8
- package/styles/_dropdown.scss +438 -0
- package/styles/_form-controls.scss +13 -0
- package/styles/_progress-bar.scss +43 -0
- package/styles/_skeleton.scss +176 -0
- package/styles/_spinner.scss +58 -0
- package/styles/_variables.scss +37 -0
- package/styles/index.scss +6 -1
- package/types/weeehaoooz-talos-ui-button.d.ts +92 -0
- package/types/weeehaoooz-talos-ui-button.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-data-display.d.ts +1018 -0
- package/types/weeehaoooz-talos-ui-data-display.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-data-viz.d.ts +839 -0
- package/types/weeehaoooz-talos-ui-data-viz.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-dropdown.d.ts +187 -0
- package/types/weeehaoooz-talos-ui-dropdown.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-feedback.d.ts +1495 -0
- package/types/weeehaoooz-talos-ui-feedback.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-form.d.ts +2639 -0
- package/types/weeehaoooz-talos-ui-form.d.ts.map +1 -0
- package/types/weeehaoooz-talos-ui-layout.d.ts +851 -3
- package/types/weeehaoooz-talos-ui-layout.d.ts.map +1 -1
- package/types/weeehaoooz-talos-ui-nav.d.ts +36 -10
- package/types/weeehaoooz-talos-ui-nav.d.ts.map +1 -1
- package/types/weeehaoooz-talos-ui.d.ts +6949 -4137
- package/types/weeehaoooz-talos-ui.d.ts.map +1 -1
- package/fesm2022/weeehaoooz-talos-ui-button-button-group.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-button-button.mjs +0 -93
- package/fesm2022/weeehaoooz-talos-ui-button-button.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-display-badge.mjs +0 -233
- package/fesm2022/weeehaoooz-talos-ui-data-display-badge.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-display-status-tag.mjs +0 -361
- package/fesm2022/weeehaoooz-talos-ui-data-display-status-tag.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-display-timeline.mjs +0 -312
- package/fesm2022/weeehaoooz-talos-ui-data-display-timeline.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-display-trend-indicator.mjs +0 -235
- package/fesm2022/weeehaoooz-talos-ui-data-display-trend-indicator.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-viz-category-bar.mjs +0 -462
- package/fesm2022/weeehaoooz-talos-ui-data-viz-category-bar.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-viz-heatmap.mjs +0 -805
- package/fesm2022/weeehaoooz-talos-ui-data-viz-heatmap.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-viz-mini-donut.mjs +0 -267
- package/fesm2022/weeehaoooz-talos-ui-data-viz-mini-donut.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-viz-mini-gauge.mjs +0 -351
- package/fesm2022/weeehaoooz-talos-ui-data-viz-mini-gauge.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-data-viz-sparkline.mjs +0 -566
- package/fesm2022/weeehaoooz-talos-ui-data-viz-sparkline.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-feedback-alert.mjs +0 -534
- package/fesm2022/weeehaoooz-talos-ui-feedback-alert.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-feedback-dialog.mjs +0 -446
- package/fesm2022/weeehaoooz-talos-ui-feedback-dialog.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-feedback-snackbar.mjs +0 -376
- package/fesm2022/weeehaoooz-talos-ui-feedback-snackbar.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-feedback-tooltip.mjs +0 -147
- package/fesm2022/weeehaoooz-talos-ui-feedback-tooltip.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-affix.mjs +0 -242
- package/fesm2022/weeehaoooz-talos-ui-form-affix.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-autocomplete.mjs +0 -514
- package/fesm2022/weeehaoooz-talos-ui-form-autocomplete.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-checkbox.mjs +0 -548
- package/fesm2022/weeehaoooz-talos-ui-form-checkbox.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-chips.mjs +0 -692
- package/fesm2022/weeehaoooz-talos-ui-form-chips.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-color-picker.mjs +0 -761
- package/fesm2022/weeehaoooz-talos-ui-form-color-picker.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-date-picker.mjs +0 -896
- package/fesm2022/weeehaoooz-talos-ui-form-date-picker.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-date-range-picker.mjs +0 -872
- package/fesm2022/weeehaoooz-talos-ui-form-date-range-picker.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-date-time-picker.mjs +0 -119
- package/fesm2022/weeehaoooz-talos-ui-form-date-time-picker.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-date-time-range-picker.mjs +0 -135
- package/fesm2022/weeehaoooz-talos-ui-form-date-time-range-picker.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-drop-zone.mjs +0 -666
- package/fesm2022/weeehaoooz-talos-ui-form-drop-zone.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-form-field.mjs +0 -147
- package/fesm2022/weeehaoooz-talos-ui-form-form-field.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-input.mjs +0 -33
- package/fesm2022/weeehaoooz-talos-ui-form-input.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-otp.mjs +0 -455
- package/fesm2022/weeehaoooz-talos-ui-form-otp.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-password-input.mjs +0 -379
- package/fesm2022/weeehaoooz-talos-ui-form-password-input.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-radio.mjs +0 -297
- package/fesm2022/weeehaoooz-talos-ui-form-radio.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-range-input.mjs +0 -661
- package/fesm2022/weeehaoooz-talos-ui-form-range-input.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-rating.mjs +0 -346
- package/fesm2022/weeehaoooz-talos-ui-form-rating.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-select-input.mjs +0 -896
- package/fesm2022/weeehaoooz-talos-ui-form-select-input.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-slide-toggle.mjs +0 -252
- package/fesm2022/weeehaoooz-talos-ui-form-slide-toggle.mjs.map +0 -1
- package/fesm2022/weeehaoooz-talos-ui-form-transfer-list.mjs +0 -916
- package/fesm2022/weeehaoooz-talos-ui-form-transfer-list.mjs.map +0 -1
- package/types/weeehaoooz-talos-ui-button-button-group.d.ts +0 -59
- package/types/weeehaoooz-talos-ui-button-button-group.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-button-button.d.ts +0 -38
- package/types/weeehaoooz-talos-ui-button-button.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-display-badge.d.ts +0 -119
- package/types/weeehaoooz-talos-ui-data-display-badge.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-display-status-tag.d.ts +0 -135
- package/types/weeehaoooz-talos-ui-data-display-status-tag.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-display-timeline.d.ts +0 -144
- package/types/weeehaoooz-talos-ui-data-display-timeline.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-display-trend-indicator.d.ts +0 -83
- package/types/weeehaoooz-talos-ui-data-display-trend-indicator.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-viz-category-bar.d.ts +0 -255
- package/types/weeehaoooz-talos-ui-data-viz-category-bar.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-viz-heatmap.d.ts +0 -204
- package/types/weeehaoooz-talos-ui-data-viz-heatmap.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-viz-mini-donut.d.ts +0 -101
- package/types/weeehaoooz-talos-ui-data-viz-mini-donut.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-viz-mini-gauge.d.ts +0 -138
- package/types/weeehaoooz-talos-ui-data-viz-mini-gauge.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-data-viz-sparkline.d.ts +0 -161
- package/types/weeehaoooz-talos-ui-data-viz-sparkline.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-feedback-alert.d.ts +0 -212
- package/types/weeehaoooz-talos-ui-feedback-alert.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-feedback-dialog.d.ts +0 -259
- package/types/weeehaoooz-talos-ui-feedback-dialog.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-feedback-snackbar.d.ts +0 -147
- package/types/weeehaoooz-talos-ui-feedback-snackbar.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-feedback-tooltip.d.ts +0 -32
- package/types/weeehaoooz-talos-ui-feedback-tooltip.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-affix.d.ts +0 -88
- package/types/weeehaoooz-talos-ui-form-affix.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-autocomplete.d.ts +0 -113
- package/types/weeehaoooz-talos-ui-form-autocomplete.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-checkbox.d.ts +0 -165
- package/types/weeehaoooz-talos-ui-form-checkbox.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-chips.d.ts +0 -152
- package/types/weeehaoooz-talos-ui-form-chips.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-color-picker.d.ts +0 -144
- package/types/weeehaoooz-talos-ui-form-color-picker.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-date-picker.d.ts +0 -138
- package/types/weeehaoooz-talos-ui-form-date-picker.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-date-range-picker.d.ts +0 -148
- package/types/weeehaoooz-talos-ui-form-date-range-picker.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-date-time-picker.d.ts +0 -44
- package/types/weeehaoooz-talos-ui-form-date-time-picker.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-date-time-range-picker.d.ts +0 -48
- package/types/weeehaoooz-talos-ui-form-date-time-range-picker.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-drop-zone.d.ts +0 -227
- package/types/weeehaoooz-talos-ui-form-drop-zone.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-form-field.d.ts +0 -42
- package/types/weeehaoooz-talos-ui-form-form-field.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-input.d.ts +0 -15
- package/types/weeehaoooz-talos-ui-form-input.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-otp.d.ts +0 -111
- package/types/weeehaoooz-talos-ui-form-otp.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-password-input.d.ts +0 -145
- package/types/weeehaoooz-talos-ui-form-password-input.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-radio.d.ts +0 -84
- package/types/weeehaoooz-talos-ui-form-radio.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-range-input.d.ts +0 -181
- package/types/weeehaoooz-talos-ui-form-range-input.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-rating.d.ts +0 -110
- package/types/weeehaoooz-talos-ui-form-rating.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-select-input.d.ts +0 -195
- package/types/weeehaoooz-talos-ui-form-select-input.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-slide-toggle.d.ts +0 -79
- package/types/weeehaoooz-talos-ui-form-slide-toggle.d.ts.map +0 -1
- package/types/weeehaoooz-talos-ui-form-transfer-list.d.ts +0 -192
- package/types/weeehaoooz-talos-ui-form-transfer-list.d.ts.map +0 -1
|
@@ -0,0 +1,2414 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, ElementRef, input, output, signal, computed, Component, NgModule, model, effect, afterNextRender, DestroyRef } from '@angular/core';
|
|
3
|
+
import { TalosTooltipDirective } from '@weeehaoooz/talos-ui/feedback';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
5
|
+
import { FormsModule } from '@angular/forms';
|
|
6
|
+
import { LucideRefreshCw, LucideUpload, LucideMaximize2, LucideMinimize2, LucideChevronDown, LucideCheck, LucideFileSpreadsheet, LucideFileJson, LucideCalendar, LucideClock, LucideArrowLeftRight } from '@lucide/angular';
|
|
7
|
+
import { startOfDay, endOfDay, startOfISOWeek, subWeeks, endOfISOWeek, isSameDay, isBefore, addDays, addWeeks, isValid } from 'date-fns';
|
|
8
|
+
import { formatDate } from '@weeehaoooz/talos-ui/core';
|
|
9
|
+
import { DateRangePickerComponent } from '@weeehaoooz/talos-ui/form';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Talos Category Bar Type Definitions
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const PRESET_PALETTE = {
|
|
16
|
+
blue: '#3b82f6',
|
|
17
|
+
emerald: '#10b981',
|
|
18
|
+
violet: '#8b5cf6',
|
|
19
|
+
purple: '#9333ea',
|
|
20
|
+
amber: '#f59e0b',
|
|
21
|
+
orange: '#f97316',
|
|
22
|
+
rose: '#f43f5e',
|
|
23
|
+
pink: '#ec4899',
|
|
24
|
+
cyan: '#06b6d4',
|
|
25
|
+
lime: '#84cc16',
|
|
26
|
+
indigo: '#6366f1',
|
|
27
|
+
teal: '#14b8a6',
|
|
28
|
+
sky: '#0284c7',
|
|
29
|
+
red: '#ef4444',
|
|
30
|
+
slate: '#64748b'
|
|
31
|
+
};
|
|
32
|
+
const DEFAULT_COLOR_ORDER = [
|
|
33
|
+
'#3b82f6', // blue
|
|
34
|
+
'#10b981', // emerald
|
|
35
|
+
'#8b5cf6', // violet
|
|
36
|
+
'#f59e0b', // amber
|
|
37
|
+
'#ec4899', // pink
|
|
38
|
+
'#06b6d4', // cyan
|
|
39
|
+
'#84cc16', // lime
|
|
40
|
+
'#f97316', // orange
|
|
41
|
+
'#6366f1', // indigo
|
|
42
|
+
'#14b8a6' // teal
|
|
43
|
+
];
|
|
44
|
+
class TalosCategoryBarComponent {
|
|
45
|
+
elementRef = inject(ElementRef);
|
|
46
|
+
/**
|
|
47
|
+
* Simple numeric values array for each category (e.g. [120, 80, 60, 40]).
|
|
48
|
+
*/
|
|
49
|
+
values = input(null, /* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/**
|
|
52
|
+
* Detailed category segments with labels, colors, and metadata.
|
|
53
|
+
*/
|
|
54
|
+
categories = input(null, /* @ts-ignore */
|
|
55
|
+
...(ngDevMode ? [{ debugName: "categories" }] : /* istanbul ignore next */ []));
|
|
56
|
+
/**
|
|
57
|
+
* Custom palette or color list for segments. Can be CSS color strings or preset names.
|
|
58
|
+
*/
|
|
59
|
+
colors = input(DEFAULT_COLOR_ORDER, /* @ts-ignore */
|
|
60
|
+
...(ngDevMode ? [{ debugName: "colors" }] : /* istanbul ignore next */ []));
|
|
61
|
+
/**
|
|
62
|
+
* Numeric position for the needle marker.
|
|
63
|
+
*/
|
|
64
|
+
markerValue = input(null, /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "markerValue" }] : /* istanbul ignore next */ []));
|
|
66
|
+
/**
|
|
67
|
+
* Detailed marker object definition.
|
|
68
|
+
*/
|
|
69
|
+
marker = input(null, /* @ts-ignore */
|
|
70
|
+
...(ngDevMode ? [{ debugName: "marker" }] : /* istanbul ignore next */ []));
|
|
71
|
+
/**
|
|
72
|
+
* Minimum scale bound (defaults to 0).
|
|
73
|
+
*/
|
|
74
|
+
minValue = input(null, /* @ts-ignore */
|
|
75
|
+
...(ngDevMode ? [{ debugName: "minValue" }] : /* istanbul ignore next */ []));
|
|
76
|
+
/**
|
|
77
|
+
* Maximum scale bound (defaults to sum of all category values).
|
|
78
|
+
*/
|
|
79
|
+
maxValue = input(null, /* @ts-ignore */
|
|
80
|
+
...(ngDevMode ? [{ debugName: "maxValue" }] : /* istanbul ignore next */ []));
|
|
81
|
+
/**
|
|
82
|
+
* Whether to show category scale labels at segment boundaries.
|
|
83
|
+
*/
|
|
84
|
+
showLabels = input(true, /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "showLabels" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/**
|
|
87
|
+
* Custom boundary label strings. If not provided, computed cumulative values are shown.
|
|
88
|
+
*/
|
|
89
|
+
labels = input(null, /* @ts-ignore */
|
|
90
|
+
...(ngDevMode ? [{ debugName: "labels" }] : /* istanbul ignore next */ []));
|
|
91
|
+
/**
|
|
92
|
+
* Placement of scale boundary labels: 'top', 'bottom', or 'none'.
|
|
93
|
+
*/
|
|
94
|
+
labelPosition = input('top', /* @ts-ignore */
|
|
95
|
+
...(ngDevMode ? [{ debugName: "labelPosition" }] : /* istanbul ignore next */ []));
|
|
96
|
+
/**
|
|
97
|
+
* Custom label formatter function for boundary values.
|
|
98
|
+
*/
|
|
99
|
+
labelFormatter = input(null, /* @ts-ignore */
|
|
100
|
+
...(ngDevMode ? [{ debugName: "labelFormatter" }] : /* istanbul ignore next */ []));
|
|
101
|
+
/**
|
|
102
|
+
* Custom value formatter for segment tooltip / legends.
|
|
103
|
+
*/
|
|
104
|
+
valueFormatter = input(null, /* @ts-ignore */
|
|
105
|
+
...(ngDevMode ? [{ debugName: "valueFormatter" }] : /* istanbul ignore next */ []));
|
|
106
|
+
/**
|
|
107
|
+
* Custom marker value formatter for tooltip.
|
|
108
|
+
*/
|
|
109
|
+
markerFormatter = input(null, /* @ts-ignore */
|
|
110
|
+
...(ngDevMode ? [{ debugName: "markerFormatter" }] : /* istanbul ignore next */ []));
|
|
111
|
+
/**
|
|
112
|
+
* Height size variant of the category bar: 'xs' (6px), 'sm' (8px), 'md' (12px), 'lg' (16px).
|
|
113
|
+
*/
|
|
114
|
+
size = input('md', /* @ts-ignore */
|
|
115
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
116
|
+
/**
|
|
117
|
+
* Corner shape of the bar: 'pill' (fully rounded capsule), 'rounded' (6px), 'square' (0px).
|
|
118
|
+
*/
|
|
119
|
+
shape = input('pill', /* @ts-ignore */
|
|
120
|
+
...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
121
|
+
/**
|
|
122
|
+
* Optional title above the bar.
|
|
123
|
+
*/
|
|
124
|
+
title = input('', /* @ts-ignore */
|
|
125
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
126
|
+
/**
|
|
127
|
+
* Optional subtitle below the title.
|
|
128
|
+
*/
|
|
129
|
+
subtitle = input('', /* @ts-ignore */
|
|
130
|
+
...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
|
|
131
|
+
/**
|
|
132
|
+
* Whether to display a bottom legend row with category names and color indicators.
|
|
133
|
+
*/
|
|
134
|
+
showLegend = input(false, /* @ts-ignore */
|
|
135
|
+
...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
|
|
136
|
+
/**
|
|
137
|
+
* Whether hovering segments or marker displays interactive tooltips.
|
|
138
|
+
*/
|
|
139
|
+
showTooltip = input(true, /* @ts-ignore */
|
|
140
|
+
...(ngDevMode ? [{ debugName: "showTooltip" }] : /* istanbul ignore next */ []));
|
|
141
|
+
/**
|
|
142
|
+
* Enable smooth CSS transition animation.
|
|
143
|
+
*/
|
|
144
|
+
animated = input(true, /* @ts-ignore */
|
|
145
|
+
...(ngDevMode ? [{ debugName: "animated" }] : /* istanbul ignore next */ []));
|
|
146
|
+
/**
|
|
147
|
+
* Output event when a segment is clicked.
|
|
148
|
+
*/
|
|
149
|
+
segmentClick = output();
|
|
150
|
+
/**
|
|
151
|
+
* Output event when the marker is clicked.
|
|
152
|
+
*/
|
|
153
|
+
markerClick = output();
|
|
154
|
+
/**
|
|
155
|
+
* Output event when a segment is hovered.
|
|
156
|
+
*/
|
|
157
|
+
segmentHover = output();
|
|
158
|
+
// Internal reactive hover states
|
|
159
|
+
hoveredSegment = signal(null, /* @ts-ignore */
|
|
160
|
+
...(ngDevMode ? [{ debugName: "hoveredSegment" }] : /* istanbul ignore next */ []));
|
|
161
|
+
hoveredMarker = signal(false, /* @ts-ignore */
|
|
162
|
+
...(ngDevMode ? [{ debugName: "hoveredMarker" }] : /* istanbul ignore next */ []));
|
|
163
|
+
tooltipPosition = signal(null, /* @ts-ignore */
|
|
164
|
+
...(ngDevMode ? [{ debugName: "tooltipPosition" }] : /* istanbul ignore next */ []));
|
|
165
|
+
/**
|
|
166
|
+
* Resolves raw category items from either `categories` or `values` input.
|
|
167
|
+
*/
|
|
168
|
+
rawCategories = computed(() => {
|
|
169
|
+
const cats = this.categories();
|
|
170
|
+
if (cats && cats.length > 0) {
|
|
171
|
+
return cats;
|
|
172
|
+
}
|
|
173
|
+
const vals = this.values();
|
|
174
|
+
if (vals && vals.length > 0) {
|
|
175
|
+
return vals.map((val, idx) => ({
|
|
176
|
+
value: val,
|
|
177
|
+
label: `Category ${idx + 1}`
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
return [];
|
|
181
|
+
}, /* @ts-ignore */
|
|
182
|
+
...(ngDevMode ? [{ debugName: "rawCategories" }] : /* istanbul ignore next */ []));
|
|
183
|
+
/**
|
|
184
|
+
* Resolved color array mapping named color presets to hex codes if needed.
|
|
185
|
+
*/
|
|
186
|
+
resolvedPalette = computed(() => {
|
|
187
|
+
const customColors = this.colors();
|
|
188
|
+
if (customColors && customColors.length > 0) {
|
|
189
|
+
return customColors.map(c => PRESET_PALETTE[c] || c);
|
|
190
|
+
}
|
|
191
|
+
return DEFAULT_COLOR_ORDER;
|
|
192
|
+
}, /* @ts-ignore */
|
|
193
|
+
...(ngDevMode ? [{ debugName: "resolvedPalette" }] : /* istanbul ignore next */ []));
|
|
194
|
+
/**
|
|
195
|
+
* Total sum of all positive category values.
|
|
196
|
+
*/
|
|
197
|
+
totalSum = computed(() => {
|
|
198
|
+
const raw = this.rawCategories();
|
|
199
|
+
return raw.reduce((sum, item) => sum + Math.max(0, item.value || 0), 0);
|
|
200
|
+
}, /* @ts-ignore */
|
|
201
|
+
...(ngDevMode ? [{ debugName: "totalSum" }] : /* istanbul ignore next */ []));
|
|
202
|
+
/**
|
|
203
|
+
* Computed minimum boundary.
|
|
204
|
+
*/
|
|
205
|
+
computedMin = computed(() => {
|
|
206
|
+
const customMin = this.minValue();
|
|
207
|
+
return customMin !== null ? customMin : 0;
|
|
208
|
+
}, /* @ts-ignore */
|
|
209
|
+
...(ngDevMode ? [{ debugName: "computedMin" }] : /* istanbul ignore next */ []));
|
|
210
|
+
/**
|
|
211
|
+
* Computed maximum boundary.
|
|
212
|
+
*/
|
|
213
|
+
computedMax = computed(() => {
|
|
214
|
+
const customMax = this.maxValue();
|
|
215
|
+
if (customMax !== null) {
|
|
216
|
+
return customMax;
|
|
217
|
+
}
|
|
218
|
+
const sum = this.totalSum();
|
|
219
|
+
return sum > 0 ? sum : 100;
|
|
220
|
+
}, /* @ts-ignore */
|
|
221
|
+
...(ngDevMode ? [{ debugName: "computedMax" }] : /* istanbul ignore next */ []));
|
|
222
|
+
/**
|
|
223
|
+
* Normalized computed segments for rendering with cumulative positions and percentages.
|
|
224
|
+
*/
|
|
225
|
+
normalizedSegments = computed(() => {
|
|
226
|
+
const raw = this.rawCategories();
|
|
227
|
+
const total = this.totalSum();
|
|
228
|
+
const palette = this.resolvedPalette();
|
|
229
|
+
const vFormatter = this.valueFormatter();
|
|
230
|
+
if (raw.length === 0 || total <= 0) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
let cumulative = 0;
|
|
234
|
+
return raw.map((item, index) => {
|
|
235
|
+
const val = Math.max(0, item.value || 0);
|
|
236
|
+
const percentage = (val / total) * 100;
|
|
237
|
+
const start = cumulative;
|
|
238
|
+
const end = cumulative + val;
|
|
239
|
+
const startPercent = (start / total) * 100;
|
|
240
|
+
const endPercent = (end / total) * 100;
|
|
241
|
+
cumulative = end;
|
|
242
|
+
// Color resolution
|
|
243
|
+
let color = item.color;
|
|
244
|
+
if (!color && item.colorScheme) {
|
|
245
|
+
color = PRESET_PALETTE[item.colorScheme] || item.colorScheme;
|
|
246
|
+
}
|
|
247
|
+
if (!color) {
|
|
248
|
+
color = palette[index % palette.length];
|
|
249
|
+
}
|
|
250
|
+
const formattedVal = vFormatter ? vFormatter(val) : val.toLocaleString();
|
|
251
|
+
const label = item.label || `Category ${index + 1}`;
|
|
252
|
+
const tooltipText = item.tooltip || `${label}: ${formattedVal} (${percentage.toFixed(1)}%)`;
|
|
253
|
+
return {
|
|
254
|
+
index,
|
|
255
|
+
value: val,
|
|
256
|
+
label,
|
|
257
|
+
color,
|
|
258
|
+
percentage,
|
|
259
|
+
cumulativeStart: start,
|
|
260
|
+
cumulativeEnd: end,
|
|
261
|
+
cumulativeStartPercent: startPercent,
|
|
262
|
+
cumulativeEndPercent: endPercent,
|
|
263
|
+
formattedValue: formattedVal,
|
|
264
|
+
tooltipText,
|
|
265
|
+
raw: item
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}, /* @ts-ignore */
|
|
269
|
+
...(ngDevMode ? [{ debugName: "normalizedSegments" }] : /* istanbul ignore next */ []));
|
|
270
|
+
/**
|
|
271
|
+
* Computed scale boundary labels positioned at tick intersections.
|
|
272
|
+
*/
|
|
273
|
+
scaleLabels = computed(() => {
|
|
274
|
+
if (!this.showLabels() || this.labelPosition() === 'none') {
|
|
275
|
+
return [];
|
|
276
|
+
}
|
|
277
|
+
const customLabels = this.labels();
|
|
278
|
+
const segments = this.normalizedSegments();
|
|
279
|
+
const lFormatter = this.labelFormatter();
|
|
280
|
+
const min = this.computedMin();
|
|
281
|
+
const max = this.computedMax();
|
|
282
|
+
if (customLabels && customLabels.length > 0) {
|
|
283
|
+
const count = customLabels.length;
|
|
284
|
+
return customLabels.map((text, index) => {
|
|
285
|
+
const leftPercent = count > 1 ? (index / (count - 1)) * 100 : 0;
|
|
286
|
+
return {
|
|
287
|
+
index,
|
|
288
|
+
value: min + ((max - min) * leftPercent) / 100,
|
|
289
|
+
text,
|
|
290
|
+
leftPercent
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (segments.length === 0) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
// Generate boundary ticks: [0, seg1.end, seg2.end, ...]
|
|
298
|
+
const ticks = [];
|
|
299
|
+
// Start tick (0 / min)
|
|
300
|
+
const startVal = min;
|
|
301
|
+
const startText = lFormatter ? lFormatter(startVal, 0) : startVal.toLocaleString();
|
|
302
|
+
ticks.push({
|
|
303
|
+
index: 0,
|
|
304
|
+
value: startVal,
|
|
305
|
+
text: startText,
|
|
306
|
+
leftPercent: 0
|
|
307
|
+
});
|
|
308
|
+
segments.forEach((seg, idx) => {
|
|
309
|
+
const val = seg.cumulativeEnd;
|
|
310
|
+
const text = lFormatter ? lFormatter(val, idx + 1) : val.toLocaleString();
|
|
311
|
+
ticks.push({
|
|
312
|
+
index: idx + 1,
|
|
313
|
+
value: val,
|
|
314
|
+
text,
|
|
315
|
+
leftPercent: seg.cumulativeEndPercent
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
return ticks;
|
|
319
|
+
}, /* @ts-ignore */
|
|
320
|
+
...(ngDevMode ? [{ debugName: "scaleLabels" }] : /* istanbul ignore next */ []));
|
|
321
|
+
/**
|
|
322
|
+
* Resolved marker value.
|
|
323
|
+
*/
|
|
324
|
+
markerVal = computed(() => {
|
|
325
|
+
const m = this.marker();
|
|
326
|
+
if (m && typeof m.value === 'number') {
|
|
327
|
+
return m.value;
|
|
328
|
+
}
|
|
329
|
+
const mv = this.markerValue();
|
|
330
|
+
return typeof mv === 'number' ? mv : null;
|
|
331
|
+
}, /* @ts-ignore */
|
|
332
|
+
...(ngDevMode ? [{ debugName: "markerVal" }] : /* istanbul ignore next */ []));
|
|
333
|
+
/**
|
|
334
|
+
* Marker needle placement and metadata.
|
|
335
|
+
*/
|
|
336
|
+
markerState = computed(() => {
|
|
337
|
+
const val = this.markerVal();
|
|
338
|
+
if (val === null) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const min = this.computedMin();
|
|
342
|
+
const max = this.computedMax();
|
|
343
|
+
const span = max - min;
|
|
344
|
+
if (span <= 0) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
const rawPercent = ((val - min) / span) * 100;
|
|
348
|
+
const clampedPercent = Math.max(0, Math.min(100, rawPercent));
|
|
349
|
+
const m = this.marker();
|
|
350
|
+
const mFormatter = this.markerFormatter();
|
|
351
|
+
const formattedVal = mFormatter ? mFormatter(val) : val.toLocaleString();
|
|
352
|
+
// Pick marker color: custom marker color -> category color at that position -> primary accent
|
|
353
|
+
let color = m?.color;
|
|
354
|
+
if (!color) {
|
|
355
|
+
const segments = this.normalizedSegments();
|
|
356
|
+
const currentSegment = segments.find(s => clampedPercent >= s.cumulativeStartPercent && clampedPercent <= s.cumulativeEndPercent);
|
|
357
|
+
color = currentSegment ? currentSegment.color : '#6366f1';
|
|
358
|
+
}
|
|
359
|
+
const label = m?.label || `Marker: ${formattedVal}`;
|
|
360
|
+
const tooltip = m?.tooltip || `${label} (${clampedPercent.toFixed(1)}%)`;
|
|
361
|
+
const variant = m?.variant || 'pill';
|
|
362
|
+
return {
|
|
363
|
+
visible: true,
|
|
364
|
+
percent: clampedPercent,
|
|
365
|
+
color,
|
|
366
|
+
label,
|
|
367
|
+
tooltip,
|
|
368
|
+
variant
|
|
369
|
+
};
|
|
370
|
+
}, /* @ts-ignore */
|
|
371
|
+
...(ngDevMode ? [{ debugName: "markerState" }] : /* istanbul ignore next */ []));
|
|
372
|
+
/**
|
|
373
|
+
* Accessible description for screen readers.
|
|
374
|
+
*/
|
|
375
|
+
ariaLabel = computed(() => {
|
|
376
|
+
const t = this.title();
|
|
377
|
+
const mVal = this.markerVal();
|
|
378
|
+
const total = this.totalSum();
|
|
379
|
+
if (t) {
|
|
380
|
+
return `${t}, Total: ${total}${mVal !== null ? `, Current Marker: ${mVal}` : ''}`;
|
|
381
|
+
}
|
|
382
|
+
return `Category bar with ${this.rawCategories().length} categories, Total: ${total}`;
|
|
383
|
+
}, /* @ts-ignore */
|
|
384
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
385
|
+
// Event handlers
|
|
386
|
+
onSegmentClick(seg, event) {
|
|
387
|
+
this.segmentClick.emit({
|
|
388
|
+
segment: seg,
|
|
389
|
+
index: seg.index,
|
|
390
|
+
event
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
onSegmentMouseEnter(seg, event) {
|
|
394
|
+
this.hoveredSegment.set(seg);
|
|
395
|
+
this.segmentHover.emit(seg);
|
|
396
|
+
this.updateTooltipPos(event);
|
|
397
|
+
}
|
|
398
|
+
onSegmentMouseLeave() {
|
|
399
|
+
this.hoveredSegment.set(null);
|
|
400
|
+
this.segmentHover.emit(null);
|
|
401
|
+
}
|
|
402
|
+
onMarkerMouseEnter(event) {
|
|
403
|
+
this.hoveredMarker.set(true);
|
|
404
|
+
this.updateTooltipPos(event);
|
|
405
|
+
}
|
|
406
|
+
onMarkerMouseLeave() {
|
|
407
|
+
this.hoveredMarker.set(false);
|
|
408
|
+
}
|
|
409
|
+
onMarkerClick(event) {
|
|
410
|
+
const ms = this.markerState();
|
|
411
|
+
const mVal = this.markerVal();
|
|
412
|
+
if (ms && mVal !== null) {
|
|
413
|
+
const mObj = this.marker() || { value: mVal };
|
|
414
|
+
this.markerClick.emit({
|
|
415
|
+
marker: mObj,
|
|
416
|
+
value: mVal,
|
|
417
|
+
percent: ms.percent,
|
|
418
|
+
event
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
updateTooltipPos(event) {
|
|
423
|
+
const hostEl = this.elementRef.nativeElement;
|
|
424
|
+
const rect = hostEl.getBoundingClientRect();
|
|
425
|
+
this.tooltipPosition.set({
|
|
426
|
+
x: event.clientX - rect.left,
|
|
427
|
+
y: event.clientY - rect.top
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
431
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosCategoryBarComponent, isStandalone: true, selector: "talos-category-bar", inputs: { values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, categories: { classPropertyName: "categories", publicName: "categories", isSignal: true, isRequired: false, transformFunction: null }, colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null }, markerValue: { classPropertyName: "markerValue", publicName: "markerValue", isSignal: true, isRequired: false, transformFunction: null }, marker: { classPropertyName: "marker", publicName: "marker", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, showLabels: { classPropertyName: "showLabels", publicName: "showLabels", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null }, labelPosition: { classPropertyName: "labelPosition", publicName: "labelPosition", isSignal: true, isRequired: false, transformFunction: null }, labelFormatter: { classPropertyName: "labelFormatter", publicName: "labelFormatter", isSignal: true, isRequired: false, transformFunction: null }, valueFormatter: { classPropertyName: "valueFormatter", publicName: "valueFormatter", isSignal: true, isRequired: false, transformFunction: null }, markerFormatter: { classPropertyName: "markerFormatter", publicName: "markerFormatter", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showTooltip: { classPropertyName: "showTooltip", publicName: "showTooltip", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { segmentClick: "segmentClick", markerClick: "markerClick", segmentHover: "segmentHover" }, host: { properties: { "class.talos-category-bar-animated": "animated()", "class.talos-category-bar-sm": "size() === \"sm\"", "class.talos-category-bar-md": "size() === \"md\"", "class.talos-category-bar-lg": "size() === \"lg\"", "class.talos-category-bar-xs": "size() === \"xs\"", "attr.role": "\"meter\"", "attr.aria-valuenow": "markerVal()", "attr.aria-valuemin": "computedMin()", "attr.aria-valuemax": "computedMax()", "attr.aria-label": "ariaLabel()" }, classAttribute: "talos-category-bar" }, ngImport: i0, template: "<!-- Header Block (Title / Subtitle / Custom Header) -->\n@if (title() || subtitle()) {\n<div class=\"talos-category-bar-header\">\n @if (title()) {\n <div class=\"talos-category-bar-title\">{{ title() }}</div>\n }\n @if (subtitle()) {\n <div class=\"talos-category-bar-subtitle\">{{ subtitle() }}</div>\n }\n</div>\n}\n\n<ng-content select=\"[talosCategoryBarHeader]\"></ng-content>\n\n<!-- Top Scale Boundary Labels -->\n@if (showLabels() && labelPosition() === 'top' && scaleLabels().length > 0) {\n<div class=\"talos-category-bar-labels talos-category-bar-labels-top\" aria-hidden=\"true\">\n @for (tick of scaleLabels(); track tick.index; let first = $first; let last = $last) {\n <span class=\"talos-category-bar-label-tick\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [style.left.%]=\"tick.leftPercent\">\n {{ tick.text }}\n </span>\n }\n</div>\n}\n\n<!-- Main Bar Track Wrapper -->\n<div class=\"talos-category-bar-track-wrapper\">\n <div class=\"talos-category-bar-track\" [class.shape-pill]=\"shape() === 'pill'\"\n [class.shape-rounded]=\"shape() === 'rounded'\" [class.shape-square]=\"shape() === 'square'\"\n [class.size-xs]=\"size() === 'xs'\" [class.size-sm]=\"size() === 'sm'\" [class.size-md]=\"size() === 'md'\"\n [class.size-lg]=\"size() === 'lg'\">\n @for (seg of normalizedSegments(); track seg.index; let first = $first; let last = $last) {\n <div class=\"talos-category-bar-segment\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [class.is-hovered]=\"hoveredSegment()?.index === seg.index\" [style.width.%]=\"seg.percentage\"\n [style.background-color]=\"seg.color\" [talosTooltip]=\"seg.tooltipText\" [tooltipDisabled]=\"!showTooltip()\"\n tooltipPosition=\"top\" role=\"button\" tabindex=\"0\" [attr.aria-label]=\"seg.tooltipText\"\n (mouseenter)=\"onSegmentMouseEnter(seg, $event)\" (mouseleave)=\"onSegmentMouseLeave()\"\n (click)=\"onSegmentClick(seg, $event)\" (keydown.enter)=\"onSegmentClick(seg, $event)\"\n (keydown.space)=\"onSegmentClick(seg, $event)\"></div>\n }\n\n <!-- Marker Needle / Pin Indicator -->\n @if (markerState(); as ms) {\n <div class=\"talos-category-bar-marker\" [class.variant-pill]=\"ms.variant === 'pill'\"\n [class.variant-line]=\"ms.variant === 'line'\" [class.variant-needle]=\"ms.variant === 'needle'\"\n [class.is-hovered]=\"hoveredMarker()\" [style.left.%]=\"ms.percent\" [style.--marker-color]=\"ms.color\"\n [talosTooltip]=\"ms.tooltip\" [tooltipDisabled]=\"!showTooltip()\" tooltipPosition=\"top\" role=\"slider\" tabindex=\"0\"\n [attr.aria-valuenow]=\"markerVal()\" [attr.aria-label]=\"ms.label\" (mouseenter)=\"onMarkerMouseEnter($event)\"\n (mouseleave)=\"onMarkerMouseLeave()\" (click)=\"onMarkerClick($event)\" (keydown.enter)=\"onMarkerClick($event)\"\n (keydown.space)=\"onMarkerClick($event)\">\n <div class=\"talos-category-bar-marker-handle\"></div>\n </div>\n }\n </div>\n</div>\n\n<!-- Bottom Scale Boundary Labels -->\n@if (showLabels() && labelPosition() === 'bottom' && scaleLabels().length > 0) {\n<div class=\"talos-category-bar-labels talos-category-bar-labels-bottom\" aria-hidden=\"true\">\n @for (tick of scaleLabels(); track tick.index; let first = $first; let last = $last) {\n <span class=\"talos-category-bar-label-tick\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [style.left.%]=\"tick.leftPercent\">\n {{ tick.text }}\n </span>\n }\n</div>\n}\n\n<!-- Optional Legend Row -->\n@if (showLegend() && normalizedSegments().length > 0) {\n<div class=\"talos-category-bar-legend\">\n @for (seg of normalizedSegments(); track seg.index) {\n <div class=\"talos-category-bar-legend-item\" [class.is-hovered]=\"hoveredSegment()?.index === seg.index\"\n [talosTooltip]=\"seg.tooltipText\" [tooltipDisabled]=\"!showTooltip()\" tooltipPosition=\"top\"\n (mouseenter)=\"onSegmentMouseEnter(seg, $event)\" (mouseleave)=\"onSegmentMouseLeave()\"\n (click)=\"onSegmentClick(seg, $event)\">\n <span class=\"legend-dot\" [style.background-color]=\"seg.color\"></span>\n <span class=\"legend-label\">{{ seg.label }}</span>\n <span class=\"legend-value\">{{ seg.formattedValue }}</span>\n </div>\n }\n</div>\n}", styles: [":host{display:block;width:100%;box-sizing:border-box;font-family:inherit;color:var(--talos-text-color, var(--text-color, #0f172a));-webkit-user-select:none;user-select:none}.talos-category-bar-header{margin-bottom:.625rem}.talos-category-bar-header .talos-category-bar-title{font-size:.9375rem;font-weight:700;line-height:1.35;letter-spacing:-.01em;color:var(--talos-text-color, var(--text-color, #0f172a))}.talos-category-bar-header .talos-category-bar-subtitle{font-size:var(--talos-control-font-size, var(--control-font-size, .8125rem));color:var(--talos-text-muted, var(--text-muted, #64748b));margin:.25rem 0 0;line-height:1.4}.talos-category-bar-labels{position:relative;width:100%;height:1.25rem;box-sizing:border-box;pointer-events:none}.talos-category-bar-labels.talos-category-bar-labels-top{margin-bottom:.375rem}.talos-category-bar-labels.talos-category-bar-labels-bottom{margin-top:.375rem}.talos-category-bar-labels .talos-category-bar-label-tick{position:absolute;top:50%;transform:translate(-50%,-50%);font-size:.75rem;font-weight:500;color:var(--talos-text-muted, var(--text-muted, #64748b));white-space:nowrap;line-height:1}.talos-category-bar-labels .talos-category-bar-label-tick.is-first{left:0!important;transform:translateY(-50%)}.talos-category-bar-labels .talos-category-bar-label-tick.is-last{left:100%!important;transform:translate(-100%,-50%)}.talos-category-bar-track-wrapper{position:relative;width:100%;box-sizing:border-box}.talos-category-bar-track{position:relative;display:flex;width:100%;overflow:visible;background-color:var(--talos-category-bar-track-bg, #f1f5f9);box-sizing:border-box;align-items:center}.talos-category-bar-track.size-xs{height:var(--talos-category-bar-height-xs, .25rem)}.talos-category-bar-track.size-sm{height:var(--talos-category-bar-height-sm, .375rem)}.talos-category-bar-track.size-md{height:var(--talos-category-bar-height-md, .5rem)}.talos-category-bar-track.size-lg{height:var(--talos-category-bar-height-lg, .75rem)}.talos-category-bar-track.shape-pill{border-radius:9999px}.talos-category-bar-track.shape-pill .talos-category-bar-segment.is-first{border-top-left-radius:9999px;border-bottom-left-radius:9999px}.talos-category-bar-track.shape-pill .talos-category-bar-segment.is-last{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.talos-category-bar-track.shape-rounded{border-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-rounded .talos-category-bar-segment.is-first{border-top-left-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)));border-bottom-left-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-rounded .talos-category-bar-segment.is-last{border-top-right-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)));border-bottom-right-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-square{border-radius:0}.talos-category-bar-segment{position:relative;height:100%;flex-shrink:0;cursor:pointer;outline:none;transition:opacity .15s ease,filter .15s ease;box-sizing:border-box;border-right:var(--talos-category-bar-gap, 2px) solid var(--talos-card-bg, var(--card-bg, #ffffff))}.talos-category-bar-segment.is-last{border-right:none}.talos-category-bar-segment:hover,.talos-category-bar-segment.is-hovered{filter:brightness(1.1);z-index:1}.talos-category-bar-segment:focus-visible{box-shadow:var(--talos-focus-ring, var(--focus-ring, 0 0 0 3px rgba(37, 99, 235, .25)));z-index:2}.talos-category-bar-marker{position:absolute;top:50%;transform:translate(-50%,-50%);z-index:5;cursor:pointer;outline:none;display:flex;align-items:center;justify-content:center;transition:left .3s cubic-bezier(.4,0,.2,1),transform .15s ease}.talos-category-bar-marker .talos-category-bar-marker-handle{background-color:var(--marker-color, var(--talos-primary-color, var(--primary-color, #2592eb)));box-shadow:0 0 0 2px var(--talos-category-bar-marker-ring, var(--talos-card-bg, var(--card-bg, #ffffff))),0 2px 6px #0000002e;transition:transform .15s ease,box-shadow .15s ease,filter .15s ease}.talos-category-bar-marker.variant-pill .talos-category-bar-marker-handle{width:5px;height:20px;border-radius:9999px;border:1px solid rgba(255,255,255,.7)}.talos-category-bar-marker.variant-line .talos-category-bar-marker-handle{width:2.5px;height:24px;border-radius:1px;box-shadow:0 0 0 1.5px var(--talos-category-bar-marker-ring, var(--talos-card-bg, #ffffff)),0 2px 4px #00000026}.talos-category-bar-marker.variant-needle .talos-category-bar-marker-handle{width:10px;height:10px;border-radius:50%;border:2px solid #ffffff;box-shadow:0 0 0 2px var(--talos-category-bar-marker-ring, var(--talos-card-bg, #ffffff)),0 2px 6px #0003}.talos-category-bar-marker:hover .talos-category-bar-marker-handle,.talos-category-bar-marker.is-hovered .talos-category-bar-marker-handle{transform:scale(1.2);filter:brightness(1.1);box-shadow:0 0 0 3px var(--talos-category-bar-marker-ring, var(--talos-card-bg, var(--card-bg, #ffffff))),0 2px 8px #00000040}.talos-category-bar-marker:focus-visible .talos-category-bar-marker-handle{box-shadow:var(--talos-focus-ring, var(--focus-ring, 0 0 0 3px rgba(37, 99, 235, .25))),0 0 8px #2563eb66}.talos-category-bar-legend{display:flex;flex-wrap:wrap;align-items:center;gap:.875rem 1.25rem;margin-top:.75rem;font-size:var(--talos-control-font-size, var(--control-font-size, .8125rem))}.talos-category-bar-legend .talos-category-bar-legend-item{display:inline-flex;align-items:center;gap:.375rem;cursor:pointer;color:var(--talos-text-muted, var(--text-muted, #64748b));transition:color .15s ease}.talos-category-bar-legend .talos-category-bar-legend-item .legend-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.talos-category-bar-legend .talos-category-bar-legend-item .legend-label{font-weight:500}.talos-category-bar-legend .talos-category-bar-legend-item .legend-value{font-weight:600;color:var(--talos-text-color, var(--text-color, #0f172a))}.talos-category-bar-legend .talos-category-bar-legend-item:hover,.talos-category-bar-legend .talos-category-bar-legend-item.is-hovered{color:var(--talos-text-color, var(--text-color, #0f172a))}\n"], dependencies: [{ kind: "directive", type: TalosTooltipDirective, selector: "[talosTooltip], [tooltip]", inputs: ["talosTooltip", "tooltip", "tooltipPosition", "tooltipDisabled"] }] });
|
|
432
|
+
}
|
|
433
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarComponent, decorators: [{
|
|
434
|
+
type: Component,
|
|
435
|
+
args: [{ selector: 'talos-category-bar', imports: [TalosTooltipDirective], host: {
|
|
436
|
+
'class': 'talos-category-bar',
|
|
437
|
+
'[class.talos-category-bar-animated]': 'animated()',
|
|
438
|
+
'[class.talos-category-bar-sm]': 'size() === "sm"',
|
|
439
|
+
'[class.talos-category-bar-md]': 'size() === "md"',
|
|
440
|
+
'[class.talos-category-bar-lg]': 'size() === "lg"',
|
|
441
|
+
'[class.talos-category-bar-xs]': 'size() === "xs"',
|
|
442
|
+
'[attr.role]': '"meter"',
|
|
443
|
+
'[attr.aria-valuenow]': 'markerVal()',
|
|
444
|
+
'[attr.aria-valuemin]': 'computedMin()',
|
|
445
|
+
'[attr.aria-valuemax]': 'computedMax()',
|
|
446
|
+
'[attr.aria-label]': 'ariaLabel()'
|
|
447
|
+
}, template: "<!-- Header Block (Title / Subtitle / Custom Header) -->\n@if (title() || subtitle()) {\n<div class=\"talos-category-bar-header\">\n @if (title()) {\n <div class=\"talos-category-bar-title\">{{ title() }}</div>\n }\n @if (subtitle()) {\n <div class=\"talos-category-bar-subtitle\">{{ subtitle() }}</div>\n }\n</div>\n}\n\n<ng-content select=\"[talosCategoryBarHeader]\"></ng-content>\n\n<!-- Top Scale Boundary Labels -->\n@if (showLabels() && labelPosition() === 'top' && scaleLabels().length > 0) {\n<div class=\"talos-category-bar-labels talos-category-bar-labels-top\" aria-hidden=\"true\">\n @for (tick of scaleLabels(); track tick.index; let first = $first; let last = $last) {\n <span class=\"talos-category-bar-label-tick\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [style.left.%]=\"tick.leftPercent\">\n {{ tick.text }}\n </span>\n }\n</div>\n}\n\n<!-- Main Bar Track Wrapper -->\n<div class=\"talos-category-bar-track-wrapper\">\n <div class=\"talos-category-bar-track\" [class.shape-pill]=\"shape() === 'pill'\"\n [class.shape-rounded]=\"shape() === 'rounded'\" [class.shape-square]=\"shape() === 'square'\"\n [class.size-xs]=\"size() === 'xs'\" [class.size-sm]=\"size() === 'sm'\" [class.size-md]=\"size() === 'md'\"\n [class.size-lg]=\"size() === 'lg'\">\n @for (seg of normalizedSegments(); track seg.index; let first = $first; let last = $last) {\n <div class=\"talos-category-bar-segment\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [class.is-hovered]=\"hoveredSegment()?.index === seg.index\" [style.width.%]=\"seg.percentage\"\n [style.background-color]=\"seg.color\" [talosTooltip]=\"seg.tooltipText\" [tooltipDisabled]=\"!showTooltip()\"\n tooltipPosition=\"top\" role=\"button\" tabindex=\"0\" [attr.aria-label]=\"seg.tooltipText\"\n (mouseenter)=\"onSegmentMouseEnter(seg, $event)\" (mouseleave)=\"onSegmentMouseLeave()\"\n (click)=\"onSegmentClick(seg, $event)\" (keydown.enter)=\"onSegmentClick(seg, $event)\"\n (keydown.space)=\"onSegmentClick(seg, $event)\"></div>\n }\n\n <!-- Marker Needle / Pin Indicator -->\n @if (markerState(); as ms) {\n <div class=\"talos-category-bar-marker\" [class.variant-pill]=\"ms.variant === 'pill'\"\n [class.variant-line]=\"ms.variant === 'line'\" [class.variant-needle]=\"ms.variant === 'needle'\"\n [class.is-hovered]=\"hoveredMarker()\" [style.left.%]=\"ms.percent\" [style.--marker-color]=\"ms.color\"\n [talosTooltip]=\"ms.tooltip\" [tooltipDisabled]=\"!showTooltip()\" tooltipPosition=\"top\" role=\"slider\" tabindex=\"0\"\n [attr.aria-valuenow]=\"markerVal()\" [attr.aria-label]=\"ms.label\" (mouseenter)=\"onMarkerMouseEnter($event)\"\n (mouseleave)=\"onMarkerMouseLeave()\" (click)=\"onMarkerClick($event)\" (keydown.enter)=\"onMarkerClick($event)\"\n (keydown.space)=\"onMarkerClick($event)\">\n <div class=\"talos-category-bar-marker-handle\"></div>\n </div>\n }\n </div>\n</div>\n\n<!-- Bottom Scale Boundary Labels -->\n@if (showLabels() && labelPosition() === 'bottom' && scaleLabels().length > 0) {\n<div class=\"talos-category-bar-labels talos-category-bar-labels-bottom\" aria-hidden=\"true\">\n @for (tick of scaleLabels(); track tick.index; let first = $first; let last = $last) {\n <span class=\"talos-category-bar-label-tick\" [class.is-first]=\"first\" [class.is-last]=\"last\"\n [style.left.%]=\"tick.leftPercent\">\n {{ tick.text }}\n </span>\n }\n</div>\n}\n\n<!-- Optional Legend Row -->\n@if (showLegend() && normalizedSegments().length > 0) {\n<div class=\"talos-category-bar-legend\">\n @for (seg of normalizedSegments(); track seg.index) {\n <div class=\"talos-category-bar-legend-item\" [class.is-hovered]=\"hoveredSegment()?.index === seg.index\"\n [talosTooltip]=\"seg.tooltipText\" [tooltipDisabled]=\"!showTooltip()\" tooltipPosition=\"top\"\n (mouseenter)=\"onSegmentMouseEnter(seg, $event)\" (mouseleave)=\"onSegmentMouseLeave()\"\n (click)=\"onSegmentClick(seg, $event)\">\n <span class=\"legend-dot\" [style.background-color]=\"seg.color\"></span>\n <span class=\"legend-label\">{{ seg.label }}</span>\n <span class=\"legend-value\">{{ seg.formattedValue }}</span>\n </div>\n }\n</div>\n}", styles: [":host{display:block;width:100%;box-sizing:border-box;font-family:inherit;color:var(--talos-text-color, var(--text-color, #0f172a));-webkit-user-select:none;user-select:none}.talos-category-bar-header{margin-bottom:.625rem}.talos-category-bar-header .talos-category-bar-title{font-size:.9375rem;font-weight:700;line-height:1.35;letter-spacing:-.01em;color:var(--talos-text-color, var(--text-color, #0f172a))}.talos-category-bar-header .talos-category-bar-subtitle{font-size:var(--talos-control-font-size, var(--control-font-size, .8125rem));color:var(--talos-text-muted, var(--text-muted, #64748b));margin:.25rem 0 0;line-height:1.4}.talos-category-bar-labels{position:relative;width:100%;height:1.25rem;box-sizing:border-box;pointer-events:none}.talos-category-bar-labels.talos-category-bar-labels-top{margin-bottom:.375rem}.talos-category-bar-labels.talos-category-bar-labels-bottom{margin-top:.375rem}.talos-category-bar-labels .talos-category-bar-label-tick{position:absolute;top:50%;transform:translate(-50%,-50%);font-size:.75rem;font-weight:500;color:var(--talos-text-muted, var(--text-muted, #64748b));white-space:nowrap;line-height:1}.talos-category-bar-labels .talos-category-bar-label-tick.is-first{left:0!important;transform:translateY(-50%)}.talos-category-bar-labels .talos-category-bar-label-tick.is-last{left:100%!important;transform:translate(-100%,-50%)}.talos-category-bar-track-wrapper{position:relative;width:100%;box-sizing:border-box}.talos-category-bar-track{position:relative;display:flex;width:100%;overflow:visible;background-color:var(--talos-category-bar-track-bg, #f1f5f9);box-sizing:border-box;align-items:center}.talos-category-bar-track.size-xs{height:var(--talos-category-bar-height-xs, .25rem)}.talos-category-bar-track.size-sm{height:var(--talos-category-bar-height-sm, .375rem)}.talos-category-bar-track.size-md{height:var(--talos-category-bar-height-md, .5rem)}.talos-category-bar-track.size-lg{height:var(--talos-category-bar-height-lg, .75rem)}.talos-category-bar-track.shape-pill{border-radius:9999px}.talos-category-bar-track.shape-pill .talos-category-bar-segment.is-first{border-top-left-radius:9999px;border-bottom-left-radius:9999px}.talos-category-bar-track.shape-pill .talos-category-bar-segment.is-last{border-top-right-radius:9999px;border-bottom-right-radius:9999px}.talos-category-bar-track.shape-rounded{border-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-rounded .talos-category-bar-segment.is-first{border-top-left-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)));border-bottom-left-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-rounded .talos-category-bar-segment.is-last{border-top-right-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)));border-bottom-right-radius:var(--talos-category-bar-border-radius, var(--talos-border-radius, var(--border-radius, .375rem)))}.talos-category-bar-track.shape-square{border-radius:0}.talos-category-bar-segment{position:relative;height:100%;flex-shrink:0;cursor:pointer;outline:none;transition:opacity .15s ease,filter .15s ease;box-sizing:border-box;border-right:var(--talos-category-bar-gap, 2px) solid var(--talos-card-bg, var(--card-bg, #ffffff))}.talos-category-bar-segment.is-last{border-right:none}.talos-category-bar-segment:hover,.talos-category-bar-segment.is-hovered{filter:brightness(1.1);z-index:1}.talos-category-bar-segment:focus-visible{box-shadow:var(--talos-focus-ring, var(--focus-ring, 0 0 0 3px rgba(37, 99, 235, .25)));z-index:2}.talos-category-bar-marker{position:absolute;top:50%;transform:translate(-50%,-50%);z-index:5;cursor:pointer;outline:none;display:flex;align-items:center;justify-content:center;transition:left .3s cubic-bezier(.4,0,.2,1),transform .15s ease}.talos-category-bar-marker .talos-category-bar-marker-handle{background-color:var(--marker-color, var(--talos-primary-color, var(--primary-color, #2592eb)));box-shadow:0 0 0 2px var(--talos-category-bar-marker-ring, var(--talos-card-bg, var(--card-bg, #ffffff))),0 2px 6px #0000002e;transition:transform .15s ease,box-shadow .15s ease,filter .15s ease}.talos-category-bar-marker.variant-pill .talos-category-bar-marker-handle{width:5px;height:20px;border-radius:9999px;border:1px solid rgba(255,255,255,.7)}.talos-category-bar-marker.variant-line .talos-category-bar-marker-handle{width:2.5px;height:24px;border-radius:1px;box-shadow:0 0 0 1.5px var(--talos-category-bar-marker-ring, var(--talos-card-bg, #ffffff)),0 2px 4px #00000026}.talos-category-bar-marker.variant-needle .talos-category-bar-marker-handle{width:10px;height:10px;border-radius:50%;border:2px solid #ffffff;box-shadow:0 0 0 2px var(--talos-category-bar-marker-ring, var(--talos-card-bg, #ffffff)),0 2px 6px #0003}.talos-category-bar-marker:hover .talos-category-bar-marker-handle,.talos-category-bar-marker.is-hovered .talos-category-bar-marker-handle{transform:scale(1.2);filter:brightness(1.1);box-shadow:0 0 0 3px var(--talos-category-bar-marker-ring, var(--talos-card-bg, var(--card-bg, #ffffff))),0 2px 8px #00000040}.talos-category-bar-marker:focus-visible .talos-category-bar-marker-handle{box-shadow:var(--talos-focus-ring, var(--focus-ring, 0 0 0 3px rgba(37, 99, 235, .25))),0 0 8px #2563eb66}.talos-category-bar-legend{display:flex;flex-wrap:wrap;align-items:center;gap:.875rem 1.25rem;margin-top:.75rem;font-size:var(--talos-control-font-size, var(--control-font-size, .8125rem))}.talos-category-bar-legend .talos-category-bar-legend-item{display:inline-flex;align-items:center;gap:.375rem;cursor:pointer;color:var(--talos-text-muted, var(--text-muted, #64748b));transition:color .15s ease}.talos-category-bar-legend .talos-category-bar-legend-item .legend-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.talos-category-bar-legend .talos-category-bar-legend-item .legend-label{font-weight:500}.talos-category-bar-legend .talos-category-bar-legend-item .legend-value{font-weight:600;color:var(--talos-text-color, var(--text-color, #0f172a))}.talos-category-bar-legend .talos-category-bar-legend-item:hover,.talos-category-bar-legend .talos-category-bar-legend-item.is-hovered{color:var(--talos-text-color, var(--text-color, #0f172a))}\n"] }]
|
|
448
|
+
}], propDecorators: { values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], categories: [{ type: i0.Input, args: [{ isSignal: true, alias: "categories", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], markerValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "markerValue", required: false }] }], marker: [{ type: i0.Input, args: [{ isSignal: true, alias: "marker", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], showLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabels", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], labelPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPosition", required: false }] }], labelFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelFormatter", required: false }] }], valueFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormatter", required: false }] }], markerFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "markerFormatter", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTooltip", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }], segmentClick: [{ type: i0.Output, args: ["segmentClick"] }], markerClick: [{ type: i0.Output, args: ["markerClick"] }], segmentHover: [{ type: i0.Output, args: ["segmentHover"] }] } });
|
|
449
|
+
|
|
450
|
+
class TalosCategoryBarModule {
|
|
451
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
452
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarModule, imports: [TalosCategoryBarComponent], exports: [TalosCategoryBarComponent] });
|
|
453
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarModule });
|
|
454
|
+
}
|
|
455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosCategoryBarModule, decorators: [{
|
|
456
|
+
type: NgModule,
|
|
457
|
+
args: [{
|
|
458
|
+
imports: [TalosCategoryBarComponent],
|
|
459
|
+
exports: [TalosCategoryBarComponent]
|
|
460
|
+
}]
|
|
461
|
+
}] });
|
|
462
|
+
|
|
463
|
+
const DEFAULT_DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
464
|
+
const PALETTE_CONFIG = {
|
|
465
|
+
indigo: {
|
|
466
|
+
zero: { bg: '#eef2ff', text: '#4338ca' },
|
|
467
|
+
min: { r: 219, g: 228, b: 255 }, // #dbe4ff
|
|
468
|
+
max: { r: 67, g: 56, b: 202 }, // #4338ca
|
|
469
|
+
gradientCss: 'linear-gradient(90deg, #dbe4ff 0%, #818cf8 50%, #4338ca 100%)'
|
|
470
|
+
},
|
|
471
|
+
blue: {
|
|
472
|
+
zero: { bg: '#eff6ff', text: '#1d4ed8' },
|
|
473
|
+
min: { r: 219, g: 234, b: 254 }, // #dbeafe
|
|
474
|
+
max: { r: 29, g: 78, b: 216 }, // #1d4ed8
|
|
475
|
+
gradientCss: 'linear-gradient(90deg, #dbeafe 0%, #60a5fa 50%, #1d4ed8 100%)'
|
|
476
|
+
},
|
|
477
|
+
emerald: {
|
|
478
|
+
zero: { bg: '#ecfdf5', text: '#047857' },
|
|
479
|
+
min: { r: 209, g: 250, b: 229 }, // #d1fae5
|
|
480
|
+
max: { r: 4, g: 120, b: 87 }, // #047857
|
|
481
|
+
gradientCss: 'linear-gradient(90deg, #d1fae5 0%, #34d399 50%, #047857 100%)'
|
|
482
|
+
},
|
|
483
|
+
rose: {
|
|
484
|
+
zero: { bg: '#fff1f2', text: '#be123c' },
|
|
485
|
+
min: { r: 255, g: 228, b: 230 }, // #ffe4e6
|
|
486
|
+
max: { r: 190, g: 18, b: 60 }, // #be123c
|
|
487
|
+
gradientCss: 'linear-gradient(90deg, #ffe4e6 0%, #fb7185 50%, #be123c 100%)'
|
|
488
|
+
},
|
|
489
|
+
amber: {
|
|
490
|
+
zero: { bg: '#fffbeb', text: '#b45309' },
|
|
491
|
+
min: { r: 254, g: 243, b: 199 }, // #fef3c7
|
|
492
|
+
max: { r: 180, g: 83, b: 9 }, // #b45309
|
|
493
|
+
gradientCss: 'linear-gradient(90deg, #fef3c7 0%, #fbbf24 50%, #b45309 100%)'
|
|
494
|
+
},
|
|
495
|
+
violet: {
|
|
496
|
+
zero: { bg: '#f5f3ff', text: '#6d28d9' },
|
|
497
|
+
min: { r: 237, g: 233, b: 254 }, // #ede9fe
|
|
498
|
+
max: { r: 109, g: 40, b: 217 }, // #6d28d9
|
|
499
|
+
gradientCss: 'linear-gradient(90deg, #ede9fe 0%, #a78bfa 50%, #6d28d9 100%)'
|
|
500
|
+
},
|
|
501
|
+
slate: {
|
|
502
|
+
zero: { bg: '#f8fafc', text: '#334155' },
|
|
503
|
+
min: { r: 226, g: 232, b: 240 }, // #e2e8f0
|
|
504
|
+
max: { r: 51, g: 65, b: 85 }, // #334155
|
|
505
|
+
gradientCss: 'linear-gradient(90deg, #e2e8f0 0%, #94a3b8 50%, #334155 100%)'
|
|
506
|
+
},
|
|
507
|
+
custom: {
|
|
508
|
+
zero: { bg: '#eef2ff', text: '#4338ca' },
|
|
509
|
+
min: { r: 219, g: 228, b: 255 },
|
|
510
|
+
max: { r: 67, g: 56, b: 202 },
|
|
511
|
+
gradientCss: 'linear-gradient(90deg, #dbe4ff 0%, #4338ca 100%)'
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
class TalosHeatmapComponent {
|
|
515
|
+
elementRef = inject(ElementRef);
|
|
516
|
+
// Inputs & Models
|
|
517
|
+
data = input([], /* @ts-ignore */
|
|
518
|
+
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
519
|
+
metrics = input([], /* @ts-ignore */
|
|
520
|
+
...(ngDevMode ? [{ debugName: "metrics" }] : /* istanbul ignore next */ []));
|
|
521
|
+
selectedMetricId = model(undefined, /* @ts-ignore */
|
|
522
|
+
...(ngDevMode ? [{ debugName: "selectedMetricId" }] : /* istanbul ignore next */ []));
|
|
523
|
+
interval = model('hour', /* @ts-ignore */
|
|
524
|
+
...(ngDevMode ? [{ debugName: "interval" }] : /* istanbul ignore next */ []));
|
|
525
|
+
dateRange = model(null, /* @ts-ignore */
|
|
526
|
+
...(ngDevMode ? [{ debugName: "dateRange" }] : /* istanbul ignore next */ []));
|
|
527
|
+
swapAxes = model(false, /* @ts-ignore */
|
|
528
|
+
...(ngDevMode ? [{ debugName: "swapAxes" }] : /* istanbul ignore next */ []));
|
|
529
|
+
title = input('Busiest times', /* @ts-ignore */
|
|
530
|
+
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
531
|
+
hoursRange = input([9, 17], /* @ts-ignore */
|
|
532
|
+
...(ngDevMode ? [{ debugName: "hoursRange" }] : /* istanbul ignore next */ []));
|
|
533
|
+
timeFormat = input('12h', /* @ts-ignore */
|
|
534
|
+
...(ngDevMode ? [{ debugName: "timeFormat" }] : /* istanbul ignore next */ []));
|
|
535
|
+
days = input(DEFAULT_DAYS, /* @ts-ignore */
|
|
536
|
+
...(ngDevMode ? [{ debugName: "days" }] : /* istanbul ignore next */ []));
|
|
537
|
+
colorScheme = input('indigo', /* @ts-ignore */
|
|
538
|
+
...(ngDevMode ? [{ debugName: "colorScheme" }] : /* istanbul ignore next */ []));
|
|
539
|
+
customColorStops = input(undefined, /* @ts-ignore */
|
|
540
|
+
...(ngDevMode ? [{ debugName: "customColorStops" }] : /* istanbul ignore next */ []));
|
|
541
|
+
showLegend = input(true, /* @ts-ignore */
|
|
542
|
+
...(ngDevMode ? [{ debugName: "showLegend" }] : /* istanbul ignore next */ []));
|
|
543
|
+
showValues = input(true, /* @ts-ignore */
|
|
544
|
+
...(ngDevMode ? [{ debugName: "showValues" }] : /* istanbul ignore next */ []));
|
|
545
|
+
cellShape = input('pill', /* @ts-ignore */
|
|
546
|
+
...(ngDevMode ? [{ debugName: "cellShape" }] : /* istanbul ignore next */ []));
|
|
547
|
+
cellSize = input('md', /* @ts-ignore */
|
|
548
|
+
...(ngDevMode ? [{ debugName: "cellSize" }] : /* istanbul ignore next */ []));
|
|
549
|
+
fitWidth = input(undefined, /* @ts-ignore */
|
|
550
|
+
...(ngDevMode ? [{ debugName: "fitWidth" }] : /* istanbul ignore next */ []));
|
|
551
|
+
loading = input(false, /* @ts-ignore */
|
|
552
|
+
...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
553
|
+
showToolbar = input(true, /* @ts-ignore */
|
|
554
|
+
...(ngDevMode ? [{ debugName: "showToolbar" }] : /* istanbul ignore next */ []));
|
|
555
|
+
showRefresh = input(true, /* @ts-ignore */
|
|
556
|
+
...(ngDevMode ? [{ debugName: "showRefresh" }] : /* istanbul ignore next */ []));
|
|
557
|
+
showExport = input(true, /* @ts-ignore */
|
|
558
|
+
...(ngDevMode ? [{ debugName: "showExport" }] : /* istanbul ignore next */ []));
|
|
559
|
+
showExpand = input(true, /* @ts-ignore */
|
|
560
|
+
...(ngDevMode ? [{ debugName: "showExpand" }] : /* istanbul ignore next */ []));
|
|
561
|
+
showSwapAxes = input(false, /* @ts-ignore */
|
|
562
|
+
...(ngDevMode ? [{ debugName: "showSwapAxes" }] : /* istanbul ignore next */ []));
|
|
563
|
+
showIntervalToggle = input(false, /* @ts-ignore */
|
|
564
|
+
...(ngDevMode ? [{ debugName: "showIntervalToggle" }] : /* istanbul ignore next */ []));
|
|
565
|
+
showDateRangePicker = input(false, /* @ts-ignore */
|
|
566
|
+
...(ngDevMode ? [{ debugName: "showDateRangePicker" }] : /* istanbul ignore next */ []));
|
|
567
|
+
dateRangePresets = input(null, /* @ts-ignore */
|
|
568
|
+
...(ngDevMode ? [{ debugName: "dateRangePresets" }] : /* istanbul ignore next */ []));
|
|
569
|
+
unit = input('', /* @ts-ignore */
|
|
570
|
+
...(ngDevMode ? [{ debugName: "unit" }] : /* istanbul ignore next */ []));
|
|
571
|
+
maxValue = input(undefined, /* @ts-ignore */
|
|
572
|
+
...(ngDevMode ? [{ debugName: "maxValue" }] : /* istanbul ignore next */ []));
|
|
573
|
+
minValue = input(undefined, /* @ts-ignore */
|
|
574
|
+
...(ngDevMode ? [{ debugName: "minValue" }] : /* istanbul ignore next */ []));
|
|
575
|
+
// Outputs
|
|
576
|
+
cellClick = output();
|
|
577
|
+
cellHover = output();
|
|
578
|
+
timeRangeChange = output();
|
|
579
|
+
refresh = output();
|
|
580
|
+
metricChange = output();
|
|
581
|
+
export = output();
|
|
582
|
+
// State signals
|
|
583
|
+
isExpanded = signal(false, /* @ts-ignore */
|
|
584
|
+
...(ngDevMode ? [{ debugName: "isExpanded" }] : /* istanbul ignore next */ []));
|
|
585
|
+
isMetricDropdownOpen = signal(false, /* @ts-ignore */
|
|
586
|
+
...(ngDevMode ? [{ debugName: "isMetricDropdownOpen" }] : /* istanbul ignore next */ []));
|
|
587
|
+
isExportDropdownOpen = signal(false, /* @ts-ignore */
|
|
588
|
+
...(ngDevMode ? [{ debugName: "isExportDropdownOpen" }] : /* istanbul ignore next */ []));
|
|
589
|
+
hoveredCell = signal(null, /* @ts-ignore */
|
|
590
|
+
...(ngDevMode ? [{ debugName: "hoveredCell" }] : /* istanbul ignore next */ []));
|
|
591
|
+
focusedCoord = signal(null, /* @ts-ignore */
|
|
592
|
+
...(ngDevMode ? [{ debugName: "focusedCoord" }] : /* istanbul ignore next */ []));
|
|
593
|
+
// Layout Fit computation
|
|
594
|
+
isFitWidth = computed(() => {
|
|
595
|
+
const fw = this.fitWidth();
|
|
596
|
+
if (fw !== undefined)
|
|
597
|
+
return fw;
|
|
598
|
+
return this.cellShape() !== 'square';
|
|
599
|
+
}, /* @ts-ignore */
|
|
600
|
+
...(ngDevMode ? [{ debugName: "isFitWidth" }] : /* istanbul ignore next */ []));
|
|
601
|
+
// Active Metric computation
|
|
602
|
+
currentMetric = computed(() => {
|
|
603
|
+
const list = this.metrics();
|
|
604
|
+
if (!list || list.length === 0)
|
|
605
|
+
return undefined;
|
|
606
|
+
const selectedId = this.selectedMetricId();
|
|
607
|
+
if (selectedId) {
|
|
608
|
+
const found = list.find((m) => m.id === selectedId);
|
|
609
|
+
if (found)
|
|
610
|
+
return found;
|
|
611
|
+
}
|
|
612
|
+
return list[0];
|
|
613
|
+
}, /* @ts-ignore */
|
|
614
|
+
...(ngDevMode ? [{ debugName: "currentMetric" }] : /* istanbul ignore next */ []));
|
|
615
|
+
// Effective Active Color Scheme
|
|
616
|
+
activeColorScheme = computed(() => {
|
|
617
|
+
const metric = this.currentMetric();
|
|
618
|
+
if (metric && metric.colorScheme)
|
|
619
|
+
return metric.colorScheme;
|
|
620
|
+
return this.colorScheme();
|
|
621
|
+
}, /* @ts-ignore */
|
|
622
|
+
...(ngDevMode ? [{ debugName: "activeColorScheme" }] : /* istanbul ignore next */ []));
|
|
623
|
+
// Effective Unit
|
|
624
|
+
activeUnit = computed(() => {
|
|
625
|
+
const metric = this.currentMetric();
|
|
626
|
+
if (metric && metric.unit !== undefined)
|
|
627
|
+
return metric.unit;
|
|
628
|
+
return this.unit();
|
|
629
|
+
}, /* @ts-ignore */
|
|
630
|
+
...(ngDevMode ? [{ debugName: "activeUnit" }] : /* istanbul ignore next */ []));
|
|
631
|
+
// Effective Raw Data Input
|
|
632
|
+
activeDataInput = computed(() => {
|
|
633
|
+
const metric = this.currentMetric();
|
|
634
|
+
if (metric)
|
|
635
|
+
return metric.data;
|
|
636
|
+
return this.data();
|
|
637
|
+
}, /* @ts-ignore */
|
|
638
|
+
...(ngDevMode ? [{ debugName: "activeDataInput" }] : /* istanbul ignore next */ []));
|
|
639
|
+
// Hours array from range (for hour-by-hour mode)
|
|
640
|
+
hours = computed(() => {
|
|
641
|
+
const [start, end] = this.hoursRange();
|
|
642
|
+
const clampedStart = Math.max(0, Math.min(23, start));
|
|
643
|
+
const clampedEnd = Math.max(clampedStart, Math.min(23, end));
|
|
644
|
+
const result = [];
|
|
645
|
+
for (let h = clampedStart; h <= clampedEnd; h++) {
|
|
646
|
+
result.push(h);
|
|
647
|
+
}
|
|
648
|
+
return result;
|
|
649
|
+
}, /* @ts-ignore */
|
|
650
|
+
...(ngDevMode ? [{ debugName: "hours" }] : /* istanbul ignore next */ []));
|
|
651
|
+
// Resolved Date Range bounds
|
|
652
|
+
parsedDateRange = computed(() => {
|
|
653
|
+
const dr = this.dateRange();
|
|
654
|
+
if (!dr || (!dr.startDate && !dr.endDate)) {
|
|
655
|
+
return { start: null, end: null };
|
|
656
|
+
}
|
|
657
|
+
const s = dr.startDate ? startOfDay(new Date(dr.startDate)) : null;
|
|
658
|
+
const e = dr.endDate ? endOfDay(new Date(dr.endDate)) : (s ? endOfDay(s) : null);
|
|
659
|
+
return { start: s, end: e };
|
|
660
|
+
}, /* @ts-ignore */
|
|
661
|
+
...(ngDevMode ? [{ debugName: "parsedDateRange" }] : /* istanbul ignore next */ []));
|
|
662
|
+
// Day list for Day-by-Day mode (weeks grouping)
|
|
663
|
+
dayModeWeeks = computed(() => {
|
|
664
|
+
const { start, end } = this.parsedDateRange();
|
|
665
|
+
const effectiveStart = start || startOfISOWeek(subWeeks(new Date(), 3));
|
|
666
|
+
const effectiveEnd = end || endOfISOWeek(new Date());
|
|
667
|
+
const weeks = [];
|
|
668
|
+
let currentWeekStart = startOfISOWeek(effectiveStart);
|
|
669
|
+
const lastWeekEnd = endOfISOWeek(effectiveEnd);
|
|
670
|
+
while (isSameDay(currentWeekStart, lastWeekEnd) || isBefore(currentWeekStart, lastWeekEnd)) {
|
|
671
|
+
const currentWeekEnd = addDays(currentWeekStart, 6);
|
|
672
|
+
const daysInWeek = [];
|
|
673
|
+
for (let i = 0; i < 7; i++) {
|
|
674
|
+
daysInWeek.push(addDays(currentWeekStart, i));
|
|
675
|
+
}
|
|
676
|
+
const label = `${formatDate(currentWeekStart, 'MMM d')} - ${formatDate(currentWeekEnd, 'MMM d')}`;
|
|
677
|
+
weeks.push({
|
|
678
|
+
label,
|
|
679
|
+
start: currentWeekStart,
|
|
680
|
+
end: currentWeekEnd,
|
|
681
|
+
days: daysInWeek
|
|
682
|
+
});
|
|
683
|
+
currentWeekStart = addWeeks(currentWeekStart, 1);
|
|
684
|
+
}
|
|
685
|
+
return weeks;
|
|
686
|
+
}, /* @ts-ignore */
|
|
687
|
+
...(ngDevMode ? [{ debugName: "dayModeWeeks" }] : /* istanbul ignore next */ []));
|
|
688
|
+
// Normalized Value Map:
|
|
689
|
+
// For 'hour': Map<`${colKey}_${hour}`, { value, meta }>
|
|
690
|
+
// For 'day': Map<`${yyyy-MM-dd}`, { value, meta }> AND Map<`${colKey}`, { value, meta }>
|
|
691
|
+
normalizedValueMap = computed(() => {
|
|
692
|
+
const map = new Map();
|
|
693
|
+
const rawInput = this.activeDataInput();
|
|
694
|
+
const currentDays = this.days();
|
|
695
|
+
const currentInterval = this.interval();
|
|
696
|
+
if (!rawInput)
|
|
697
|
+
return map;
|
|
698
|
+
if (Array.isArray(rawInput)) {
|
|
699
|
+
if (rawInput.length === 0)
|
|
700
|
+
return map;
|
|
701
|
+
const firstItem = rawInput[0];
|
|
702
|
+
if (typeof firstItem === 'string' ||
|
|
703
|
+
firstItem instanceof Date ||
|
|
704
|
+
(typeof firstItem === 'number' && firstItem > 1000000000)) {
|
|
705
|
+
// Raw Timestamp list
|
|
706
|
+
rawInput.forEach((ts) => {
|
|
707
|
+
const date = new Date(ts);
|
|
708
|
+
if (isValid(date)) {
|
|
709
|
+
if (currentInterval === 'day') {
|
|
710
|
+
const dayKey = formatDate(date, 'yyyy-MM-dd');
|
|
711
|
+
const existing = map.get(dayKey);
|
|
712
|
+
if (existing)
|
|
713
|
+
existing.value += 1;
|
|
714
|
+
else
|
|
715
|
+
map.set(dayKey, { value: 1 });
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
719
|
+
const dayName = dayNames[date.getDay()];
|
|
720
|
+
const matchedDay = currentDays.find((d) => d.toLowerCase().startsWith(dayName.toLowerCase())) || dayName;
|
|
721
|
+
const hour = date.getHours();
|
|
722
|
+
const key = `${matchedDay}_${hour}`;
|
|
723
|
+
const existing = map.get(key);
|
|
724
|
+
if (existing)
|
|
725
|
+
existing.value += 1;
|
|
726
|
+
else
|
|
727
|
+
map.set(key, { value: 1 });
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
// HeatmapDataPoint array
|
|
734
|
+
rawInput.forEach((point) => {
|
|
735
|
+
if (currentInterval === 'day') {
|
|
736
|
+
let dayKey = '';
|
|
737
|
+
if (point.date) {
|
|
738
|
+
dayKey = typeof point.date === 'string' && /^\d{4}-\d{2}-\d{2}/.test(point.date)
|
|
739
|
+
? point.date.substring(0, 10)
|
|
740
|
+
: formatDate(new Date(point.date), 'yyyy-MM-dd');
|
|
741
|
+
}
|
|
742
|
+
else if (point.day instanceof Date) {
|
|
743
|
+
dayKey = formatDate(point.day, 'yyyy-MM-dd');
|
|
744
|
+
}
|
|
745
|
+
else if (typeof point.day === 'string' && /^\d{4}-\d{2}-\d{2}/.test(point.day)) {
|
|
746
|
+
dayKey = point.day.substring(0, 10);
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
dayKey = String(point.day ?? '');
|
|
750
|
+
}
|
|
751
|
+
if (dayKey) {
|
|
752
|
+
const existing = map.get(dayKey);
|
|
753
|
+
if (existing) {
|
|
754
|
+
existing.value += point.value;
|
|
755
|
+
if (point.meta)
|
|
756
|
+
existing.meta = { ...existing.meta, ...point.meta };
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
map.set(dayKey, { value: point.value, meta: point.meta });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
// Hour by hour
|
|
765
|
+
let dayKey = '';
|
|
766
|
+
if (point.day instanceof Date) {
|
|
767
|
+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
768
|
+
const dayName = dayNames[point.day.getDay()];
|
|
769
|
+
dayKey = currentDays.find((d) => d.toLowerCase().startsWith(dayName.toLowerCase())) || dayName;
|
|
770
|
+
}
|
|
771
|
+
else if (typeof point.day === 'number') {
|
|
772
|
+
dayKey = currentDays[point.day] || String(point.day);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
dayKey = String(point.day ?? '');
|
|
776
|
+
}
|
|
777
|
+
const hour = point.hour ?? 0;
|
|
778
|
+
const key = `${dayKey}_${hour}`;
|
|
779
|
+
const existing = map.get(key);
|
|
780
|
+
if (existing) {
|
|
781
|
+
existing.value += point.value;
|
|
782
|
+
if (point.meta)
|
|
783
|
+
existing.meta = { ...existing.meta, ...point.meta };
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
map.set(key, { value: point.value, meta: point.meta });
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
else if (typeof rawInput === 'object') {
|
|
793
|
+
// HeatmapMatrixData
|
|
794
|
+
const matrix = rawInput;
|
|
795
|
+
Object.entries(matrix).forEach(([outerKey, innerObj]) => {
|
|
796
|
+
if (typeof innerObj === 'object' && innerObj !== null) {
|
|
797
|
+
Object.entries(innerObj).forEach(([innerKey, val]) => {
|
|
798
|
+
const numVal = Number(val) || 0;
|
|
799
|
+
if (currentInterval === 'day') {
|
|
800
|
+
const key = `${outerKey}_${innerKey}`;
|
|
801
|
+
map.set(key, { value: numVal });
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
const isInnerHour = !isNaN(Number(innerKey));
|
|
805
|
+
if (isInnerHour) {
|
|
806
|
+
const hour = Number(innerKey);
|
|
807
|
+
const key = `${outerKey}_${hour}`;
|
|
808
|
+
map.set(key, { value: numVal });
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
const hour = Number(outerKey);
|
|
812
|
+
const key = `${innerKey}_${hour}`;
|
|
813
|
+
map.set(key, { value: numVal });
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
return map;
|
|
821
|
+
}, /* @ts-ignore */
|
|
822
|
+
...(ngDevMode ? [{ debugName: "normalizedValueMap" }] : /* istanbul ignore next */ []));
|
|
823
|
+
// Effective min & max value for heat scale
|
|
824
|
+
computedScaleBounds = computed(() => {
|
|
825
|
+
const customMin = this.minValue();
|
|
826
|
+
const customMax = this.maxValue();
|
|
827
|
+
const map = this.normalizedValueMap();
|
|
828
|
+
let dataMax = 0;
|
|
829
|
+
for (const item of map.values()) {
|
|
830
|
+
if (item.value > dataMax)
|
|
831
|
+
dataMax = item.value;
|
|
832
|
+
}
|
|
833
|
+
const min = customMin !== undefined ? customMin : 0;
|
|
834
|
+
const max = customMax !== undefined ? customMax : Math.max(1, dataMax);
|
|
835
|
+
return { min, max };
|
|
836
|
+
}, /* @ts-ignore */
|
|
837
|
+
...(ngDevMode ? [{ debugName: "computedScaleBounds" }] : /* istanbul ignore next */ []));
|
|
838
|
+
// Gradient CSS for Legend bar
|
|
839
|
+
legendGradientCss = computed(() => {
|
|
840
|
+
const scheme = this.activeColorScheme();
|
|
841
|
+
return PALETTE_CONFIG[scheme]?.gradientCss || PALETTE_CONFIG.indigo.gradientCss;
|
|
842
|
+
}, /* @ts-ignore */
|
|
843
|
+
...(ngDevMode ? [{ debugName: "legendGradientCss" }] : /* istanbul ignore next */ []));
|
|
844
|
+
// Column Headers computation (supports axis swapping)
|
|
845
|
+
columnHeaders = computed(() => {
|
|
846
|
+
const isSwapped = this.swapAxes();
|
|
847
|
+
const currentInterval = this.interval();
|
|
848
|
+
if (!isSwapped) {
|
|
849
|
+
return this.days().map((day) => ({ key: day, label: day }));
|
|
850
|
+
}
|
|
851
|
+
// When axes are swapped:
|
|
852
|
+
if (currentInterval === 'day') {
|
|
853
|
+
const weeks = this.dayModeWeeks();
|
|
854
|
+
return weeks.map((w) => ({ key: w.label, label: w.label }));
|
|
855
|
+
}
|
|
856
|
+
const hoursList = this.hours();
|
|
857
|
+
const format = this.timeFormat();
|
|
858
|
+
return hoursList.map((hour) => ({
|
|
859
|
+
key: hour,
|
|
860
|
+
label: this.formatHourLabel(hour, format)
|
|
861
|
+
}));
|
|
862
|
+
}, /* @ts-ignore */
|
|
863
|
+
...(ngDevMode ? [{ debugName: "columnHeaders" }] : /* istanbul ignore next */ []));
|
|
864
|
+
// Grid Matrix computation
|
|
865
|
+
gridRows = computed(() => {
|
|
866
|
+
const currentInterval = this.interval();
|
|
867
|
+
const isSwapped = this.swapAxes();
|
|
868
|
+
const map = this.normalizedValueMap();
|
|
869
|
+
const { min, max } = this.computedScaleBounds();
|
|
870
|
+
const scheme = this.activeColorScheme();
|
|
871
|
+
const palette = PALETTE_CONFIG[scheme] || PALETTE_CONFIG.indigo;
|
|
872
|
+
const unitStr = this.activeUnit();
|
|
873
|
+
const unitSuffix = unitStr ? ` ${unitStr}` : '';
|
|
874
|
+
const format = this.timeFormat();
|
|
875
|
+
const getVisuals = (val) => {
|
|
876
|
+
const intensity = max > min ? Math.max(0, Math.min(1, (val - min) / (max - min))) : 0;
|
|
877
|
+
let bgColor = '';
|
|
878
|
+
let textColor = '';
|
|
879
|
+
if (val === 0) {
|
|
880
|
+
bgColor = palette.zero.bg;
|
|
881
|
+
textColor = palette.zero.text;
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
const r = Math.round(palette.min.r + (palette.max.r - palette.min.r) * intensity);
|
|
885
|
+
const g = Math.round(palette.min.g + (palette.max.g - palette.min.g) * intensity);
|
|
886
|
+
const b = Math.round(palette.min.b + (palette.max.b - palette.min.b) * intensity);
|
|
887
|
+
bgColor = `rgb(${r}, ${g}, ${b})`;
|
|
888
|
+
textColor = intensity >= 0.55 ? '#ffffff' : palette.zero.text;
|
|
889
|
+
}
|
|
890
|
+
return { intensity, bgColor, textColor };
|
|
891
|
+
};
|
|
892
|
+
// --- DAY-BY-DAY MODE ---
|
|
893
|
+
if (currentInterval === 'day') {
|
|
894
|
+
const weeks = this.dayModeWeeks();
|
|
895
|
+
const dayLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
|
896
|
+
if (!isSwapped) {
|
|
897
|
+
// Standard: Rows = Weeks, Columns = Days
|
|
898
|
+
return weeks.map((week, weekIdx) => {
|
|
899
|
+
const cells = week.days.map((d, dayIdx) => {
|
|
900
|
+
const dateStr = formatDate(d, 'yyyy-MM-dd');
|
|
901
|
+
const dayName = dayLabels[dayIdx];
|
|
902
|
+
const entry = map.get(dateStr) || map.get(`${dayName}_${weekIdx}`);
|
|
903
|
+
const val = entry?.value ?? 0;
|
|
904
|
+
const { intensity, bgColor, textColor } = getVisuals(val);
|
|
905
|
+
const tooltip = `${formatDate(d, 'EEEE, MMMM d, yyyy')}: ${val}${unitSuffix}`;
|
|
906
|
+
return {
|
|
907
|
+
id: dateStr,
|
|
908
|
+
colKey: dayName,
|
|
909
|
+
colLabel: `${dayName} (${formatDate(d, 'd')})`,
|
|
910
|
+
rowKey: week.label,
|
|
911
|
+
rowLabel: week.label,
|
|
912
|
+
value: val,
|
|
913
|
+
intensity,
|
|
914
|
+
backgroundColor: bgColor,
|
|
915
|
+
textColor,
|
|
916
|
+
formattedValue: String(val),
|
|
917
|
+
tooltipText: tooltip,
|
|
918
|
+
date: d,
|
|
919
|
+
meta: entry?.meta
|
|
920
|
+
};
|
|
921
|
+
});
|
|
922
|
+
return {
|
|
923
|
+
key: week.label,
|
|
924
|
+
label: week.label,
|
|
925
|
+
cells
|
|
926
|
+
};
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
// Swapped: Rows = Days (Mon..Sun), Columns = Weeks
|
|
931
|
+
return dayLabels.map((dayName, dayIdx) => {
|
|
932
|
+
const cells = weeks.map((week, weekIdx) => {
|
|
933
|
+
const d = week.days[dayIdx];
|
|
934
|
+
const dateStr = formatDate(d, 'yyyy-MM-dd');
|
|
935
|
+
const entry = map.get(dateStr) || map.get(`${dayName}_${weekIdx}`);
|
|
936
|
+
const val = entry?.value ?? 0;
|
|
937
|
+
const { intensity, bgColor, textColor } = getVisuals(val);
|
|
938
|
+
const tooltip = `${formatDate(d, 'EEEE, MMMM d, yyyy')}: ${val}${unitSuffix}`;
|
|
939
|
+
return {
|
|
940
|
+
id: `${dateStr}_swapped`,
|
|
941
|
+
colKey: week.label,
|
|
942
|
+
colLabel: week.label,
|
|
943
|
+
rowKey: dayName,
|
|
944
|
+
rowLabel: dayName,
|
|
945
|
+
value: val,
|
|
946
|
+
intensity,
|
|
947
|
+
backgroundColor: bgColor,
|
|
948
|
+
textColor,
|
|
949
|
+
formattedValue: String(val),
|
|
950
|
+
tooltipText: tooltip,
|
|
951
|
+
date: d,
|
|
952
|
+
meta: entry?.meta
|
|
953
|
+
};
|
|
954
|
+
});
|
|
955
|
+
return {
|
|
956
|
+
key: dayName,
|
|
957
|
+
label: dayName,
|
|
958
|
+
cells
|
|
959
|
+
};
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
// --- HOUR-BY-HOUR MODE ---
|
|
964
|
+
const hoursList = this.hours();
|
|
965
|
+
const colsList = this.days();
|
|
966
|
+
if (!isSwapped) {
|
|
967
|
+
// Standard: Rows = Hours, Columns = Days
|
|
968
|
+
return hoursList.map((hour) => {
|
|
969
|
+
const hourLabel = this.formatHourLabel(hour, format);
|
|
970
|
+
const cells = colsList.map((col) => {
|
|
971
|
+
const key = `${col}_${hour}`;
|
|
972
|
+
const entry = map.get(key);
|
|
973
|
+
const val = entry?.value ?? 0;
|
|
974
|
+
const { intensity, bgColor, textColor } = getVisuals(val);
|
|
975
|
+
const tooltip = `${col}, ${hourLabel}: ${val}${unitSuffix}`;
|
|
976
|
+
return {
|
|
977
|
+
id: key,
|
|
978
|
+
colKey: col,
|
|
979
|
+
colLabel: col,
|
|
980
|
+
rowKey: hour,
|
|
981
|
+
rowLabel: hourLabel,
|
|
982
|
+
value: val,
|
|
983
|
+
intensity,
|
|
984
|
+
backgroundColor: bgColor,
|
|
985
|
+
textColor,
|
|
986
|
+
formattedValue: String(val),
|
|
987
|
+
tooltipText: tooltip,
|
|
988
|
+
meta: entry?.meta
|
|
989
|
+
};
|
|
990
|
+
});
|
|
991
|
+
return {
|
|
992
|
+
key: hour,
|
|
993
|
+
label: hourLabel,
|
|
994
|
+
cells
|
|
995
|
+
};
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
else {
|
|
999
|
+
// Swapped: Rows = Days, Columns = Hours
|
|
1000
|
+
return colsList.map((day) => {
|
|
1001
|
+
const cells = hoursList.map((hour) => {
|
|
1002
|
+
const hourLabel = this.formatHourLabel(hour, format);
|
|
1003
|
+
const key = `${day}_${hour}`;
|
|
1004
|
+
const entry = map.get(key);
|
|
1005
|
+
const val = entry?.value ?? 0;
|
|
1006
|
+
const { intensity, bgColor, textColor } = getVisuals(val);
|
|
1007
|
+
const tooltip = `${day}, ${hourLabel}: ${val}${unitSuffix}`;
|
|
1008
|
+
return {
|
|
1009
|
+
id: `${key}_swapped`,
|
|
1010
|
+
colKey: hour,
|
|
1011
|
+
colLabel: hourLabel,
|
|
1012
|
+
rowKey: day,
|
|
1013
|
+
rowLabel: day,
|
|
1014
|
+
value: val,
|
|
1015
|
+
intensity,
|
|
1016
|
+
backgroundColor: bgColor,
|
|
1017
|
+
textColor,
|
|
1018
|
+
formattedValue: String(val),
|
|
1019
|
+
tooltipText: tooltip,
|
|
1020
|
+
meta: entry?.meta
|
|
1021
|
+
};
|
|
1022
|
+
});
|
|
1023
|
+
return {
|
|
1024
|
+
key: day,
|
|
1025
|
+
label: day,
|
|
1026
|
+
cells
|
|
1027
|
+
};
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
}, /* @ts-ignore */
|
|
1031
|
+
...(ngDevMode ? [{ debugName: "gridRows" }] : /* istanbul ignore next */ []));
|
|
1032
|
+
constructor() {
|
|
1033
|
+
// Notify parent on time range boundaries changes
|
|
1034
|
+
effect(() => {
|
|
1035
|
+
const days = this.days();
|
|
1036
|
+
const hours = this.hours();
|
|
1037
|
+
const inter = this.interval();
|
|
1038
|
+
const dr = this.dateRange();
|
|
1039
|
+
const swapped = this.swapAxes();
|
|
1040
|
+
this.timeRangeChange.emit({
|
|
1041
|
+
interval: inter,
|
|
1042
|
+
startHour: hours.length > 0 ? hours[0] : undefined,
|
|
1043
|
+
endHour: hours.length > 0 ? hours[hours.length - 1] : undefined,
|
|
1044
|
+
days,
|
|
1045
|
+
startDate: dr?.startDate ? new Date(dr.startDate) : undefined,
|
|
1046
|
+
endDate: dr?.endDate ? new Date(dr.endDate) : undefined,
|
|
1047
|
+
dateRange: dr,
|
|
1048
|
+
swapAxes: swapped
|
|
1049
|
+
});
|
|
1050
|
+
});
|
|
1051
|
+
// Close dropdowns on outside click
|
|
1052
|
+
if (typeof document !== 'undefined') {
|
|
1053
|
+
document.addEventListener('click', (event) => {
|
|
1054
|
+
const target = event.target;
|
|
1055
|
+
if (!this.elementRef.nativeElement.contains(target)) {
|
|
1056
|
+
this.isMetricDropdownOpen.set(false);
|
|
1057
|
+
this.isExportDropdownOpen.set(false);
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
formatHourLabel(hour, format) {
|
|
1063
|
+
if (format === '24h') {
|
|
1064
|
+
return `${hour.toString().padStart(2, '0')}:00`;
|
|
1065
|
+
}
|
|
1066
|
+
if (hour === 0)
|
|
1067
|
+
return '12 AM';
|
|
1068
|
+
if (hour < 12)
|
|
1069
|
+
return `${hour} AM`;
|
|
1070
|
+
if (hour === 12)
|
|
1071
|
+
return '12 PM';
|
|
1072
|
+
return `${hour - 12} PM`;
|
|
1073
|
+
}
|
|
1074
|
+
setInterval(newInterval) {
|
|
1075
|
+
this.interval.set(newInterval);
|
|
1076
|
+
}
|
|
1077
|
+
toggleSwapAxes() {
|
|
1078
|
+
this.swapAxes.update((v) => !v);
|
|
1079
|
+
}
|
|
1080
|
+
onDateRangePickerChange(val) {
|
|
1081
|
+
this.dateRange.set(val);
|
|
1082
|
+
}
|
|
1083
|
+
toggleExpand() {
|
|
1084
|
+
this.isExpanded.update((v) => !v);
|
|
1085
|
+
}
|
|
1086
|
+
toggleMetricDropdown(event) {
|
|
1087
|
+
event.stopPropagation();
|
|
1088
|
+
this.isMetricDropdownOpen.update((v) => !v);
|
|
1089
|
+
this.isExportDropdownOpen.set(false);
|
|
1090
|
+
}
|
|
1091
|
+
toggleExportDropdown(event) {
|
|
1092
|
+
event.stopPropagation();
|
|
1093
|
+
this.isExportDropdownOpen.update((v) => !v);
|
|
1094
|
+
this.isMetricDropdownOpen.set(false);
|
|
1095
|
+
}
|
|
1096
|
+
selectMetric(metric) {
|
|
1097
|
+
this.selectedMetricId.set(metric.id);
|
|
1098
|
+
this.isMetricDropdownOpen.set(false);
|
|
1099
|
+
this.metricChange.emit(metric);
|
|
1100
|
+
}
|
|
1101
|
+
onRefreshClick() {
|
|
1102
|
+
this.refresh.emit();
|
|
1103
|
+
}
|
|
1104
|
+
onCellMouseEnter(cell) {
|
|
1105
|
+
this.hoveredCell.set(cell);
|
|
1106
|
+
this.cellHover.emit(cell);
|
|
1107
|
+
}
|
|
1108
|
+
onCellMouseLeave() {
|
|
1109
|
+
this.hoveredCell.set(null);
|
|
1110
|
+
this.cellHover.emit(null);
|
|
1111
|
+
}
|
|
1112
|
+
onCellClick(cell, event) {
|
|
1113
|
+
this.cellClick.emit({
|
|
1114
|
+
cell,
|
|
1115
|
+
colKey: cell.colKey,
|
|
1116
|
+
rowKey: cell.rowKey,
|
|
1117
|
+
value: cell.value,
|
|
1118
|
+
interval: this.interval(),
|
|
1119
|
+
event
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
onKeydown(event, colIdx, rowIdx) {
|
|
1123
|
+
const rows = this.gridRows();
|
|
1124
|
+
const cols = this.columnHeaders();
|
|
1125
|
+
let targetRow = rowIdx;
|
|
1126
|
+
let targetCol = colIdx;
|
|
1127
|
+
if (event.key === 'ArrowUp') {
|
|
1128
|
+
targetRow = Math.max(0, rowIdx - 1);
|
|
1129
|
+
event.preventDefault();
|
|
1130
|
+
}
|
|
1131
|
+
else if (event.key === 'ArrowDown') {
|
|
1132
|
+
targetRow = Math.min(rows.length - 1, rowIdx + 1);
|
|
1133
|
+
event.preventDefault();
|
|
1134
|
+
}
|
|
1135
|
+
else if (event.key === 'ArrowLeft') {
|
|
1136
|
+
targetCol = Math.max(0, colIdx - 1);
|
|
1137
|
+
event.preventDefault();
|
|
1138
|
+
}
|
|
1139
|
+
else if (event.key === 'ArrowRight') {
|
|
1140
|
+
targetCol = Math.min(cols.length - 1, colIdx + 1);
|
|
1141
|
+
event.preventDefault();
|
|
1142
|
+
}
|
|
1143
|
+
else if (event.key === 'Enter' || event.key === ' ') {
|
|
1144
|
+
const cell = rows[rowIdx]?.cells[colIdx];
|
|
1145
|
+
if (cell) {
|
|
1146
|
+
this.cellClick.emit({
|
|
1147
|
+
cell,
|
|
1148
|
+
colKey: cell.colKey,
|
|
1149
|
+
rowKey: cell.rowKey,
|
|
1150
|
+
value: cell.value,
|
|
1151
|
+
interval: this.interval(),
|
|
1152
|
+
event
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
event.preventDefault();
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
if (targetRow !== rowIdx || targetCol !== colIdx) {
|
|
1159
|
+
this.focusedCoord.set({ colIndex: targetCol, rowIndex: targetRow });
|
|
1160
|
+
const cellEl = this.elementRef.nativeElement.querySelector(`[data-cell-coord="${targetRow}_${targetCol}"]`);
|
|
1161
|
+
cellEl?.focus();
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
triggerExport(format) {
|
|
1165
|
+
this.isExportDropdownOpen.set(false);
|
|
1166
|
+
const rows = this.gridRows();
|
|
1167
|
+
const cols = this.days();
|
|
1168
|
+
if (format === 'csv') {
|
|
1169
|
+
const header = ['Period', ...cols].join(',');
|
|
1170
|
+
const body = rows.map((r) => [r.label, ...r.cells.map((c) => c.value)].join(',')).join('\n');
|
|
1171
|
+
const csvContent = `${header}\n${body}`;
|
|
1172
|
+
this.downloadFile(csvContent, 'text/csv;charset=utf-8;', `heatmap_${this.title().toLowerCase().replace(/\s+/g, '_')}.csv`);
|
|
1173
|
+
this.export.emit({ format: 'csv', data: csvContent });
|
|
1174
|
+
}
|
|
1175
|
+
else if (format === 'json') {
|
|
1176
|
+
const jsonRecords = {};
|
|
1177
|
+
rows.forEach((r) => {
|
|
1178
|
+
jsonRecords[r.label] = {};
|
|
1179
|
+
r.cells.forEach((c) => {
|
|
1180
|
+
jsonRecords[r.label][c.colKey] = c.value;
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
const jsonStr = JSON.stringify(jsonRecords, null, 2);
|
|
1184
|
+
this.downloadFile(jsonStr, 'application/json;charset=utf-8;', `heatmap_${this.title().toLowerCase().replace(/\s+/g, '_')}.json`);
|
|
1185
|
+
this.export.emit({ format: 'json', data: jsonRecords });
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
downloadFile(content, mimeType, filename) {
|
|
1189
|
+
if (typeof window === 'undefined')
|
|
1190
|
+
return;
|
|
1191
|
+
const blob = new Blob([content], { type: mimeType });
|
|
1192
|
+
const url = URL.createObjectURL(blob);
|
|
1193
|
+
const a = document.createElement('a');
|
|
1194
|
+
a.href = url;
|
|
1195
|
+
a.download = filename;
|
|
1196
|
+
document.body.appendChild(a);
|
|
1197
|
+
a.click();
|
|
1198
|
+
document.body.removeChild(a);
|
|
1199
|
+
URL.revokeObjectURL(url);
|
|
1200
|
+
}
|
|
1201
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1202
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosHeatmapComponent, isStandalone: true, selector: "talos-heatmap", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, metrics: { classPropertyName: "metrics", publicName: "metrics", isSignal: true, isRequired: false, transformFunction: null }, selectedMetricId: { classPropertyName: "selectedMetricId", publicName: "selectedMetricId", isSignal: true, isRequired: false, transformFunction: null }, interval: { classPropertyName: "interval", publicName: "interval", isSignal: true, isRequired: false, transformFunction: null }, dateRange: { classPropertyName: "dateRange", publicName: "dateRange", isSignal: true, isRequired: false, transformFunction: null }, swapAxes: { classPropertyName: "swapAxes", publicName: "swapAxes", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, hoursRange: { classPropertyName: "hoursRange", publicName: "hoursRange", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, days: { classPropertyName: "days", publicName: "days", isSignal: true, isRequired: false, transformFunction: null }, colorScheme: { classPropertyName: "colorScheme", publicName: "colorScheme", isSignal: true, isRequired: false, transformFunction: null }, customColorStops: { classPropertyName: "customColorStops", publicName: "customColorStops", isSignal: true, isRequired: false, transformFunction: null }, showLegend: { classPropertyName: "showLegend", publicName: "showLegend", isSignal: true, isRequired: false, transformFunction: null }, showValues: { classPropertyName: "showValues", publicName: "showValues", isSignal: true, isRequired: false, transformFunction: null }, cellShape: { classPropertyName: "cellShape", publicName: "cellShape", isSignal: true, isRequired: false, transformFunction: null }, cellSize: { classPropertyName: "cellSize", publicName: "cellSize", isSignal: true, isRequired: false, transformFunction: null }, fitWidth: { classPropertyName: "fitWidth", publicName: "fitWidth", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showExport: { classPropertyName: "showExport", publicName: "showExport", isSignal: true, isRequired: false, transformFunction: null }, showExpand: { classPropertyName: "showExpand", publicName: "showExpand", isSignal: true, isRequired: false, transformFunction: null }, showSwapAxes: { classPropertyName: "showSwapAxes", publicName: "showSwapAxes", isSignal: true, isRequired: false, transformFunction: null }, showIntervalToggle: { classPropertyName: "showIntervalToggle", publicName: "showIntervalToggle", isSignal: true, isRequired: false, transformFunction: null }, showDateRangePicker: { classPropertyName: "showDateRangePicker", publicName: "showDateRangePicker", isSignal: true, isRequired: false, transformFunction: null }, dateRangePresets: { classPropertyName: "dateRangePresets", publicName: "dateRangePresets", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, maxValue: { classPropertyName: "maxValue", publicName: "maxValue", isSignal: true, isRequired: false, transformFunction: null }, minValue: { classPropertyName: "minValue", publicName: "minValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedMetricId: "selectedMetricIdChange", interval: "intervalChange", dateRange: "dateRangeChange", swapAxes: "swapAxesChange", cellClick: "cellClick", cellHover: "cellHover", timeRangeChange: "timeRangeChange", refresh: "refresh", metricChange: "metricChange", export: "export" }, host: { properties: { "class.talos-heatmap-expanded": "isExpanded()", "class.talos-heatmap-loading": "loading()", "class.talos-heatmap-sm": "cellSize() === \"sm\"", "class.talos-heatmap-md": "cellSize() === \"md\"", "class.talos-heatmap-lg": "cellSize() === \"lg\"", "class.talos-heatmap-pill": "cellShape() === \"pill\"", "class.talos-heatmap-rounded": "cellShape() === \"rounded\"", "class.talos-heatmap-square": "cellShape() === \"square\"", "class.talos-heatmap-fit-width": "isFitWidth()", "class.talos-heatmap-compact-square": "!isFitWidth()", "class.talos-heatmap-swapped-axes": "swapAxes()", "attr.role": "\"region\"", "attr.aria-label": "title()" }, classAttribute: "talos-heatmap" }, ngImport: i0, template: "<div class=\"talos-heatmap-container\">\n <!-- Header Toolbar -->\n @if (showToolbar()) {\n <div class=\"talos-heatmap-header\">\n <div class=\"talos-heatmap-title-area\">\n <h3 class=\"talos-heatmap-title\">{{ title() }}</h3>\n </div>\n\n <div class=\"talos-heatmap-actions\">\n <!-- Interval Selector Toggle (Hour vs Day) -->\n @if (showIntervalToggle()) {\n <div class=\"talos-heatmap-interval-group\" role=\"radiogroup\" aria-label=\"Time Interval\">\n <button type=\"button\" class=\"talos-heatmap-interval-btn\" [class.is-active]=\"interval() === 'hour'\"\n (click)=\"setInterval('hour')\" role=\"radio\" [attr.aria-checked]=\"interval() === 'hour'\">\n <svg lucideClock [size]=\"13\"></svg>\n <span>Hour</span>\n </button>\n <button type=\"button\" class=\"talos-heatmap-interval-btn\" [class.is-active]=\"interval() === 'day'\"\n (click)=\"setInterval('day')\" role=\"radio\" [attr.aria-checked]=\"interval() === 'day'\">\n <svg lucideCalendar [size]=\"13\"></svg>\n <span>Day</span>\n </button>\n </div>\n }\n\n <!-- Date Range Picker Integration -->\n @if (showDateRangePicker()) {\n <div class=\"talos-heatmap-daterange-box\">\n <talos-date-range-picker size=\"sm\" [ngModel]=\"dateRange()\" (rangeChange)=\"onDateRangePickerChange($event)\"\n [presets]=\"dateRangePresets()\" placeholder=\"Select date range\" />\n </div>\n }\n\n <!-- Metric Dropdown Selector -->\n @if (metrics().length > 0) {\n <div class=\"talos-heatmap-dropdown-wrapper\">\n <button type=\"button\" class=\"talos-heatmap-dropdown-btn\" [class.is-open]=\"isMetricDropdownOpen()\"\n (click)=\"toggleMetricDropdown($event)\" aria-haspopup=\"listbox\" [attr.aria-expanded]=\"isMetricDropdownOpen()\">\n <span class=\"talos-heatmap-dropdown-label\">{{ currentMetric()?.label || 'Select metric' }}</span>\n <svg lucideChevronDown [size]=\"14\" class=\"talos-heatmap-dropdown-icon\"></svg>\n </button>\n\n @if (isMetricDropdownOpen()) {\n <div class=\"talos-heatmap-menu\" role=\"listbox\">\n @for (m of metrics(); track m.id) {\n <button type=\"button\" class=\"talos-heatmap-menu-item\" [class.is-selected]=\"m.id === currentMetric()?.id\"\n (click)=\"selectMetric(m)\" role=\"option\" [attr.aria-selected]=\"m.id === currentMetric()?.id\">\n <span>{{ m.label }}</span>\n @if (m.id === currentMetric()?.id) {\n <svg lucideCheck [size]=\"14\" class=\"talos-heatmap-check-icon\"></svg>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n <!-- Refresh Button -->\n @if (showRefresh()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" [class.is-spinning]=\"loading()\" (click)=\"onRefreshClick()\"\n title=\"Refresh data\" aria-label=\"Refresh data\">\n <svg lucideRefreshCw [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n }\n\n <!-- Export Dropdown Button -->\n @if (showExport()) {\n <div class=\"talos-heatmap-dropdown-wrapper\">\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" (click)=\"toggleExportDropdown($event)\" title=\"Export data\"\n aria-label=\"Export data\">\n <svg lucideUpload [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n\n @if (isExportDropdownOpen()) {\n <div class=\"talos-heatmap-menu talos-heatmap-export-menu\" role=\"menu\">\n <button type=\"button\" class=\"talos-heatmap-menu-item\" (click)=\"triggerExport('csv')\" role=\"menuitem\">\n <svg lucideFileSpreadsheet [size]=\"14\" class=\"talos-heatmap-menu-icon\"></svg>\n <span>Export to CSV</span>\n </button>\n <button type=\"button\" class=\"talos-heatmap-menu-item\" (click)=\"triggerExport('json')\" role=\"menuitem\">\n <svg lucideFileJson [size]=\"14\" class=\"talos-heatmap-menu-icon\"></svg>\n <span>Export to JSON</span>\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Swap Axes Button -->\n @if (showSwapAxes()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" [class.is-active]=\"swapAxes()\" (click)=\"toggleSwapAxes()\"\n [title]=\"swapAxes() ? 'Reset axis orientation' : 'Swap axes (Days / Hours)'\"\n [attr.aria-label]=\"swapAxes() ? 'Reset axis orientation' : 'Swap axes (Days / Hours)'\">\n <svg lucideArrowLeftRight [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n }\n\n <!-- Fullscreen / Expand Button -->\n @if (showExpand()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" (click)=\"toggleExpand()\"\n [title]=\"isExpanded() ? 'Minimize' : 'Expand'\" [aria-label]=\"isExpanded() ? 'Minimize' : 'Expand'\">\n @if (isExpanded()) {\n <svg lucideMinimize2 [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n } @else {\n <svg lucideMaximize2 [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Heatmap Matrix Grid Wrapper -->\n <div class=\"talos-heatmap-body\" [class.is-loading]=\"loading()\">\n <!-- Grid Table -->\n <div class=\"talos-heatmap-grid\" role=\"grid\" [attr.aria-rowcount]=\"gridRows().length + 1\"\n [attr.aria-colcount]=\"columnHeaders().length + 1\">\n <!-- Column Headers -->\n <div class=\"talos-heatmap-row talos-heatmap-header-row\" role=\"row\" aria-rowindex=\"1\">\n <div class=\"talos-heatmap-corner-cell\" role=\"columnheader\"></div>\n @for (col of columnHeaders(); track col.key; let colIdx = $index) {\n <div class=\"talos-heatmap-col-header\" role=\"columnheader\" [attr.aria-colindex]=\"colIdx + 2\">\n {{ col.label }}\n </div>\n }\n </div>\n\n <!-- Data Rows -->\n @for (row of gridRows(); track row.key; let rowIdx = $index) {\n <div class=\"talos-heatmap-row\" role=\"row\" [attr.aria-rowindex]=\"rowIdx + 2\">\n <!-- Row Header (Hour or Week Range) -->\n <div class=\"talos-heatmap-row-header\" role=\"rowheader\" aria-colindex=\"1\" [title]=\"row.label\">\n {{ row.label }}\n </div>\n\n <!-- Heatmap Cells -->\n @for (cell of row.cells; track cell.id; let colIdx = $index) {\n <div class=\"talos-heatmap-cell-wrapper\" role=\"gridcell\" [attr.aria-colindex]=\"colIdx + 2\"\n [attr.aria-label]=\"cell.tooltipText\" tabindex=\"0\" [attr.data-cell-coord]=\"rowIdx + '_' + colIdx\"\n [talosTooltip]=\"cell.tooltipText\" (mouseenter)=\"onCellMouseEnter(cell)\" (mouseleave)=\"onCellMouseLeave()\"\n (click)=\"onCellClick(cell, $event)\" (keydown)=\"onKeydown($event, colIdx, rowIdx)\">\n <div class=\"talos-heatmap-pill\" [style.background-color]=\"cell.backgroundColor\" [style.color]=\"cell.textColor\"\n [class.has-value]=\"cell.value > 0\">\n @if (showValues()) {\n <span class=\"talos-heatmap-cell-value\">{{ cell.formattedValue }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Loading Skeleton Shimmer Overlay -->\n @if (loading()) {\n <div class=\"talos-heatmap-loading-overlay\" aria-hidden=\"true\">\n <div class=\"talos-heatmap-loading-spinner-box\">\n <svg lucideRefreshCw [size]=\"16\" class=\"talos-heatmap-loading-spinner\"></svg>\n <span>Loading data...</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Bottom Legend Bar -->\n @if (showLegend()) {\n <div class=\"talos-heatmap-legend\">\n <span class=\"talos-heatmap-legend-bound\">{{ computedScaleBounds().min }}</span>\n <div class=\"talos-heatmap-legend-bar\" [style.background]=\"legendGradientCss()\" aria-hidden=\"true\"></div>\n <span class=\"talos-heatmap-legend-bound\">{{ computedScaleBounds().max }}</span>\n </div>\n }\n</div>", styles: [":host{display:block;font-family:inherit;--talos-heatmap-bg: var(--talos-card-bg, var(--talos-bg-surface, #ffffff));--talos-heatmap-text: var(--talos-text-color, #0f172a);--talos-heatmap-muted: var(--talos-text-muted, #64748b);--talos-heatmap-border: var(--talos-border-color, #e2e8f0);--talos-heatmap-hover-bg: var(--talos-primary-light, #f1f5f9);--talos-heatmap-cell-height: 28px;--talos-heatmap-cell-radius: 9999px;--talos-heatmap-row-header-width: 68px;--talos-heatmap-cell-font-size: .85rem;--talos-heatmap-gap: 6px;--talos-heatmap-square-size: 32px}:host(.talos-heatmap-sm){--talos-heatmap-cell-height: 22px;--talos-heatmap-row-header-width: 56px;--talos-heatmap-cell-font-size: .72rem;--talos-heatmap-square-size: 24px;--talos-heatmap-gap: 4px}:host(.talos-heatmap-md){--talos-heatmap-cell-height: 28px;--talos-heatmap-row-header-width: 68px;--talos-heatmap-cell-font-size: .82rem;--talos-heatmap-square-size: 32px;--talos-heatmap-gap: 6px}:host(.talos-heatmap-lg){--talos-heatmap-cell-height: 36px;--talos-heatmap-row-header-width: 80px;--talos-heatmap-cell-font-size: .92rem;--talos-heatmap-square-size: 40px;--talos-heatmap-gap: 8px}:host(.talos-heatmap-square){--talos-heatmap-cell-radius: 6px}:host(.talos-heatmap-rounded){--talos-heatmap-cell-radius: 8px}:host(.talos-heatmap-pill){--talos-heatmap-cell-radius: 9999px}:host(.talos-heatmap-expanded){position:fixed;inset:16px;z-index:1000;background:var(--talos-heatmap-bg);box-shadow:0 25px 50px -12px #00000059;border-radius:16px;padding:24px;overflow:auto;border:1px solid var(--talos-heatmap-border)}.talos-heatmap-container{display:flex;flex-direction:column;background:var(--talos-heatmap-bg);border-radius:12px;position:relative;width:100%}.talos-heatmap-header{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:20px;flex-wrap:wrap}.talos-heatmap-title{margin:0;font-size:1.15rem;font-weight:700;color:var(--talos-heatmap-text);letter-spacing:-.015em}.talos-heatmap-actions{display:flex;align-items:center;gap:8px;position:relative;flex-wrap:wrap}.talos-heatmap-interval-group{display:inline-flex;align-items:center;padding:2px;background:var(--talos-bg-color, #f1f5f9);border-radius:8px;border:1px solid var(--talos-heatmap-border);gap:2px}.talos-heatmap-interval-btn{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;font-size:.78rem;font-weight:600;color:var(--talos-heatmap-muted);background:transparent;border:none;border-radius:6px;cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-interval-btn:hover{color:var(--talos-heatmap-text)}.talos-heatmap-interval-btn.is-active{background:var(--talos-card-bg, #ffffff);color:var(--talos-primary-color, #4338ca);box-shadow:0 1px 3px #00000014}.talos-heatmap-daterange-box{min-width:220px}.talos-heatmap-dropdown-wrapper{position:relative}.talos-heatmap-dropdown-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;font-size:.85rem;font-weight:500;color:var(--talos-heatmap-text);background:var(--talos-bg-color, #f8fafc);border:1px solid var(--talos-heatmap-border);border-radius:8px;cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-dropdown-btn:hover{background:var(--talos-primary-light, #f1f5f9);border-color:var(--talos-border-hover, #cbd5e1)}.talos-heatmap-dropdown-btn.is-open{background:var(--talos-primary-light-hover, #e2e8f0);border-color:var(--talos-border-hover, #94a3b8)}.talos-heatmap-dropdown-btn .talos-heatmap-dropdown-icon{width:14px;height:14px;color:var(--talos-heatmap-muted)}.talos-heatmap-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;border:1px solid transparent;background:transparent;color:var(--talos-heatmap-muted);cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-icon-btn:hover{background:var(--talos-heatmap-hover-bg);color:var(--talos-heatmap-text)}.talos-heatmap-icon-btn.is-active{background:var(--talos-primary-light, #e0e7ff);color:var(--talos-primary-color, #4338ca);border-color:var(--talos-border-color, #c7d2fe)}.talos-heatmap-icon-btn:active{transform:scale(.95)}.talos-heatmap-icon-btn.is-spinning .talos-heatmap-action-icon{animation:talos-heatmap-spin .8s linear infinite}.talos-heatmap-icon-btn .talos-heatmap-action-icon{width:16px;height:16px}@keyframes talos-heatmap-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.talos-heatmap-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:50;min-width:170px;background:var(--talos-dropdown-bg, var(--talos-card-bg, #ffffff));border:1px solid var(--talos-dropdown-border, var(--talos-heatmap-border));border-radius:10px;box-shadow:var(--talos-dropdown-shadow, 0 10px 25px -5px rgba(0, 0, 0, .12), 0 8px 10px -6px rgba(0, 0, 0, .08));padding:4px;display:flex;flex-direction:column;gap:2px;animation:talos-heatmap-menu-fade .15s cubic-bezier(.16,1,.3,1)}@keyframes talos-heatmap-menu-fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.talos-heatmap-menu-item{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:7px 10px;font-size:.82rem;font-weight:500;color:var(--talos-heatmap-text);background:transparent;border:none;border-radius:6px;text-align:left;cursor:pointer;transition:background .12s ease}.talos-heatmap-menu-item:hover{background:var(--talos-dropdown-hover-bg, #f1f5f9)}.talos-heatmap-menu-item.is-selected{font-weight:600;color:var(--talos-primary-color, #4338ca)}.talos-heatmap-menu-item .talos-heatmap-check-icon{width:14px;height:14px;color:var(--talos-primary-color, #4338ca)}.talos-heatmap-menu-item .talos-heatmap-menu-icon{width:14px;height:14px;color:var(--talos-heatmap-muted)}.talos-heatmap-body{position:relative;width:100%;overflow-x:auto;padding-bottom:8px}.talos-heatmap-body.is-loading{pointer-events:none;-webkit-user-select:none;user-select:none}.talos-heatmap-grid{display:flex;flex-direction:column;gap:var(--talos-heatmap-gap);width:100%}.talos-heatmap-row{display:flex;align-items:center;gap:var(--talos-heatmap-gap);width:100%}.talos-heatmap-header-row{margin-bottom:2px}.talos-heatmap-corner-cell{width:var(--talos-heatmap-row-header-width);flex-shrink:0}.talos-heatmap-row-header{width:var(--talos-heatmap-row-header-width);flex-shrink:0;text-align:right;padding-right:12px;font-size:.82rem;font-weight:500;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host(.talos-heatmap-fit-width) .talos-heatmap-grid{min-width:440px;width:100%}:host(.talos-heatmap-fit-width) .talos-heatmap-col-header{flex:1;text-align:center;font-size:.82rem;font-weight:600;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;letter-spacing:.01em}:host(.talos-heatmap-fit-width) .talos-heatmap-cell-wrapper{flex:1;height:var(--talos-heatmap-cell-height);position:relative;display:flex;align-items:center;justify-content:center;cursor:pointer;outline:none;border-radius:var(--talos-heatmap-cell-radius);transition:transform .15s ease}:host(.talos-heatmap-fit-width) .talos-heatmap-cell-wrapper:focus-visible{box-shadow:0 0 0 2px #4338ca,0 0 0 4px #4338ca40;z-index:5}:host(.talos-heatmap-compact-square) .talos-heatmap-grid{min-width:auto;width:max-content}:host(.talos-heatmap-compact-square) .talos-heatmap-col-header{width:var(--talos-heatmap-square-size);flex:0 0 var(--talos-heatmap-square-size);text-align:center;font-size:.78rem;font-weight:600;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;letter-spacing:.01em}:host(.talos-heatmap-compact-square) .talos-heatmap-cell-wrapper{width:var(--talos-heatmap-square-size);height:var(--talos-heatmap-square-size);flex:0 0 var(--talos-heatmap-square-size);aspect-ratio:1/1;position:relative;display:flex;align-items:center;justify-content:center;cursor:pointer;outline:none;border-radius:var(--talos-heatmap-cell-radius);transition:transform .15s ease}:host(.talos-heatmap-compact-square) .talos-heatmap-cell-wrapper:focus-visible{box-shadow:0 0 0 2px #4338ca,0 0 0 4px #4338ca40;z-index:5}.talos-heatmap-pill{width:100%;height:100%;display:flex;align-items:center;justify-content:center;border-radius:var(--talos-heatmap-cell-radius);font-size:var(--talos-heatmap-cell-font-size);font-weight:600;line-height:1;-webkit-user-select:none;user-select:none;box-shadow:inset 0 0 0 1px #0000000a;transition:transform .15s cubic-bezier(.16,1,.3,1),box-shadow .15s ease,filter .15s ease}.talos-heatmap-pill:not(.has-value){box-shadow:inset 0 0 0 1px #e2e8f0e6}.talos-heatmap-cell-wrapper:hover .talos-heatmap-pill{transform:translateY(-2px) scale(1.05);box-shadow:0 6px 14px -2px #00000024,0 2px 4px -1px #0000000f;filter:brightness(1.03);z-index:2}.talos-heatmap-cell-wrapper:active .talos-heatmap-pill{transform:translateY(0) scale(.96)}.talos-heatmap-cell-value{display:inline-block;text-align:center}.talos-heatmap-loading-overlay{position:absolute;inset:0;background:var(--talos-loading-overlay-bg, rgba(255, 255, 255, .75));-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:40;border-radius:8px;animation:talos-heatmap-fade-in .2s ease}@keyframes talos-heatmap-fade-in{0%{opacity:0}to{opacity:1}}.talos-heatmap-loading-spinner-box{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--talos-card-bg, #ffffff);border:1px solid var(--talos-heatmap-border);border-radius:8px;box-shadow:0 4px 12px #00000014;font-size:.85rem;font-weight:500;color:var(--talos-heatmap-text)}.talos-heatmap-loading-spinner-box .talos-heatmap-loading-spinner{width:16px;height:16px;color:var(--talos-primary-color, #4338ca);animation:talos-heatmap-spin .8s linear infinite}.talos-heatmap-legend{display:flex;align-items:center;gap:12px;margin-top:18px;padding-left:var(--talos-heatmap-row-header-width)}.talos-heatmap-legend-bound{font-size:.82rem;font-weight:600;color:var(--talos-heatmap-muted);min-width:12px}.talos-heatmap-legend-bar{height:8px;width:130px;border-radius:9999px;box-shadow:inset 0 1px 2px #00000014}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: DateRangePickerComponent, selector: "talos-date-range-picker", inputs: ["placeholder", "label", "floatingLabel", "floating", "required", "disabled", "clearable", "size", "displayFormat", "valueFormat", "showTime", "use24Hour", "showSeconds", "minuteStep", "minDate", "maxDate", "minSpan", "maxSpan", "firstDayOfWeek", "presets", "presetType", "filterDate"], outputs: ["rangeChange", "opened", "closed"] }, { kind: "directive", type: TalosTooltipDirective, selector: "[talosTooltip], [tooltip]", inputs: ["talosTooltip", "tooltip", "tooltipPosition", "tooltipDisabled"] }, { kind: "component", type: LucideRefreshCw, selector: "svg[lucideRefreshCw]" }, { kind: "component", type: LucideUpload, selector: "svg[lucideUpload]" }, { kind: "component", type: LucideMaximize2, selector: "svg[lucideMaximize2]" }, { kind: "component", type: LucideMinimize2, selector: "svg[lucideMinimize2]" }, { kind: "component", type: LucideChevronDown, selector: "svg[lucideChevronDown]" }, { kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideFileSpreadsheet, selector: "svg[lucideFileSpreadsheet]" }, { kind: "component", type: LucideFileJson, selector: "svg[lucideFileBraces], svg[lucideFileJson]" }, { kind: "component", type: LucideCalendar, selector: "svg[lucideCalendar]" }, { kind: "component", type: LucideClock, selector: "svg[lucideClock]" }, { kind: "component", type: LucideArrowLeftRight, selector: "svg[lucideArrowLeftRight]" }] });
|
|
1203
|
+
}
|
|
1204
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapComponent, decorators: [{
|
|
1205
|
+
type: Component,
|
|
1206
|
+
args: [{ selector: 'talos-heatmap', imports: [
|
|
1207
|
+
FormsModule,
|
|
1208
|
+
DateRangePickerComponent,
|
|
1209
|
+
TalosTooltipDirective,
|
|
1210
|
+
LucideRefreshCw,
|
|
1211
|
+
LucideUpload,
|
|
1212
|
+
LucideMaximize2,
|
|
1213
|
+
LucideMinimize2,
|
|
1214
|
+
LucideChevronDown,
|
|
1215
|
+
LucideCheck,
|
|
1216
|
+
LucideFileSpreadsheet,
|
|
1217
|
+
LucideFileJson,
|
|
1218
|
+
LucideCalendar,
|
|
1219
|
+
LucideClock,
|
|
1220
|
+
LucideArrowLeftRight
|
|
1221
|
+
], host: {
|
|
1222
|
+
'class': 'talos-heatmap',
|
|
1223
|
+
'[class.talos-heatmap-expanded]': 'isExpanded()',
|
|
1224
|
+
'[class.talos-heatmap-loading]': 'loading()',
|
|
1225
|
+
'[class.talos-heatmap-sm]': 'cellSize() === "sm"',
|
|
1226
|
+
'[class.talos-heatmap-md]': 'cellSize() === "md"',
|
|
1227
|
+
'[class.talos-heatmap-lg]': 'cellSize() === "lg"',
|
|
1228
|
+
'[class.talos-heatmap-pill]': 'cellShape() === "pill"',
|
|
1229
|
+
'[class.talos-heatmap-rounded]': 'cellShape() === "rounded"',
|
|
1230
|
+
'[class.talos-heatmap-square]': 'cellShape() === "square"',
|
|
1231
|
+
'[class.talos-heatmap-fit-width]': 'isFitWidth()',
|
|
1232
|
+
'[class.talos-heatmap-compact-square]': '!isFitWidth()',
|
|
1233
|
+
'[class.talos-heatmap-swapped-axes]': 'swapAxes()',
|
|
1234
|
+
'[attr.role]': '"region"',
|
|
1235
|
+
'[attr.aria-label]': 'title()'
|
|
1236
|
+
}, template: "<div class=\"talos-heatmap-container\">\n <!-- Header Toolbar -->\n @if (showToolbar()) {\n <div class=\"talos-heatmap-header\">\n <div class=\"talos-heatmap-title-area\">\n <h3 class=\"talos-heatmap-title\">{{ title() }}</h3>\n </div>\n\n <div class=\"talos-heatmap-actions\">\n <!-- Interval Selector Toggle (Hour vs Day) -->\n @if (showIntervalToggle()) {\n <div class=\"talos-heatmap-interval-group\" role=\"radiogroup\" aria-label=\"Time Interval\">\n <button type=\"button\" class=\"talos-heatmap-interval-btn\" [class.is-active]=\"interval() === 'hour'\"\n (click)=\"setInterval('hour')\" role=\"radio\" [attr.aria-checked]=\"interval() === 'hour'\">\n <svg lucideClock [size]=\"13\"></svg>\n <span>Hour</span>\n </button>\n <button type=\"button\" class=\"talos-heatmap-interval-btn\" [class.is-active]=\"interval() === 'day'\"\n (click)=\"setInterval('day')\" role=\"radio\" [attr.aria-checked]=\"interval() === 'day'\">\n <svg lucideCalendar [size]=\"13\"></svg>\n <span>Day</span>\n </button>\n </div>\n }\n\n <!-- Date Range Picker Integration -->\n @if (showDateRangePicker()) {\n <div class=\"talos-heatmap-daterange-box\">\n <talos-date-range-picker size=\"sm\" [ngModel]=\"dateRange()\" (rangeChange)=\"onDateRangePickerChange($event)\"\n [presets]=\"dateRangePresets()\" placeholder=\"Select date range\" />\n </div>\n }\n\n <!-- Metric Dropdown Selector -->\n @if (metrics().length > 0) {\n <div class=\"talos-heatmap-dropdown-wrapper\">\n <button type=\"button\" class=\"talos-heatmap-dropdown-btn\" [class.is-open]=\"isMetricDropdownOpen()\"\n (click)=\"toggleMetricDropdown($event)\" aria-haspopup=\"listbox\" [attr.aria-expanded]=\"isMetricDropdownOpen()\">\n <span class=\"talos-heatmap-dropdown-label\">{{ currentMetric()?.label || 'Select metric' }}</span>\n <svg lucideChevronDown [size]=\"14\" class=\"talos-heatmap-dropdown-icon\"></svg>\n </button>\n\n @if (isMetricDropdownOpen()) {\n <div class=\"talos-heatmap-menu\" role=\"listbox\">\n @for (m of metrics(); track m.id) {\n <button type=\"button\" class=\"talos-heatmap-menu-item\" [class.is-selected]=\"m.id === currentMetric()?.id\"\n (click)=\"selectMetric(m)\" role=\"option\" [attr.aria-selected]=\"m.id === currentMetric()?.id\">\n <span>{{ m.label }}</span>\n @if (m.id === currentMetric()?.id) {\n <svg lucideCheck [size]=\"14\" class=\"talos-heatmap-check-icon\"></svg>\n }\n </button>\n }\n </div>\n }\n </div>\n }\n\n <!-- Refresh Button -->\n @if (showRefresh()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" [class.is-spinning]=\"loading()\" (click)=\"onRefreshClick()\"\n title=\"Refresh data\" aria-label=\"Refresh data\">\n <svg lucideRefreshCw [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n }\n\n <!-- Export Dropdown Button -->\n @if (showExport()) {\n <div class=\"talos-heatmap-dropdown-wrapper\">\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" (click)=\"toggleExportDropdown($event)\" title=\"Export data\"\n aria-label=\"Export data\">\n <svg lucideUpload [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n\n @if (isExportDropdownOpen()) {\n <div class=\"talos-heatmap-menu talos-heatmap-export-menu\" role=\"menu\">\n <button type=\"button\" class=\"talos-heatmap-menu-item\" (click)=\"triggerExport('csv')\" role=\"menuitem\">\n <svg lucideFileSpreadsheet [size]=\"14\" class=\"talos-heatmap-menu-icon\"></svg>\n <span>Export to CSV</span>\n </button>\n <button type=\"button\" class=\"talos-heatmap-menu-item\" (click)=\"triggerExport('json')\" role=\"menuitem\">\n <svg lucideFileJson [size]=\"14\" class=\"talos-heatmap-menu-icon\"></svg>\n <span>Export to JSON</span>\n </button>\n </div>\n }\n </div>\n }\n\n <!-- Swap Axes Button -->\n @if (showSwapAxes()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" [class.is-active]=\"swapAxes()\" (click)=\"toggleSwapAxes()\"\n [title]=\"swapAxes() ? 'Reset axis orientation' : 'Swap axes (Days / Hours)'\"\n [attr.aria-label]=\"swapAxes() ? 'Reset axis orientation' : 'Swap axes (Days / Hours)'\">\n <svg lucideArrowLeftRight [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n </button>\n }\n\n <!-- Fullscreen / Expand Button -->\n @if (showExpand()) {\n <button type=\"button\" class=\"talos-heatmap-icon-btn\" (click)=\"toggleExpand()\"\n [title]=\"isExpanded() ? 'Minimize' : 'Expand'\" [aria-label]=\"isExpanded() ? 'Minimize' : 'Expand'\">\n @if (isExpanded()) {\n <svg lucideMinimize2 [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n } @else {\n <svg lucideMaximize2 [size]=\"16\" class=\"talos-heatmap-action-icon\"></svg>\n }\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Heatmap Matrix Grid Wrapper -->\n <div class=\"talos-heatmap-body\" [class.is-loading]=\"loading()\">\n <!-- Grid Table -->\n <div class=\"talos-heatmap-grid\" role=\"grid\" [attr.aria-rowcount]=\"gridRows().length + 1\"\n [attr.aria-colcount]=\"columnHeaders().length + 1\">\n <!-- Column Headers -->\n <div class=\"talos-heatmap-row talos-heatmap-header-row\" role=\"row\" aria-rowindex=\"1\">\n <div class=\"talos-heatmap-corner-cell\" role=\"columnheader\"></div>\n @for (col of columnHeaders(); track col.key; let colIdx = $index) {\n <div class=\"talos-heatmap-col-header\" role=\"columnheader\" [attr.aria-colindex]=\"colIdx + 2\">\n {{ col.label }}\n </div>\n }\n </div>\n\n <!-- Data Rows -->\n @for (row of gridRows(); track row.key; let rowIdx = $index) {\n <div class=\"talos-heatmap-row\" role=\"row\" [attr.aria-rowindex]=\"rowIdx + 2\">\n <!-- Row Header (Hour or Week Range) -->\n <div class=\"talos-heatmap-row-header\" role=\"rowheader\" aria-colindex=\"1\" [title]=\"row.label\">\n {{ row.label }}\n </div>\n\n <!-- Heatmap Cells -->\n @for (cell of row.cells; track cell.id; let colIdx = $index) {\n <div class=\"talos-heatmap-cell-wrapper\" role=\"gridcell\" [attr.aria-colindex]=\"colIdx + 2\"\n [attr.aria-label]=\"cell.tooltipText\" tabindex=\"0\" [attr.data-cell-coord]=\"rowIdx + '_' + colIdx\"\n [talosTooltip]=\"cell.tooltipText\" (mouseenter)=\"onCellMouseEnter(cell)\" (mouseleave)=\"onCellMouseLeave()\"\n (click)=\"onCellClick(cell, $event)\" (keydown)=\"onKeydown($event, colIdx, rowIdx)\">\n <div class=\"talos-heatmap-pill\" [style.background-color]=\"cell.backgroundColor\" [style.color]=\"cell.textColor\"\n [class.has-value]=\"cell.value > 0\">\n @if (showValues()) {\n <span class=\"talos-heatmap-cell-value\">{{ cell.formattedValue }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Loading Skeleton Shimmer Overlay -->\n @if (loading()) {\n <div class=\"talos-heatmap-loading-overlay\" aria-hidden=\"true\">\n <div class=\"talos-heatmap-loading-spinner-box\">\n <svg lucideRefreshCw [size]=\"16\" class=\"talos-heatmap-loading-spinner\"></svg>\n <span>Loading data...</span>\n </div>\n </div>\n }\n </div>\n\n <!-- Bottom Legend Bar -->\n @if (showLegend()) {\n <div class=\"talos-heatmap-legend\">\n <span class=\"talos-heatmap-legend-bound\">{{ computedScaleBounds().min }}</span>\n <div class=\"talos-heatmap-legend-bar\" [style.background]=\"legendGradientCss()\" aria-hidden=\"true\"></div>\n <span class=\"talos-heatmap-legend-bound\">{{ computedScaleBounds().max }}</span>\n </div>\n }\n</div>", styles: [":host{display:block;font-family:inherit;--talos-heatmap-bg: var(--talos-card-bg, var(--talos-bg-surface, #ffffff));--talos-heatmap-text: var(--talos-text-color, #0f172a);--talos-heatmap-muted: var(--talos-text-muted, #64748b);--talos-heatmap-border: var(--talos-border-color, #e2e8f0);--talos-heatmap-hover-bg: var(--talos-primary-light, #f1f5f9);--talos-heatmap-cell-height: 28px;--talos-heatmap-cell-radius: 9999px;--talos-heatmap-row-header-width: 68px;--talos-heatmap-cell-font-size: .85rem;--talos-heatmap-gap: 6px;--talos-heatmap-square-size: 32px}:host(.talos-heatmap-sm){--talos-heatmap-cell-height: 22px;--talos-heatmap-row-header-width: 56px;--talos-heatmap-cell-font-size: .72rem;--talos-heatmap-square-size: 24px;--talos-heatmap-gap: 4px}:host(.talos-heatmap-md){--talos-heatmap-cell-height: 28px;--talos-heatmap-row-header-width: 68px;--talos-heatmap-cell-font-size: .82rem;--talos-heatmap-square-size: 32px;--talos-heatmap-gap: 6px}:host(.talos-heatmap-lg){--talos-heatmap-cell-height: 36px;--talos-heatmap-row-header-width: 80px;--talos-heatmap-cell-font-size: .92rem;--talos-heatmap-square-size: 40px;--talos-heatmap-gap: 8px}:host(.talos-heatmap-square){--talos-heatmap-cell-radius: 6px}:host(.talos-heatmap-rounded){--talos-heatmap-cell-radius: 8px}:host(.talos-heatmap-pill){--talos-heatmap-cell-radius: 9999px}:host(.talos-heatmap-expanded){position:fixed;inset:16px;z-index:1000;background:var(--talos-heatmap-bg);box-shadow:0 25px 50px -12px #00000059;border-radius:16px;padding:24px;overflow:auto;border:1px solid var(--talos-heatmap-border)}.talos-heatmap-container{display:flex;flex-direction:column;background:var(--talos-heatmap-bg);border-radius:12px;position:relative;width:100%}.talos-heatmap-header{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:20px;flex-wrap:wrap}.talos-heatmap-title{margin:0;font-size:1.15rem;font-weight:700;color:var(--talos-heatmap-text);letter-spacing:-.015em}.talos-heatmap-actions{display:flex;align-items:center;gap:8px;position:relative;flex-wrap:wrap}.talos-heatmap-interval-group{display:inline-flex;align-items:center;padding:2px;background:var(--talos-bg-color, #f1f5f9);border-radius:8px;border:1px solid var(--talos-heatmap-border);gap:2px}.talos-heatmap-interval-btn{display:inline-flex;align-items:center;gap:5px;padding:4px 10px;font-size:.78rem;font-weight:600;color:var(--talos-heatmap-muted);background:transparent;border:none;border-radius:6px;cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-interval-btn:hover{color:var(--talos-heatmap-text)}.talos-heatmap-interval-btn.is-active{background:var(--talos-card-bg, #ffffff);color:var(--talos-primary-color, #4338ca);box-shadow:0 1px 3px #00000014}.talos-heatmap-daterange-box{min-width:220px}.talos-heatmap-dropdown-wrapper{position:relative}.talos-heatmap-dropdown-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;font-size:.85rem;font-weight:500;color:var(--talos-heatmap-text);background:var(--talos-bg-color, #f8fafc);border:1px solid var(--talos-heatmap-border);border-radius:8px;cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-dropdown-btn:hover{background:var(--talos-primary-light, #f1f5f9);border-color:var(--talos-border-hover, #cbd5e1)}.talos-heatmap-dropdown-btn.is-open{background:var(--talos-primary-light-hover, #e2e8f0);border-color:var(--talos-border-hover, #94a3b8)}.talos-heatmap-dropdown-btn .talos-heatmap-dropdown-icon{width:14px;height:14px;color:var(--talos-heatmap-muted)}.talos-heatmap-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;border:1px solid transparent;background:transparent;color:var(--talos-heatmap-muted);cursor:pointer;outline:none;transition:all .15s ease}.talos-heatmap-icon-btn:hover{background:var(--talos-heatmap-hover-bg);color:var(--talos-heatmap-text)}.talos-heatmap-icon-btn.is-active{background:var(--talos-primary-light, #e0e7ff);color:var(--talos-primary-color, #4338ca);border-color:var(--talos-border-color, #c7d2fe)}.talos-heatmap-icon-btn:active{transform:scale(.95)}.talos-heatmap-icon-btn.is-spinning .talos-heatmap-action-icon{animation:talos-heatmap-spin .8s linear infinite}.talos-heatmap-icon-btn .talos-heatmap-action-icon{width:16px;height:16px}@keyframes talos-heatmap-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.talos-heatmap-menu{position:absolute;top:calc(100% + 4px);right:0;z-index:50;min-width:170px;background:var(--talos-dropdown-bg, var(--talos-card-bg, #ffffff));border:1px solid var(--talos-dropdown-border, var(--talos-heatmap-border));border-radius:10px;box-shadow:var(--talos-dropdown-shadow, 0 10px 25px -5px rgba(0, 0, 0, .12), 0 8px 10px -6px rgba(0, 0, 0, .08));padding:4px;display:flex;flex-direction:column;gap:2px;animation:talos-heatmap-menu-fade .15s cubic-bezier(.16,1,.3,1)}@keyframes talos-heatmap-menu-fade{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.talos-heatmap-menu-item{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:7px 10px;font-size:.82rem;font-weight:500;color:var(--talos-heatmap-text);background:transparent;border:none;border-radius:6px;text-align:left;cursor:pointer;transition:background .12s ease}.talos-heatmap-menu-item:hover{background:var(--talos-dropdown-hover-bg, #f1f5f9)}.talos-heatmap-menu-item.is-selected{font-weight:600;color:var(--talos-primary-color, #4338ca)}.talos-heatmap-menu-item .talos-heatmap-check-icon{width:14px;height:14px;color:var(--talos-primary-color, #4338ca)}.talos-heatmap-menu-item .talos-heatmap-menu-icon{width:14px;height:14px;color:var(--talos-heatmap-muted)}.talos-heatmap-body{position:relative;width:100%;overflow-x:auto;padding-bottom:8px}.talos-heatmap-body.is-loading{pointer-events:none;-webkit-user-select:none;user-select:none}.talos-heatmap-grid{display:flex;flex-direction:column;gap:var(--talos-heatmap-gap);width:100%}.talos-heatmap-row{display:flex;align-items:center;gap:var(--talos-heatmap-gap);width:100%}.talos-heatmap-header-row{margin-bottom:2px}.talos-heatmap-corner-cell{width:var(--talos-heatmap-row-header-width);flex-shrink:0}.talos-heatmap-row-header{width:var(--talos-heatmap-row-header-width);flex-shrink:0;text-align:right;padding-right:12px;font-size:.82rem;font-weight:500;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}:host(.talos-heatmap-fit-width) .talos-heatmap-grid{min-width:440px;width:100%}:host(.talos-heatmap-fit-width) .talos-heatmap-col-header{flex:1;text-align:center;font-size:.82rem;font-weight:600;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;letter-spacing:.01em}:host(.talos-heatmap-fit-width) .talos-heatmap-cell-wrapper{flex:1;height:var(--talos-heatmap-cell-height);position:relative;display:flex;align-items:center;justify-content:center;cursor:pointer;outline:none;border-radius:var(--talos-heatmap-cell-radius);transition:transform .15s ease}:host(.talos-heatmap-fit-width) .talos-heatmap-cell-wrapper:focus-visible{box-shadow:0 0 0 2px #4338ca,0 0 0 4px #4338ca40;z-index:5}:host(.talos-heatmap-compact-square) .talos-heatmap-grid{min-width:auto;width:max-content}:host(.talos-heatmap-compact-square) .talos-heatmap-col-header{width:var(--talos-heatmap-square-size);flex:0 0 var(--talos-heatmap-square-size);text-align:center;font-size:.78rem;font-weight:600;color:var(--talos-heatmap-muted);-webkit-user-select:none;user-select:none;letter-spacing:.01em}:host(.talos-heatmap-compact-square) .talos-heatmap-cell-wrapper{width:var(--talos-heatmap-square-size);height:var(--talos-heatmap-square-size);flex:0 0 var(--talos-heatmap-square-size);aspect-ratio:1/1;position:relative;display:flex;align-items:center;justify-content:center;cursor:pointer;outline:none;border-radius:var(--talos-heatmap-cell-radius);transition:transform .15s ease}:host(.talos-heatmap-compact-square) .talos-heatmap-cell-wrapper:focus-visible{box-shadow:0 0 0 2px #4338ca,0 0 0 4px #4338ca40;z-index:5}.talos-heatmap-pill{width:100%;height:100%;display:flex;align-items:center;justify-content:center;border-radius:var(--talos-heatmap-cell-radius);font-size:var(--talos-heatmap-cell-font-size);font-weight:600;line-height:1;-webkit-user-select:none;user-select:none;box-shadow:inset 0 0 0 1px #0000000a;transition:transform .15s cubic-bezier(.16,1,.3,1),box-shadow .15s ease,filter .15s ease}.talos-heatmap-pill:not(.has-value){box-shadow:inset 0 0 0 1px #e2e8f0e6}.talos-heatmap-cell-wrapper:hover .talos-heatmap-pill{transform:translateY(-2px) scale(1.05);box-shadow:0 6px 14px -2px #00000024,0 2px 4px -1px #0000000f;filter:brightness(1.03);z-index:2}.talos-heatmap-cell-wrapper:active .talos-heatmap-pill{transform:translateY(0) scale(.96)}.talos-heatmap-cell-value{display:inline-block;text-align:center}.talos-heatmap-loading-overlay{position:absolute;inset:0;background:var(--talos-loading-overlay-bg, rgba(255, 255, 255, .75));-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:40;border-radius:8px;animation:talos-heatmap-fade-in .2s ease}@keyframes talos-heatmap-fade-in{0%{opacity:0}to{opacity:1}}.talos-heatmap-loading-spinner-box{display:flex;align-items:center;gap:8px;padding:8px 16px;background:var(--talos-card-bg, #ffffff);border:1px solid var(--talos-heatmap-border);border-radius:8px;box-shadow:0 4px 12px #00000014;font-size:.85rem;font-weight:500;color:var(--talos-heatmap-text)}.talos-heatmap-loading-spinner-box .talos-heatmap-loading-spinner{width:16px;height:16px;color:var(--talos-primary-color, #4338ca);animation:talos-heatmap-spin .8s linear infinite}.talos-heatmap-legend{display:flex;align-items:center;gap:12px;margin-top:18px;padding-left:var(--talos-heatmap-row-header-width)}.talos-heatmap-legend-bound{font-size:.82rem;font-weight:600;color:var(--talos-heatmap-muted);min-width:12px}.talos-heatmap-legend-bar{height:8px;width:130px;border-radius:9999px;box-shadow:inset 0 1px 2px #00000014}\n"] }]
|
|
1237
|
+
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], metrics: [{ type: i0.Input, args: [{ isSignal: true, alias: "metrics", required: false }] }], selectedMetricId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMetricId", required: false }] }, { type: i0.Output, args: ["selectedMetricIdChange"] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }, { type: i0.Output, args: ["intervalChange"] }], dateRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateRange", required: false }] }, { type: i0.Output, args: ["dateRangeChange"] }], swapAxes: [{ type: i0.Input, args: [{ isSignal: true, alias: "swapAxes", required: false }] }, { type: i0.Output, args: ["swapAxesChange"] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], hoursRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "hoursRange", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], days: [{ type: i0.Input, args: [{ isSignal: true, alias: "days", required: false }] }], colorScheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorScheme", required: false }] }], customColorStops: [{ type: i0.Input, args: [{ isSignal: true, alias: "customColorStops", required: false }] }], showLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLegend", required: false }] }], showValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValues", required: false }] }], cellShape: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellShape", required: false }] }], cellSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellSize", required: false }] }], fitWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitWidth", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showExport: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExport", required: false }] }], showExpand: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExpand", required: false }] }], showSwapAxes: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSwapAxes", required: false }] }], showIntervalToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIntervalToggle", required: false }] }], showDateRangePicker: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDateRangePicker", required: false }] }], dateRangePresets: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateRangePresets", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], maxValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxValue", required: false }] }], minValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "minValue", required: false }] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], cellHover: [{ type: i0.Output, args: ["cellHover"] }], timeRangeChange: [{ type: i0.Output, args: ["timeRangeChange"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], metricChange: [{ type: i0.Output, args: ["metricChange"] }], export: [{ type: i0.Output, args: ["export"] }] } });
|
|
1238
|
+
|
|
1239
|
+
class TalosHeatmapModule {
|
|
1240
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1241
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapModule, imports: [TalosHeatmapComponent], exports: [TalosHeatmapComponent] });
|
|
1242
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapModule, imports: [TalosHeatmapComponent] });
|
|
1243
|
+
}
|
|
1244
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosHeatmapModule, decorators: [{
|
|
1245
|
+
type: NgModule,
|
|
1246
|
+
args: [{
|
|
1247
|
+
imports: [TalosHeatmapComponent],
|
|
1248
|
+
exports: [TalosHeatmapComponent]
|
|
1249
|
+
}]
|
|
1250
|
+
}] });
|
|
1251
|
+
|
|
1252
|
+
/**
|
|
1253
|
+
* Types and interfaces for TalosMiniDonutComponent
|
|
1254
|
+
*/
|
|
1255
|
+
|
|
1256
|
+
const PRESET_COLORS$1 = {
|
|
1257
|
+
primary: '#2563eb',
|
|
1258
|
+
blue: '#3b82f6',
|
|
1259
|
+
success: '#16a34a',
|
|
1260
|
+
emerald: '#10b981',
|
|
1261
|
+
danger: '#ef4444',
|
|
1262
|
+
rose: '#f43f5e',
|
|
1263
|
+
warning: '#f59e0b',
|
|
1264
|
+
amber: '#d97706',
|
|
1265
|
+
info: '#0284c7',
|
|
1266
|
+
cyan: '#06b6d4',
|
|
1267
|
+
violet: '#8b5cf6',
|
|
1268
|
+
purple: '#9333ea',
|
|
1269
|
+
pink: '#ec4899',
|
|
1270
|
+
slate: '#64748b'
|
|
1271
|
+
};
|
|
1272
|
+
const DEFAULT_PALETTE = ['#3b82f6', '#10b981', '#8b5cf6', '#f59e0b', '#ec4899', '#06b6d4'];
|
|
1273
|
+
const SIZE_PRESETS = {
|
|
1274
|
+
xs: { px: 24, stroke: 3 },
|
|
1275
|
+
sm: { px: 32, stroke: 4 },
|
|
1276
|
+
md: { px: 48, stroke: 5 },
|
|
1277
|
+
lg: { px: 64, stroke: 6 },
|
|
1278
|
+
xl: { px: 88, stroke: 8 }
|
|
1279
|
+
};
|
|
1280
|
+
class TalosMiniDonutComponent {
|
|
1281
|
+
/** Single progress value (e.g. 75 or 450) */
|
|
1282
|
+
value = input(null, /* @ts-ignore */
|
|
1283
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1284
|
+
/** Minimum value for single progress mode */
|
|
1285
|
+
min = input(0, /* @ts-ignore */
|
|
1286
|
+
...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
1287
|
+
/** Maximum value for single progress mode */
|
|
1288
|
+
max = input(100, /* @ts-ignore */
|
|
1289
|
+
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
1290
|
+
/** Multi-segment dataset */
|
|
1291
|
+
segments = input(null, /* @ts-ignore */
|
|
1292
|
+
...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
|
|
1293
|
+
/** Size preset */
|
|
1294
|
+
size = input('md', /* @ts-ignore */
|
|
1295
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1296
|
+
/** Custom dimension override in px */
|
|
1297
|
+
dimension = input(null, /* @ts-ignore */
|
|
1298
|
+
...(ngDevMode ? [{ debugName: "dimension" }] : /* istanbul ignore next */ []));
|
|
1299
|
+
/** Custom stroke width override */
|
|
1300
|
+
strokeWidth = input(null, /* @ts-ignore */
|
|
1301
|
+
...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
|
|
1302
|
+
/** Color scheme or hex string for single progress mode */
|
|
1303
|
+
color = input('primary', /* @ts-ignore */
|
|
1304
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
1305
|
+
/** Background track ring color */
|
|
1306
|
+
trackColor = input(null, /* @ts-ignore */
|
|
1307
|
+
...(ngDevMode ? [{ debugName: "trackColor" }] : /* istanbul ignore next */ []));
|
|
1308
|
+
/** Show background track */
|
|
1309
|
+
showTrack = input(true, /* @ts-ignore */
|
|
1310
|
+
...(ngDevMode ? [{ debugName: "showTrack" }] : /* istanbul ignore next */ []));
|
|
1311
|
+
/** SVG stroke linecap */
|
|
1312
|
+
lineCap = input('round', /* @ts-ignore */
|
|
1313
|
+
...(ngDevMode ? [{ debugName: "lineCap" }] : /* istanbul ignore next */ []));
|
|
1314
|
+
/** Gap spacing between segments in multi-segment mode (in px) */
|
|
1315
|
+
gap = input(2, /* @ts-ignore */
|
|
1316
|
+
...(ngDevMode ? [{ debugName: "gap" }] : /* istanbul ignore next */ []));
|
|
1317
|
+
/** Show built-in center percentage or text */
|
|
1318
|
+
showCenterText = input(false, /* @ts-ignore */
|
|
1319
|
+
...(ngDevMode ? [{ debugName: "showCenterText" }] : /* istanbul ignore next */ []));
|
|
1320
|
+
/** Explicit center primary text override */
|
|
1321
|
+
centerText = input(null, /* @ts-ignore */
|
|
1322
|
+
...(ngDevMode ? [{ debugName: "centerText" }] : /* istanbul ignore next */ []));
|
|
1323
|
+
/** Secondary center subtext */
|
|
1324
|
+
centerSubtext = input(null, /* @ts-ignore */
|
|
1325
|
+
...(ngDevMode ? [{ debugName: "centerSubtext" }] : /* istanbul ignore next */ []));
|
|
1326
|
+
/** Enable smooth CSS animation */
|
|
1327
|
+
animated = input(true, /* @ts-ignore */
|
|
1328
|
+
...(ngDevMode ? [{ debugName: "animated" }] : /* istanbul ignore next */ []));
|
|
1329
|
+
/** Custom ARIA label */
|
|
1330
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
1331
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1332
|
+
/** Segment click event */
|
|
1333
|
+
segmentClick = output();
|
|
1334
|
+
// Internal SVG coordinate constants (viewBox is 0 0 100 100)
|
|
1335
|
+
svgSize = 100;
|
|
1336
|
+
center = 50;
|
|
1337
|
+
/** Computed pixel dimension */
|
|
1338
|
+
pixelDimension = computed(() => {
|
|
1339
|
+
const custom = this.dimension();
|
|
1340
|
+
if (custom !== null)
|
|
1341
|
+
return custom;
|
|
1342
|
+
const s = this.size();
|
|
1343
|
+
return s !== 'custom' ? SIZE_PRESETS[s].px : 48;
|
|
1344
|
+
}, /* @ts-ignore */
|
|
1345
|
+
...(ngDevMode ? [{ debugName: "pixelDimension" }] : /* istanbul ignore next */ []));
|
|
1346
|
+
/** Computed stroke width in SVG coordinates */
|
|
1347
|
+
computedStrokeWidth = computed(() => {
|
|
1348
|
+
const custom = this.strokeWidth();
|
|
1349
|
+
if (custom !== null) {
|
|
1350
|
+
// Scale according to ratio with pixel dimension
|
|
1351
|
+
const px = this.pixelDimension();
|
|
1352
|
+
return (custom / px) * this.svgSize;
|
|
1353
|
+
}
|
|
1354
|
+
const s = this.size();
|
|
1355
|
+
const preset = s !== 'custom' ? SIZE_PRESETS[s] : SIZE_PRESETS['md'];
|
|
1356
|
+
return (preset.stroke / preset.px) * this.svgSize;
|
|
1357
|
+
}, /* @ts-ignore */
|
|
1358
|
+
...(ngDevMode ? [{ debugName: "computedStrokeWidth" }] : /* istanbul ignore next */ []));
|
|
1359
|
+
/** Radius in SVG coordinates */
|
|
1360
|
+
radius = computed(() => {
|
|
1361
|
+
const sw = this.computedStrokeWidth();
|
|
1362
|
+
return (this.svgSize - sw) / 2;
|
|
1363
|
+
}, /* @ts-ignore */
|
|
1364
|
+
...(ngDevMode ? [{ debugName: "radius" }] : /* istanbul ignore next */ []));
|
|
1365
|
+
/** Circumference */
|
|
1366
|
+
circumference = computed(() => {
|
|
1367
|
+
return 2 * Math.PI * this.radius();
|
|
1368
|
+
}, /* @ts-ignore */
|
|
1369
|
+
...(ngDevMode ? [{ debugName: "circumference" }] : /* istanbul ignore next */ []));
|
|
1370
|
+
/** Effective single progress color */
|
|
1371
|
+
effectiveColor = computed(() => {
|
|
1372
|
+
const c = this.color();
|
|
1373
|
+
return PRESET_COLORS$1[c] || c;
|
|
1374
|
+
}, /* @ts-ignore */
|
|
1375
|
+
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
1376
|
+
/** Computed percentage for single progress mode */
|
|
1377
|
+
singlePercentage = computed(() => {
|
|
1378
|
+
const val = this.value();
|
|
1379
|
+
if (val === null)
|
|
1380
|
+
return 0;
|
|
1381
|
+
const minVal = this.min();
|
|
1382
|
+
const maxVal = this.max();
|
|
1383
|
+
if (maxVal <= minVal)
|
|
1384
|
+
return 0;
|
|
1385
|
+
const pct = ((val - minVal) / (maxVal - minVal)) * 100;
|
|
1386
|
+
return Math.min(100, Math.max(0, pct));
|
|
1387
|
+
}, /* @ts-ignore */
|
|
1388
|
+
...(ngDevMode ? [{ debugName: "singlePercentage" }] : /* istanbul ignore next */ []));
|
|
1389
|
+
/** Single progress dash offset/array */
|
|
1390
|
+
singleDashArray = computed(() => {
|
|
1391
|
+
const circ = this.circumference();
|
|
1392
|
+
const pct = this.singlePercentage();
|
|
1393
|
+
const filled = (pct / 100) * circ;
|
|
1394
|
+
return `${filled} ${circ}`;
|
|
1395
|
+
}, /* @ts-ignore */
|
|
1396
|
+
...(ngDevMode ? [{ debugName: "singleDashArray" }] : /* istanbul ignore next */ []));
|
|
1397
|
+
/** Computed multi-segment values with offsets */
|
|
1398
|
+
computedSegments = computed(() => {
|
|
1399
|
+
const segs = this.segments();
|
|
1400
|
+
if (!segs || segs.length === 0)
|
|
1401
|
+
return [];
|
|
1402
|
+
const circ = this.circumference();
|
|
1403
|
+
const totalVal = segs.reduce((acc, s) => acc + Math.max(0, s.value), 0);
|
|
1404
|
+
if (totalVal <= 0)
|
|
1405
|
+
return [];
|
|
1406
|
+
const gapPx = this.gap();
|
|
1407
|
+
const count = segs.length;
|
|
1408
|
+
const totalGap = count > 1 ? count * gapPx : 0;
|
|
1409
|
+
const availableCirc = Math.max(0, circ - totalGap);
|
|
1410
|
+
let accumulatedOffset = 0;
|
|
1411
|
+
return segs.map((seg, idx) => {
|
|
1412
|
+
const pct = (Math.max(0, seg.value) / totalVal) * 100;
|
|
1413
|
+
const length = (Math.max(0, seg.value) / totalVal) * availableCirc;
|
|
1414
|
+
const color = seg.color ? (PRESET_COLORS$1[seg.color] || seg.color) : DEFAULT_PALETTE[idx % DEFAULT_PALETTE.length];
|
|
1415
|
+
const dashArray = `${length} ${circ}`;
|
|
1416
|
+
// Dashoffset starts at accumulatedOffset (negative because SVG stroke-dashoffset clockwise)
|
|
1417
|
+
const dashOffset = -accumulatedOffset;
|
|
1418
|
+
accumulatedOffset += length + (count > 1 ? gapPx : 0);
|
|
1419
|
+
return {
|
|
1420
|
+
index: idx,
|
|
1421
|
+
value: seg.value,
|
|
1422
|
+
percentage: pct,
|
|
1423
|
+
color,
|
|
1424
|
+
label: seg.label,
|
|
1425
|
+
tooltip: seg.tooltip,
|
|
1426
|
+
dashArray,
|
|
1427
|
+
dashOffset,
|
|
1428
|
+
pathLength: circ
|
|
1429
|
+
};
|
|
1430
|
+
});
|
|
1431
|
+
}, /* @ts-ignore */
|
|
1432
|
+
...(ngDevMode ? [{ debugName: "computedSegments" }] : /* istanbul ignore next */ []));
|
|
1433
|
+
/** Display text inside center */
|
|
1434
|
+
computedCenterText = computed(() => {
|
|
1435
|
+
const explicit = this.centerText();
|
|
1436
|
+
if (explicit !== null)
|
|
1437
|
+
return explicit;
|
|
1438
|
+
if (this.value() !== null) {
|
|
1439
|
+
return `${Math.round(this.singlePercentage())}%`;
|
|
1440
|
+
}
|
|
1441
|
+
return '';
|
|
1442
|
+
}, /* @ts-ignore */
|
|
1443
|
+
...(ngDevMode ? [{ debugName: "computedCenterText" }] : /* istanbul ignore next */ []));
|
|
1444
|
+
/** ARIA attributes */
|
|
1445
|
+
currentAriaValue = computed(() => {
|
|
1446
|
+
if (this.value() !== null)
|
|
1447
|
+
return this.value();
|
|
1448
|
+
const segs = this.segments();
|
|
1449
|
+
if (segs && segs.length > 0)
|
|
1450
|
+
return segs.reduce((a, b) => a + b.value, 0);
|
|
1451
|
+
return 0;
|
|
1452
|
+
}, /* @ts-ignore */
|
|
1453
|
+
...(ngDevMode ? [{ debugName: "currentAriaValue" }] : /* istanbul ignore next */ []));
|
|
1454
|
+
computedAriaLabel = computed(() => {
|
|
1455
|
+
const custom = this.ariaLabel();
|
|
1456
|
+
if (custom)
|
|
1457
|
+
return custom;
|
|
1458
|
+
if (this.value() !== null) {
|
|
1459
|
+
return `Progress: ${Math.round(this.singlePercentage())}%`;
|
|
1460
|
+
}
|
|
1461
|
+
const segs = this.segments();
|
|
1462
|
+
if (segs && segs.length > 0) {
|
|
1463
|
+
return `Donut chart with ${segs.length} categories`;
|
|
1464
|
+
}
|
|
1465
|
+
return 'Mini donut chart';
|
|
1466
|
+
}, /* @ts-ignore */
|
|
1467
|
+
...(ngDevMode ? [{ debugName: "computedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
1468
|
+
onSegmentClick(segIndex) {
|
|
1469
|
+
const rawSegs = this.segments();
|
|
1470
|
+
if (rawSegs && rawSegs[segIndex]) {
|
|
1471
|
+
this.segmentClick.emit(rawSegs[segIndex]);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1475
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosMiniDonutComponent, isStandalone: true, selector: "talos-mini-donut", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, segments: { classPropertyName: "segments", publicName: "segments", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dimension: { classPropertyName: "dimension", publicName: "dimension", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, showTrack: { classPropertyName: "showTrack", publicName: "showTrack", isSignal: true, isRequired: false, transformFunction: null }, lineCap: { classPropertyName: "lineCap", publicName: "lineCap", isSignal: true, isRequired: false, transformFunction: null }, gap: { classPropertyName: "gap", publicName: "gap", isSignal: true, isRequired: false, transformFunction: null }, showCenterText: { classPropertyName: "showCenterText", publicName: "showCenterText", isSignal: true, isRequired: false, transformFunction: null }, centerText: { classPropertyName: "centerText", publicName: "centerText", isSignal: true, isRequired: false, transformFunction: null }, centerSubtext: { classPropertyName: "centerSubtext", publicName: "centerSubtext", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { segmentClick: "segmentClick" }, host: { properties: { "class.talos-mini-donut-xs": "size() === \"xs\"", "class.talos-mini-donut-sm": "size() === \"sm\"", "class.talos-mini-donut-md": "size() === \"md\"", "class.talos-mini-donut-lg": "size() === \"lg\"", "class.talos-mini-donut-xl": "size() === \"xl\"", "class.talos-mini-donut-animated": "animated()", "style.width.px": "pixelDimension()", "style.height.px": "pixelDimension()", "attr.role": "\"meter\"", "attr.aria-valuenow": "currentAriaValue()", "attr.aria-valuemin": "min()", "attr.aria-valuemax": "max()", "attr.aria-label": "computedAriaLabel()" }, classAttribute: "talos-mini-donut" }, ngImport: i0, template: "<svg\n class=\"talos-mini-donut-svg\"\n [attr.viewBox]=\"'0 0 ' + svgSize + ' ' + svgSize\"\n aria-hidden=\"true\"\n>\n <!-- Background Track -->\n @if (showTrack()) {\n <circle\n class=\"talos-mini-donut-track\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n fill=\"none\"\n />\n }\n\n <!-- Single Value Progress Mode -->\n @if (segments() === null && value() !== null) {\n <circle\n class=\"talos-mini-donut-progress\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"effectiveColor()\"\n [attr.stroke-dasharray]=\"singleDashArray()\"\n [attr.stroke-linecap]=\"lineCap()\"\n fill=\"none\"\n />\n }\n\n <!-- Multi-Segment Mode -->\n @if (segments() !== null) {\n @for (seg of computedSegments(); track seg.index) {\n <circle\n class=\"talos-mini-donut-segment\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"seg.color\"\n [attr.stroke-dasharray]=\"seg.dashArray\"\n [attr.stroke-dashoffset]=\"seg.dashOffset\"\n [attr.stroke-linecap]=\"lineCap()\"\n fill=\"none\"\n (click)=\"onSegmentClick(seg.index)\"\n />\n }\n }\n</svg>\n\n<!-- Center Content Slot or Built-in Text -->\n<div class=\"talos-mini-donut-center\">\n <ng-content>\n @if (showCenterText() && computedCenterText()) {\n <span class=\"talos-mini-donut-center-text\">{{ computedCenterText() }}</span>\n @if (centerSubtext()) {\n <span class=\"talos-mini-donut-center-subtext\">{{ centerSubtext() }}</span>\n }\n }\n </ng-content>\n</div>\n", styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;box-sizing:border-box;flex-shrink:0}:host .talos-mini-donut-svg{width:100%;height:100%;overflow:visible;transform:rotate(-90deg)}:host .talos-mini-donut-track{opacity:.7;transition:stroke .2s ease}:host .talos-mini-donut-progress,:host .talos-mini-donut-segment{cursor:pointer}:host.talos-mini-donut-animated .talos-mini-donut-progress,:host.talos-mini-donut-animated .talos-mini-donut-segment{transition:stroke-dasharray .4s cubic-bezier(.4,0,.2,1),stroke-dashoffset .4s cubic-bezier(.4,0,.2,1),stroke .2s ease}:host .talos-mini-donut-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;text-align:center;line-height:1}:host .talos-mini-donut-center .talos-mini-donut-center-text{font-weight:700;color:var(--talos-text-color, #0f172a);letter-spacing:-.02em}:host .talos-mini-donut-center .talos-mini-donut-center-subtext{font-size:.625rem;font-weight:500;color:var(--talos-text-muted, #64748b);margin-top:2px}:host.talos-mini-donut-xs .talos-mini-donut-center-text{font-size:.5625rem}:host.talos-mini-donut-sm .talos-mini-donut-center-text{font-size:.6875rem}:host.talos-mini-donut-md .talos-mini-donut-center-text{font-size:.8125rem}:host.talos-mini-donut-lg .talos-mini-donut-center-text{font-size:1rem}:host.talos-mini-donut-xl .talos-mini-donut-center-text{font-size:1.25rem}:root[data-theme=dark] :host .talos-mini-donut-track,:root.dark :host .talos-mini-donut-track,[data-theme=dark] :host .talos-mini-donut-track,[data-mode=dark] :host .talos-mini-donut-track,.dark :host .talos-mini-donut-track,.dark-theme :host .talos-mini-donut-track{stroke:var(--talos-border-color, #334155);opacity:.5}:root[data-theme=dark] :host .talos-mini-donut-center-text,:root.dark :host .talos-mini-donut-center-text,[data-theme=dark] :host .talos-mini-donut-center-text,[data-mode=dark] :host .talos-mini-donut-center-text,.dark :host .talos-mini-donut-center-text,.dark-theme :host .talos-mini-donut-center-text{color:var(--talos-text-color, #f8fafc)}:root[data-theme=dark] :host .talos-mini-donut-center-subtext,:root.dark :host .talos-mini-donut-center-subtext,[data-theme=dark] :host .talos-mini-donut-center-subtext,[data-mode=dark] :host .talos-mini-donut-center-subtext,.dark :host .talos-mini-donut-center-subtext,.dark-theme :host .talos-mini-donut-center-subtext{color:var(--talos-text-muted, #94a3b8)}\n"] });
|
|
1476
|
+
}
|
|
1477
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutComponent, decorators: [{
|
|
1478
|
+
type: Component,
|
|
1479
|
+
args: [{ selector: 'talos-mini-donut', host: {
|
|
1480
|
+
'class': 'talos-mini-donut',
|
|
1481
|
+
'[class.talos-mini-donut-xs]': 'size() === "xs"',
|
|
1482
|
+
'[class.talos-mini-donut-sm]': 'size() === "sm"',
|
|
1483
|
+
'[class.talos-mini-donut-md]': 'size() === "md"',
|
|
1484
|
+
'[class.talos-mini-donut-lg]': 'size() === "lg"',
|
|
1485
|
+
'[class.talos-mini-donut-xl]': 'size() === "xl"',
|
|
1486
|
+
'[class.talos-mini-donut-animated]': 'animated()',
|
|
1487
|
+
'[style.width.px]': 'pixelDimension()',
|
|
1488
|
+
'[style.height.px]': 'pixelDimension()',
|
|
1489
|
+
'[attr.role]': '"meter"',
|
|
1490
|
+
'[attr.aria-valuenow]': 'currentAriaValue()',
|
|
1491
|
+
'[attr.aria-valuemin]': 'min()',
|
|
1492
|
+
'[attr.aria-valuemax]': 'max()',
|
|
1493
|
+
'[attr.aria-label]': 'computedAriaLabel()'
|
|
1494
|
+
}, template: "<svg\n class=\"talos-mini-donut-svg\"\n [attr.viewBox]=\"'0 0 ' + svgSize + ' ' + svgSize\"\n aria-hidden=\"true\"\n>\n <!-- Background Track -->\n @if (showTrack()) {\n <circle\n class=\"talos-mini-donut-track\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n fill=\"none\"\n />\n }\n\n <!-- Single Value Progress Mode -->\n @if (segments() === null && value() !== null) {\n <circle\n class=\"talos-mini-donut-progress\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"effectiveColor()\"\n [attr.stroke-dasharray]=\"singleDashArray()\"\n [attr.stroke-linecap]=\"lineCap()\"\n fill=\"none\"\n />\n }\n\n <!-- Multi-Segment Mode -->\n @if (segments() !== null) {\n @for (seg of computedSegments(); track seg.index) {\n <circle\n class=\"talos-mini-donut-segment\"\n [attr.cx]=\"center\"\n [attr.cy]=\"center\"\n [attr.r]=\"radius()\"\n [attr.stroke-width]=\"computedStrokeWidth()\"\n [attr.stroke]=\"seg.color\"\n [attr.stroke-dasharray]=\"seg.dashArray\"\n [attr.stroke-dashoffset]=\"seg.dashOffset\"\n [attr.stroke-linecap]=\"lineCap()\"\n fill=\"none\"\n (click)=\"onSegmentClick(seg.index)\"\n />\n }\n }\n</svg>\n\n<!-- Center Content Slot or Built-in Text -->\n<div class=\"talos-mini-donut-center\">\n <ng-content>\n @if (showCenterText() && computedCenterText()) {\n <span class=\"talos-mini-donut-center-text\">{{ computedCenterText() }}</span>\n @if (centerSubtext()) {\n <span class=\"talos-mini-donut-center-subtext\">{{ centerSubtext() }}</span>\n }\n }\n </ng-content>\n</div>\n", styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;box-sizing:border-box;flex-shrink:0}:host .talos-mini-donut-svg{width:100%;height:100%;overflow:visible;transform:rotate(-90deg)}:host .talos-mini-donut-track{opacity:.7;transition:stroke .2s ease}:host .talos-mini-donut-progress,:host .talos-mini-donut-segment{cursor:pointer}:host.talos-mini-donut-animated .talos-mini-donut-progress,:host.talos-mini-donut-animated .talos-mini-donut-segment{transition:stroke-dasharray .4s cubic-bezier(.4,0,.2,1),stroke-dashoffset .4s cubic-bezier(.4,0,.2,1),stroke .2s ease}:host .talos-mini-donut-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;text-align:center;line-height:1}:host .talos-mini-donut-center .talos-mini-donut-center-text{font-weight:700;color:var(--talos-text-color, #0f172a);letter-spacing:-.02em}:host .talos-mini-donut-center .talos-mini-donut-center-subtext{font-size:.625rem;font-weight:500;color:var(--talos-text-muted, #64748b);margin-top:2px}:host.talos-mini-donut-xs .talos-mini-donut-center-text{font-size:.5625rem}:host.talos-mini-donut-sm .talos-mini-donut-center-text{font-size:.6875rem}:host.talos-mini-donut-md .talos-mini-donut-center-text{font-size:.8125rem}:host.talos-mini-donut-lg .talos-mini-donut-center-text{font-size:1rem}:host.talos-mini-donut-xl .talos-mini-donut-center-text{font-size:1.25rem}:root[data-theme=dark] :host .talos-mini-donut-track,:root.dark :host .talos-mini-donut-track,[data-theme=dark] :host .talos-mini-donut-track,[data-mode=dark] :host .talos-mini-donut-track,.dark :host .talos-mini-donut-track,.dark-theme :host .talos-mini-donut-track{stroke:var(--talos-border-color, #334155);opacity:.5}:root[data-theme=dark] :host .talos-mini-donut-center-text,:root.dark :host .talos-mini-donut-center-text,[data-theme=dark] :host .talos-mini-donut-center-text,[data-mode=dark] :host .talos-mini-donut-center-text,.dark :host .talos-mini-donut-center-text,.dark-theme :host .talos-mini-donut-center-text{color:var(--talos-text-color, #f8fafc)}:root[data-theme=dark] :host .talos-mini-donut-center-subtext,:root.dark :host .talos-mini-donut-center-subtext,[data-theme=dark] :host .talos-mini-donut-center-subtext,[data-mode=dark] :host .talos-mini-donut-center-subtext,.dark :host .talos-mini-donut-center-subtext,.dark-theme :host .talos-mini-donut-center-subtext{color:var(--talos-text-muted, #94a3b8)}\n"] }]
|
|
1495
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], segments: [{ type: i0.Input, args: [{ isSignal: true, alias: "segments", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dimension: [{ type: i0.Input, args: [{ isSignal: true, alias: "dimension", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], showTrack: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTrack", required: false }] }], lineCap: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineCap", required: false }] }], gap: [{ type: i0.Input, args: [{ isSignal: true, alias: "gap", required: false }] }], showCenterText: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCenterText", required: false }] }], centerText: [{ type: i0.Input, args: [{ isSignal: true, alias: "centerText", required: false }] }], centerSubtext: [{ type: i0.Input, args: [{ isSignal: true, alias: "centerSubtext", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], segmentClick: [{ type: i0.Output, args: ["segmentClick"] }] } });
|
|
1496
|
+
|
|
1497
|
+
class TalosMiniDonutModule {
|
|
1498
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1499
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutModule, imports: [TalosMiniDonutComponent], exports: [TalosMiniDonutComponent] });
|
|
1500
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutModule });
|
|
1501
|
+
}
|
|
1502
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniDonutModule, decorators: [{
|
|
1503
|
+
type: NgModule,
|
|
1504
|
+
args: [{
|
|
1505
|
+
imports: [TalosMiniDonutComponent],
|
|
1506
|
+
exports: [TalosMiniDonutComponent]
|
|
1507
|
+
}]
|
|
1508
|
+
}] });
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* Types and interfaces for TalosMiniGaugeComponent
|
|
1512
|
+
*/
|
|
1513
|
+
|
|
1514
|
+
const PRESET_COLORS = {
|
|
1515
|
+
primary: '#2563eb',
|
|
1516
|
+
blue: '#3b82f6',
|
|
1517
|
+
success: '#16a34a',
|
|
1518
|
+
emerald: '#10b981',
|
|
1519
|
+
danger: '#ef4444',
|
|
1520
|
+
rose: '#f43f5e',
|
|
1521
|
+
warning: '#f59e0b',
|
|
1522
|
+
amber: '#d97706',
|
|
1523
|
+
info: '#0284c7',
|
|
1524
|
+
cyan: '#06b6d4',
|
|
1525
|
+
violet: '#8b5cf6',
|
|
1526
|
+
slate: '#64748b'
|
|
1527
|
+
};
|
|
1528
|
+
const DEFAULT_THRESHOLDS = [
|
|
1529
|
+
{ from: 0, to: 60, color: '#10b981' },
|
|
1530
|
+
{ from: 60, to: 85, color: '#f59e0b' },
|
|
1531
|
+
{ from: 85, to: 100, color: '#ef4444' }
|
|
1532
|
+
];
|
|
1533
|
+
class TalosMiniGaugeComponent {
|
|
1534
|
+
/** Current gauge value */
|
|
1535
|
+
value = input(0, /* @ts-ignore */
|
|
1536
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
1537
|
+
/** Minimum gauge scale */
|
|
1538
|
+
min = input(0, /* @ts-ignore */
|
|
1539
|
+
...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
1540
|
+
/** Maximum gauge scale */
|
|
1541
|
+
max = input(100, /* @ts-ignore */
|
|
1542
|
+
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
1543
|
+
/** Optional target benchmark marker value */
|
|
1544
|
+
target = input(null, /* @ts-ignore */
|
|
1545
|
+
...(ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []));
|
|
1546
|
+
/** Gauge rendering style */
|
|
1547
|
+
type = input('semi-circle', /* @ts-ignore */
|
|
1548
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
1549
|
+
/** Value indicator pointer style: arc fill, needle, or dot */
|
|
1550
|
+
indicator = input('arc', /* @ts-ignore */
|
|
1551
|
+
...(ngDevMode ? [{ debugName: "indicator" }] : /* istanbul ignore next */ []));
|
|
1552
|
+
/** Custom colored threshold zones */
|
|
1553
|
+
thresholds = input(null, /* @ts-ignore */
|
|
1554
|
+
...(ngDevMode ? [{ debugName: "thresholds" }] : /* istanbul ignore next */ []));
|
|
1555
|
+
/** Base color or palette when no thresholds are used */
|
|
1556
|
+
color = input('primary', /* @ts-ignore */
|
|
1557
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
1558
|
+
/** Background track color */
|
|
1559
|
+
trackColor = input(null, /* @ts-ignore */
|
|
1560
|
+
...(ngDevMode ? [{ debugName: "trackColor" }] : /* istanbul ignore next */ []));
|
|
1561
|
+
/** Preset size */
|
|
1562
|
+
size = input('md', /* @ts-ignore */
|
|
1563
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1564
|
+
/** Custom width */
|
|
1565
|
+
width = input(null, /* @ts-ignore */
|
|
1566
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1567
|
+
/** Custom height */
|
|
1568
|
+
height = input(null, /* @ts-ignore */
|
|
1569
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
1570
|
+
/** Arc stroke width */
|
|
1571
|
+
strokeWidth = input(8, /* @ts-ignore */
|
|
1572
|
+
...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
|
|
1573
|
+
/** Arc stroke linecap style ('round' | 'butt') */
|
|
1574
|
+
lineCap = input('round', /* @ts-ignore */
|
|
1575
|
+
...(ngDevMode ? [{ debugName: "lineCap" }] : /* istanbul ignore next */ []));
|
|
1576
|
+
/** Show numerical value readout in center/bottom */
|
|
1577
|
+
showValue = input(true, /* @ts-ignore */
|
|
1578
|
+
...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
|
|
1579
|
+
/** Show min and max numbers at gauge edges */
|
|
1580
|
+
showMinMax = input(false, /* @ts-ignore */
|
|
1581
|
+
...(ngDevMode ? [{ debugName: "showMinMax" }] : /* istanbul ignore next */ []));
|
|
1582
|
+
/** Unit label string (e.g. '%', 'ms', 'rpm') */
|
|
1583
|
+
unit = input('', /* @ts-ignore */
|
|
1584
|
+
...(ngDevMode ? [{ debugName: "unit" }] : /* istanbul ignore next */ []));
|
|
1585
|
+
/** Value formatter */
|
|
1586
|
+
valueFormatter = input((val) => val.toLocaleString(), /* @ts-ignore */
|
|
1587
|
+
...(ngDevMode ? [{ debugName: "valueFormatter" }] : /* istanbul ignore next */ []));
|
|
1588
|
+
/** Custom ARIA label */
|
|
1589
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
1590
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1591
|
+
/** Enable animation */
|
|
1592
|
+
animated = input(true, /* @ts-ignore */
|
|
1593
|
+
...(ngDevMode ? [{ debugName: "animated" }] : /* istanbul ignore next */ []));
|
|
1594
|
+
/** SVG Coordinate constants */
|
|
1595
|
+
cx = 50;
|
|
1596
|
+
cySemi = 44;
|
|
1597
|
+
cyArch = 42;
|
|
1598
|
+
radius = 36;
|
|
1599
|
+
/** Needle length tailored per circular gauge type */
|
|
1600
|
+
needleLength = computed(() => {
|
|
1601
|
+
return this.type() === 'semi-circle' ? 30 : 26;
|
|
1602
|
+
}, /* @ts-ignore */
|
|
1603
|
+
...(ngDevMode ? [{ debugName: "needleLength" }] : /* istanbul ignore next */ []));
|
|
1604
|
+
hostWidth = computed(() => {
|
|
1605
|
+
const custom = this.width();
|
|
1606
|
+
if (custom !== null)
|
|
1607
|
+
return typeof custom === 'number' ? `${custom}px` : custom;
|
|
1608
|
+
if (this.size() === 'responsive')
|
|
1609
|
+
return '100%';
|
|
1610
|
+
return null;
|
|
1611
|
+
}, /* @ts-ignore */
|
|
1612
|
+
...(ngDevMode ? [{ debugName: "hostWidth" }] : /* istanbul ignore next */ []));
|
|
1613
|
+
hostHeight = computed(() => {
|
|
1614
|
+
const custom = this.height();
|
|
1615
|
+
if (custom !== null)
|
|
1616
|
+
return typeof custom === 'number' ? `${custom}px` : custom;
|
|
1617
|
+
return null;
|
|
1618
|
+
}, /* @ts-ignore */
|
|
1619
|
+
...(ngDevMode ? [{ debugName: "hostHeight" }] : /* istanbul ignore next */ []));
|
|
1620
|
+
/** Clamped and normalized ratio [0..1] */
|
|
1621
|
+
normalizedRatio = computed(() => {
|
|
1622
|
+
const val = this.value();
|
|
1623
|
+
const minVal = this.min();
|
|
1624
|
+
const maxVal = this.max();
|
|
1625
|
+
if (maxVal <= minVal)
|
|
1626
|
+
return 0;
|
|
1627
|
+
return Math.min(1, Math.max(0, (val - minVal) / (maxVal - minVal)));
|
|
1628
|
+
}, /* @ts-ignore */
|
|
1629
|
+
...(ngDevMode ? [{ debugName: "normalizedRatio" }] : /* istanbul ignore next */ []));
|
|
1630
|
+
/** Target ratio [0..1] */
|
|
1631
|
+
targetRatio = computed(() => {
|
|
1632
|
+
const tgt = this.target();
|
|
1633
|
+
if (tgt === null)
|
|
1634
|
+
return null;
|
|
1635
|
+
const minVal = this.min();
|
|
1636
|
+
const maxVal = this.max();
|
|
1637
|
+
if (maxVal <= minVal)
|
|
1638
|
+
return 0;
|
|
1639
|
+
return Math.min(1, Math.max(0, (tgt - minVal) / (maxVal - minVal)));
|
|
1640
|
+
}, /* @ts-ignore */
|
|
1641
|
+
...(ngDevMode ? [{ debugName: "targetRatio" }] : /* istanbul ignore next */ []));
|
|
1642
|
+
/** ViewBox string based on gauge type */
|
|
1643
|
+
viewBox = computed(() => {
|
|
1644
|
+
const t = this.type();
|
|
1645
|
+
if (t === 'semi-circle')
|
|
1646
|
+
return '0 0 100 50';
|
|
1647
|
+
if (t === 'arch')
|
|
1648
|
+
return '0 0 100 66';
|
|
1649
|
+
return '0 0 100 14';
|
|
1650
|
+
}, /* @ts-ignore */
|
|
1651
|
+
...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
|
|
1652
|
+
/** Start & End angles in degrees for circular gauge types */
|
|
1653
|
+
angleSpan = computed(() => {
|
|
1654
|
+
const t = this.type();
|
|
1655
|
+
if (t === 'semi-circle') {
|
|
1656
|
+
return { start: 180, end: 0, total: 180 };
|
|
1657
|
+
}
|
|
1658
|
+
// Arch: 240 degree arc from 210° (bottom-left) to -30° (bottom-right)
|
|
1659
|
+
return { start: 210, end: -30, total: 240 };
|
|
1660
|
+
}, /* @ts-ignore */
|
|
1661
|
+
...(ngDevMode ? [{ debugName: "angleSpan" }] : /* istanbul ignore next */ []));
|
|
1662
|
+
/** Current value angle in degrees */
|
|
1663
|
+
currentAngle = computed(() => {
|
|
1664
|
+
const { start, total } = this.angleSpan();
|
|
1665
|
+
const ratio = this.normalizedRatio();
|
|
1666
|
+
return start - ratio * total;
|
|
1667
|
+
}, /* @ts-ignore */
|
|
1668
|
+
...(ngDevMode ? [{ debugName: "currentAngle" }] : /* istanbul ignore next */ []));
|
|
1669
|
+
/** Needle transform for needle indicator */
|
|
1670
|
+
needleTransform = computed(() => {
|
|
1671
|
+
const t = this.type();
|
|
1672
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1673
|
+
const angle = this.currentAngle();
|
|
1674
|
+
// SVG rotation: angle 0 is right, 180 is left. Standard SVG rotate origin is center.
|
|
1675
|
+
const svgRotation = 180 - angle;
|
|
1676
|
+
return `rotate(${svgRotation} ${this.cx} ${cy})`;
|
|
1677
|
+
}, /* @ts-ignore */
|
|
1678
|
+
...(ngDevMode ? [{ debugName: "needleTransform" }] : /* istanbul ignore next */ []));
|
|
1679
|
+
/** Target marker position & rotation */
|
|
1680
|
+
targetMarkerProps = computed(() => {
|
|
1681
|
+
const ratio = this.targetRatio();
|
|
1682
|
+
if (ratio === null)
|
|
1683
|
+
return null;
|
|
1684
|
+
const t = this.type();
|
|
1685
|
+
if (t === 'linear') {
|
|
1686
|
+
return { x: ratio * 100, y: 0, rotation: 0 };
|
|
1687
|
+
}
|
|
1688
|
+
const { start, total } = this.angleSpan();
|
|
1689
|
+
const angle = start - ratio * total;
|
|
1690
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1691
|
+
const rad = (angle * Math.PI) / 180;
|
|
1692
|
+
const r = this.radius;
|
|
1693
|
+
const x = this.cx + r * Math.cos(rad);
|
|
1694
|
+
const y = cy - r * Math.sin(rad);
|
|
1695
|
+
return { x, y, angle };
|
|
1696
|
+
}, /* @ts-ignore */
|
|
1697
|
+
...(ngDevMode ? [{ debugName: "targetMarkerProps" }] : /* istanbul ignore next */ []));
|
|
1698
|
+
/** Dot transform for dot indicator (rotates along the circular arc path) */
|
|
1699
|
+
dotTransform = computed(() => {
|
|
1700
|
+
const t = this.type();
|
|
1701
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1702
|
+
const angle = this.currentAngle();
|
|
1703
|
+
// SVG rotation: angle 0 is right, 180 is left. Standard SVG rotate origin is center.
|
|
1704
|
+
const svgRotation = 180 - angle;
|
|
1705
|
+
return `rotate(${svgRotation} ${this.cx} ${cy})`;
|
|
1706
|
+
}, /* @ts-ignore */
|
|
1707
|
+
...(ngDevMode ? [{ debugName: "dotTransform" }] : /* istanbul ignore next */ []));
|
|
1708
|
+
/** Dot indicator position */
|
|
1709
|
+
dotIndicatorPosition = computed(() => {
|
|
1710
|
+
const t = this.type();
|
|
1711
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1712
|
+
const angle = this.currentAngle();
|
|
1713
|
+
const rad = (angle * Math.PI) / 180;
|
|
1714
|
+
const r = this.radius;
|
|
1715
|
+
const x = this.cx + r * Math.cos(rad);
|
|
1716
|
+
const y = cy - r * Math.sin(rad);
|
|
1717
|
+
return { x, y };
|
|
1718
|
+
}, /* @ts-ignore */
|
|
1719
|
+
...(ngDevMode ? [{ debugName: "dotIndicatorPosition" }] : /* istanbul ignore next */ []));
|
|
1720
|
+
/** Base track SVG path */
|
|
1721
|
+
trackPath = computed(() => {
|
|
1722
|
+
const t = this.type();
|
|
1723
|
+
if (t === 'linear')
|
|
1724
|
+
return '';
|
|
1725
|
+
const { start, end } = this.angleSpan();
|
|
1726
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1727
|
+
return this.createArcSvgPath(this.cx, cy, this.radius, start, end);
|
|
1728
|
+
}, /* @ts-ignore */
|
|
1729
|
+
...(ngDevMode ? [{ debugName: "trackPath" }] : /* istanbul ignore next */ []));
|
|
1730
|
+
/** Active value arc SVG path */
|
|
1731
|
+
valueArcPath = computed(() => {
|
|
1732
|
+
const t = this.type();
|
|
1733
|
+
if (t === 'linear')
|
|
1734
|
+
return '';
|
|
1735
|
+
const { start } = this.angleSpan();
|
|
1736
|
+
const curAngle = this.currentAngle();
|
|
1737
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1738
|
+
// Minimum small arc to prevent SVG zero-length render glitches
|
|
1739
|
+
const safeEndAngle = Math.abs(curAngle - start) < 0.1 ? start - 0.1 : curAngle;
|
|
1740
|
+
return this.createArcSvgPath(this.cx, cy, this.radius, start, safeEndAngle);
|
|
1741
|
+
}, /* @ts-ignore */
|
|
1742
|
+
...(ngDevMode ? [{ debugName: "valueArcPath" }] : /* istanbul ignore next */ []));
|
|
1743
|
+
/** Active color based on thresholds or color input */
|
|
1744
|
+
activeColor = computed(() => {
|
|
1745
|
+
const val = this.value();
|
|
1746
|
+
const ths = this.thresholds();
|
|
1747
|
+
if (ths && ths.length > 0) {
|
|
1748
|
+
for (const th of ths) {
|
|
1749
|
+
if (val >= th.from && val <= th.to) {
|
|
1750
|
+
return PRESET_COLORS[th.color] || th.color;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return PRESET_COLORS[ths[ths.length - 1].color] || ths[ths.length - 1].color;
|
|
1754
|
+
}
|
|
1755
|
+
const c = this.color();
|
|
1756
|
+
return PRESET_COLORS[c] || c;
|
|
1757
|
+
}, /* @ts-ignore */
|
|
1758
|
+
...(ngDevMode ? [{ debugName: "activeColor" }] : /* istanbul ignore next */ []));
|
|
1759
|
+
/** Threshold band segments */
|
|
1760
|
+
thresholdSegments = computed(() => {
|
|
1761
|
+
const ths = this.thresholds();
|
|
1762
|
+
if (!ths || ths.length === 0)
|
|
1763
|
+
return [];
|
|
1764
|
+
const t = this.type();
|
|
1765
|
+
const minVal = this.min();
|
|
1766
|
+
const maxVal = this.max();
|
|
1767
|
+
const span = maxVal - minVal;
|
|
1768
|
+
if (span <= 0)
|
|
1769
|
+
return [];
|
|
1770
|
+
const { start, total } = this.angleSpan();
|
|
1771
|
+
const cy = t === 'semi-circle' ? this.cySemi : this.cyArch;
|
|
1772
|
+
return ths.map((th, idx) => {
|
|
1773
|
+
const fromRatio = Math.min(1, Math.max(0, (th.from - minVal) / span));
|
|
1774
|
+
const toRatio = Math.min(1, Math.max(0, (th.to - minVal) / span));
|
|
1775
|
+
const angleStart = start - fromRatio * total;
|
|
1776
|
+
const angleEnd = start - toRatio * total;
|
|
1777
|
+
const path = t !== 'linear' ? this.createArcSvgPath(this.cx, cy, this.radius, angleStart, angleEnd) : '';
|
|
1778
|
+
const color = PRESET_COLORS[th.color] || th.color;
|
|
1779
|
+
return {
|
|
1780
|
+
path,
|
|
1781
|
+
color,
|
|
1782
|
+
from: th.from,
|
|
1783
|
+
to: th.to,
|
|
1784
|
+
isFirst: idx === 0,
|
|
1785
|
+
isLast: idx === ths.length - 1
|
|
1786
|
+
};
|
|
1787
|
+
});
|
|
1788
|
+
}, /* @ts-ignore */
|
|
1789
|
+
...(ngDevMode ? [{ debugName: "thresholdSegments" }] : /* istanbul ignore next */ []));
|
|
1790
|
+
computedAriaLabel = computed(() => {
|
|
1791
|
+
const custom = this.ariaLabel();
|
|
1792
|
+
if (custom)
|
|
1793
|
+
return custom;
|
|
1794
|
+
const u = this.unit() ? ` ${this.unit()}` : '';
|
|
1795
|
+
return `Gauge: ${this.valueFormatter()(this.value())}${u} (Range ${this.min()} to ${this.max()})`;
|
|
1796
|
+
}, /* @ts-ignore */
|
|
1797
|
+
...(ngDevMode ? [{ debugName: "computedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
1798
|
+
/** Helper to generate SVG Arc Path 'M x1 y1 A r r 0 largeArc sweep x2 y2' */
|
|
1799
|
+
createArcSvgPath(cx, cy, r, startAngleDeg, endAngleDeg) {
|
|
1800
|
+
const startRad = (startAngleDeg * Math.PI) / 180;
|
|
1801
|
+
const endRad = (endAngleDeg * Math.PI) / 180;
|
|
1802
|
+
const x1 = cx + r * Math.cos(startRad);
|
|
1803
|
+
const y1 = cy - r * Math.sin(startRad);
|
|
1804
|
+
const x2 = cx + r * Math.cos(endRad);
|
|
1805
|
+
const y2 = cy - r * Math.sin(endRad);
|
|
1806
|
+
// Delta angle in degrees clockwise
|
|
1807
|
+
const deltaAngle = startAngleDeg - endAngleDeg;
|
|
1808
|
+
const largeArcFlag = deltaAngle > 180 ? 1 : 0;
|
|
1809
|
+
const sweepFlag = 1; // clockwise downwards
|
|
1810
|
+
return `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${r} ${r} 0 ${largeArcFlag} ${sweepFlag} ${x2.toFixed(2)} ${y2.toFixed(2)}`;
|
|
1811
|
+
}
|
|
1812
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1813
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosMiniGaugeComponent, isStandalone: true, selector: "talos-mini-gauge", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, indicator: { classPropertyName: "indicator", publicName: "indicator", isSignal: true, isRequired: false, transformFunction: null }, thresholds: { classPropertyName: "thresholds", publicName: "thresholds", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, trackColor: { classPropertyName: "trackColor", publicName: "trackColor", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, lineCap: { classPropertyName: "lineCap", publicName: "lineCap", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, showMinMax: { classPropertyName: "showMinMax", publicName: "showMinMax", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, valueFormatter: { classPropertyName: "valueFormatter", publicName: "valueFormatter", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.talos-gauge-semi-circle": "type() === \"semi-circle\"", "class.talos-gauge-arch": "type() === \"arch\"", "class.talos-gauge-linear": "type() === \"linear\"", "class.talos-gauge-xs": "size() === \"xs\"", "class.talos-gauge-sm": "size() === \"sm\"", "class.talos-gauge-md": "size() === \"md\"", "class.talos-gauge-lg": "size() === \"lg\"", "class.talos-gauge-xl": "size() === \"xl\"", "class.talos-gauge-responsive": "size() === \"responsive\"", "class.talos-gauge-animated": "animated()", "style.width": "hostWidth()", "style.height": "hostHeight()", "attr.role": "\"meter\"", "attr.aria-valuenow": "value()", "attr.aria-valuemin": "min()", "attr.aria-valuemax": "max()", "attr.aria-label": "computedAriaLabel()" }, classAttribute: "talos-mini-gauge" }, ngImport: i0, template: "<svg\n class=\"talos-mini-gauge-svg\"\n [attr.viewBox]=\"viewBox()\"\n aria-hidden=\"true\"\n>\n <!-- Circular Types: Semi-circle and Arch -->\n @if (type() === 'semi-circle' || type() === 'arch') {\n <!-- Full Background Track (if no thresholds or as base) -->\n @if (thresholds() === null) {\n <path\n class=\"talos-gauge-track\"\n [attr.d]=\"trackPath()\"\n [attr.stroke]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap()\"\n />\n }\n\n <!-- Colored Threshold Bands with curved outer ends -->\n @if (thresholds() !== null) {\n @for (band of thresholdSegments(); track band.from) {\n <path\n class=\"talos-gauge-threshold-band\"\n [attr.d]=\"band.path\"\n [attr.stroke]=\"band.color\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap() === 'round' && (band.isFirst || band.isLast) ? 'round' : 'butt'\"\n />\n }\n }\n\n <!-- Value Indicator: Arc Fill -->\n @if (indicator() === 'arc' && thresholds() === null) {\n <path\n class=\"talos-gauge-value-arc\"\n [attr.d]=\"valueArcPath()\"\n [attr.stroke]=\"activeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap()\"\n />\n }\n\n <!-- Value Indicator: Needle -->\n @if (indicator() === 'needle') {\n <g\n class=\"talos-gauge-needle-group\"\n [attr.transform]=\"needleTransform()\"\n >\n <line\n [attr.x1]=\"cx\"\n [attr.y1]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n [attr.x2]=\"cx - needleLength()\"\n [attr.y2]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n [attr.stroke]=\"activeColor()\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <circle\n [attr.cx]=\"cx\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"4\"\n [attr.fill]=\"activeColor()\"\n stroke=\"#ffffff\"\n stroke-width=\"1.5\"\n />\n <circle\n [attr.cx]=\"cx\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"1.5\"\n fill=\"#ffffff\"\n />\n </g>\n }\n\n <!-- Value Indicator: Dot Glow Marker -->\n @if (indicator() === 'dot') {\n <g\n class=\"talos-gauge-dot-group\"\n [attr.transform]=\"dotTransform()\"\n >\n <circle\n class=\"talos-gauge-dot-outer\"\n [attr.cx]=\"cx - radius\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"6\"\n [attr.fill]=\"activeColor()\"\n opacity=\"0.3\"\n />\n <circle\n class=\"talos-gauge-dot\"\n [attr.cx]=\"cx - radius\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"3.5\"\n [attr.fill]=\"activeColor()\"\n stroke=\"#ffffff\"\n stroke-width=\"1.5\"\n />\n </g>\n }\n\n <!-- Target Marker Tick -->\n @if (targetMarkerProps(); as tgt) {\n <circle\n class=\"talos-gauge-target-tick\"\n [attr.cx]=\"tgt.x\"\n [attr.cy]=\"tgt.y\"\n r=\"2.5\"\n fill=\"var(--talos-text-color, #0f172a)\"\n stroke=\"#ffffff\"\n stroke-width=\"1\"\n />\n }\n }\n\n <!-- Linear / Bullet Type -->\n @if (type() === 'linear') {\n <!-- Linear Track -->\n <rect\n class=\"talos-gauge-linear-track\"\n x=\"0\"\n y=\"2\"\n width=\"100\"\n height=\"8\"\n rx=\"4\"\n ry=\"4\"\n [attr.fill]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n />\n\n <!-- Linear Value Fill -->\n <rect\n class=\"talos-gauge-linear-fill\"\n x=\"0\"\n y=\"2\"\n [attr.width]=\"normalizedRatio() * 100\"\n height=\"8\"\n rx=\"4\"\n ry=\"4\"\n [attr.fill]=\"activeColor()\"\n />\n\n <!-- Linear Target Marker -->\n @if (targetRatio() !== null) {\n <line\n class=\"talos-gauge-linear-target\"\n [attr.x1]=\"targetRatio()! * 100\"\n y1=\"0\"\n [attr.x2]=\"targetRatio()! * 100\"\n y2=\"12\"\n stroke=\"var(--talos-text-color, #0f172a)\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n }\n }\n</svg>\n\n<!-- Center / Bottom Readout -->\n@if (showValue()) {\n <div class=\"talos-gauge-readout\">\n <span class=\"talos-gauge-value\">{{ valueFormatter()(value()) }}</span>\n @if (unit()) {\n <span class=\"talos-gauge-unit\">{{ unit() }}</span>\n }\n </div>\n}\n\n<!-- Min / Max labels -->\n@if (showMinMax()) {\n <div class=\"talos-gauge-minmax\">\n <span class=\"talos-gauge-min\">{{ min() }}</span>\n <span class=\"talos-gauge-max\">{{ max() }}</span>\n </div>\n}\n", styles: [":host{position:relative;display:inline-flex;flex-direction:column;align-items:center;justify-content:flex-start;vertical-align:middle;box-sizing:border-box;-webkit-user-select:none;user-select:none;height:auto;container-type:inline-size}:host.talos-gauge-xs{width:56px}:host.talos-gauge-xs.talos-gauge-linear{width:90px}:host.talos-gauge-sm{width:72px}:host.talos-gauge-sm.talos-gauge-linear{width:110px}:host.talos-gauge-md{width:96px}:host.talos-gauge-md.talos-gauge-linear{width:140px}:host.talos-gauge-lg{width:128px}:host.talos-gauge-lg.talos-gauge-linear{width:180px}:host.talos-gauge-xl{width:160px}:host.talos-gauge-xl.talos-gauge-linear{width:220px}:host.talos-gauge-responsive{width:100%;display:flex}:host .talos-mini-gauge-svg{width:100%;height:auto;display:block;flex-shrink:0;overflow:visible}:host .talos-gauge-track{opacity:.6}:host .talos-gauge-value-arc{transition:d .3s cubic-bezier(.4,0,.2,1),stroke .2s ease}:host .talos-gauge-needle-group{transition:transform .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-dot-group{transition:transform .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-dot,:host .talos-gauge-dot-outer{transition:fill .2s ease}:host .talos-gauge-linear-fill{transition:width .3s cubic-bezier(.4,0,.2,1),fill .2s ease}:host .talos-gauge-linear-target{filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-target-tick{filter:drop-shadow(0 1px 2px rgba(0,0,0,.3))}:host .talos-gauge-readout{position:relative;display:flex;align-items:baseline;justify-content:center;gap:1.5px;line-height:1.1;text-align:center;font-weight:700;color:var(--talos-text-color, #0f172a);font-variant-numeric:tabular-nums;letter-spacing:-.02em;width:100%;margin-top:4px}:host .talos-gauge-readout .talos-gauge-value{font-size:clamp(.75rem,16cqi,3.5rem)}:host .talos-gauge-readout .talos-gauge-unit{font-size:clamp(.5625rem,10.5cqi,2rem);font-weight:500;color:var(--talos-text-muted, #64748b);margin-left:1px}:host.talos-gauge-semi-circle .talos-gauge-readout{margin-top:4px}:host.talos-gauge-arch .talos-gauge-readout{margin-top:2px}:host.talos-gauge-linear .talos-gauge-readout{margin-top:6px}:host.talos-gauge-linear .talos-gauge-readout .talos-gauge-value{font-size:clamp(.75rem,9cqi,1.75rem)}:host.talos-gauge-linear .talos-gauge-readout .talos-gauge-unit{font-size:clamp(.5625rem,6.5cqi,1.25rem)}:host .talos-gauge-minmax{width:100%;display:flex;justify-content:space-between;font-size:clamp(.5rem,8cqi,1rem);color:var(--talos-text-muted, #64748b);font-weight:500;margin-top:2px;padding:0 4px;box-sizing:border-box}:host:not(.talos-gauge-animated) .talos-gauge-value-arc,:host:not(.talos-gauge-animated) .talos-gauge-needle-group,:host:not(.talos-gauge-animated) .talos-gauge-dot-group,:host:not(.talos-gauge-animated) .talos-gauge-linear-fill{transition:none!important}:root[data-theme=dark] :host .talos-gauge-track,:root[data-theme=dark] :host .talos-gauge-linear-track,:root.dark :host .talos-gauge-track,:root.dark :host .talos-gauge-linear-track,[data-theme=dark] :host .talos-gauge-track,[data-theme=dark] :host .talos-gauge-linear-track,[data-mode=dark] :host .talos-gauge-track,[data-mode=dark] :host .talos-gauge-linear-track,.dark :host .talos-gauge-track,.dark :host .talos-gauge-linear-track,.dark-theme :host .talos-gauge-track,.dark-theme :host .talos-gauge-linear-track{stroke:var(--talos-border-color, #334155);fill:var(--talos-border-color, #334155)}:root[data-theme=dark] :host .talos-gauge-readout,:root.dark :host .talos-gauge-readout,[data-theme=dark] :host .talos-gauge-readout,[data-mode=dark] :host .talos-gauge-readout,.dark :host .talos-gauge-readout,.dark-theme :host .talos-gauge-readout{color:var(--talos-text-color, #f8fafc)}:root[data-theme=dark] :host .talos-gauge-readout .talos-gauge-unit,:root.dark :host .talos-gauge-readout .talos-gauge-unit,[data-theme=dark] :host .talos-gauge-readout .talos-gauge-unit,[data-mode=dark] :host .talos-gauge-readout .talos-gauge-unit,.dark :host .talos-gauge-readout .talos-gauge-unit,.dark-theme :host .talos-gauge-readout .talos-gauge-unit{color:var(--talos-text-muted, #94a3b8)}:root[data-theme=dark] :host .talos-gauge-minmax,:root.dark :host .talos-gauge-minmax,[data-theme=dark] :host .talos-gauge-minmax,[data-mode=dark] :host .talos-gauge-minmax,.dark :host .talos-gauge-minmax,.dark-theme :host .talos-gauge-minmax{color:var(--talos-text-muted, #94a3b8)}:root[data-theme=dark] :host .talos-gauge-target-tick,:root.dark :host .talos-gauge-target-tick,[data-theme=dark] :host .talos-gauge-target-tick,[data-mode=dark] :host .talos-gauge-target-tick,.dark :host .talos-gauge-target-tick,.dark-theme :host .talos-gauge-target-tick{fill:#fff;stroke:#0f172a}:root[data-theme=dark] :host .talos-gauge-linear-target,:root.dark :host .talos-gauge-linear-target,[data-theme=dark] :host .talos-gauge-linear-target,[data-mode=dark] :host .talos-gauge-linear-target,.dark :host .talos-gauge-linear-target,.dark-theme :host .talos-gauge-linear-target{stroke:#fff}\n"] });
|
|
1814
|
+
}
|
|
1815
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeComponent, decorators: [{
|
|
1816
|
+
type: Component,
|
|
1817
|
+
args: [{ selector: 'talos-mini-gauge', host: {
|
|
1818
|
+
'class': 'talos-mini-gauge',
|
|
1819
|
+
'[class.talos-gauge-semi-circle]': 'type() === "semi-circle"',
|
|
1820
|
+
'[class.talos-gauge-arch]': 'type() === "arch"',
|
|
1821
|
+
'[class.talos-gauge-linear]': 'type() === "linear"',
|
|
1822
|
+
'[class.talos-gauge-xs]': 'size() === "xs"',
|
|
1823
|
+
'[class.talos-gauge-sm]': 'size() === "sm"',
|
|
1824
|
+
'[class.talos-gauge-md]': 'size() === "md"',
|
|
1825
|
+
'[class.talos-gauge-lg]': 'size() === "lg"',
|
|
1826
|
+
'[class.talos-gauge-xl]': 'size() === "xl"',
|
|
1827
|
+
'[class.talos-gauge-responsive]': 'size() === "responsive"',
|
|
1828
|
+
'[class.talos-gauge-animated]': 'animated()',
|
|
1829
|
+
'[style.width]': 'hostWidth()',
|
|
1830
|
+
'[style.height]': 'hostHeight()',
|
|
1831
|
+
'[attr.role]': '"meter"',
|
|
1832
|
+
'[attr.aria-valuenow]': 'value()',
|
|
1833
|
+
'[attr.aria-valuemin]': 'min()',
|
|
1834
|
+
'[attr.aria-valuemax]': 'max()',
|
|
1835
|
+
'[attr.aria-label]': 'computedAriaLabel()'
|
|
1836
|
+
}, template: "<svg\n class=\"talos-mini-gauge-svg\"\n [attr.viewBox]=\"viewBox()\"\n aria-hidden=\"true\"\n>\n <!-- Circular Types: Semi-circle and Arch -->\n @if (type() === 'semi-circle' || type() === 'arch') {\n <!-- Full Background Track (if no thresholds or as base) -->\n @if (thresholds() === null) {\n <path\n class=\"talos-gauge-track\"\n [attr.d]=\"trackPath()\"\n [attr.stroke]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap()\"\n />\n }\n\n <!-- Colored Threshold Bands with curved outer ends -->\n @if (thresholds() !== null) {\n @for (band of thresholdSegments(); track band.from) {\n <path\n class=\"talos-gauge-threshold-band\"\n [attr.d]=\"band.path\"\n [attr.stroke]=\"band.color\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap() === 'round' && (band.isFirst || band.isLast) ? 'round' : 'butt'\"\n />\n }\n }\n\n <!-- Value Indicator: Arc Fill -->\n @if (indicator() === 'arc' && thresholds() === null) {\n <path\n class=\"talos-gauge-value-arc\"\n [attr.d]=\"valueArcPath()\"\n [attr.stroke]=\"activeColor()\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n [attr.stroke-linecap]=\"lineCap()\"\n />\n }\n\n <!-- Value Indicator: Needle -->\n @if (indicator() === 'needle') {\n <g\n class=\"talos-gauge-needle-group\"\n [attr.transform]=\"needleTransform()\"\n >\n <line\n [attr.x1]=\"cx\"\n [attr.y1]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n [attr.x2]=\"cx - needleLength()\"\n [attr.y2]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n [attr.stroke]=\"activeColor()\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n <circle\n [attr.cx]=\"cx\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"4\"\n [attr.fill]=\"activeColor()\"\n stroke=\"#ffffff\"\n stroke-width=\"1.5\"\n />\n <circle\n [attr.cx]=\"cx\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"1.5\"\n fill=\"#ffffff\"\n />\n </g>\n }\n\n <!-- Value Indicator: Dot Glow Marker -->\n @if (indicator() === 'dot') {\n <g\n class=\"talos-gauge-dot-group\"\n [attr.transform]=\"dotTransform()\"\n >\n <circle\n class=\"talos-gauge-dot-outer\"\n [attr.cx]=\"cx - radius\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"6\"\n [attr.fill]=\"activeColor()\"\n opacity=\"0.3\"\n />\n <circle\n class=\"talos-gauge-dot\"\n [attr.cx]=\"cx - radius\"\n [attr.cy]=\"type() === 'semi-circle' ? cySemi : cyArch\"\n r=\"3.5\"\n [attr.fill]=\"activeColor()\"\n stroke=\"#ffffff\"\n stroke-width=\"1.5\"\n />\n </g>\n }\n\n <!-- Target Marker Tick -->\n @if (targetMarkerProps(); as tgt) {\n <circle\n class=\"talos-gauge-target-tick\"\n [attr.cx]=\"tgt.x\"\n [attr.cy]=\"tgt.y\"\n r=\"2.5\"\n fill=\"var(--talos-text-color, #0f172a)\"\n stroke=\"#ffffff\"\n stroke-width=\"1\"\n />\n }\n }\n\n <!-- Linear / Bullet Type -->\n @if (type() === 'linear') {\n <!-- Linear Track -->\n <rect\n class=\"talos-gauge-linear-track\"\n x=\"0\"\n y=\"2\"\n width=\"100\"\n height=\"8\"\n rx=\"4\"\n ry=\"4\"\n [attr.fill]=\"trackColor() || 'var(--talos-border-color, #e2e8f0)'\"\n />\n\n <!-- Linear Value Fill -->\n <rect\n class=\"talos-gauge-linear-fill\"\n x=\"0\"\n y=\"2\"\n [attr.width]=\"normalizedRatio() * 100\"\n height=\"8\"\n rx=\"4\"\n ry=\"4\"\n [attr.fill]=\"activeColor()\"\n />\n\n <!-- Linear Target Marker -->\n @if (targetRatio() !== null) {\n <line\n class=\"talos-gauge-linear-target\"\n [attr.x1]=\"targetRatio()! * 100\"\n y1=\"0\"\n [attr.x2]=\"targetRatio()! * 100\"\n y2=\"12\"\n stroke=\"var(--talos-text-color, #0f172a)\"\n stroke-width=\"2.5\"\n stroke-linecap=\"round\"\n />\n }\n }\n</svg>\n\n<!-- Center / Bottom Readout -->\n@if (showValue()) {\n <div class=\"talos-gauge-readout\">\n <span class=\"talos-gauge-value\">{{ valueFormatter()(value()) }}</span>\n @if (unit()) {\n <span class=\"talos-gauge-unit\">{{ unit() }}</span>\n }\n </div>\n}\n\n<!-- Min / Max labels -->\n@if (showMinMax()) {\n <div class=\"talos-gauge-minmax\">\n <span class=\"talos-gauge-min\">{{ min() }}</span>\n <span class=\"talos-gauge-max\">{{ max() }}</span>\n </div>\n}\n", styles: [":host{position:relative;display:inline-flex;flex-direction:column;align-items:center;justify-content:flex-start;vertical-align:middle;box-sizing:border-box;-webkit-user-select:none;user-select:none;height:auto;container-type:inline-size}:host.talos-gauge-xs{width:56px}:host.talos-gauge-xs.talos-gauge-linear{width:90px}:host.talos-gauge-sm{width:72px}:host.talos-gauge-sm.talos-gauge-linear{width:110px}:host.talos-gauge-md{width:96px}:host.talos-gauge-md.talos-gauge-linear{width:140px}:host.talos-gauge-lg{width:128px}:host.talos-gauge-lg.talos-gauge-linear{width:180px}:host.talos-gauge-xl{width:160px}:host.talos-gauge-xl.talos-gauge-linear{width:220px}:host.talos-gauge-responsive{width:100%;display:flex}:host .talos-mini-gauge-svg{width:100%;height:auto;display:block;flex-shrink:0;overflow:visible}:host .talos-gauge-track{opacity:.6}:host .talos-gauge-value-arc{transition:d .3s cubic-bezier(.4,0,.2,1),stroke .2s ease}:host .talos-gauge-needle-group{transition:transform .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-dot-group{transition:transform .35s cubic-bezier(.34,1.56,.64,1);filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-dot,:host .talos-gauge-dot-outer{transition:fill .2s ease}:host .talos-gauge-linear-fill{transition:width .3s cubic-bezier(.4,0,.2,1),fill .2s ease}:host .talos-gauge-linear-target{filter:drop-shadow(0 1px 2px rgba(0,0,0,.25))}:host .talos-gauge-target-tick{filter:drop-shadow(0 1px 2px rgba(0,0,0,.3))}:host .talos-gauge-readout{position:relative;display:flex;align-items:baseline;justify-content:center;gap:1.5px;line-height:1.1;text-align:center;font-weight:700;color:var(--talos-text-color, #0f172a);font-variant-numeric:tabular-nums;letter-spacing:-.02em;width:100%;margin-top:4px}:host .talos-gauge-readout .talos-gauge-value{font-size:clamp(.75rem,16cqi,3.5rem)}:host .talos-gauge-readout .talos-gauge-unit{font-size:clamp(.5625rem,10.5cqi,2rem);font-weight:500;color:var(--talos-text-muted, #64748b);margin-left:1px}:host.talos-gauge-semi-circle .talos-gauge-readout{margin-top:4px}:host.talos-gauge-arch .talos-gauge-readout{margin-top:2px}:host.talos-gauge-linear .talos-gauge-readout{margin-top:6px}:host.talos-gauge-linear .talos-gauge-readout .talos-gauge-value{font-size:clamp(.75rem,9cqi,1.75rem)}:host.talos-gauge-linear .talos-gauge-readout .talos-gauge-unit{font-size:clamp(.5625rem,6.5cqi,1.25rem)}:host .talos-gauge-minmax{width:100%;display:flex;justify-content:space-between;font-size:clamp(.5rem,8cqi,1rem);color:var(--talos-text-muted, #64748b);font-weight:500;margin-top:2px;padding:0 4px;box-sizing:border-box}:host:not(.talos-gauge-animated) .talos-gauge-value-arc,:host:not(.talos-gauge-animated) .talos-gauge-needle-group,:host:not(.talos-gauge-animated) .talos-gauge-dot-group,:host:not(.talos-gauge-animated) .talos-gauge-linear-fill{transition:none!important}:root[data-theme=dark] :host .talos-gauge-track,:root[data-theme=dark] :host .talos-gauge-linear-track,:root.dark :host .talos-gauge-track,:root.dark :host .talos-gauge-linear-track,[data-theme=dark] :host .talos-gauge-track,[data-theme=dark] :host .talos-gauge-linear-track,[data-mode=dark] :host .talos-gauge-track,[data-mode=dark] :host .talos-gauge-linear-track,.dark :host .talos-gauge-track,.dark :host .talos-gauge-linear-track,.dark-theme :host .talos-gauge-track,.dark-theme :host .talos-gauge-linear-track{stroke:var(--talos-border-color, #334155);fill:var(--talos-border-color, #334155)}:root[data-theme=dark] :host .talos-gauge-readout,:root.dark :host .talos-gauge-readout,[data-theme=dark] :host .talos-gauge-readout,[data-mode=dark] :host .talos-gauge-readout,.dark :host .talos-gauge-readout,.dark-theme :host .talos-gauge-readout{color:var(--talos-text-color, #f8fafc)}:root[data-theme=dark] :host .talos-gauge-readout .talos-gauge-unit,:root.dark :host .talos-gauge-readout .talos-gauge-unit,[data-theme=dark] :host .talos-gauge-readout .talos-gauge-unit,[data-mode=dark] :host .talos-gauge-readout .talos-gauge-unit,.dark :host .talos-gauge-readout .talos-gauge-unit,.dark-theme :host .talos-gauge-readout .talos-gauge-unit{color:var(--talos-text-muted, #94a3b8)}:root[data-theme=dark] :host .talos-gauge-minmax,:root.dark :host .talos-gauge-minmax,[data-theme=dark] :host .talos-gauge-minmax,[data-mode=dark] :host .talos-gauge-minmax,.dark :host .talos-gauge-minmax,.dark-theme :host .talos-gauge-minmax{color:var(--talos-text-muted, #94a3b8)}:root[data-theme=dark] :host .talos-gauge-target-tick,:root.dark :host .talos-gauge-target-tick,[data-theme=dark] :host .talos-gauge-target-tick,[data-mode=dark] :host .talos-gauge-target-tick,.dark :host .talos-gauge-target-tick,.dark-theme :host .talos-gauge-target-tick{fill:#fff;stroke:#0f172a}:root[data-theme=dark] :host .talos-gauge-linear-target,:root.dark :host .talos-gauge-linear-target,[data-theme=dark] :host .talos-gauge-linear-target,[data-mode=dark] :host .talos-gauge-linear-target,.dark :host .talos-gauge-linear-target,.dark-theme :host .talos-gauge-linear-target{stroke:#fff}\n"] }]
|
|
1837
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], indicator: [{ type: i0.Input, args: [{ isSignal: true, alias: "indicator", required: false }] }], thresholds: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholds", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], trackColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "trackColor", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], lineCap: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineCap", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], showMinMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMinMax", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], valueFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormatter", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }] } });
|
|
1838
|
+
|
|
1839
|
+
class TalosMiniGaugeModule {
|
|
1840
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1841
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeModule, imports: [TalosMiniGaugeComponent], exports: [TalosMiniGaugeComponent] });
|
|
1842
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeModule });
|
|
1843
|
+
}
|
|
1844
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosMiniGaugeModule, decorators: [{
|
|
1845
|
+
type: NgModule,
|
|
1846
|
+
args: [{
|
|
1847
|
+
imports: [TalosMiniGaugeComponent],
|
|
1848
|
+
exports: [TalosMiniGaugeComponent]
|
|
1849
|
+
}]
|
|
1850
|
+
}] });
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* Types and interfaces for TalosSparklineComponent
|
|
1854
|
+
*/
|
|
1855
|
+
const GOLDEN_RATIO = 1.618;
|
|
1856
|
+
|
|
1857
|
+
const PRESET_PALETTES = {
|
|
1858
|
+
primary: { stroke: '#2563eb', fillStart: 'rgba(37, 99, 235, 0.35)', fillEnd: 'rgba(37, 99, 235, 0.0)' },
|
|
1859
|
+
blue: { stroke: '#3b82f6', fillStart: 'rgba(59, 130, 246, 0.35)', fillEnd: 'rgba(59, 130, 246, 0.0)' },
|
|
1860
|
+
success: { stroke: '#16a34a', fillStart: 'rgba(22, 163, 74, 0.35)', fillEnd: 'rgba(22, 163, 74, 0.0)' },
|
|
1861
|
+
emerald: { stroke: '#10b981', fillStart: 'rgba(16, 185, 129, 0.35)', fillEnd: 'rgba(16, 185, 129, 0.0)' },
|
|
1862
|
+
danger: { stroke: '#ef4444', fillStart: 'rgba(239, 68, 68, 0.35)', fillEnd: 'rgba(239, 68, 68, 0.0)' },
|
|
1863
|
+
rose: { stroke: '#f43f5e', fillStart: 'rgba(244, 63, 94, 0.35)', fillEnd: 'rgba(244, 63, 94, 0.0)' },
|
|
1864
|
+
warning: { stroke: '#f59e0b', fillStart: 'rgba(245, 158, 11, 0.35)', fillEnd: 'rgba(245, 158, 11, 0.0)' },
|
|
1865
|
+
amber: { stroke: '#d97706', fillStart: 'rgba(217, 119, 6, 0.35)', fillEnd: 'rgba(217, 119, 6, 0.0)' },
|
|
1866
|
+
info: { stroke: '#0284c7', fillStart: 'rgba(2, 132, 199, 0.35)', fillEnd: 'rgba(2, 132, 199, 0.0)' },
|
|
1867
|
+
cyan: { stroke: '#06b6d4', fillStart: 'rgba(6, 182, 212, 0.35)', fillEnd: 'rgba(6, 182, 212, 0.0)' },
|
|
1868
|
+
violet: { stroke: '#8b5cf6', fillStart: 'rgba(139, 92, 246, 0.35)', fillEnd: 'rgba(139, 92, 246, 0.0)' },
|
|
1869
|
+
slate: { stroke: '#64748b', fillStart: 'rgba(100, 116, 139, 0.3)', fillEnd: 'rgba(100, 116, 139, 0.0)' }
|
|
1870
|
+
};
|
|
1871
|
+
// Golden Ratio (~1.618:1) and proportional defaults
|
|
1872
|
+
const DEFAULT_SIZE_DIMENSIONS = {
|
|
1873
|
+
xs: { width: 64, height: 40 },
|
|
1874
|
+
sm: { width: 96, height: 60 },
|
|
1875
|
+
md: { width: 130, height: 80 },
|
|
1876
|
+
lg: { width: 180, height: 110 },
|
|
1877
|
+
responsive: { width: 240, height: 60 },
|
|
1878
|
+
custom: { width: 130, height: 80 }
|
|
1879
|
+
};
|
|
1880
|
+
let uniqueIdCounter = 0;
|
|
1881
|
+
class TalosSparklineComponent {
|
|
1882
|
+
elementRef = inject(ElementRef);
|
|
1883
|
+
resizeObserver;
|
|
1884
|
+
/** Data points (numbers or objects) */
|
|
1885
|
+
data = input([], /* @ts-ignore */
|
|
1886
|
+
...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
1887
|
+
/** Chart type: line, area, or bar */
|
|
1888
|
+
type = input('area', /* @ts-ignore */
|
|
1889
|
+
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
1890
|
+
/** Visual color theme or custom hex code */
|
|
1891
|
+
color = input('primary', /* @ts-ignore */
|
|
1892
|
+
...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
1893
|
+
/** Automatically choose green/red based on first -> last delta */
|
|
1894
|
+
autoColorTrend = input(false, /* @ts-ignore */
|
|
1895
|
+
...(ngDevMode ? [{ debugName: "autoColorTrend" }] : /* istanbul ignore next */ []));
|
|
1896
|
+
/** Smooth Bezier interpolation vs straight line */
|
|
1897
|
+
smooth = input(true, /* @ts-ignore */
|
|
1898
|
+
...(ngDevMode ? [{ debugName: "smooth" }] : /* istanbul ignore next */ []));
|
|
1899
|
+
/** Stroke line width */
|
|
1900
|
+
strokeWidth = input(2, /* @ts-ignore */
|
|
1901
|
+
...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
|
|
1902
|
+
/** Marker dots display mode */
|
|
1903
|
+
showMarkers = input('none', /* @ts-ignore */
|
|
1904
|
+
...(ngDevMode ? [{ debugName: "showMarkers" }] : /* istanbul ignore next */ []));
|
|
1905
|
+
/** Marker circle radius */
|
|
1906
|
+
markerRadius = input(3.5, /* @ts-ignore */
|
|
1907
|
+
...(ngDevMode ? [{ debugName: "markerRadius" }] : /* istanbul ignore next */ []));
|
|
1908
|
+
/** Optional reference horizontal baseline */
|
|
1909
|
+
referenceValue = input(null, /* @ts-ignore */
|
|
1910
|
+
...(ngDevMode ? [{ debugName: "referenceValue" }] : /* istanbul ignore next */ []));
|
|
1911
|
+
/** Show interactive hover tooltip */
|
|
1912
|
+
showTooltip = input(true, /* @ts-ignore */
|
|
1913
|
+
...(ngDevMode ? [{ debugName: "showTooltip" }] : /* istanbul ignore next */ []));
|
|
1914
|
+
/** Interactive mouse events enabled */
|
|
1915
|
+
interactive = input(true, /* @ts-ignore */
|
|
1916
|
+
...(ngDevMode ? [{ debugName: "interactive" }] : /* istanbul ignore next */ []));
|
|
1917
|
+
/** Gap between bars for bar sparklines */
|
|
1918
|
+
barGap = input(3, /* @ts-ignore */
|
|
1919
|
+
...(ngDevMode ? [{ debugName: "barGap" }] : /* istanbul ignore next */ []));
|
|
1920
|
+
/** Corner radius for bars */
|
|
1921
|
+
barRadius = input(2, /* @ts-ignore */
|
|
1922
|
+
...(ngDevMode ? [{ debugName: "barRadius" }] : /* istanbul ignore next */ []));
|
|
1923
|
+
/** Preset size */
|
|
1924
|
+
size = input('md', /* @ts-ignore */
|
|
1925
|
+
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
1926
|
+
/** Aspect ratio configuration (e.g. 'golden', 1.618, '2:1', '16:9') */
|
|
1927
|
+
aspectRatio = input(null, /* @ts-ignore */
|
|
1928
|
+
...(ngDevMode ? [{ debugName: "aspectRatio" }] : /* istanbul ignore next */ []));
|
|
1929
|
+
/** Custom width override (e.g. '120px' or '100%') */
|
|
1930
|
+
width = input(null, /* @ts-ignore */
|
|
1931
|
+
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1932
|
+
/** Custom height override (e.g. '36px') */
|
|
1933
|
+
height = input(null, /* @ts-ignore */
|
|
1934
|
+
...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
1935
|
+
/** Explicit minimum value bounds */
|
|
1936
|
+
min = input(null, /* @ts-ignore */
|
|
1937
|
+
...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
1938
|
+
/** Explicit maximum value bounds */
|
|
1939
|
+
max = input(null, /* @ts-ignore */
|
|
1940
|
+
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
1941
|
+
/** Custom accessible label */
|
|
1942
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
1943
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
1944
|
+
/** Value formatter for tooltip */
|
|
1945
|
+
valueFormatter = input((val) => val.toLocaleString(), /* @ts-ignore */
|
|
1946
|
+
...(ngDevMode ? [{ debugName: "valueFormatter" }] : /* istanbul ignore next */ []));
|
|
1947
|
+
/** Hover event emitter */
|
|
1948
|
+
pointHover = output();
|
|
1949
|
+
/** Click event emitter */
|
|
1950
|
+
pointClick = output();
|
|
1951
|
+
// Unique ID for SVG linear gradient IDs
|
|
1952
|
+
gradientId = `talos-sparkline-grad-${++uniqueIdCounter}`;
|
|
1953
|
+
// Interactive hover state
|
|
1954
|
+
hoveredPoint = signal(null, /* @ts-ignore */
|
|
1955
|
+
...(ngDevMode ? [{ debugName: "hoveredPoint" }] : /* istanbul ignore next */ []));
|
|
1956
|
+
isHovering = signal(false, /* @ts-ignore */
|
|
1957
|
+
...(ngDevMode ? [{ debugName: "isHovering" }] : /* istanbul ignore next */ []));
|
|
1958
|
+
// Dynamic container dimensions for 1:1 SVG coordinate rendering (prevents non-uniform stretching)
|
|
1959
|
+
containerWidth = signal(120, /* @ts-ignore */
|
|
1960
|
+
...(ngDevMode ? [{ debugName: "containerWidth" }] : /* istanbul ignore next */ []));
|
|
1961
|
+
containerHeight = signal(36, /* @ts-ignore */
|
|
1962
|
+
...(ngDevMode ? [{ debugName: "containerHeight" }] : /* istanbul ignore next */ []));
|
|
1963
|
+
svgWidth = computed(() => this.containerWidth(), /* @ts-ignore */
|
|
1964
|
+
...(ngDevMode ? [{ debugName: "svgWidth" }] : /* istanbul ignore next */ []));
|
|
1965
|
+
svgHeight = computed(() => this.containerHeight(), /* @ts-ignore */
|
|
1966
|
+
...(ngDevMode ? [{ debugName: "svgHeight" }] : /* istanbul ignore next */ []));
|
|
1967
|
+
padding = computed(() => {
|
|
1968
|
+
if (this.type() === 'bar')
|
|
1969
|
+
return 2;
|
|
1970
|
+
if (this.showMarkers() !== 'none' || this.interactive())
|
|
1971
|
+
return 5;
|
|
1972
|
+
return 3;
|
|
1973
|
+
}, /* @ts-ignore */
|
|
1974
|
+
...(ngDevMode ? [{ debugName: "padding" }] : /* istanbul ignore next */ []));
|
|
1975
|
+
constructor() {
|
|
1976
|
+
afterNextRender(() => {
|
|
1977
|
+
this.updateDimensions();
|
|
1978
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
1979
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
1980
|
+
for (const entry of entries) {
|
|
1981
|
+
const { width, height } = entry.contentRect;
|
|
1982
|
+
if (width > 0 && height > 0) {
|
|
1983
|
+
this.containerWidth.set(Math.round(width));
|
|
1984
|
+
this.containerHeight.set(Math.round(height));
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
this.resizeObserver.observe(this.elementRef.nativeElement);
|
|
1989
|
+
}
|
|
1990
|
+
});
|
|
1991
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1992
|
+
this.resizeObserver?.disconnect();
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
updateDimensions() {
|
|
1996
|
+
const el = this.elementRef.nativeElement;
|
|
1997
|
+
const rect = el.getBoundingClientRect();
|
|
1998
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
1999
|
+
this.containerWidth.set(Math.round(rect.width));
|
|
2000
|
+
this.containerHeight.set(Math.round(rect.height));
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
const defaultDims = DEFAULT_SIZE_DIMENSIONS[this.size()] || DEFAULT_SIZE_DIMENSIONS.md;
|
|
2004
|
+
this.containerWidth.set(defaultDims.width);
|
|
2005
|
+
this.containerHeight.set(defaultDims.height);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
/** Host styles */
|
|
2009
|
+
hostWidth = computed(() => {
|
|
2010
|
+
const custom = this.width();
|
|
2011
|
+
if (custom !== null)
|
|
2012
|
+
return typeof custom === 'number' ? `${custom}px` : custom;
|
|
2013
|
+
return null;
|
|
2014
|
+
}, /* @ts-ignore */
|
|
2015
|
+
...(ngDevMode ? [{ debugName: "hostWidth" }] : /* istanbul ignore next */ []));
|
|
2016
|
+
hostHeight = computed(() => {
|
|
2017
|
+
const custom = this.height();
|
|
2018
|
+
if (custom !== null)
|
|
2019
|
+
return typeof custom === 'number' ? `${custom}px` : custom;
|
|
2020
|
+
return null;
|
|
2021
|
+
}, /* @ts-ignore */
|
|
2022
|
+
...(ngDevMode ? [{ debugName: "hostHeight" }] : /* istanbul ignore next */ []));
|
|
2023
|
+
computedAspectRatio = computed(() => {
|
|
2024
|
+
const ar = this.aspectRatio();
|
|
2025
|
+
if (ar === 'golden')
|
|
2026
|
+
return `${GOLDEN_RATIO} / 1`;
|
|
2027
|
+
if (typeof ar === 'number')
|
|
2028
|
+
return `${ar} / 1`;
|
|
2029
|
+
if (typeof ar === 'string') {
|
|
2030
|
+
if (ar.includes(':')) {
|
|
2031
|
+
const [w, h] = ar.split(':');
|
|
2032
|
+
return `${w.trim()} / ${h.trim()}`;
|
|
2033
|
+
}
|
|
2034
|
+
return ar;
|
|
2035
|
+
}
|
|
2036
|
+
return null;
|
|
2037
|
+
}, /* @ts-ignore */
|
|
2038
|
+
...(ngDevMode ? [{ debugName: "computedAspectRatio" }] : /* istanbul ignore next */ []));
|
|
2039
|
+
/** Normalized points array */
|
|
2040
|
+
normalizedPoints = computed(() => {
|
|
2041
|
+
const raw = this.data();
|
|
2042
|
+
if (!raw || raw.length === 0)
|
|
2043
|
+
return [];
|
|
2044
|
+
return raw.map((item, idx) => {
|
|
2045
|
+
if (typeof item === 'number') {
|
|
2046
|
+
return { value: item };
|
|
2047
|
+
}
|
|
2048
|
+
return item;
|
|
2049
|
+
});
|
|
2050
|
+
}, /* @ts-ignore */
|
|
2051
|
+
...(ngDevMode ? [{ debugName: "normalizedPoints" }] : /* istanbul ignore next */ []));
|
|
2052
|
+
/** Determine effective color palette */
|
|
2053
|
+
effectiveColor = computed(() => {
|
|
2054
|
+
if (this.autoColorTrend()) {
|
|
2055
|
+
const pts = this.normalizedPoints();
|
|
2056
|
+
if (pts.length >= 2) {
|
|
2057
|
+
const first = pts[0].value;
|
|
2058
|
+
const last = pts[pts.length - 1].value;
|
|
2059
|
+
if (last >= first) {
|
|
2060
|
+
return PRESET_PALETTES['success'];
|
|
2061
|
+
}
|
|
2062
|
+
else {
|
|
2063
|
+
return PRESET_PALETTES['danger'];
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
const c = this.color();
|
|
2068
|
+
if (PRESET_PALETTES[c]) {
|
|
2069
|
+
return PRESET_PALETTES[c];
|
|
2070
|
+
}
|
|
2071
|
+
// Custom hex/rgb
|
|
2072
|
+
return {
|
|
2073
|
+
stroke: c,
|
|
2074
|
+
fillStart: c.startsWith('#') ? `${c}40` : 'rgba(37, 99, 235, 0.3)',
|
|
2075
|
+
fillEnd: c.startsWith('#') ? `${c}00` : 'rgba(37, 99, 235, 0.0)'
|
|
2076
|
+
};
|
|
2077
|
+
}, /* @ts-ignore */
|
|
2078
|
+
...(ngDevMode ? [{ debugName: "effectiveColor" }] : /* istanbul ignore next */ []));
|
|
2079
|
+
/** Min & Max calculation */
|
|
2080
|
+
valueBounds = computed(() => {
|
|
2081
|
+
const pts = this.normalizedPoints();
|
|
2082
|
+
if (pts.length === 0)
|
|
2083
|
+
return { min: 0, max: 100, span: 100, rawMin: 0, rawMax: 100 };
|
|
2084
|
+
let calculatedMin = pts[0].value;
|
|
2085
|
+
let calculatedMax = pts[0].value;
|
|
2086
|
+
for (const p of pts) {
|
|
2087
|
+
if (p.value < calculatedMin)
|
|
2088
|
+
calculatedMin = p.value;
|
|
2089
|
+
if (p.value > calculatedMax)
|
|
2090
|
+
calculatedMax = p.value;
|
|
2091
|
+
}
|
|
2092
|
+
// Include reference value if set
|
|
2093
|
+
const ref = this.referenceValue();
|
|
2094
|
+
if (ref !== null) {
|
|
2095
|
+
if (ref < calculatedMin)
|
|
2096
|
+
calculatedMin = ref;
|
|
2097
|
+
if (ref > calculatedMax)
|
|
2098
|
+
calculatedMax = ref;
|
|
2099
|
+
}
|
|
2100
|
+
// Explicit override
|
|
2101
|
+
const explicitMin = this.min();
|
|
2102
|
+
const explicitMax = this.max();
|
|
2103
|
+
const finalMin = explicitMin !== null ? explicitMin : calculatedMin;
|
|
2104
|
+
const finalMax = explicitMax !== null ? explicitMax : calculatedMax;
|
|
2105
|
+
// Prevent divide by zero if constant value
|
|
2106
|
+
const span = finalMax === finalMin ? 1 : finalMax - finalMin;
|
|
2107
|
+
return { min: finalMin, max: finalMax, span, rawMin: calculatedMin, rawMax: calculatedMax };
|
|
2108
|
+
}, /* @ts-ignore */
|
|
2109
|
+
...(ngDevMode ? [{ debugName: "valueBounds" }] : /* istanbul ignore next */ []));
|
|
2110
|
+
/** Computed internal points with SVG coordinates */
|
|
2111
|
+
internalPoints = computed(() => {
|
|
2112
|
+
const pts = this.normalizedPoints();
|
|
2113
|
+
if (pts.length === 0)
|
|
2114
|
+
return [];
|
|
2115
|
+
const { min, span, rawMin, rawMax } = this.valueBounds();
|
|
2116
|
+
const w = this.svgWidth();
|
|
2117
|
+
const h = this.svgHeight();
|
|
2118
|
+
const p = this.padding();
|
|
2119
|
+
const availableW = Math.max(1, w - p * 2);
|
|
2120
|
+
const availableH = Math.max(1, h - p * 2);
|
|
2121
|
+
const count = pts.length;
|
|
2122
|
+
const stepX = count > 1 ? availableW / (count - 1) : availableW / 2;
|
|
2123
|
+
return pts.map((pt, idx) => {
|
|
2124
|
+
const x = count === 1 ? w / 2 : p + idx * stepX;
|
|
2125
|
+
// Invert Y because SVG 0 is top
|
|
2126
|
+
const normalizedRatio = (pt.value - min) / span;
|
|
2127
|
+
const y = h - p - normalizedRatio * availableH;
|
|
2128
|
+
return {
|
|
2129
|
+
index: idx,
|
|
2130
|
+
value: pt.value,
|
|
2131
|
+
label: pt.label,
|
|
2132
|
+
date: pt.date,
|
|
2133
|
+
x,
|
|
2134
|
+
y,
|
|
2135
|
+
color: pt.color,
|
|
2136
|
+
isMin: pt.value === rawMin,
|
|
2137
|
+
isMax: pt.value === rawMax,
|
|
2138
|
+
isFirst: idx === 0,
|
|
2139
|
+
isLast: idx === count - 1
|
|
2140
|
+
};
|
|
2141
|
+
});
|
|
2142
|
+
}, /* @ts-ignore */
|
|
2143
|
+
...(ngDevMode ? [{ debugName: "internalPoints" }] : /* istanbul ignore next */ []));
|
|
2144
|
+
/** SVG Path line data */
|
|
2145
|
+
linePath = computed(() => {
|
|
2146
|
+
const pts = this.internalPoints();
|
|
2147
|
+
if (pts.length < 2)
|
|
2148
|
+
return '';
|
|
2149
|
+
if (!this.smooth()) {
|
|
2150
|
+
return pts.map((p, idx) => `${idx === 0 ? 'M' : 'L'} ${p.x.toFixed(2)} ${p.y.toFixed(2)}`).join(' ');
|
|
2151
|
+
}
|
|
2152
|
+
// Smooth Bezier Curve (Catmull-Rom or Monotone-like control points)
|
|
2153
|
+
let d = `M ${pts[0].x.toFixed(2)} ${pts[0].y.toFixed(2)}`;
|
|
2154
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
2155
|
+
const p0 = i > 0 ? pts[i - 1] : pts[i];
|
|
2156
|
+
const p1 = pts[i];
|
|
2157
|
+
const p2 = pts[i + 1];
|
|
2158
|
+
const p3 = i < pts.length - 2 ? pts[i + 2] : p2;
|
|
2159
|
+
const cp1x = p1.x + (p2.x - p0.x) / 6;
|
|
2160
|
+
const cp1y = p1.y + (p2.y - p0.y) / 6;
|
|
2161
|
+
const cp2x = p2.x - (p3.x - p1.x) / 6;
|
|
2162
|
+
const cp2y = p2.y - (p3.y - p1.y) / 6;
|
|
2163
|
+
d += ` C ${cp1x.toFixed(2)} ${cp1y.toFixed(2)}, ${cp2x.toFixed(2)} ${cp2y.toFixed(2)}, ${p2.x.toFixed(2)} ${p2.y.toFixed(2)}`;
|
|
2164
|
+
}
|
|
2165
|
+
return d;
|
|
2166
|
+
}, /* @ts-ignore */
|
|
2167
|
+
...(ngDevMode ? [{ debugName: "linePath" }] : /* istanbul ignore next */ []));
|
|
2168
|
+
/** SVG Area path (closed polygon under curve) */
|
|
2169
|
+
areaPath = computed(() => {
|
|
2170
|
+
const line = this.linePath();
|
|
2171
|
+
const pts = this.internalPoints();
|
|
2172
|
+
if (!line || pts.length < 2)
|
|
2173
|
+
return '';
|
|
2174
|
+
const lastPt = pts[pts.length - 1];
|
|
2175
|
+
const firstPt = pts[0];
|
|
2176
|
+
const baselineY = this.svgHeight();
|
|
2177
|
+
return `${line} L ${lastPt.x.toFixed(2)} ${baselineY} L ${firstPt.x.toFixed(2)} ${baselineY} Z`;
|
|
2178
|
+
}, /* @ts-ignore */
|
|
2179
|
+
...(ngDevMode ? [{ debugName: "areaPath" }] : /* istanbul ignore next */ []));
|
|
2180
|
+
/** Bar chart segments */
|
|
2181
|
+
barSegments = computed(() => {
|
|
2182
|
+
const pts = this.normalizedPoints();
|
|
2183
|
+
if (pts.length === 0)
|
|
2184
|
+
return [];
|
|
2185
|
+
const explicitMin = this.min();
|
|
2186
|
+
const explicitMax = this.max();
|
|
2187
|
+
const { rawMin, rawMax } = this.valueBounds();
|
|
2188
|
+
// For bar charts, default baseline is 0 unless min is explicitly provided
|
|
2189
|
+
const barMin = explicitMin !== null ? explicitMin : Math.min(0, rawMin);
|
|
2190
|
+
const barMax = explicitMax !== null ? explicitMax : Math.max(0, rawMax);
|
|
2191
|
+
const barSpan = barMax === barMin ? (barMax !== 0 ? Math.abs(barMax) : 1) : barMax - barMin;
|
|
2192
|
+
const w = this.svgWidth();
|
|
2193
|
+
const h = this.svgHeight();
|
|
2194
|
+
const p = this.padding();
|
|
2195
|
+
const gap = this.barGap();
|
|
2196
|
+
const count = pts.length;
|
|
2197
|
+
const availableW = Math.max(1, w - p * 2);
|
|
2198
|
+
const totalGaps = (count - 1) * gap;
|
|
2199
|
+
const barW = Math.max(2, (availableW - totalGaps) / count);
|
|
2200
|
+
const availableH = Math.max(1, h - p * 2);
|
|
2201
|
+
const baseColor = this.effectiveColor().stroke;
|
|
2202
|
+
// Calculate Y position of zero baseline
|
|
2203
|
+
const zeroRatio = Math.max(0, Math.min(1, (0 - barMin) / barSpan));
|
|
2204
|
+
const zeroY = h - p - zeroRatio * availableH;
|
|
2205
|
+
return pts.map((pt, idx) => {
|
|
2206
|
+
const x = p + idx * (barW + gap);
|
|
2207
|
+
const val = pt.value;
|
|
2208
|
+
let y;
|
|
2209
|
+
let barH;
|
|
2210
|
+
if (barMin >= 0) {
|
|
2211
|
+
// All positive (standard case)
|
|
2212
|
+
const ratio = Math.max(0, Math.min(1, (val - barMin) / barSpan));
|
|
2213
|
+
barH = Math.max(2, ratio * availableH);
|
|
2214
|
+
y = h - p - barH;
|
|
2215
|
+
}
|
|
2216
|
+
else if (barMax <= 0) {
|
|
2217
|
+
// All negative
|
|
2218
|
+
const ratio = Math.max(0, Math.min(1, Math.abs(val) / barSpan));
|
|
2219
|
+
barH = Math.max(2, ratio * availableH);
|
|
2220
|
+
y = p;
|
|
2221
|
+
}
|
|
2222
|
+
else {
|
|
2223
|
+
// Mixed positive & negative values around zero baseline
|
|
2224
|
+
if (val >= 0) {
|
|
2225
|
+
const ratio = Math.min(1, val / barSpan);
|
|
2226
|
+
barH = Math.max(2, ratio * availableH);
|
|
2227
|
+
y = zeroY - barH;
|
|
2228
|
+
}
|
|
2229
|
+
else {
|
|
2230
|
+
const ratio = Math.min(1, Math.abs(val) / barSpan);
|
|
2231
|
+
barH = Math.max(2, ratio * availableH);
|
|
2232
|
+
y = zeroY;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return {
|
|
2236
|
+
index: idx,
|
|
2237
|
+
value: pt.value,
|
|
2238
|
+
label: pt.label,
|
|
2239
|
+
x,
|
|
2240
|
+
y,
|
|
2241
|
+
width: barW,
|
|
2242
|
+
height: barH,
|
|
2243
|
+
color: pt.color || baseColor
|
|
2244
|
+
};
|
|
2245
|
+
});
|
|
2246
|
+
}, /* @ts-ignore */
|
|
2247
|
+
...(ngDevMode ? [{ debugName: "barSegments" }] : /* istanbul ignore next */ []));
|
|
2248
|
+
/** Highlight markers based on showMarkers input */
|
|
2249
|
+
markersToRender = computed(() => {
|
|
2250
|
+
const mode = this.showMarkers();
|
|
2251
|
+
if (mode === 'none')
|
|
2252
|
+
return [];
|
|
2253
|
+
const pts = this.internalPoints();
|
|
2254
|
+
switch (mode) {
|
|
2255
|
+
case 'all':
|
|
2256
|
+
return pts;
|
|
2257
|
+
case 'endpoints':
|
|
2258
|
+
return pts.filter((p) => p.isFirst || p.isLast);
|
|
2259
|
+
case 'min-max':
|
|
2260
|
+
return pts.filter((p) => p.isMin || p.isMax);
|
|
2261
|
+
case 'last':
|
|
2262
|
+
return pts.length > 0 ? [pts[pts.length - 1]] : [];
|
|
2263
|
+
default:
|
|
2264
|
+
return [];
|
|
2265
|
+
}
|
|
2266
|
+
}, /* @ts-ignore */
|
|
2267
|
+
...(ngDevMode ? [{ debugName: "markersToRender" }] : /* istanbul ignore next */ []));
|
|
2268
|
+
/** Reference baseline Y coordinate */
|
|
2269
|
+
referenceLineY = computed(() => {
|
|
2270
|
+
const ref = this.referenceValue();
|
|
2271
|
+
if (ref === null)
|
|
2272
|
+
return null;
|
|
2273
|
+
const { min, span } = this.valueBounds();
|
|
2274
|
+
const h = this.svgHeight();
|
|
2275
|
+
const p = this.padding();
|
|
2276
|
+
const availableH = Math.max(1, h - p * 2);
|
|
2277
|
+
const ratio = (ref - min) / span;
|
|
2278
|
+
return h - p - ratio * availableH;
|
|
2279
|
+
}, /* @ts-ignore */
|
|
2280
|
+
...(ngDevMode ? [{ debugName: "referenceLineY" }] : /* istanbul ignore next */ []));
|
|
2281
|
+
/** Aria label for accessibility */
|
|
2282
|
+
computedAriaLabel = computed(() => {
|
|
2283
|
+
const custom = this.ariaLabel();
|
|
2284
|
+
if (custom)
|
|
2285
|
+
return custom;
|
|
2286
|
+
const pts = this.normalizedPoints();
|
|
2287
|
+
if (pts.length === 0)
|
|
2288
|
+
return 'Empty sparkline';
|
|
2289
|
+
const first = pts[0].value;
|
|
2290
|
+
const last = pts[pts.length - 1].value;
|
|
2291
|
+
const delta = last - first;
|
|
2292
|
+
const direction = delta >= 0 ? 'increased' : 'decreased';
|
|
2293
|
+
return `Sparkline trend: ${first} to ${last} (${direction} by ${Math.abs(delta).toFixed(1)})`;
|
|
2294
|
+
}, /* @ts-ignore */
|
|
2295
|
+
...(ngDevMode ? [{ debugName: "computedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
2296
|
+
/** Handle mouse move for interactive hover tracking */
|
|
2297
|
+
onMouseMove(event) {
|
|
2298
|
+
if (!this.interactive())
|
|
2299
|
+
return;
|
|
2300
|
+
const rect = this.elementRef.nativeElement.getBoundingClientRect();
|
|
2301
|
+
const mouseX = event.clientX - rect.left;
|
|
2302
|
+
if (this.type() === 'bar') {
|
|
2303
|
+
const bars = this.barSegments();
|
|
2304
|
+
if (bars.length === 0)
|
|
2305
|
+
return;
|
|
2306
|
+
let nearest = bars[0];
|
|
2307
|
+
let minDistance = Math.abs(bars[0].x + bars[0].width / 2 - mouseX);
|
|
2308
|
+
for (let i = 1; i < bars.length; i++) {
|
|
2309
|
+
const centerX = bars[i].x + bars[i].width / 2;
|
|
2310
|
+
const dist = Math.abs(centerX - mouseX);
|
|
2311
|
+
if (dist < minDistance) {
|
|
2312
|
+
minDistance = dist;
|
|
2313
|
+
nearest = bars[i];
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
const point = {
|
|
2317
|
+
index: nearest.index,
|
|
2318
|
+
value: nearest.value,
|
|
2319
|
+
label: nearest.label,
|
|
2320
|
+
x: Math.round(nearest.x + nearest.width / 2),
|
|
2321
|
+
y: nearest.y
|
|
2322
|
+
};
|
|
2323
|
+
this.hoveredPoint.set(point);
|
|
2324
|
+
this.isHovering.set(true);
|
|
2325
|
+
this.pointHover.emit({
|
|
2326
|
+
index: nearest.index,
|
|
2327
|
+
value: nearest.value,
|
|
2328
|
+
label: nearest.label,
|
|
2329
|
+
x: point.x,
|
|
2330
|
+
y: point.y
|
|
2331
|
+
});
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
const pts = this.internalPoints();
|
|
2335
|
+
if (pts.length === 0)
|
|
2336
|
+
return;
|
|
2337
|
+
// Find nearest point based on direct pixel coordinate
|
|
2338
|
+
let nearest = pts[0];
|
|
2339
|
+
let minDistance = Math.abs(pts[0].x - mouseX);
|
|
2340
|
+
for (let i = 1; i < pts.length; i++) {
|
|
2341
|
+
const dist = Math.abs(pts[i].x - mouseX);
|
|
2342
|
+
if (dist < minDistance) {
|
|
2343
|
+
minDistance = dist;
|
|
2344
|
+
nearest = pts[i];
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
this.hoveredPoint.set(nearest);
|
|
2348
|
+
this.isHovering.set(true);
|
|
2349
|
+
this.pointHover.emit({
|
|
2350
|
+
index: nearest.index,
|
|
2351
|
+
value: nearest.value,
|
|
2352
|
+
label: nearest.label,
|
|
2353
|
+
x: nearest.x,
|
|
2354
|
+
y: nearest.y
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
/** Handle mouse leave */
|
|
2358
|
+
onMouseLeave() {
|
|
2359
|
+
if (!this.interactive())
|
|
2360
|
+
return;
|
|
2361
|
+
this.hoveredPoint.set(null);
|
|
2362
|
+
this.isHovering.set(false);
|
|
2363
|
+
this.pointHover.emit(null);
|
|
2364
|
+
}
|
|
2365
|
+
/** Handle point click */
|
|
2366
|
+
onPointClick(point) {
|
|
2367
|
+
const x = 'width' in point ? Math.round(point.x + point.width / 2) : ('x' in point ? point.x : 0);
|
|
2368
|
+
this.pointClick.emit({
|
|
2369
|
+
index: point.index,
|
|
2370
|
+
value: point.value,
|
|
2371
|
+
label: point.label,
|
|
2372
|
+
x,
|
|
2373
|
+
y: 'y' in point ? point.y : 0
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2377
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.1", type: TalosSparklineComponent, isStandalone: true, selector: "talos-sparkline", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, autoColorTrend: { classPropertyName: "autoColorTrend", publicName: "autoColorTrend", isSignal: true, isRequired: false, transformFunction: null }, smooth: { classPropertyName: "smooth", publicName: "smooth", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, showMarkers: { classPropertyName: "showMarkers", publicName: "showMarkers", isSignal: true, isRequired: false, transformFunction: null }, markerRadius: { classPropertyName: "markerRadius", publicName: "markerRadius", isSignal: true, isRequired: false, transformFunction: null }, referenceValue: { classPropertyName: "referenceValue", publicName: "referenceValue", isSignal: true, isRequired: false, transformFunction: null }, showTooltip: { classPropertyName: "showTooltip", publicName: "showTooltip", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, barGap: { classPropertyName: "barGap", publicName: "barGap", isSignal: true, isRequired: false, transformFunction: null }, barRadius: { classPropertyName: "barRadius", publicName: "barRadius", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, aspectRatio: { classPropertyName: "aspectRatio", publicName: "aspectRatio", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, valueFormatter: { classPropertyName: "valueFormatter", publicName: "valueFormatter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointHover: "pointHover", pointClick: "pointClick" }, host: { properties: { "class.talos-sparkline-xs": "size() === \"xs\"", "class.talos-sparkline-sm": "size() === \"sm\"", "class.talos-sparkline-md": "size() === \"md\"", "class.talos-sparkline-lg": "size() === \"lg\"", "class.talos-sparkline-responsive": "size() === \"responsive\"", "style.width": "hostWidth()", "style.height": "hostHeight()", "style.aspectRatio": "computedAspectRatio()", "attr.role": "\"img\"", "attr.aria-label": "computedAriaLabel()" }, classAttribute: "talos-sparkline" }, ngImport: i0, template: "<svg\n class=\"talos-sparkline-svg\"\n [attr.viewBox]=\"'0 0 ' + svgWidth() + ' ' + svgHeight()\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseleave)=\"onMouseLeave()\"\n aria-hidden=\"true\"\n>\n <defs>\n <linearGradient [id]=\"gradientId\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" [attr.stop-color]=\"effectiveColor().fillStart\" />\n <stop offset=\"100%\" [attr.stop-color]=\"effectiveColor().fillEnd\" />\n </linearGradient>\n </defs>\n\n <!-- Reference Baseline (if provided) -->\n @if (referenceLineY() !== null) {\n <line\n class=\"talos-sparkline-reference-line\"\n [attr.x1]=\"0\"\n [attr.y1]=\"referenceLineY()\"\n [attr.x2]=\"svgWidth()\"\n [attr.y2]=\"referenceLineY()\"\n />\n }\n\n <!-- Chart Type: Area / Line -->\n @if (type() === 'area' || type() === 'line') {\n @if (type() === 'area' && areaPath()) {\n <path\n class=\"talos-sparkline-area\"\n [attr.d]=\"areaPath()\"\n [attr.fill]=\"'url(#' + gradientId + ')'\"\n />\n }\n\n @if (linePath()) {\n <path\n class=\"talos-sparkline-line\"\n [attr.d]=\"linePath()\"\n [attr.stroke]=\"effectiveColor().stroke\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n }\n }\n\n <!-- Chart Type: Bar -->\n @if (type() === 'bar') {\n @for (bar of barSegments(); track bar.index) {\n <rect\n class=\"talos-sparkline-bar\"\n [class.talos-sparkline-bar-hovered]=\"isHovering() && hoveredPoint()?.index === bar.index\"\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.rx]=\"barRadius()\"\n [attr.ry]=\"barRadius()\"\n [attr.fill]=\"bar.color\"\n (click)=\"onPointClick(bar)\"\n />\n }\n }\n\n <!-- Highlight Markers -->\n @for (marker of markersToRender(); track marker.index) {\n <circle\n class=\"talos-sparkline-marker\"\n [attr.cx]=\"marker.x\"\n [attr.cy]=\"marker.y\"\n [attr.r]=\"markerRadius()\"\n [attr.fill]=\"marker.color || effectiveColor().stroke\"\n [attr.stroke]=\"'#ffffff'\"\n [attr.stroke-width]=\"1.5\"\n (click)=\"onPointClick(marker)\"\n />\n }\n\n <!-- Interactive Hover Crosshair / Cursor Dot (Line/Area only) -->\n @if (type() !== 'bar' && isHovering() && hoveredPoint(); as pt) {\n <line\n class=\"talos-sparkline-cursor-line\"\n [attr.x1]=\"pt.x\"\n [attr.y1]=\"0\"\n [attr.x2]=\"pt.x\"\n [attr.y2]=\"svgHeight()\"\n />\n <circle\n class=\"talos-sparkline-hover-dot-outer\"\n [attr.cx]=\"pt.x\"\n [attr.cy]=\"pt.y\"\n [attr.r]=\"markerRadius() + 2.5\"\n [attr.fill]=\"effectiveColor().stroke\"\n opacity=\"0.25\"\n />\n <circle\n class=\"talos-sparkline-hover-dot\"\n [attr.cx]=\"pt.x\"\n [attr.cy]=\"pt.y\"\n [attr.r]=\"markerRadius() + 0.5\"\n [attr.fill]=\"effectiveColor().stroke\"\n stroke=\"#ffffff\"\n [attr.stroke-width]=\"1.5\"\n />\n }\n</svg>\n\n<!-- Floating Tooltip on Hover -->\n@if (showTooltip() && isHovering() && hoveredPoint(); as pt) {\n <div\n class=\"talos-sparkline-tooltip\"\n [style.left.px]=\"pt.x\"\n >\n @if (pt.label) {\n <span class=\"talos-sparkline-tooltip-label\">{{ pt.label }}</span>\n }\n <span class=\"talos-sparkline-tooltip-value\">{{ valueFormatter()(pt.value) }}</span>\n </div>\n}\n", styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;box-sizing:border-box;overflow:visible;-webkit-user-select:none;user-select:none}:host.talos-sparkline-xs{width:64px;height:40px}:host.talos-sparkline-sm{width:96px;height:60px}:host.talos-sparkline-md{width:130px;height:80px}:host.talos-sparkline-lg{width:180px;height:110px}:host.talos-sparkline-responsive{display:block;width:100%;height:100%;min-height:48px}:host .talos-sparkline-svg{display:block;width:100%;height:100%;overflow:visible;cursor:crosshair}:host .talos-sparkline-area{transition:d .25s ease,opacity .2s ease}:host .talos-sparkline-line{transition:d .25s ease,stroke .2s ease}:host .talos-sparkline-bar{transition:height .2s ease,y .2s ease,opacity .15s ease,filter .15s ease;cursor:pointer}:host .talos-sparkline-bar:hover,:host .talos-sparkline-bar.talos-sparkline-bar-hovered{opacity:.85;filter:brightness(1.1)}:host .talos-sparkline-reference-line{stroke:var(--talos-border-hover, #94a3b8);stroke-width:1;stroke-dasharray:2 2;opacity:.65}:host .talos-sparkline-cursor-line{stroke:var(--talos-text-muted, #64748b);stroke-width:1;stroke-dasharray:2 2;opacity:.7;pointer-events:none}:host .talos-sparkline-marker{filter:drop-shadow(0 1px 2px rgba(0,0,0,.15));cursor:pointer;transition:transform .15s ease}:host .talos-sparkline-marker:hover{transform:scale(1.2)}:host .talos-sparkline-hover-dot{pointer-events:none;filter:drop-shadow(0 2px 4px rgba(0,0,0,.2))}:host .talos-sparkline-tooltip{position:absolute;bottom:calc(100% + 4px);transform:translate(-50%);pointer-events:none;z-index:30;display:flex;flex-direction:column;align-items:center;padding:2px 6px;border-radius:4px;background:var(--talos-text-color, #0f172a);color:var(--talos-card-bg, #ffffff);font-size:.6875rem;font-weight:600;line-height:1.2;white-space:nowrap;box-shadow:0 4px 6px -1px #00000026,0 2px 4px -2px #0000001a;animation:talosSparklineFadeIn .12s ease-out}:host .talos-sparkline-tooltip .talos-sparkline-tooltip-label{font-size:.625rem;font-weight:400;opacity:.8}:host .talos-sparkline-tooltip .talos-sparkline-tooltip-value{letter-spacing:-.01em}:host .talos-sparkline-tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border-width:3px;border-style:solid;border-color:var(--talos-text-color, #0f172a) transparent transparent transparent}@keyframes talosSparklineFadeIn{0%{opacity:0;transform:translate(-50%,2px)}to{opacity:1;transform:translate(-50%)}}\n"] });
|
|
2378
|
+
}
|
|
2379
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineComponent, decorators: [{
|
|
2380
|
+
type: Component,
|
|
2381
|
+
args: [{ selector: 'talos-sparkline', host: {
|
|
2382
|
+
'class': 'talos-sparkline',
|
|
2383
|
+
'[class.talos-sparkline-xs]': 'size() === "xs"',
|
|
2384
|
+
'[class.talos-sparkline-sm]': 'size() === "sm"',
|
|
2385
|
+
'[class.talos-sparkline-md]': 'size() === "md"',
|
|
2386
|
+
'[class.talos-sparkline-lg]': 'size() === "lg"',
|
|
2387
|
+
'[class.talos-sparkline-responsive]': 'size() === "responsive"',
|
|
2388
|
+
'[style.width]': 'hostWidth()',
|
|
2389
|
+
'[style.height]': 'hostHeight()',
|
|
2390
|
+
'[style.aspectRatio]': 'computedAspectRatio()',
|
|
2391
|
+
'[attr.role]': '"img"',
|
|
2392
|
+
'[attr.aria-label]': 'computedAriaLabel()'
|
|
2393
|
+
}, template: "<svg\n class=\"talos-sparkline-svg\"\n [attr.viewBox]=\"'0 0 ' + svgWidth() + ' ' + svgHeight()\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseleave)=\"onMouseLeave()\"\n aria-hidden=\"true\"\n>\n <defs>\n <linearGradient [id]=\"gradientId\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" [attr.stop-color]=\"effectiveColor().fillStart\" />\n <stop offset=\"100%\" [attr.stop-color]=\"effectiveColor().fillEnd\" />\n </linearGradient>\n </defs>\n\n <!-- Reference Baseline (if provided) -->\n @if (referenceLineY() !== null) {\n <line\n class=\"talos-sparkline-reference-line\"\n [attr.x1]=\"0\"\n [attr.y1]=\"referenceLineY()\"\n [attr.x2]=\"svgWidth()\"\n [attr.y2]=\"referenceLineY()\"\n />\n }\n\n <!-- Chart Type: Area / Line -->\n @if (type() === 'area' || type() === 'line') {\n @if (type() === 'area' && areaPath()) {\n <path\n class=\"talos-sparkline-area\"\n [attr.d]=\"areaPath()\"\n [attr.fill]=\"'url(#' + gradientId + ')'\"\n />\n }\n\n @if (linePath()) {\n <path\n class=\"talos-sparkline-line\"\n [attr.d]=\"linePath()\"\n [attr.stroke]=\"effectiveColor().stroke\"\n [attr.stroke-width]=\"strokeWidth()\"\n fill=\"none\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n }\n }\n\n <!-- Chart Type: Bar -->\n @if (type() === 'bar') {\n @for (bar of barSegments(); track bar.index) {\n <rect\n class=\"talos-sparkline-bar\"\n [class.talos-sparkline-bar-hovered]=\"isHovering() && hoveredPoint()?.index === bar.index\"\n [attr.x]=\"bar.x\"\n [attr.y]=\"bar.y\"\n [attr.width]=\"bar.width\"\n [attr.height]=\"bar.height\"\n [attr.rx]=\"barRadius()\"\n [attr.ry]=\"barRadius()\"\n [attr.fill]=\"bar.color\"\n (click)=\"onPointClick(bar)\"\n />\n }\n }\n\n <!-- Highlight Markers -->\n @for (marker of markersToRender(); track marker.index) {\n <circle\n class=\"talos-sparkline-marker\"\n [attr.cx]=\"marker.x\"\n [attr.cy]=\"marker.y\"\n [attr.r]=\"markerRadius()\"\n [attr.fill]=\"marker.color || effectiveColor().stroke\"\n [attr.stroke]=\"'#ffffff'\"\n [attr.stroke-width]=\"1.5\"\n (click)=\"onPointClick(marker)\"\n />\n }\n\n <!-- Interactive Hover Crosshair / Cursor Dot (Line/Area only) -->\n @if (type() !== 'bar' && isHovering() && hoveredPoint(); as pt) {\n <line\n class=\"talos-sparkline-cursor-line\"\n [attr.x1]=\"pt.x\"\n [attr.y1]=\"0\"\n [attr.x2]=\"pt.x\"\n [attr.y2]=\"svgHeight()\"\n />\n <circle\n class=\"talos-sparkline-hover-dot-outer\"\n [attr.cx]=\"pt.x\"\n [attr.cy]=\"pt.y\"\n [attr.r]=\"markerRadius() + 2.5\"\n [attr.fill]=\"effectiveColor().stroke\"\n opacity=\"0.25\"\n />\n <circle\n class=\"talos-sparkline-hover-dot\"\n [attr.cx]=\"pt.x\"\n [attr.cy]=\"pt.y\"\n [attr.r]=\"markerRadius() + 0.5\"\n [attr.fill]=\"effectiveColor().stroke\"\n stroke=\"#ffffff\"\n [attr.stroke-width]=\"1.5\"\n />\n }\n</svg>\n\n<!-- Floating Tooltip on Hover -->\n@if (showTooltip() && isHovering() && hoveredPoint(); as pt) {\n <div\n class=\"talos-sparkline-tooltip\"\n [style.left.px]=\"pt.x\"\n >\n @if (pt.label) {\n <span class=\"talos-sparkline-tooltip-label\">{{ pt.label }}</span>\n }\n <span class=\"talos-sparkline-tooltip-value\">{{ valueFormatter()(pt.value) }}</span>\n </div>\n}\n", styles: [":host{position:relative;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;box-sizing:border-box;overflow:visible;-webkit-user-select:none;user-select:none}:host.talos-sparkline-xs{width:64px;height:40px}:host.talos-sparkline-sm{width:96px;height:60px}:host.talos-sparkline-md{width:130px;height:80px}:host.talos-sparkline-lg{width:180px;height:110px}:host.talos-sparkline-responsive{display:block;width:100%;height:100%;min-height:48px}:host .talos-sparkline-svg{display:block;width:100%;height:100%;overflow:visible;cursor:crosshair}:host .talos-sparkline-area{transition:d .25s ease,opacity .2s ease}:host .talos-sparkline-line{transition:d .25s ease,stroke .2s ease}:host .talos-sparkline-bar{transition:height .2s ease,y .2s ease,opacity .15s ease,filter .15s ease;cursor:pointer}:host .talos-sparkline-bar:hover,:host .talos-sparkline-bar.talos-sparkline-bar-hovered{opacity:.85;filter:brightness(1.1)}:host .talos-sparkline-reference-line{stroke:var(--talos-border-hover, #94a3b8);stroke-width:1;stroke-dasharray:2 2;opacity:.65}:host .talos-sparkline-cursor-line{stroke:var(--talos-text-muted, #64748b);stroke-width:1;stroke-dasharray:2 2;opacity:.7;pointer-events:none}:host .talos-sparkline-marker{filter:drop-shadow(0 1px 2px rgba(0,0,0,.15));cursor:pointer;transition:transform .15s ease}:host .talos-sparkline-marker:hover{transform:scale(1.2)}:host .talos-sparkline-hover-dot{pointer-events:none;filter:drop-shadow(0 2px 4px rgba(0,0,0,.2))}:host .talos-sparkline-tooltip{position:absolute;bottom:calc(100% + 4px);transform:translate(-50%);pointer-events:none;z-index:30;display:flex;flex-direction:column;align-items:center;padding:2px 6px;border-radius:4px;background:var(--talos-text-color, #0f172a);color:var(--talos-card-bg, #ffffff);font-size:.6875rem;font-weight:600;line-height:1.2;white-space:nowrap;box-shadow:0 4px 6px -1px #00000026,0 2px 4px -2px #0000001a;animation:talosSparklineFadeIn .12s ease-out}:host .talos-sparkline-tooltip .talos-sparkline-tooltip-label{font-size:.625rem;font-weight:400;opacity:.8}:host .talos-sparkline-tooltip .talos-sparkline-tooltip-value{letter-spacing:-.01em}:host .talos-sparkline-tooltip:after{content:\"\";position:absolute;top:100%;left:50%;transform:translate(-50%);border-width:3px;border-style:solid;border-color:var(--talos-text-color, #0f172a) transparent transparent transparent}@keyframes talosSparklineFadeIn{0%{opacity:0;transform:translate(-50%,2px)}to{opacity:1;transform:translate(-50%)}}\n"] }]
|
|
2394
|
+
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], autoColorTrend: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoColorTrend", required: false }] }], smooth: [{ type: i0.Input, args: [{ isSignal: true, alias: "smooth", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], showMarkers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMarkers", required: false }] }], markerRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "markerRadius", required: false }] }], referenceValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "referenceValue", required: false }] }], showTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTooltip", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], barGap: [{ type: i0.Input, args: [{ isSignal: true, alias: "barGap", required: false }] }], barRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "barRadius", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], aspectRatio: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspectRatio", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], valueFormatter: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueFormatter", required: false }] }], pointHover: [{ type: i0.Output, args: ["pointHover"] }], pointClick: [{ type: i0.Output, args: ["pointClick"] }] } });
|
|
2395
|
+
|
|
2396
|
+
class TalosSparklineModule {
|
|
2397
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2398
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineModule, imports: [TalosSparklineComponent], exports: [TalosSparklineComponent] });
|
|
2399
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineModule });
|
|
2400
|
+
}
|
|
2401
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.1", ngImport: i0, type: TalosSparklineModule, decorators: [{
|
|
2402
|
+
type: NgModule,
|
|
2403
|
+
args: [{
|
|
2404
|
+
imports: [TalosSparklineComponent],
|
|
2405
|
+
exports: [TalosSparklineComponent]
|
|
2406
|
+
}]
|
|
2407
|
+
}] });
|
|
2408
|
+
|
|
2409
|
+
/**
|
|
2410
|
+
* Generated bundle index. Do not edit.
|
|
2411
|
+
*/
|
|
2412
|
+
|
|
2413
|
+
export { GOLDEN_RATIO, TalosCategoryBarComponent, TalosCategoryBarModule, TalosHeatmapComponent, TalosHeatmapModule, TalosMiniDonutComponent, TalosMiniDonutModule, TalosMiniGaugeComponent, TalosMiniGaugeModule, TalosSparklineComponent, TalosSparklineModule };
|
|
2414
|
+
//# sourceMappingURL=weeehaoooz-talos-ui-data-viz.mjs.map
|