@openremote/or-dashboard-builder 1.2.0-snapshot.20240512160221
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 +156 -0
- package/build.gradle +15 -0
- package/lib/controls/dashboard-refresh-controls.d.ts +17 -0
- package/lib/controls/dashboard-refresh-controls.js +17 -0
- package/lib/controls/dashboard-refresh-controls.js.map +1 -0
- package/lib/index.d.ts +71 -0
- package/lib/index.js +285 -0
- package/lib/index.js.map +1 -0
- package/lib/or-dashboard-boardsettings.d.ts +19 -0
- package/lib/or-dashboard-boardsettings.js +121 -0
- package/lib/or-dashboard-boardsettings.js.map +1 -0
- package/lib/or-dashboard-browser.d.ts +9 -0
- package/lib/or-dashboard-browser.js +35 -0
- package/lib/or-dashboard-browser.js.map +1 -0
- package/lib/or-dashboard-engine.d.ts +4 -0
- package/lib/or-dashboard-engine.js +1 -0
- package/lib/or-dashboard-engine.js.map +1 -0
- package/lib/or-dashboard-keyhandler.d.ts +6 -0
- package/lib/or-dashboard-keyhandler.js +1 -0
- package/lib/or-dashboard-keyhandler.js.map +1 -0
- package/lib/or-dashboard-preview.d.ts +70 -0
- package/lib/or-dashboard-preview.js +155 -0
- package/lib/or-dashboard-preview.js.map +1 -0
- package/lib/or-dashboard-settingspanel.d.ts +44 -0
- package/lib/or-dashboard-settingspanel.js +216 -0
- package/lib/or-dashboard-settingspanel.js.map +1 -0
- package/lib/or-dashboard-tree.d.ts +25 -0
- package/lib/or-dashboard-tree.js +62 -0
- package/lib/or-dashboard-tree.js.map +1 -0
- package/lib/or-dashboard-widget.d.ts +18 -0
- package/lib/or-dashboard-widget.js +15 -0
- package/lib/or-dashboard-widget.js.map +1 -0
- package/lib/or-dashboard-widgetcontainer.d.ts +23 -0
- package/lib/or-dashboard-widgetcontainer.js +20 -0
- package/lib/or-dashboard-widgetcontainer.js.map +1 -0
- package/lib/or-dashboard-widgetsettings.d.ts +15 -0
- package/lib/or-dashboard-widgetsettings.js +16 -0
- package/lib/or-dashboard-widgetsettings.js.map +1 -0
- package/lib/panels/assettypes-panel.d.ts +44 -0
- package/lib/panels/assettypes-panel.js +51 -0
- package/lib/panels/assettypes-panel.js.map +1 -0
- package/lib/panels/attributes-panel.d.ts +41 -0
- package/lib/panels/attributes-panel.js +126 -0
- package/lib/panels/attributes-panel.js.map +1 -0
- package/lib/panels/thresholds-panel.d.ts +39 -0
- package/lib/panels/thresholds-panel.js +129 -0
- package/lib/panels/thresholds-panel.js.map +1 -0
- package/lib/service/dashboard-service.d.ts +13 -0
- package/lib/service/dashboard-service.js +1 -0
- package/lib/service/dashboard-service.js.map +1 -0
- package/lib/service/widget-service.d.ts +8 -0
- package/lib/service/widget-service.js +1 -0
- package/lib/service/widget-service.js.map +1 -0
- package/lib/settings/attribute-input-settings.d.ts +13 -0
- package/lib/settings/attribute-input-settings.js +36 -0
- package/lib/settings/attribute-input-settings.js.map +1 -0
- package/lib/settings/chart-settings.d.ts +30 -0
- package/lib/settings/chart-settings.js +144 -0
- package/lib/settings/chart-settings.js.map +1 -0
- package/lib/settings/gauge-settings.d.ts +15 -0
- package/lib/settings/gauge-settings.js +37 -0
- package/lib/settings/gauge-settings.js.map +1 -0
- package/lib/settings/image-settings.d.ts +17 -0
- package/lib/settings/image-settings.js +52 -0
- package/lib/settings/image-settings.js.map +1 -0
- package/lib/settings/kpi-settings.d.ts +15 -0
- package/lib/settings/kpi-settings.js +47 -0
- package/lib/settings/kpi-settings.js.map +1 -0
- package/lib/settings/map-settings.d.ts +21 -0
- package/lib/settings/map-settings.js +72 -0
- package/lib/settings/map-settings.js.map +1 -0
- package/lib/settings/table-settings.d.ts +14 -0
- package/lib/settings/table-settings.js +33 -0
- package/lib/settings/table-settings.js.map +1 -0
- package/lib/style.d.ts +1 -0
- package/lib/style.js +99 -0
- package/lib/style.js.map +1 -0
- package/lib/util/or-asset-widget.d.ts +20 -0
- package/lib/util/or-asset-widget.js +1 -0
- package/lib/util/or-asset-widget.js.map +1 -0
- package/lib/util/or-widget.d.ts +31 -0
- package/lib/util/or-widget.js +1 -0
- package/lib/util/or-widget.js.map +1 -0
- package/lib/util/settings-panel.d.ts +9 -0
- package/lib/util/settings-panel.js +56 -0
- package/lib/util/settings-panel.js.map +1 -0
- package/lib/util/widget-config.d.ts +2 -0
- package/lib/util/widget-config.js +1 -0
- package/lib/util/widget-config.js.map +1 -0
- package/lib/util/widget-settings.d.ts +23 -0
- package/lib/util/widget-settings.js +1 -0
- package/lib/util/widget-settings.js.map +1 -0
- package/lib/widgets/attribute-input-widget.d.ts +24 -0
- package/lib/widgets/attribute-input-widget.js +31 -0
- package/lib/widgets/attribute-input-widget.js.map +1 -0
- package/lib/widgets/chart-widget.d.ts +25 -0
- package/lib/widgets/chart-widget.js +15 -0
- package/lib/widgets/chart-widget.js.map +1 -0
- package/lib/widgets/gauge-widget.d.ts +22 -0
- package/lib/widgets/gauge-widget.js +12 -0
- package/lib/widgets/gauge-widget.js.map +1 -0
- package/lib/widgets/image-widget.d.ts +25 -0
- package/lib/widgets/image-widget.js +54 -0
- package/lib/widgets/image-widget.js.map +1 -0
- package/lib/widgets/kpi-widget.d.ts +21 -0
- package/lib/widgets/kpi-widget.js +8 -0
- package/lib/widgets/kpi-widget.js.map +1 -0
- package/lib/widgets/map-widget.d.ts +39 -0
- package/lib/widgets/map-widget.js +9 -0
- package/lib/widgets/map-widget.js.map +1 -0
- package/lib/widgets/or-base-widget.d.ts +15 -0
- package/lib/widgets/or-base-widget.js +1 -0
- package/lib/widgets/or-base-widget.js.map +1 -0
- package/lib/widgets/or-chart-widget.d.ts +36 -0
- package/lib/widgets/or-chart-widget.js +112 -0
- package/lib/widgets/or-chart-widget.js.map +1 -0
- package/lib/widgets/or-gauge-widget.d.ts +46 -0
- package/lib/widgets/or-gauge-widget.js +60 -0
- package/lib/widgets/or-gauge-widget.js.map +1 -0
- package/lib/widgets/or-kpi-widget.d.ts +44 -0
- package/lib/widgets/or-kpi-widget.js +63 -0
- package/lib/widgets/or-kpi-widget.js.map +1 -0
- package/lib/widgets/or-map-widget.d.ts +49 -0
- package/lib/widgets/or-map-widget.js +75 -0
- package/lib/widgets/or-map-widget.js.map +1 -0
- package/lib/widgets/table-widget.d.ts +25 -0
- package/lib/widgets/table-widget.js +12 -0
- package/lib/widgets/table-widget.js.map +1 -0
- package/package.json +32 -0
- package/src/controls/dashboard-refresh-controls.ts +100 -0
- package/src/index.ts +731 -0
- package/src/or-dashboard-boardsettings.ts +249 -0
- package/src/or-dashboard-browser.ts +160 -0
- package/src/or-dashboard-engine.ts +17 -0
- package/src/or-dashboard-keyhandler.ts +25 -0
- package/src/or-dashboard-preview.ts +713 -0
- package/src/or-dashboard-tree.ts +304 -0
- package/src/or-dashboard-widgetcontainer.ts +155 -0
- package/src/or-dashboard-widgetsettings.ts +91 -0
- package/src/panels/assettypes-panel.ts +311 -0
- package/src/panels/attributes-panel.ts +304 -0
- package/src/panels/thresholds-panel.ts +285 -0
- package/src/service/dashboard-service.ts +89 -0
- package/src/service/widget-service.ts +48 -0
- package/src/settings/attribute-input-settings.ts +79 -0
- package/src/settings/chart-settings.ts +306 -0
- package/src/settings/gauge-settings.ts +93 -0
- package/src/settings/image-settings.ts +175 -0
- package/src/settings/kpi-settings.ts +106 -0
- package/src/settings/map-settings.ts +185 -0
- package/src/settings/table-settings.ts +92 -0
- package/src/style.ts +104 -0
- package/src/util/or-asset-widget.ts +110 -0
- package/src/util/or-widget.ts +60 -0
- package/src/util/settings-panel.ts +93 -0
- package/src/util/widget-config.ts +2 -0
- package/src/util/widget-settings.ts +58 -0
- package/src/widgets/attribute-input-widget.ts +143 -0
- package/src/widgets/chart-widget.ts +203 -0
- package/src/widgets/gauge-widget.ts +111 -0
- package/src/widgets/image-widget.ts +180 -0
- package/src/widgets/kpi-widget.ts +97 -0
- package/src/widgets/map-widget.ts +187 -0
- package/src/widgets/table-widget.ts +157 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/webpack.config.js +10 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import {css, html, TemplateResult } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
4
|
+
import "../panels/attributes-panel";
|
|
5
|
+
import "../util/settings-panel";
|
|
6
|
+
import {i18next} from "@openremote/or-translate";
|
|
7
|
+
import {AttributeAction, AttributeActionEvent, AttributesSelectEvent} from "../panels/attributes-panel";
|
|
8
|
+
import {Asset, AssetDatapointIntervalQuery, AssetDatapointIntervalQueryFormula, Attribute, AttributeRef} from "@openremote/model";
|
|
9
|
+
import {ChartWidgetConfig} from "../widgets/chart-widget";
|
|
10
|
+
import {InputType, OrInputChangedEvent} from "@openremote/or-mwc-components/or-mwc-input";
|
|
11
|
+
import {TimePresetCallback} from "@openremote/or-chart";
|
|
12
|
+
import {when} from "lit/directives/when.js";
|
|
13
|
+
|
|
14
|
+
const styling = css`
|
|
15
|
+
.switch-container {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
}
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@customElement("chart-settings")
|
|
24
|
+
export class ChartSettings extends WidgetSettings {
|
|
25
|
+
|
|
26
|
+
protected readonly widgetConfig!: ChartWidgetConfig
|
|
27
|
+
|
|
28
|
+
protected timePresetOptions: Map<string, TimePresetCallback> = new Map<string, TimePresetCallback>();
|
|
29
|
+
protected samplingOptions: Map<string, string> = new Map<string, string>();
|
|
30
|
+
|
|
31
|
+
public setTimePresetOptions(options: Map<string, TimePresetCallback>) {
|
|
32
|
+
this.timePresetOptions = options;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public setSamplingOptions(options: Map<string, string>) {
|
|
36
|
+
this.samplingOptions = options;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static get styles() {
|
|
40
|
+
return [...super.styles, styling];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected render(): TemplateResult {
|
|
44
|
+
const attributeFilter: (attr: Attribute<any>) => boolean = (attr): boolean => {
|
|
45
|
+
return ["boolean", "positiveInteger", "positiveNumber", "number", "long", "integer", "bigInteger", "negativeInteger", "negativeNumber", "bigNumber", "integerByte", "direction"].includes(attr.type!)
|
|
46
|
+
};
|
|
47
|
+
const min = this.widgetConfig.chartOptions.options?.scales?.y?.min;
|
|
48
|
+
const max = this.widgetConfig.chartOptions.options?.scales?.y?.max;
|
|
49
|
+
const isMultiAxis = this.widgetConfig.rightAxisAttributes.length > 0;
|
|
50
|
+
const samplingValue = Array.from(this.samplingOptions.entries()).find((entry => entry[1] === this.widgetConfig.datapointQuery.type))![0]
|
|
51
|
+
const attributeLabelCallback = (asset: Asset, attribute: Attribute<any>, attributeLabel: string) => {
|
|
52
|
+
const isOnRightAxis = isMultiAxis && this.widgetConfig.rightAxisAttributes.find(ar => ar.id === asset.id && ar.name === attribute.name) !== undefined;
|
|
53
|
+
return html`
|
|
54
|
+
<span>${asset.name}</span>
|
|
55
|
+
<span style="font-size:14px; color:grey;">${attributeLabel}</span>
|
|
56
|
+
${when(isOnRightAxis, () => html`
|
|
57
|
+
<span style="position: absolute; right: 0; margin-bottom: 16px; font-size:14px; color:grey;"><or-translate value="right"></or-translate></span>
|
|
58
|
+
`)}
|
|
59
|
+
`
|
|
60
|
+
}
|
|
61
|
+
const attributeActionCallback = (attributeRef: AttributeRef): AttributeAction[] => {
|
|
62
|
+
return [{
|
|
63
|
+
icon: this.widgetConfig.rightAxisAttributes.includes(attributeRef) ? "arrow-right-bold" : "arrow-left-bold",
|
|
64
|
+
tooltip: i18next.t('dashboard.toggleAxis'),
|
|
65
|
+
disabled: false
|
|
66
|
+
}]
|
|
67
|
+
}
|
|
68
|
+
return html`
|
|
69
|
+
<div>
|
|
70
|
+
<!-- Attribute selection -->
|
|
71
|
+
<settings-panel displayName="attributes" expanded="${true}">
|
|
72
|
+
<attributes-panel .attributeRefs="${this.widgetConfig.attributeRefs}" multi="${true}" onlyDataAttrs="${true}" .attributeFilter="${attributeFilter}" style="padding-bottom: 12px;"
|
|
73
|
+
.attributeLabelCallback="${attributeLabelCallback}" .attributeActionCallback="${attributeActionCallback}"
|
|
74
|
+
@attribute-action="${(ev: AttributeActionEvent) => this.onAttributeAction(ev)}"
|
|
75
|
+
@attribute-select="${(ev: AttributesSelectEvent) => this.onAttributesSelect(ev)}"
|
|
76
|
+
></attributes-panel>
|
|
77
|
+
</settings-panel>
|
|
78
|
+
|
|
79
|
+
<!-- Display options -->
|
|
80
|
+
<settings-panel displayName="display" expanded="${true}">
|
|
81
|
+
<div style="padding-bottom: 12px; display: flex; flex-direction: column; gap: 6px;">
|
|
82
|
+
<!-- Timeframe -->
|
|
83
|
+
<div>
|
|
84
|
+
<or-mwc-input .type="${InputType.SELECT}" label="${i18next.t('timeframeDefault')}" style="width: 100%;"
|
|
85
|
+
.options="${Array.from(this.timePresetOptions.keys())}" value="${this.widgetConfig.defaultTimePresetKey}"
|
|
86
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onTimePresetSelect(ev)}"
|
|
87
|
+
></or-mwc-input>
|
|
88
|
+
</div>
|
|
89
|
+
<!-- Y Min/max options -->
|
|
90
|
+
<div>
|
|
91
|
+
<div class="switch-container">
|
|
92
|
+
<span><or-translate value="dashboard.allowTimerangeSelect"></or-translate></span>
|
|
93
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px;" .value="${!this.widgetConfig.showTimestampControls}"
|
|
94
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onTimestampControlsToggle(ev)}"
|
|
95
|
+
></or-mwc-input>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="switch-container">
|
|
98
|
+
<span><or-translate value="dashboard.showLegend"></or-translate></span>
|
|
99
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px;" .value="${this.widgetConfig.showLegend}"
|
|
100
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onShowLegendToggle(ev)}"
|
|
101
|
+
></or-mwc-input>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</settings-panel>
|
|
106
|
+
|
|
107
|
+
<!-- Axis configuration -->
|
|
108
|
+
<settings-panel displayName="dashboard.axisConfig" expanded="${true}">
|
|
109
|
+
<div style="padding-bottom: 12px; display: flex; flex-direction: column; gap: 16px;">
|
|
110
|
+
|
|
111
|
+
<!-- Left axis configuration -->
|
|
112
|
+
<div>
|
|
113
|
+
${when(isMultiAxis, () => html`
|
|
114
|
+
<div style="margin-bottom: 8px;">
|
|
115
|
+
<span><or-translate value="dashboard.leftAxis"></or-translate></span>
|
|
116
|
+
</div>
|
|
117
|
+
`)}
|
|
118
|
+
<div style="display: flex;">
|
|
119
|
+
${max !== undefined ? html`
|
|
120
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('yAxis') + ' ' + i18next.t('max')}" .value="${max}" style="width: 100%;"
|
|
121
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('left', 'max', ev)}"
|
|
122
|
+
></or-mwc-input>
|
|
123
|
+
` : html`
|
|
124
|
+
<or-mwc-input .type="${InputType.TEXT}" label="${i18next.t('yAxis') + ' ' + i18next.t('max')}" disabled="true" value="auto" style="width: 100%;"></or-mwc-input>
|
|
125
|
+
`}
|
|
126
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px 0 0;" .value="${max !== undefined}"
|
|
127
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueToggle('left', 'max', ev)}"
|
|
128
|
+
></or-mwc-input>
|
|
129
|
+
</div>
|
|
130
|
+
<div style="display: flex; margin-top: 12px;">
|
|
131
|
+
${min !== undefined ? html`
|
|
132
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('yAxis') + ' ' + i18next.t('min')}" .value="${min}" style="width: 100%;"
|
|
133
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('left', 'min', ev)}"
|
|
134
|
+
></or-mwc-input>
|
|
135
|
+
` : html`
|
|
136
|
+
<or-mwc-input .type="${InputType.TEXT}" label="${i18next.t('yAxis') + ' ' + i18next.t('min')}" disabled="true" value="auto" style="width: 100%;"></or-mwc-input>
|
|
137
|
+
`}
|
|
138
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px 0 0;" .value="${min !== undefined}"
|
|
139
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueToggle('left', 'min', ev)}"
|
|
140
|
+
></or-mwc-input>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<!-- Right axis configuration -->
|
|
145
|
+
${when(isMultiAxis, () => {
|
|
146
|
+
const rightMin = this.widgetConfig.chartOptions.options?.scales?.y1?.min;
|
|
147
|
+
const rightMax = this.widgetConfig.chartOptions.options?.scales?.y1?.max;
|
|
148
|
+
return html`
|
|
149
|
+
<div>
|
|
150
|
+
<div style="margin-bottom: 8px;">
|
|
151
|
+
<span><or-translate value="dashboard.rightAxis"></or-translate></span>
|
|
152
|
+
</div>
|
|
153
|
+
<div style="display: flex;">
|
|
154
|
+
${rightMax !== undefined ? html`
|
|
155
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('yAxis') + ' ' + i18next.t('max')}" .value="${rightMax}" style="width: 100%;"
|
|
156
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('right', 'max', ev)}"
|
|
157
|
+
></or-mwc-input>
|
|
158
|
+
` : html`
|
|
159
|
+
<or-mwc-input .type="${InputType.TEXT}" label="${i18next.t('yAxis') + ' ' + i18next.t('max')}" disabled="true" value="auto"
|
|
160
|
+
style="width: 100%;"></or-mwc-input>
|
|
161
|
+
`}
|
|
162
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px 0 0;" .value="${rightMax !== undefined}"
|
|
163
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueToggle('right', 'max', ev)}"
|
|
164
|
+
></or-mwc-input>
|
|
165
|
+
</div>
|
|
166
|
+
<div style="display: flex; margin-top: 12px;">
|
|
167
|
+
${rightMin !== undefined ? html`
|
|
168
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('yAxis') + ' ' + i18next.t('min')}" .value="${rightMin}" style="width: 100%;"
|
|
169
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('right', 'min', ev)}"
|
|
170
|
+
></or-mwc-input>
|
|
171
|
+
` : html`
|
|
172
|
+
<or-mwc-input .type="${InputType.TEXT}" label="${i18next.t('yAxis') + ' ' + i18next.t('min')}" disabled="true" value="auto"
|
|
173
|
+
style="width: 100%;"></or-mwc-input>
|
|
174
|
+
`}
|
|
175
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px 0 0;" .value="${rightMin !== undefined}"
|
|
176
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueToggle('right', 'min', ev)}"
|
|
177
|
+
></or-mwc-input>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
`
|
|
181
|
+
})}
|
|
182
|
+
</div>
|
|
183
|
+
</settings-panel>
|
|
184
|
+
|
|
185
|
+
<!-- Data sampling options -->
|
|
186
|
+
<settings-panel displayName="dataSampling" expanded="${true}">
|
|
187
|
+
<div style="padding-bottom: 12px; display: flex; flex-direction: column; gap: 12px;">
|
|
188
|
+
<div>
|
|
189
|
+
<or-mwc-input .type="${InputType.SELECT}" style="width: 100%" .options="${Array.from(this.samplingOptions.keys())}" .value="${samplingValue}"
|
|
190
|
+
label="${i18next.t('algorithm')}" @or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onSamplingQueryChange(ev)}"
|
|
191
|
+
></or-mwc-input>
|
|
192
|
+
</div>
|
|
193
|
+
<div>
|
|
194
|
+
${this.getSamplingOptionsTemplate(this.widgetConfig.datapointQuery.type)}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</settings-panel>
|
|
198
|
+
</div>
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected getSamplingOptionsTemplate(type: any): TemplateResult {
|
|
203
|
+
switch (type) {
|
|
204
|
+
case 'interval': {
|
|
205
|
+
const intervalQuery = this.widgetConfig.datapointQuery as AssetDatapointIntervalQuery;
|
|
206
|
+
const formulaOptions = [AssetDatapointIntervalQueryFormula.AVG, AssetDatapointIntervalQueryFormula.MIN, AssetDatapointIntervalQueryFormula.MAX];
|
|
207
|
+
return html`
|
|
208
|
+
<or-mwc-input .type="${InputType.SELECT}" style="width: 100%;" .options="${formulaOptions}"
|
|
209
|
+
.value="${intervalQuery.formula}" label="${i18next.t('algorithmMethod')}" @or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
210
|
+
intervalQuery.formula = event.detail.value;
|
|
211
|
+
this.notifyConfigUpdate();
|
|
212
|
+
}}"
|
|
213
|
+
></or-mwc-input>
|
|
214
|
+
`;
|
|
215
|
+
}
|
|
216
|
+
default:
|
|
217
|
+
return html``;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// When a user clicks on ANY action in the attribute list, we want to switch between LEFT and RIGHT axis.
|
|
222
|
+
// Since that is the only action, there is no need to check the ev.action variable.
|
|
223
|
+
protected onAttributeAction(ev: AttributeActionEvent) {
|
|
224
|
+
if(this.widgetConfig.attributeRefs.indexOf(ev.detail.attributeRef) >= 0) {
|
|
225
|
+
if(this.widgetConfig.rightAxisAttributes.includes(ev.detail.attributeRef)) {
|
|
226
|
+
this.removeFromRightAxis(ev.detail.attributeRef);
|
|
227
|
+
} else {
|
|
228
|
+
this.addToRightAxis(ev.detail.attributeRef);
|
|
229
|
+
}
|
|
230
|
+
this.notifyConfigUpdate();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// When the list of attributeRefs is changed by the asset selector,
|
|
235
|
+
// we should remove the "right axis" references for the attributes that got removed.
|
|
236
|
+
// Also update the WidgetConfig attributeRefs field as usual
|
|
237
|
+
protected onAttributesSelect(ev: AttributesSelectEvent) {
|
|
238
|
+
const removedAttributeRefs = this.widgetConfig.attributeRefs.filter(ar => !ev.detail.attributeRefs.includes(ar));
|
|
239
|
+
removedAttributeRefs.forEach(raf => this.removeFromRightAxis(raf));
|
|
240
|
+
this.widgetConfig.attributeRefs = ev.detail.attributeRefs;
|
|
241
|
+
this.notifyConfigUpdate();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
protected addToRightAxis(attributeRef: AttributeRef, notify = false) {
|
|
245
|
+
if(!this.widgetConfig.rightAxisAttributes.includes(attributeRef)) {
|
|
246
|
+
this.widgetConfig.rightAxisAttributes.push(attributeRef);
|
|
247
|
+
if(notify) {
|
|
248
|
+
this.notifyConfigUpdate();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
protected removeFromRightAxis(attributeRef: AttributeRef, notify = false) {
|
|
254
|
+
if(this.widgetConfig.rightAxisAttributes.includes(attributeRef)) {
|
|
255
|
+
this.widgetConfig.rightAxisAttributes.splice(this.widgetConfig.rightAxisAttributes.indexOf(attributeRef), 1);
|
|
256
|
+
if(notify) {
|
|
257
|
+
this.notifyConfigUpdate();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
protected onTimePresetSelect(ev: OrInputChangedEvent) {
|
|
263
|
+
this.widgetConfig.defaultTimePresetKey = ev.detail.value.toString();
|
|
264
|
+
this.notifyConfigUpdate();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
protected onTimestampControlsToggle(ev: OrInputChangedEvent) {
|
|
268
|
+
this.widgetConfig.showTimestampControls = !ev.detail.value;
|
|
269
|
+
this.notifyConfigUpdate();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
protected onShowLegendToggle(ev: OrInputChangedEvent) {
|
|
273
|
+
this.widgetConfig.showLegend = ev.detail.value;
|
|
274
|
+
this.notifyConfigUpdate();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
protected setAxisMinMaxValue(axis: 'left' | 'right', type: 'min' | 'max', value?: number) {
|
|
278
|
+
if(axis === 'left') {
|
|
279
|
+
if(type === 'min') {
|
|
280
|
+
this.widgetConfig.chartOptions.options.scales.y.min = value;
|
|
281
|
+
} else {
|
|
282
|
+
this.widgetConfig.chartOptions.options.scales.y.max = value;
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
if(type === 'min') {
|
|
286
|
+
this.widgetConfig.chartOptions.options.scales.y1.min = value;
|
|
287
|
+
} else {
|
|
288
|
+
this.widgetConfig.chartOptions.options.scales.y1.max = value;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
this.notifyConfigUpdate();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
protected onMinMaxValueChange(axis: 'left' | 'right', type: 'min' | 'max', ev: OrInputChangedEvent) {
|
|
295
|
+
this.setAxisMinMaxValue(axis, type, ev.detail.value);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
protected onMinMaxValueToggle(axis: 'left' | 'right', type: 'min' | 'max', ev: OrInputChangedEvent) {
|
|
299
|
+
this.setAxisMinMaxValue(axis, type, (ev.detail.value ? (type === 'min' ? 0 : 100) : undefined));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
protected onSamplingQueryChange(ev: OrInputChangedEvent) {
|
|
303
|
+
this.widgetConfig.datapointQuery.type = this.samplingOptions.get(ev.detail.value)! as any;
|
|
304
|
+
this.notifyConfigUpdate();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {html, TemplateResult} from "lit";
|
|
2
|
+
import {customElement} from "lit/decorators.js";
|
|
3
|
+
import {WidgetSettings} from "../util/widget-settings";
|
|
4
|
+
import {i18next} from "@openremote/or-translate";
|
|
5
|
+
import {GaugeWidgetConfig} from "../widgets/gauge-widget";
|
|
6
|
+
import {AttributesSelectEvent} from "../panels/attributes-panel";
|
|
7
|
+
import {Attribute} from "@openremote/model";
|
|
8
|
+
import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
|
|
9
|
+
import {ThresholdChangeEvent} from "../panels/thresholds-panel";
|
|
10
|
+
import "../panels/thresholds-panel";
|
|
11
|
+
|
|
12
|
+
@customElement("gauge-settings")
|
|
13
|
+
export class GaugeSettings extends WidgetSettings {
|
|
14
|
+
|
|
15
|
+
// Override of widgetConfig with extended type
|
|
16
|
+
protected widgetConfig!: GaugeWidgetConfig;
|
|
17
|
+
|
|
18
|
+
protected render(): TemplateResult {
|
|
19
|
+
const attributeFilter: (attr: Attribute<any>) => boolean = (attr): boolean => {
|
|
20
|
+
return ["positiveInteger", "positiveNumber", "number", "long", "integer", "bigInteger", "negativeInteger", "negativeNumber", "bigNumber", "integerByte", "direction"].includes(attr.type!)
|
|
21
|
+
};
|
|
22
|
+
return html`
|
|
23
|
+
<div>
|
|
24
|
+
<!-- Attribute selection -->
|
|
25
|
+
<settings-panel displayName="attributes" expanded="${true}">
|
|
26
|
+
<attributes-panel .attributeRefs="${this.widgetConfig.attributeRefs}" .attributeFilter="${attributeFilter}" style="padding-bottom: 12px;"
|
|
27
|
+
@attribute-select="${(ev: AttributesSelectEvent) => this.onAttributesSelect(ev)}"
|
|
28
|
+
></attributes-panel>
|
|
29
|
+
</settings-panel>
|
|
30
|
+
|
|
31
|
+
<!-- Min/max and decimals options-->
|
|
32
|
+
<settings-panel displayName="values" expanded="${true}">
|
|
33
|
+
<div style="padding-bottom: 12px; display: flex; flex-direction: column; gap: 12px;">
|
|
34
|
+
<div style="display: flex; gap: 8px;">
|
|
35
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('min')}" .max="${this.widgetConfig.max}" .value="${this.widgetConfig.min}"
|
|
36
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('min', ev)}"
|
|
37
|
+
></or-mwc-input>
|
|
38
|
+
<or-mwc-input .type="${InputType.NUMBER}" label="${i18next.t('max')}" .min="${this.widgetConfig.min}" .value="${this.widgetConfig.max}"
|
|
39
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onMinMaxValueChange('max', ev)}"
|
|
40
|
+
></or-mwc-input>
|
|
41
|
+
</div>
|
|
42
|
+
<div>
|
|
43
|
+
<or-mwc-input .type="${InputType.NUMBER}" style="width: 100%;" .value="${this.widgetConfig.decimals}" label="${i18next.t('decimals')}" .min="${0}"
|
|
44
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onDecimalsChange(ev)}"
|
|
45
|
+
></or-mwc-input>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</settings-panel>
|
|
49
|
+
|
|
50
|
+
<!-- Thresholds panel -->
|
|
51
|
+
<settings-panel displayName="thresholds" expanded="${true}">
|
|
52
|
+
<thresholds-panel .thresholds="${this.widgetConfig.thresholds}" .valueType="${'number'}" style="padding-bottom: 12px;"
|
|
53
|
+
.min="${this.widgetConfig.min}" .max="${this.widgetConfig.max}"
|
|
54
|
+
@threshold-change="${(ev: ThresholdChangeEvent) => this.onThresholdChange(ev)}">
|
|
55
|
+
</thresholds-panel>
|
|
56
|
+
</settings-panel>
|
|
57
|
+
</div>
|
|
58
|
+
`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// When new attributes get selected
|
|
62
|
+
// Update the displayName to the new asset and attribute name.
|
|
63
|
+
protected onAttributesSelect(ev: AttributesSelectEvent) {
|
|
64
|
+
this.widgetConfig.attributeRefs = ev.detail.attributeRefs;
|
|
65
|
+
if(ev.detail.attributeRefs.length === 1) {
|
|
66
|
+
const attributeRef = ev.detail.attributeRefs[0];
|
|
67
|
+
const asset = ev.detail.assets.find((asset) => asset.id === attributeRef.id);
|
|
68
|
+
this.setDisplayName!(asset ? `${asset.name} - ${attributeRef.name}` : `${attributeRef.name}`);
|
|
69
|
+
}
|
|
70
|
+
this.notifyConfigUpdate();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
protected onMinMaxValueChange(type: 'min' | 'max', ev: OrInputChangedEvent) {
|
|
74
|
+
switch (type) {
|
|
75
|
+
case "max":
|
|
76
|
+
this.widgetConfig.max = ev.detail.value; break;
|
|
77
|
+
case "min":
|
|
78
|
+
this.widgetConfig.min = ev.detail.value; break;
|
|
79
|
+
}
|
|
80
|
+
this.notifyConfigUpdate();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected onDecimalsChange(ev: OrInputChangedEvent) {
|
|
84
|
+
this.widgetConfig.decimals = ev.detail.value;
|
|
85
|
+
this.notifyConfigUpdate();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected onThresholdChange(ev: ThresholdChangeEvent) {
|
|
89
|
+
this.widgetConfig.thresholds = ev.detail;
|
|
90
|
+
this.notifyConfigUpdate();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import {css, html, PropertyValues, TemplateResult } from "lit";
|
|
2
|
+
import { customElement, state } from "lit/decorators.js";
|
|
3
|
+
import {ImageWidgetConfig} from "../widgets/image-widget";
|
|
4
|
+
import {i18next} from "@openremote/or-translate";
|
|
5
|
+
import {AttributesSelectEvent} from "../panels/attributes-panel";
|
|
6
|
+
import {Asset, AssetModelUtil, AttributeRef} from "@openremote/model";
|
|
7
|
+
import { map } from "lit/directives/map.js";
|
|
8
|
+
import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
|
|
9
|
+
import {Util} from "@openremote/core";
|
|
10
|
+
import { when } from "lit/directives/when.js";
|
|
11
|
+
import {AssetWidgetSettings} from "../util/or-asset-widget";
|
|
12
|
+
|
|
13
|
+
const styling = css`
|
|
14
|
+
#marker-container {
|
|
15
|
+
display: flex;
|
|
16
|
+
justify-content: flex-end;
|
|
17
|
+
align-items: center;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
@customElement("image-settings")
|
|
22
|
+
export class ImageSettings extends AssetWidgetSettings {
|
|
23
|
+
|
|
24
|
+
// Override of widgetConfig with extended type
|
|
25
|
+
protected readonly widgetConfig!: ImageWidgetConfig;
|
|
26
|
+
|
|
27
|
+
static get styles() {
|
|
28
|
+
return [...super.styles, styling]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected willUpdate(changedProps: PropertyValues) {
|
|
32
|
+
super.willUpdate(changedProps);
|
|
33
|
+
if(changedProps.has('widgetConfig') && this.widgetConfig) {
|
|
34
|
+
this.updateCoordinateMap(this.widgetConfig);
|
|
35
|
+
this.loadAssets();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected loadAssets() {
|
|
40
|
+
const missingAssets = this.widgetConfig.attributeRefs.filter(ref => !this.isAttributeRefLoaded(ref));
|
|
41
|
+
if(missingAssets.length > 0) {
|
|
42
|
+
this.fetchAssets(this.widgetConfig.attributeRefs).then((assets) => {
|
|
43
|
+
if(assets === undefined) {
|
|
44
|
+
this.loadedAssets = [];
|
|
45
|
+
} else {
|
|
46
|
+
this.loadedAssets = assets;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected render(): TemplateResult {
|
|
53
|
+
return html`
|
|
54
|
+
<div>
|
|
55
|
+
<!-- Attributes selector -->
|
|
56
|
+
<settings-panel displayName="'attributes" expanded="${true}">
|
|
57
|
+
<attributes-panel .attributeRefs="${this.widgetConfig.attributeRefs}" onlyDataAttrs="${false}" multi="${true}"
|
|
58
|
+
@attribute-select="${(ev: AttributesSelectEvent) => this.onAttributesSelect(ev)}"
|
|
59
|
+
></attributes-panel>
|
|
60
|
+
</settings-panel>
|
|
61
|
+
|
|
62
|
+
<!-- Marker coordinates -->
|
|
63
|
+
<settings-panel displayName="dashboard.markerCoordinates" expanded="${true}">
|
|
64
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
65
|
+
${map(this.draftCoordinateEntries(this.widgetConfig), template => template)}
|
|
66
|
+
</div>
|
|
67
|
+
</settings-panel>
|
|
68
|
+
|
|
69
|
+
<!-- Image settings -->
|
|
70
|
+
<settings-panel displayName="dashboard.imageSettings" expanded="${true}">
|
|
71
|
+
<div>
|
|
72
|
+
<or-mwc-input style="width: 100%;" type="${InputType.TEXT}" label="${i18next.t('dashboard.imageUrl')}" .value="${this.widgetConfig.imagePath}"
|
|
73
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onImageUrlUpdate(ev)}"
|
|
74
|
+
></or-mwc-input>
|
|
75
|
+
</div>
|
|
76
|
+
</settings-panel>
|
|
77
|
+
</div>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected onAttributesSelect(ev: AttributesSelectEvent) {
|
|
82
|
+
this.widgetConfig.attributeRefs = ev.detail.attributeRefs;
|
|
83
|
+
this.notifyConfigUpdate();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected onImageUrlUpdate(ev: OrInputChangedEvent) {
|
|
87
|
+
this.widgetConfig.imagePath = ev.detail.value;
|
|
88
|
+
this.notifyConfigUpdate();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/* -------------------------------------- */
|
|
93
|
+
|
|
94
|
+
// updates coordinate map according to the attributeRef entries per id
|
|
95
|
+
updateCoordinateMap(config: ImageWidgetConfig) {
|
|
96
|
+
for (let i = 0; i < config.attributeRefs.length; i++) {
|
|
97
|
+
const attributeRef = config.attributeRefs[i];
|
|
98
|
+
if (attributeRef === undefined) {
|
|
99
|
+
console.error('attributeRef is undefined');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const index = config.markers.findIndex(m => m.attributeRef.id === attributeRef.id && m.attributeRef.name === attributeRef.name);
|
|
103
|
+
if (index === -1) {
|
|
104
|
+
config.markers.push({
|
|
105
|
+
attributeRef: attributeRef,
|
|
106
|
+
coordinates: [50, 50]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private draftCoordinateEntries(config: ImageWidgetConfig): TemplateResult[] {
|
|
113
|
+
const min = 0;
|
|
114
|
+
const max = 100;
|
|
115
|
+
|
|
116
|
+
if (config.markers.length > 0) {
|
|
117
|
+
return config.attributeRefs.map((attributeRef) => {
|
|
118
|
+
const marker = config.markers.find(m => m.attributeRef.id === attributeRef.id && m.attributeRef.name === attributeRef.name);
|
|
119
|
+
if(marker === undefined) {
|
|
120
|
+
console.error("A marker could not be found during drafting coordinate entries.");
|
|
121
|
+
return html``;
|
|
122
|
+
}
|
|
123
|
+
const index = config.markers.indexOf(marker);
|
|
124
|
+
const coordinates = marker.coordinates;
|
|
125
|
+
const asset = this.loadedAssets?.find(a => a.id === attributeRef.id);
|
|
126
|
+
let label: string | undefined;
|
|
127
|
+
if(asset) {
|
|
128
|
+
const attribute = asset.attributes![attributeRef.name!];
|
|
129
|
+
const descriptors = AssetModelUtil.getAttributeAndValueDescriptors(asset.type, attributeRef.name, attribute);
|
|
130
|
+
label = Util.getAttributeLabel(attribute, descriptors[0], asset.type, false);
|
|
131
|
+
}
|
|
132
|
+
return html`
|
|
133
|
+
<div id="marker-container">
|
|
134
|
+
<div style="flex: 1; display: flex; flex-direction: column;">
|
|
135
|
+
<span>${this.loadedAssets?.find(a => a.id === attributeRef.id)?.name}</span>
|
|
136
|
+
${when(label, () => html`
|
|
137
|
+
<span style="color: gray;">${label}</span>
|
|
138
|
+
`)}
|
|
139
|
+
</div>
|
|
140
|
+
<div style="display: flex; gap: 8px;">
|
|
141
|
+
<or-mwc-input .disableSliderNumberInput="${true}" compact style="max-width: 64px;"
|
|
142
|
+
.type="${InputType.NUMBER}" .min="${min}" .max="${max}" .value="${coordinates[0]}"
|
|
143
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onCoordinateUpdate(index, 'x', ev.detail.value)}"
|
|
144
|
+
></or-mwc-input>
|
|
145
|
+
|
|
146
|
+
<or-mwc-input .disableSliderNumberInput="${true}" compact style="max-width: 64px;"
|
|
147
|
+
.type="${InputType.NUMBER}" .min="${min}" .max="${max}" .value="${coordinates[1]}"
|
|
148
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onCoordinateUpdate(index, 'y', ev.detail.value)}"
|
|
149
|
+
></or-mwc-input>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
`;
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
return [
|
|
156
|
+
html`<span><or-translate value="noAttributeConnected"></or-translate></span>`
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
protected onCoordinateUpdate(index: number, coordinate: 'x' | 'y', value: number) {
|
|
162
|
+
let coords = this.widgetConfig.markers[index].coordinates;
|
|
163
|
+
if(!coords) {
|
|
164
|
+
coords = [0, 0];
|
|
165
|
+
}
|
|
166
|
+
if(coordinate === 'x') {
|
|
167
|
+
coords[0] = value;
|
|
168
|
+
} else if(coordinate === 'y') {
|
|
169
|
+
coords[1] = value;
|
|
170
|
+
}
|
|
171
|
+
this.widgetConfig.markers[index].coordinates = coords;
|
|
172
|
+
this.notifyConfigUpdate();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
}
|