@c8y/ngx-components 1023.65.2 → 1023.66.3
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/context-dashboard/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +4 -0
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-global-context.mjs +38 -9
- package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-kpi.mjs +11 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-kpi.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-alarms.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs +2 -2
- package/fesm2022/c8y-ngx-components-widgets-implementations-datapoints-list.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs +245 -142
- package/fesm2022/c8y-ngx-components-widgets-implementations-kpi.mjs.map +1 -1
- package/global-context/index.d.ts +4 -1
- package/global-context/index.d.ts.map +1 -1
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/locales/de.po +6 -0
- package/locales/es.po +6 -0
- package/locales/fr.po +6 -0
- package/locales/ja_JP.po +6 -0
- package/locales/ko.po +6 -0
- package/locales/locales.pot +6 -0
- package/locales/nl.po +6 -0
- package/locales/pl.po +6 -0
- package/locales/pt_BR.po +6 -0
- package/locales/zh_CN.po +6 -0
- package/locales/zh_TW.po +6 -0
- package/package.json +1 -1
- package/widgets/definitions/kpi/index.d.ts +1 -0
- package/widgets/definitions/kpi/index.d.ts.map +1 -1
- package/widgets/implementations/alarms/index.d.ts +2 -0
- package/widgets/implementations/alarms/index.d.ts.map +1 -1
- package/widgets/implementations/kpi/index.d.ts +75 -52
- package/widgets/implementations/kpi/index.d.ts.map +1 -1
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Input,
|
|
2
|
+
import { signal, inject, DestroyRef, Input, ChangeDetectionStrategy, Component, ViewChild } from '@angular/core';
|
|
3
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
3
4
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import { Validators, NgForm, ControlContainer } from '@angular/forms';
|
|
5
|
-
import
|
|
6
|
-
import { CoreModule, MeasurementRealtimeService, C8yValidators } from '@c8y/ngx-components';
|
|
5
|
+
import { Validators, NgForm, ControlContainer, ReactiveFormsModule } from '@angular/forms';
|
|
6
|
+
import { DashboardChildComponent, MeasurementRealtimeService, C8yTranslateDirective, IconDirective, LoadingComponent, EmptyStateComponent, DatePipe, C8yTranslatePipe, C8yValidators, FormGroupComponent, MessagesComponent, GuideDocsComponent, GuideHrefDirective } from '@c8y/ngx-components';
|
|
7
7
|
import * as i2 from '@c8y/ngx-components/context-dashboard';
|
|
8
|
+
import { ContextDashboardComponent } from '@c8y/ngx-components/context-dashboard';
|
|
8
9
|
import { DatapointSelectorModule } from '@c8y/ngx-components/datapoint-selector';
|
|
9
|
-
import
|
|
10
|
-
import { map, startWith, distinctUntilChanged, tap, filter, pairwise, debounceTime, takeUntil } from 'rxjs/operators';
|
|
11
|
-
import * as i4 from '@c8y/ngx-components/icon-selector';
|
|
10
|
+
import * as i3 from '@c8y/ngx-components/icon-selector';
|
|
12
11
|
import { IconSelectorModule } from '@c8y/ngx-components/icon-selector';
|
|
13
|
-
import * as
|
|
12
|
+
import * as i4 from 'ngx-bootstrap/popover';
|
|
14
13
|
import { PopoverModule } from 'ngx-bootstrap/popover';
|
|
15
|
-
import
|
|
14
|
+
import { share, pairwise, map, startWith, distinctUntilChanged, filter, switchMap, tap, debounceTime } from 'rxjs/operators';
|
|
15
|
+
import { GLOBAL_CONTEXT_DISPLAY_MODE, PRESET_NAME, WidgetConfigMigrationService, REFRESH_OPTION, GlobalContextConnectorComponent, LocalControlsComponent } from '@c8y/ngx-components/global-context';
|
|
16
|
+
import { merge, isEqual } from 'lodash-es';
|
|
17
|
+
import { NEVER, BehaviorSubject, Subject, combineLatest, merge as merge$1 } from 'rxjs';
|
|
18
|
+
import { NgClass, NgStyle, AsyncPipe, DecimalPipe } from '@angular/common';
|
|
16
19
|
|
|
17
20
|
var ColorClass;
|
|
18
21
|
(function (ColorClass) {
|
|
@@ -20,102 +23,202 @@ var ColorClass;
|
|
|
20
23
|
ColorClass["warning"] = "text-warning";
|
|
21
24
|
ColorClass["unknown"] = "";
|
|
22
25
|
})(ColorClass || (ColorClass = {}));
|
|
26
|
+
|
|
23
27
|
class KpiWidgetViewComponent {
|
|
24
|
-
constructor(
|
|
25
|
-
this.measurementRealtime = measurementRealtime;
|
|
26
|
-
this.dashboard = dashboard;
|
|
28
|
+
constructor() {
|
|
27
29
|
this.config = { datapoints: [] };
|
|
30
|
+
this.displayMode = signal(GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
|
|
31
|
+
this.contextConfig = signal({}, ...(ngDevMode ? [{ debugName: "contextConfig" }] : []));
|
|
32
|
+
this.isLinkedToGlobal = signal(undefined, ...(ngDevMode ? [{ debugName: "isLinkedToGlobal" }] : []));
|
|
33
|
+
this.widgetControls = signal(PRESET_NAME.KPI, ...(ngDevMode ? [{ debugName: "widgetControls" }] : []));
|
|
34
|
+
this.isHistoryMode = signal(false, ...(ngDevMode ? [{ debugName: "isHistoryMode" }] : []));
|
|
28
35
|
this.state$ = NEVER;
|
|
29
|
-
|
|
30
|
-
this.
|
|
36
|
+
this.noDataInitiallyInDB = signal(false, ...(ngDevMode ? [{ debugName: "noDataInitiallyInDB" }] : []));
|
|
37
|
+
this.GLOBAL_CONTEXT_DISPLAY_MODE = GLOBAL_CONTEXT_DISPLAY_MODE;
|
|
38
|
+
this.dashboardChild = inject(DashboardChildComponent, { optional: true });
|
|
39
|
+
this.dashboard = inject(ContextDashboardComponent, { optional: true });
|
|
40
|
+
this.measurementRealtime = inject(MeasurementRealtimeService);
|
|
41
|
+
this.widgetConfigMigrationService = inject(WidgetConfigMigrationService);
|
|
42
|
+
this.destroyRef = inject(DestroyRef);
|
|
43
|
+
this.context$ = new BehaviorSubject(null);
|
|
44
|
+
this.refresh$ = new Subject();
|
|
45
|
+
this.lastDatapoint = null;
|
|
46
|
+
}
|
|
47
|
+
ngOnInit() {
|
|
48
|
+
this.config = merge(this.config, this.widgetConfigMigrationService.migrateWidgetConfig(this.config));
|
|
49
|
+
this.syncDisplayState();
|
|
50
|
+
this.buildStatePipeline();
|
|
51
|
+
if (!this.isDashboardMode()) {
|
|
52
|
+
this.emitContext(this.config);
|
|
53
|
+
}
|
|
31
54
|
}
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!datapoint) {
|
|
55
|
+
ngOnChanges(changes) {
|
|
56
|
+
const cfg = changes.config?.currentValue;
|
|
57
|
+
if (!cfg) {
|
|
36
58
|
return;
|
|
37
59
|
}
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
this.config = cfg;
|
|
61
|
+
this.syncDisplayState();
|
|
62
|
+
if (this.isOnRealDashboard()) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (this.isDashboardPreviewWaitingForContext()) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
this.emitContext(this.config, this.hasDatapointChanged());
|
|
69
|
+
}
|
|
70
|
+
onContextChange(event) {
|
|
71
|
+
this.contextConfig.set(event.context);
|
|
72
|
+
this.emitContext(event.context);
|
|
73
|
+
}
|
|
74
|
+
onRefresh() {
|
|
75
|
+
this.refresh$.next();
|
|
76
|
+
}
|
|
77
|
+
getDashboardChild() {
|
|
78
|
+
return this.dashboardChild;
|
|
79
|
+
}
|
|
80
|
+
setupObservable(datapoint, context) {
|
|
81
|
+
const isHistory = context.refreshOption === REFRESH_OPTION.HISTORY;
|
|
82
|
+
const isPaused = !isHistory && context.isAutoRefreshEnabled === false;
|
|
83
|
+
this.isHistoryMode.set(isHistory);
|
|
84
|
+
this.noDataInitiallyInDB.set(false);
|
|
85
|
+
let source$;
|
|
86
|
+
if (isPaused) {
|
|
87
|
+
source$ = this.getHistoryMeasurement$(datapoint, {});
|
|
88
|
+
}
|
|
89
|
+
else if (isHistory) {
|
|
90
|
+
source$ = this.getHistoryMeasurement$(datapoint, context);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
source$ = this.getLiveMeasurement$(datapoint);
|
|
94
|
+
}
|
|
95
|
+
const shared$ = source$.pipe(share());
|
|
96
|
+
const lastTwo$ = shared$.pipe(pairwise());
|
|
46
97
|
return combineLatest([
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.
|
|
50
|
-
unit
|
|
51
|
-
this.getColorClass$(
|
|
52
|
-
]).pipe(map(([latestMeasurement, previousValue, trend, unit, colorClass]) => {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
98
|
+
shared$,
|
|
99
|
+
lastTwo$.pipe(map(([prev]) => prev), startWith(undefined)),
|
|
100
|
+
this.getTrend$(lastTwo$),
|
|
101
|
+
shared$.pipe(map(m => datapoint.unit || m.unit || ''), startWith(''), distinctUntilChanged()),
|
|
102
|
+
this.getColorClass$(shared$, datapoint)
|
|
103
|
+
]).pipe(map(([latestMeasurement, previousValue, trend, unit, colorClass]) => ({
|
|
104
|
+
latestMeasurement,
|
|
105
|
+
previousValue,
|
|
106
|
+
trend,
|
|
107
|
+
unit,
|
|
108
|
+
colorClass
|
|
109
|
+
})));
|
|
110
|
+
}
|
|
111
|
+
buildStatePipeline() {
|
|
112
|
+
const readState = () => ({ ctx: this.context$.value, dp: this.findActiveDatapoint() });
|
|
113
|
+
this.state$ = merge$1(this.context$.pipe(map(readState), distinctUntilChanged(isEqual)), this.refresh$.pipe(map(readState))).pipe(filter((s) => !!s.ctx && !!s.dp), switchMap(({ ctx, dp }) => {
|
|
114
|
+
this.assignContextFromContextDashboard(dp);
|
|
115
|
+
return this.setupObservable(dp, ctx).pipe(startWith(null));
|
|
116
|
+
}), takeUntilDestroyed(this.destroyRef));
|
|
117
|
+
}
|
|
118
|
+
syncDisplayState() {
|
|
119
|
+
const newMode = this.resolveDisplayMode();
|
|
120
|
+
if (this.displayMode() !== newMode) {
|
|
121
|
+
this.displayMode.set(newMode);
|
|
122
|
+
}
|
|
123
|
+
const newCtx = this.extractContext(this.config);
|
|
124
|
+
if (!isEqual(this.contextConfig(), newCtx)) {
|
|
125
|
+
this.contextConfig.set(newCtx);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
emitContext(source, force = false) {
|
|
129
|
+
const ctx = this.extractContext(source);
|
|
130
|
+
if (force || !isEqual(this.context$.value, ctx)) {
|
|
131
|
+
this.context$.next(ctx);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
isDashboardMode() {
|
|
135
|
+
return this.resolveDisplayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD;
|
|
136
|
+
}
|
|
137
|
+
isOnRealDashboard() {
|
|
138
|
+
return this.isDashboardMode() && !!this.dashboardChild;
|
|
139
|
+
}
|
|
140
|
+
isDashboardPreviewWaitingForContext() {
|
|
141
|
+
return this.isDashboardMode() && !this.config.isGlobalContextReady;
|
|
142
|
+
}
|
|
143
|
+
resolveDisplayMode() {
|
|
144
|
+
return (this.config?.displayMode || GLOBAL_CONTEXT_DISPLAY_MODE.CONFIG);
|
|
145
|
+
}
|
|
146
|
+
extractContext(source) {
|
|
147
|
+
return {
|
|
148
|
+
dateTimeContext: source.dateTimeContext,
|
|
149
|
+
isAutoRefreshEnabled: source.isAutoRefreshEnabled,
|
|
150
|
+
refreshInterval: source.refreshInterval,
|
|
151
|
+
refreshOption: source.refreshOption
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
findActiveDatapoint() {
|
|
155
|
+
return this.config?.datapoints?.find(dp => dp?.__active);
|
|
156
|
+
}
|
|
157
|
+
hasDatapointChanged() {
|
|
158
|
+
const dp = this.findActiveDatapoint();
|
|
159
|
+
if (!dp) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const prev = this.lastDatapoint;
|
|
163
|
+
this.lastDatapoint = dp;
|
|
164
|
+
return (!prev ||
|
|
165
|
+
dp.fragment !== prev.fragment ||
|
|
166
|
+
dp.series !== prev.series ||
|
|
167
|
+
dp.__target?.id !== prev.__target?.id ||
|
|
168
|
+
dp.unit !== prev.unit ||
|
|
169
|
+
dp.redRangeMin !== prev.redRangeMin ||
|
|
170
|
+
dp.redRangeMax !== prev.redRangeMax ||
|
|
171
|
+
dp.yellowRangeMin !== prev.yellowRangeMin ||
|
|
172
|
+
dp.yellowRangeMax !== prev.yellowRangeMax);
|
|
173
|
+
}
|
|
174
|
+
getLiveMeasurement$(datapoint) {
|
|
63
175
|
return this.measurementRealtime
|
|
64
|
-
.latestValueOfSpecificMeasurement$(datapoint.fragment, datapoint.series, datapoint.__target,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
176
|
+
.latestValueOfSpecificMeasurement$(datapoint.fragment, datapoint.series, datapoint.__target, this.config.showTrend ? 2 : 1, true)
|
|
177
|
+
.pipe(tap(m => {
|
|
178
|
+
if (!m)
|
|
179
|
+
this.noDataInitiallyInDB.set(true);
|
|
180
|
+
}), filter(m => !!m), map(m => this.toMeasurementValue(m, datapoint)));
|
|
181
|
+
}
|
|
182
|
+
getHistoryMeasurement$(datapoint, context) {
|
|
183
|
+
return this.measurementRealtime
|
|
184
|
+
.lastMeasurement$(datapoint.fragment, datapoint.series, datapoint.__target, 1, true, context.dateTimeContext?.dateFrom, context.dateTimeContext?.dateTo)
|
|
185
|
+
.pipe(tap(m => {
|
|
186
|
+
if (!m)
|
|
187
|
+
this.noDataInitiallyInDB.set(true);
|
|
188
|
+
}), filter(m => !!m), map(m => this.toMeasurementValue(m, datapoint)));
|
|
189
|
+
}
|
|
190
|
+
toMeasurementValue(m, dp) {
|
|
191
|
+
return {
|
|
192
|
+
unit: m[dp.fragment][dp.series].unit,
|
|
193
|
+
value: m[dp.fragment][dp.series].value,
|
|
194
|
+
date: m.time
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
getColorClass$(measurement$, datapoint) {
|
|
198
|
+
return measurement$.pipe(map(m => {
|
|
199
|
+
if (this.inRange(datapoint, m.value, 'redRangeMin', 'redRangeMax')) {
|
|
84
200
|
return ColorClass.danger;
|
|
85
201
|
}
|
|
86
|
-
if (this.
|
|
202
|
+
if (this.inRange(datapoint, m.value, 'yellowRangeMin', 'yellowRangeMax')) {
|
|
87
203
|
return ColorClass.warning;
|
|
88
204
|
}
|
|
89
205
|
return ColorClass.unknown;
|
|
90
206
|
}), startWith(ColorClass.unknown), distinctUntilChanged());
|
|
91
207
|
}
|
|
92
|
-
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const oldValue = res[0].value;
|
|
99
|
-
const newValue = res[1].value;
|
|
100
|
-
if (oldValue < newValue) {
|
|
101
|
-
return '45deg';
|
|
102
|
-
}
|
|
103
|
-
if (oldValue > newValue) {
|
|
104
|
-
return '135deg';
|
|
105
|
-
}
|
|
106
|
-
}
|
|
208
|
+
getTrend$(lastTwo$) {
|
|
209
|
+
return lastTwo$.pipe(map(([prev, curr]) => {
|
|
210
|
+
if (prev.value < curr.value)
|
|
211
|
+
return '45deg';
|
|
212
|
+
if (prev.value > curr.value)
|
|
213
|
+
return '135deg';
|
|
107
214
|
return '90deg';
|
|
108
215
|
}), startWith('90deg'), distinctUntilChanged());
|
|
109
216
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
typeof
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return true;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return false;
|
|
217
|
+
inRange(dp, value, minKey, maxKey) {
|
|
218
|
+
return (typeof dp[minKey] === 'number' &&
|
|
219
|
+
typeof dp[maxKey] === 'number' &&
|
|
220
|
+
value >= dp[minKey] &&
|
|
221
|
+
value < dp[maxKey]);
|
|
119
222
|
}
|
|
120
223
|
assignContextFromContextDashboard(datapoint) {
|
|
121
224
|
if (!this.dashboard?.isDeviceTypeDashboard) {
|
|
@@ -123,42 +226,46 @@ class KpiWidgetViewComponent {
|
|
|
123
226
|
}
|
|
124
227
|
const context = this.dashboard?.context;
|
|
125
228
|
if (context?.id) {
|
|
126
|
-
|
|
127
|
-
datapoint.__target = { name, id };
|
|
229
|
+
datapoint.__target = { name: context.name, id: context.id };
|
|
128
230
|
}
|
|
129
231
|
}
|
|
130
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: KpiWidgetViewComponent, deps: [
|
|
131
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
232
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: KpiWidgetViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
233
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: KpiWidgetViewComponent, isStandalone: true, selector: "c8y-kpi-widget-view", inputs: { config: "config" }, host: { classAttribute: "d-col fit-h" }, providers: [MeasurementRealtimeService], usesOnChanges: true, ngImport: i0, template: "@if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD && getDashboardChild()) {\n <c8y-global-context-connector\n [controls]=\"widgetControls()\"\n [config]=\"contextConfig()\"\n [dashboardChild]=\"getDashboardChild()\"\n [linked]=\"isLinkedToGlobal()\"\n [emitRefresh]=\"false\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-global-context-connector>\n} @else if (displayMode() !== GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n <c8y-local-controls\n [controls]=\"widgetControls()\"\n [displayMode]=\"displayMode()\"\n [config]=\"contextConfig()\"\n [emitRefresh]=\"false\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-local-controls>\n}\n\n@if (state$ | async; as lastState) {\n <div class=\"kpi-widget__container d-flex d-col flex-grow fit-w a-i-center j-c-center\">\n <div class=\"d-flex a-i-center j-c-center fit-w\">\n @if (config.icon && config.showIcon) {\n <div\n class=\"m-r-16 flex-no-shrink text-muted\"\n [ngClass]=\"lastState.colorClass\"\n >\n <i\n class=\"icon-32\"\n [c8yIcon]=\"config.icon\"\n ></i>\n </div>\n }\n <div class=\"text-truncate\">\n <span\n class=\"text-truncate text-medium\"\n [ngStyle]=\"{ 'font-size': (config.fontSize || '36') + 'px' }\"\n title=\"{{\n lastState.colorClass === 'text-danger'\n ? ('Within red range:' | translate)\n : lastState.colorClass === 'text-warning'\n ? ('Within yellow range:' | translate)\n : ''\n }} {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }} {{ lastState.unit || '' }}\"\n [ngClass]=\"lastState.colorClass\"\n >\n {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }}\n <small class=\"text-regular\">{{ lastState.unit || '' }}</small>\n </span>\n </div>\n @if (config?.showTrend && lastState.previousValue; as previousValue) {\n <div class=\"dot dot-info dot-30 m-l-16 flex-no-shrink\">\n <i\n class=\"icon-20\"\n c8yIcon=\"arrow-dotted-up\"\n [ngStyle]=\"{ transform: 'rotate(' + lastState.trend + ')' }\"\n [title]=\"\n ('Previous value' | translate) +\n ': ' +\n (previousValue.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')) +\n ' (' +\n (previousValue.date | c8yDate: 'medium') +\n ')'\n \"\n ></i>\n </div>\n }\n </div>\n <div class=\"d-flex d-col a-i-center\">\n @if (config?.showTimestamp) {\n <p class=\"icon-flex text-center text-muted small m-b-0\">\n <i c8yIcon=\"calendar\"></i>\n {{ lastState.latestMeasurement.date | c8yDate: 'medium' }}\n </p>\n }\n @if (isHistoryMode()) {\n <p class=\"text-center text-muted small m-b-0\">\n <span translate>Last measurement in selected time range</span>\n </p>\n }\n </div>\n </div>\n} @else {\n <div class=\"d-flex flex-grow fit-w j-c-center a-i-center\">\n @let noDataSubtitleLive = 'Waiting for measurements to be created.' | translate;\n @let noDataSubtitleHistory = 'No data available for the selected time period.' | translate;\n @if (noDataInitiallyInDB()) {\n <c8y-ui-empty-state\n [icon]=\"'line-chart'\"\n [title]=\"'No measurement to display.' | translate\"\n [subtitle]=\"isHistoryMode() ? noDataSubtitleHistory : noDataSubtitleLive\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n } @else {\n <c8y-loading></c8y-loading>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: GlobalContextConnectorComponent, selector: "c8y-global-context-connector", inputs: ["controls", "config", "isLoading", "dashboardChild", "linked", "emitRefresh"], outputs: ["configChange", "refresh", "linkedChange"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "emitRefresh"], outputs: ["configChange", "refresh"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }, { kind: "pipe", type: DecimalPipe, name: "number" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
132
234
|
}
|
|
133
235
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: KpiWidgetViewComponent, decorators: [{
|
|
134
236
|
type: Component,
|
|
135
|
-
args: [{ selector: 'c8y-kpi-widget-view', standalone: true, imports: [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
237
|
+
args: [{ selector: 'c8y-kpi-widget-view', standalone: true, imports: [
|
|
238
|
+
AsyncPipe,
|
|
239
|
+
DatePipe,
|
|
240
|
+
DecimalPipe,
|
|
241
|
+
NgClass,
|
|
242
|
+
NgStyle,
|
|
243
|
+
C8yTranslatePipe,
|
|
244
|
+
C8yTranslateDirective,
|
|
245
|
+
IconDirective,
|
|
246
|
+
LoadingComponent,
|
|
247
|
+
EmptyStateComponent,
|
|
248
|
+
GlobalContextConnectorComponent,
|
|
249
|
+
LocalControlsComponent
|
|
250
|
+
], providers: [MeasurementRealtimeService], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'd-col fit-h' }, template: "@if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD && getDashboardChild()) {\n <c8y-global-context-connector\n [controls]=\"widgetControls()\"\n [config]=\"contextConfig()\"\n [dashboardChild]=\"getDashboardChild()\"\n [linked]=\"isLinkedToGlobal()\"\n [emitRefresh]=\"false\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-global-context-connector>\n} @else if (displayMode() !== GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n <c8y-local-controls\n [controls]=\"widgetControls()\"\n [displayMode]=\"displayMode()\"\n [config]=\"contextConfig()\"\n [emitRefresh]=\"false\"\n (configChange)=\"onContextChange($event)\"\n (refresh)=\"onRefresh()\"\n ></c8y-local-controls>\n}\n\n@if (state$ | async; as lastState) {\n <div class=\"kpi-widget__container d-flex d-col flex-grow fit-w a-i-center j-c-center\">\n <div class=\"d-flex a-i-center j-c-center fit-w\">\n @if (config.icon && config.showIcon) {\n <div\n class=\"m-r-16 flex-no-shrink text-muted\"\n [ngClass]=\"lastState.colorClass\"\n >\n <i\n class=\"icon-32\"\n [c8yIcon]=\"config.icon\"\n ></i>\n </div>\n }\n <div class=\"text-truncate\">\n <span\n class=\"text-truncate text-medium\"\n [ngStyle]=\"{ 'font-size': (config.fontSize || '36') + 'px' }\"\n title=\"{{\n lastState.colorClass === 'text-danger'\n ? ('Within red range:' | translate)\n : lastState.colorClass === 'text-warning'\n ? ('Within yellow range:' | translate)\n : ''\n }} {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }} {{ lastState.unit || '' }}\"\n [ngClass]=\"lastState.colorClass\"\n >\n {{\n lastState.latestMeasurement.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')\n }}\n <small class=\"text-regular\">{{ lastState.unit || '' }}</small>\n </span>\n </div>\n @if (config?.showTrend && lastState.previousValue; as previousValue) {\n <div class=\"dot dot-info dot-30 m-l-16 flex-no-shrink\">\n <i\n class=\"icon-20\"\n c8yIcon=\"arrow-dotted-up\"\n [ngStyle]=\"{ transform: 'rotate(' + lastState.trend + ')' }\"\n [title]=\"\n ('Previous value' | translate) +\n ': ' +\n (previousValue.value\n | number\n : '1.' +\n (config.numberOfDecimalPlaces || '0') +\n '-' +\n (config.numberOfDecimalPlaces || '0')) +\n ' (' +\n (previousValue.date | c8yDate: 'medium') +\n ')'\n \"\n ></i>\n </div>\n }\n </div>\n <div class=\"d-flex d-col a-i-center\">\n @if (config?.showTimestamp) {\n <p class=\"icon-flex text-center text-muted small m-b-0\">\n <i c8yIcon=\"calendar\"></i>\n {{ lastState.latestMeasurement.date | c8yDate: 'medium' }}\n </p>\n }\n @if (isHistoryMode()) {\n <p class=\"text-center text-muted small m-b-0\">\n <span translate>Last measurement in selected time range</span>\n </p>\n }\n </div>\n </div>\n} @else {\n <div class=\"d-flex flex-grow fit-w j-c-center a-i-center\">\n @let noDataSubtitleLive = 'Waiting for measurements to be created.' | translate;\n @let noDataSubtitleHistory = 'No data available for the selected time period.' | translate;\n @if (noDataInitiallyInDB()) {\n <c8y-ui-empty-state\n [icon]=\"'line-chart'\"\n [title]=\"'No measurement to display.' | translate\"\n [subtitle]=\"isHistoryMode() ? noDataSubtitleHistory : noDataSubtitleLive\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n } @else {\n <c8y-loading></c8y-loading>\n }\n </div>\n}\n" }]
|
|
251
|
+
}], propDecorators: { config: [{
|
|
139
252
|
type: Input
|
|
140
253
|
}] } });
|
|
141
254
|
|
|
142
255
|
function exactlyASingleDatapointActive() {
|
|
143
256
|
return (control) => {
|
|
144
257
|
const datapoints = control.value;
|
|
145
|
-
if (!datapoints
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
const activeDatapoints = datapoints.filter(datapoint => datapoint.__active);
|
|
149
|
-
if (activeDatapoints.length === 1) {
|
|
258
|
+
if (!datapoints?.length) {
|
|
150
259
|
return null;
|
|
151
260
|
}
|
|
152
|
-
return
|
|
261
|
+
return datapoints.filter(dp => dp.__active).length === 1
|
|
262
|
+
? null
|
|
263
|
+
: { exactlyOneDatapointNeedsToBeActive: true };
|
|
153
264
|
};
|
|
154
265
|
}
|
|
155
266
|
class KpiWidgetConfigComponent {
|
|
156
267
|
set previewMapSet(template) {
|
|
157
|
-
|
|
158
|
-
this.widgetConfigService.setPreview(template);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
this.widgetConfigService.setPreview(null);
|
|
268
|
+
this.widgetConfigService.setPreview(template || null);
|
|
162
269
|
}
|
|
163
270
|
constructor(formBuilder, form, widgetConfig, widgetConfigService) {
|
|
164
271
|
this.formBuilder = formBuilder;
|
|
@@ -170,8 +277,7 @@ class KpiWidgetConfigComponent {
|
|
|
170
277
|
showRedRange: true,
|
|
171
278
|
showYellowRange: true
|
|
172
279
|
};
|
|
173
|
-
this.
|
|
174
|
-
this.destroy$ = new Subject();
|
|
280
|
+
this.destroyRef = inject(DestroyRef);
|
|
175
281
|
this.limits = {
|
|
176
282
|
fontSizeMax: 72,
|
|
177
283
|
fontSizeMin: 18,
|
|
@@ -191,60 +297,50 @@ class KpiWidgetConfigComponent {
|
|
|
191
297
|
this.formGroup.controls.datapoints.patchValue(this.config.datapoints || []);
|
|
192
298
|
}
|
|
193
299
|
}
|
|
194
|
-
|
|
300
|
+
ngOnInit() {
|
|
195
301
|
if (this.widgetConfig.context?.id) {
|
|
196
|
-
this.datapointSelectionConfig.contextAsset = this.widgetConfig
|
|
302
|
+
this.datapointSelectionConfig.contextAsset = this.widgetConfig.context;
|
|
197
303
|
}
|
|
198
304
|
this.previewConfig = { ...this.config };
|
|
199
305
|
this.initForm();
|
|
200
306
|
if (this.config?.datapoints) {
|
|
201
307
|
this.formGroup.patchValue({ datapoints: this.config.datapoints });
|
|
202
|
-
|
|
203
|
-
this.previewActiveDatapoint = this.config.datapoints.find(dp => dp.__active);
|
|
204
|
-
}
|
|
308
|
+
this.previewActiveDatapoint = this.config.datapoints.find(dp => dp.__active);
|
|
205
309
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
applyLimits(value, min, max) {
|
|
212
|
-
if (value < min) {
|
|
213
|
-
return min;
|
|
214
|
-
}
|
|
215
|
-
if (value > max) {
|
|
216
|
-
return max;
|
|
217
|
-
}
|
|
218
|
-
return value;
|
|
310
|
+
this.widgetConfigService.currentConfig$
|
|
311
|
+
.pipe(filter(c => !!c), takeUntilDestroyed(this.destroyRef))
|
|
312
|
+
.subscribe(c => {
|
|
313
|
+
this.previewConfig = { ...this.previewConfig, ...c, ...this.formGroup.value };
|
|
314
|
+
});
|
|
219
315
|
}
|
|
220
316
|
initForm() {
|
|
221
317
|
this.formGroup = this.createForm();
|
|
222
318
|
this.form.form.addControl('config', this.formGroup);
|
|
223
319
|
this.formGroup.patchValue(this.config);
|
|
224
320
|
this.formGroup.valueChanges
|
|
225
|
-
.pipe(debounceTime(100),
|
|
321
|
+
.pipe(debounceTime(100), takeUntilDestroyed(this.destroyRef))
|
|
226
322
|
.subscribe(formValue => {
|
|
227
323
|
if (formValue.datapoints) {
|
|
228
324
|
this.previewActiveDatapoint = formValue.datapoints.find(dp => dp.__active);
|
|
229
325
|
}
|
|
230
|
-
const previewLimitedValue = this.createPreviewLimitedValue(formValue);
|
|
231
326
|
if (this.formGroup.valid) {
|
|
232
|
-
|
|
233
|
-
// e.g. invalid value of numberOfDecimalPlaces provided to number pipe
|
|
234
|
-
this.previewConfig = { ...this.config, ...previewLimitedValue };
|
|
327
|
+
this.previewConfig = { ...this.config, ...this.applyLimitsToPreview(formValue) };
|
|
235
328
|
}
|
|
236
329
|
Object.assign(this.config, formValue);
|
|
237
330
|
});
|
|
238
331
|
}
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
if (
|
|
242
|
-
|
|
332
|
+
applyLimitsToPreview(formValue) {
|
|
333
|
+
const result = { ...formValue };
|
|
334
|
+
if (result.numberOfDecimalPlaces !== undefined) {
|
|
335
|
+
result.numberOfDecimalPlaces = this.clamp(result.numberOfDecimalPlaces, this.limits.numberOfDecimalPlacesMin, this.limits.numberOfDecimalPlacesMax);
|
|
243
336
|
}
|
|
244
|
-
if (
|
|
245
|
-
|
|
337
|
+
if (result.fontSize !== undefined) {
|
|
338
|
+
result.fontSize = this.clamp(result.fontSize, this.limits.fontSizeMin, this.limits.fontSizeMax);
|
|
246
339
|
}
|
|
247
|
-
return
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
clamp(value, min, max) {
|
|
343
|
+
return Math.max(min, Math.min(max, value));
|
|
248
344
|
}
|
|
249
345
|
createForm() {
|
|
250
346
|
return this.formBuilder.group({
|
|
@@ -277,17 +373,24 @@ class KpiWidgetConfigComponent {
|
|
|
277
373
|
});
|
|
278
374
|
}
|
|
279
375
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: KpiWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }, { token: i2.WidgetConfigComponent }, { token: i2.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
280
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: KpiWidgetConfigComponent, isStandalone: true, selector: "c8y-kpi-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["kpiPreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"d-flex a-i-center gap-8\">\n <div class=\"form-group form-group-sm m-b-16\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n [iconSize]=\"16\"\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group class=\"form-group-sm m-b-16 flex-grow\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-20\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n
|
|
376
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.18", type: KpiWidgetConfigComponent, isStandalone: true, selector: "c8y-kpi-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMapSet", first: true, predicate: ["kpiPreview"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"d-flex a-i-center gap-8\">\n <div class=\"form-group form-group-sm m-b-16\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n [iconSize]=\"16\"\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group class=\"form-group-sm m-b-16 flex-grow\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-20\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n <c8y-kpi-widget-view [config]=\"previewConfig\"></c8y-kpi-widget-view>\n </div>\n } @else {\n <div class=\"col-md-6 d-col a-i-start j-c-center\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n [horizontal]=\"false\"\n data-cy=\"kpi-widget--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#kpi\">user documentation</a>.\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n }\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "component", type: EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "component", type: GuideDocsComponent, selector: "[c8y-guide-docs]" }, { kind: "directive", type: GuideHrefDirective, selector: "[c8y-guide-href]", inputs: ["c8y-guide-href"] }, { kind: "ngmodule", type: DatapointSelectorModule }, { kind: "ngmodule", type: IconSelectorModule }, { kind: "component", type: i3.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i4.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "component", type: KpiWidgetViewComponent, selector: "c8y-kpi-widget-view", inputs: ["config"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
281
377
|
}
|
|
282
378
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: KpiWidgetConfigComponent, decorators: [{
|
|
283
379
|
type: Component,
|
|
284
380
|
args: [{ selector: 'c8y-kpi-widget-config', standalone: true, imports: [
|
|
285
|
-
|
|
381
|
+
ReactiveFormsModule,
|
|
382
|
+
C8yTranslatePipe,
|
|
383
|
+
C8yTranslateDirective,
|
|
384
|
+
FormGroupComponent,
|
|
385
|
+
MessagesComponent,
|
|
386
|
+
EmptyStateComponent,
|
|
387
|
+
GuideDocsComponent,
|
|
388
|
+
GuideHrefDirective,
|
|
286
389
|
DatapointSelectorModule,
|
|
287
390
|
IconSelectorModule,
|
|
288
391
|
PopoverModule,
|
|
289
392
|
KpiWidgetViewComponent
|
|
290
|
-
], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"d-flex a-i-center gap-8\">\n <div class=\"form-group form-group-sm m-b-16\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n [iconSize]=\"16\"\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group class=\"form-group-sm m-b-16 flex-grow\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-20\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n
|
|
393
|
+
], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"formGroup\">\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Layout' | translate }}</legend>\n <div class=\"d-flex a-i-center gap-8\">\n <div class=\"form-group form-group-sm m-b-16\">\n <label translate>Icon</label>\n <c8y-icon-selector-wrapper\n [iconSize]=\"16\"\n name=\"icon\"\n formControlName=\"icon\"\n ></c8y-icon-selector-wrapper>\n </div>\n <c8y-form-group class=\"form-group-sm m-b-16 flex-grow\">\n <label\n [title]=\"'Font size of measurement value (px)' | translate\"\n translate\n >\n Font size of measurement value (px)\n </label>\n <input\n class=\"form-control\"\n name=\"fontSize\"\n type=\"number\"\n formControlName=\"fontSize\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 36 }\"\n />\n <c8y-messages\n [show]=\"formGroup.controls?.fontSize?.touched && formGroup?.controls?.fontSize?.errors\"\n ></c8y-messages>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Display' | translate }}</legend>\n <div class=\"d-flex gap-16 flex-wrap\">\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show timestamp' | translate\"\n >\n <input\n name=\"showTimestamp\"\n type=\"checkbox\"\n formControlName=\"showTimestamp\"\n />\n <span></span>\n <span translate>Show timestamp</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show icon' | translate\"\n >\n <input\n name=\"showIcon\"\n type=\"checkbox\"\n formControlName=\"showIcon\"\n />\n <span></span>\n <span translate>Show icon</span>\n </label>\n </c8y-form-group>\n\n <c8y-form-group class=\"m-b-8\">\n <label\n class=\"c8y-checkbox\"\n [title]=\"'Show trend icon' | translate\"\n >\n <input\n name=\"showTrend\"\n type=\"checkbox\"\n formControlName=\"showTrend\"\n />\n <span></span>\n <span translate>Show trend icon</span>\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{\n 'Indicates the trend between the last two measurement values.' | translate\n }}\"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n </c8y-form-group>\n </div>\n </fieldset>\n\n <fieldset class=\"c8y-fieldset\">\n <legend translate>Number of decimal places</legend>\n <c8y-form-group class=\"form-group-sm m-b-20\">\n <input\n class=\"form-control\"\n name=\"numberOfDecimalPlaces\"\n type=\"number\"\n formControlName=\"numberOfDecimalPlaces\"\n [placeholder]=\"'e.g. {{ example }}' | translate: { example: 1 }\"\n />\n <c8y-messages\n [show]=\"\n formGroup.controls?.numberOfDecimalPlaces?.touched &&\n formGroup?.controls?.numberOfDecimalPlaces?.errors\n \"\n ></c8y-messages>\n </c8y-form-group>\n </fieldset>\n</form>\n\n<ng-template #kpiPreview>\n @if (formGroup && formGroup.value) {\n @if (formGroup.value.datapoints?.length > 0 && previewActiveDatapoint) {\n <div style=\"height: 300px\">\n <c8y-kpi-widget-view [config]=\"previewConfig\"></c8y-kpi-widget-view>\n </div>\n } @else {\n <div class=\"col-md-6 d-col a-i-start j-c-center\">\n <c8y-ui-empty-state\n [icon]=\"'c8y-data-points'\"\n [title]=\"'No data points selected' | translate\"\n [subtitle]=\"'Select data point to render content' | translate\"\n [horizontal]=\"false\"\n data-cy=\"kpi-widget--empty-state-no-data-point-selected\"\n >\n <p c8y-guide-docs>\n <small\n translate\n ngNonBindable\n >\n Find out more in the\n <a c8y-guide-href=\"/docs/cockpit/widgets-collection/#kpi\">user documentation</a>.\n </small>\n </p>\n </c8y-ui-empty-state>\n </div>\n }\n }\n</ng-template>\n" }]
|
|
291
394
|
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }, { type: i2.WidgetConfigComponent }, { type: i2.WidgetConfigService }], propDecorators: { previewMapSet: [{
|
|
292
395
|
type: ViewChild,
|
|
293
396
|
args: ['kpiPreview']
|
|
@@ -299,5 +402,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
299
402
|
* Generated bundle index. Do not edit.
|
|
300
403
|
*/
|
|
301
404
|
|
|
302
|
-
export { KpiWidgetConfigComponent, KpiWidgetViewComponent, exactlyASingleDatapointActive };
|
|
405
|
+
export { ColorClass, KpiWidgetConfigComponent, KpiWidgetViewComponent, exactlyASingleDatapointActive };
|
|
303
406
|
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-kpi.mjs.map
|