@openremote/or-dashboard-builder 1.2.0-snapshot.20240512154942
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-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-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/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,106 @@
|
|
|
1
|
+
import {css, html, TemplateResult } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import {AssetWidgetSettings} from "../util/or-asset-widget";
|
|
4
|
+
import {i18next} from "@openremote/or-translate";
|
|
5
|
+
import {KpiWidgetConfig} from "../widgets/kpi-widget";
|
|
6
|
+
import {AttributesSelectEvent} from "../panels/attributes-panel";
|
|
7
|
+
import {Attribute, AttributeRef} from "@openremote/model";
|
|
8
|
+
import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
|
|
9
|
+
|
|
10
|
+
const styling = css`
|
|
11
|
+
.switchMwcInputContainer {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
@customElement("kpi-settings")
|
|
19
|
+
export class KpiSettings extends AssetWidgetSettings {
|
|
20
|
+
|
|
21
|
+
protected widgetConfig!: KpiWidgetConfig;
|
|
22
|
+
|
|
23
|
+
static get styles() {
|
|
24
|
+
return [...super.styles, styling];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected render(): TemplateResult {
|
|
28
|
+
const attributeFilter: (attr: Attribute<any>) => boolean = (attr): boolean => {
|
|
29
|
+
return ["positiveInteger", "positiveNumber", "number", "long", "integer", "bigInteger", "negativeInteger", "negativeNumber", "bigNumber", "integerByte", "direction"].includes(attr.type!)
|
|
30
|
+
};
|
|
31
|
+
return html`
|
|
32
|
+
<div>
|
|
33
|
+
<!-- Attribute selector -->
|
|
34
|
+
<settings-panel displayName="attributes" expanded="${true}">
|
|
35
|
+
<attributes-panel .attributeRefs="${this.widgetConfig.attributeRefs}" onlyDataAttrs="${false}" .attributeFilter="${attributeFilter}" style="padding-bottom: 12px;"
|
|
36
|
+
@attribute-select="${(ev: AttributesSelectEvent) => this.onAttributesSelect(ev)}"
|
|
37
|
+
></attributes-panel>
|
|
38
|
+
</settings-panel>
|
|
39
|
+
|
|
40
|
+
<!-- Display settings -->
|
|
41
|
+
<settings-panel displayName="display" expanded="${true}">
|
|
42
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
43
|
+
<or-mwc-input .type="${InputType.SELECT}" style="width: 100%;"
|
|
44
|
+
.options="${['year', 'month', 'week', 'day', 'hour']}"
|
|
45
|
+
.value="${this.widgetConfig.period}" label="${i18next.t('timeframe')}"
|
|
46
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onTimeframeSelect(ev)}"
|
|
47
|
+
></or-mwc-input>
|
|
48
|
+
<div class="switchMwcInputContainer">
|
|
49
|
+
<span><or-translate value="dashboard.allowTimerangeSelect"></or-translate></span>
|
|
50
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="margin: 0 -10px;" .value="${this.widgetConfig.showTimestampControls}"
|
|
51
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onTimeframeToggle(ev)}"
|
|
52
|
+
></or-mwc-input>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</settings-panel>
|
|
56
|
+
|
|
57
|
+
<settings-panel displayName="values" expanded="${true}">
|
|
58
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
59
|
+
<or-mwc-input .type="${InputType.SELECT}" style="width: 100%;" .options="${['absolute', 'percentage']}" .value="${this.widgetConfig.deltaFormat}"
|
|
60
|
+
label="${i18next.t('dashboard.showValueAs')}"
|
|
61
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onDeltaFormatSelect(ev)}"
|
|
62
|
+
></or-mwc-input>
|
|
63
|
+
<or-mwc-input .type="${InputType.NUMBER}" style="width: 100%;" .value="${this.widgetConfig.decimals}" label="${i18next.t('decimals')}"
|
|
64
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onDecimalsChange(ev)}"
|
|
65
|
+
></or-mwc-input>
|
|
66
|
+
</div>
|
|
67
|
+
</settings-panel>
|
|
68
|
+
|
|
69
|
+
<!-- -->
|
|
70
|
+
</div>
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// When new attributes get selected
|
|
75
|
+
// Update the displayName to the new asset and attribute name.
|
|
76
|
+
protected onAttributesSelect(ev: AttributesSelectEvent) {
|
|
77
|
+
this.widgetConfig.attributeRefs = ev.detail.attributeRefs;
|
|
78
|
+
if(ev.detail.attributeRefs.length === 1) {
|
|
79
|
+
const attributeRef = ev.detail.attributeRefs[0];
|
|
80
|
+
const asset = ev.detail.assets.find((asset) => asset.id === attributeRef.id);
|
|
81
|
+
this.setDisplayName!(asset ? `${asset.name} - ${attributeRef.name}` : `${attributeRef.name}`);
|
|
82
|
+
}
|
|
83
|
+
this.notifyConfigUpdate();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
protected onTimeframeSelect(ev: OrInputChangedEvent) {
|
|
87
|
+
this.widgetConfig.period = ev.detail.value;
|
|
88
|
+
this.notifyConfigUpdate();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected onTimeframeToggle(ev: OrInputChangedEvent) {
|
|
92
|
+
this.widgetConfig.showTimestampControls = ev.detail.value;
|
|
93
|
+
this.notifyConfigUpdate();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected onDeltaFormatSelect(ev: OrInputChangedEvent) {
|
|
97
|
+
this.widgetConfig.deltaFormat = ev.detail.value;
|
|
98
|
+
this.notifyConfigUpdate();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected onDecimalsChange(ev: OrInputChangedEvent) {
|
|
102
|
+
this.widgetConfig.decimals = ev.detail.value;
|
|
103
|
+
this.notifyConfigUpdate();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {css, html, TemplateResult} from "lit";
|
|
2
|
+
import {customElement} from "lit/decorators.js";
|
|
3
|
+
import {AssetWidgetSettings} from "../util/or-asset-widget";
|
|
4
|
+
import {i18next} from "@openremote/or-translate";
|
|
5
|
+
import {InputType, OrInputChangedEvent} from "@openremote/or-mwc-components/or-mwc-input";
|
|
6
|
+
import {MapWidgetConfig} from "../widgets/map-widget";
|
|
7
|
+
import {LngLatLike} from "@openremote/or-map";
|
|
8
|
+
import "../panels/assettypes-panel";
|
|
9
|
+
import "../panels/thresholds-panel";
|
|
10
|
+
import {LngLat} from "maplibre-gl"; // TODO: Replace this import
|
|
11
|
+
import {when} from "lit/directives/when.js";
|
|
12
|
+
import {AssetTypeSelectEvent, AssetTypesFilterConfig, AttributeNamesSelectEvent} from "../panels/assettypes-panel";
|
|
13
|
+
import manager from "@openremote/core";
|
|
14
|
+
import {showSnackbar} from "@openremote/or-mwc-components/or-mwc-snackbar";
|
|
15
|
+
import {ThresholdChangeEvent} from "../panels/thresholds-panel";
|
|
16
|
+
|
|
17
|
+
const styling = css`
|
|
18
|
+
.switchMwcInputContainer {
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
@customElement("map-settings")
|
|
26
|
+
export class MapSettings extends AssetWidgetSettings {
|
|
27
|
+
|
|
28
|
+
protected widgetConfig!: MapWidgetConfig;
|
|
29
|
+
|
|
30
|
+
static get styles() {
|
|
31
|
+
return [...super.styles, styling];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected render(): TemplateResult {
|
|
35
|
+
const allowedValueTypes = ["boolean", "number", "positiveInteger", "positiveNumber", "negativeInteger", "negativeNumber", "text"];
|
|
36
|
+
const config = {
|
|
37
|
+
attributes: {
|
|
38
|
+
enabled: true,
|
|
39
|
+
valueTypes: allowedValueTypes
|
|
40
|
+
}
|
|
41
|
+
} as AssetTypesFilterConfig;
|
|
42
|
+
return html`
|
|
43
|
+
<div>
|
|
44
|
+
|
|
45
|
+
<!-- Map settings -->
|
|
46
|
+
<settings-panel displayName="configuration.mapSettings" expanded="${true}">
|
|
47
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
48
|
+
<div>
|
|
49
|
+
<or-mwc-input .type="${InputType.NUMBER}" style="width: 100%;"
|
|
50
|
+
.value="${this.widgetConfig.zoom}" label="${i18next.t('dashboard.zoom')}"
|
|
51
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onZoomUpdate(ev)}"
|
|
52
|
+
></or-mwc-input>
|
|
53
|
+
</div>
|
|
54
|
+
<div style="display: flex; gap: 8px;">
|
|
55
|
+
<or-mwc-input .type="${InputType.TEXT}" style="width: 100%;"
|
|
56
|
+
.value="${this.widgetConfig.center ? (Object.values(this.widgetConfig.center))[0] + ', ' + (Object.values(this.widgetConfig.center))[1] : undefined}"
|
|
57
|
+
label="${i18next.t('dashboard.center')}"
|
|
58
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onCenterUpdate(ev)}"
|
|
59
|
+
></or-mwc-input>
|
|
60
|
+
</div>
|
|
61
|
+
<div style="display: flex; justify-content: space-between; align-items: center;">
|
|
62
|
+
<span><or-translate value="dashboard.showGeoJson"></or-translate></span>
|
|
63
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="width: 70px;"
|
|
64
|
+
.value="${this.widgetConfig.showGeoJson}"
|
|
65
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onGeoJsonToggle(ev)}"
|
|
66
|
+
></or-mwc-input>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</settings-panel>
|
|
70
|
+
|
|
71
|
+
<!-- Panel where Asset type and the selected attribute can be customized -->
|
|
72
|
+
<settings-panel displayName="attributes" expanded="${true}">
|
|
73
|
+
<assettypes-panel .assetType="${this.widgetConfig.assetType}" .attributeNames="${this.widgetConfig.attributeName}" .config="${config}"
|
|
74
|
+
@assettype-select="${(ev: AssetTypeSelectEvent) => this.onAssetTypeSelect(ev)}"
|
|
75
|
+
@attributenames-select="${(ev: AttributeNamesSelectEvent) => this.onAttributeNameSelect(ev)}"
|
|
76
|
+
></assettypes-panel>
|
|
77
|
+
|
|
78
|
+
<!-- Other settings like labels and units-->
|
|
79
|
+
<div>
|
|
80
|
+
<div class="switchMwcInputContainer">
|
|
81
|
+
<span><or-translate value="dashboard.showLabels"></or-translate></span>
|
|
82
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="width: 70px;"
|
|
83
|
+
.value="${this.widgetConfig.showLabels}" .disabled="${!this.widgetConfig.assetType}"
|
|
84
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onShowLabelsToggle(ev)}"
|
|
85
|
+
></or-mwc-input>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="switchMwcInputContainer">
|
|
88
|
+
<span><or-translate value="dashboard.showUnits"></or-translate></span>
|
|
89
|
+
<or-mwc-input .type="${InputType.SWITCH}" style="width: 70px;"
|
|
90
|
+
.value="${this.widgetConfig.showUnits}" .disabled="${!this.widgetConfig.showLabels || !this.widgetConfig.assetType}"
|
|
91
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onShowUnitsToggle(ev)}"
|
|
92
|
+
></or-mwc-input>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</settings-panel>
|
|
96
|
+
|
|
97
|
+
<!-- List of customizable thresholds -->
|
|
98
|
+
${when(this.widgetConfig.assetIds.length > 0, () => html`
|
|
99
|
+
<settings-panel displayName="thresholds" expanded="${true}">
|
|
100
|
+
<thresholds-panel .thresholds="${this.widgetConfig.thresholds}" .valueType="${this.widgetConfig.valueType}" style="padding-bottom: 12px;"
|
|
101
|
+
.min="${this.widgetConfig.min}" .max="${this.widgetConfig.max}"
|
|
102
|
+
@threshold-change="${(ev: ThresholdChangeEvent) => this.onThresholdsChange(ev)}">
|
|
103
|
+
</thresholds-panel>
|
|
104
|
+
</settings-panel>
|
|
105
|
+
`)}
|
|
106
|
+
</div>
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
protected onZoomUpdate(ev: OrInputChangedEvent) {
|
|
111
|
+
this.widgetConfig.zoom = ev.detail.value;
|
|
112
|
+
this.notifyConfigUpdate();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected onCenterUpdate(ev: OrInputChangedEvent) {
|
|
116
|
+
if (ev.detail.value) {
|
|
117
|
+
const lngLatArr = (ev.detail.value as string).split(/[, ]/).filter(v => !!v);
|
|
118
|
+
if (lngLatArr.length === 2) {
|
|
119
|
+
const value = new LngLat(
|
|
120
|
+
Number.parseFloat(lngLatArr[0]),
|
|
121
|
+
Number.parseFloat(lngLatArr[1])
|
|
122
|
+
);
|
|
123
|
+
this.widgetConfig.center = value as LngLatLike;
|
|
124
|
+
this.notifyConfigUpdate();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected onGeoJsonToggle(ev: OrInputChangedEvent) {
|
|
130
|
+
this.widgetConfig.showGeoJson = ev.detail.value;
|
|
131
|
+
this.notifyConfigUpdate();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
protected onAssetTypeSelect(ev: AssetTypeSelectEvent) {
|
|
135
|
+
if (this.widgetConfig.assetType !== ev.detail) {
|
|
136
|
+
this.widgetConfig.attributeName = undefined;
|
|
137
|
+
this.widgetConfig.assetIds = [];
|
|
138
|
+
this.widgetConfig.showLabels = false;
|
|
139
|
+
this.widgetConfig.showUnits = false;
|
|
140
|
+
this.widgetConfig.boolColors = {type: 'boolean', 'false': '#ef5350', 'true': '#4caf50'};
|
|
141
|
+
this.widgetConfig.textColors = [['example', "#4caf50"], ['example2', "#ff9800"]];
|
|
142
|
+
this.widgetConfig.thresholds = [[0, "#4caf50"], [75, "#ff9800"], [90, "#ef5350"]];
|
|
143
|
+
this.widgetConfig.assetType = ev.detail;
|
|
144
|
+
this.notifyConfigUpdate();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
protected async onAttributeNameSelect(ev: AttributeNamesSelectEvent) {
|
|
149
|
+
const attrName = ev.detail as string;
|
|
150
|
+
this.widgetConfig.attributeName = attrName;
|
|
151
|
+
await manager.rest.api.AssetResource.queryAssets({
|
|
152
|
+
realm: {
|
|
153
|
+
name: manager.displayRealm
|
|
154
|
+
},
|
|
155
|
+
select: {
|
|
156
|
+
attributes: [attrName, 'location']
|
|
157
|
+
},
|
|
158
|
+
types: [this.widgetConfig.assetType!],
|
|
159
|
+
}).then(response => {
|
|
160
|
+
this.widgetConfig.assetIds = response.data.map((a) => a.id!);
|
|
161
|
+
this.widgetConfig.valueType = (response.data.length > 0) ? response.data[0].attributes![attrName].type : "text"; // sometimes no asset exists of that assetType, so using 'text' as fallback.
|
|
162
|
+
}).catch((reason) => {
|
|
163
|
+
console.error(reason);
|
|
164
|
+
showSnackbar(undefined, "errorOccurred");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
this.notifyConfigUpdate()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected onShowLabelsToggle(ev: OrInputChangedEvent) {
|
|
171
|
+
this.widgetConfig.showLabels = ev.detail.value;
|
|
172
|
+
this.notifyConfigUpdate();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
protected onShowUnitsToggle(ev: OrInputChangedEvent) {
|
|
176
|
+
this.widgetConfig.showUnits = ev.detail.value;
|
|
177
|
+
this.notifyConfigUpdate();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
protected onThresholdsChange(ev: ThresholdChangeEvent) {
|
|
181
|
+
this.widgetConfig.thresholds = ev.detail;
|
|
182
|
+
this.notifyConfigUpdate();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {css, html, TemplateResult } from "lit";
|
|
2
|
+
import { customElement } from "lit/decorators.js";
|
|
3
|
+
import {AssetWidgetSettings} from "../util/or-asset-widget";
|
|
4
|
+
import {TableWidgetConfig} from "../widgets/table-widget";
|
|
5
|
+
import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
|
|
6
|
+
import {AssetIdsSelectEvent, AssetTypeSelectEvent, AssetTypesFilterConfig, AttributeNamesSelectEvent} from "../panels/assettypes-panel";
|
|
7
|
+
|
|
8
|
+
const styling = css`
|
|
9
|
+
.customMwcInputContainer {
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
@customElement("table-settings")
|
|
17
|
+
export class TableSettings extends AssetWidgetSettings {
|
|
18
|
+
|
|
19
|
+
protected widgetConfig!: TableWidgetConfig;
|
|
20
|
+
|
|
21
|
+
static get styles() {
|
|
22
|
+
return [...super.styles, styling];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected render(): TemplateResult {
|
|
26
|
+
const config = {
|
|
27
|
+
assets: {
|
|
28
|
+
enabled: true,
|
|
29
|
+
multi: true
|
|
30
|
+
},
|
|
31
|
+
attributes: {
|
|
32
|
+
enabled: true,
|
|
33
|
+
multi: true
|
|
34
|
+
}
|
|
35
|
+
} as AssetTypesFilterConfig
|
|
36
|
+
return html`
|
|
37
|
+
<div>
|
|
38
|
+
<!-- Asset type, assets, and attribute picker -->
|
|
39
|
+
<settings-panel displayName="attributes" expanded="${true}">
|
|
40
|
+
<div style="padding-bottom: 12px;">
|
|
41
|
+
<assettypes-panel .assetType="${this.widgetConfig.assetType}" .config="${config}"
|
|
42
|
+
.assetIds="${this.widgetConfig.assetIds}" .attributeNames="${this.widgetConfig.attributeNames}"
|
|
43
|
+
@assettype-select="${(ev: AssetTypeSelectEvent) => this.onAssetTypeSelect(ev)}"
|
|
44
|
+
@assetids-select="${(ev: AssetIdsSelectEvent) => this.onAssetIdsSelect(ev)}"
|
|
45
|
+
@attributenames-select="${(ev: AttributeNamesSelectEvent) => this.onAttributesSelect(ev)}"
|
|
46
|
+
></assettypes-panel>
|
|
47
|
+
</div>
|
|
48
|
+
</settings-panel>
|
|
49
|
+
|
|
50
|
+
<!-- Table settings like amount of rows -->
|
|
51
|
+
<settings-panel displayName="dashboard.tableSettings" expanded="${true}">
|
|
52
|
+
<div style="padding-bottom: 12px;">
|
|
53
|
+
<div class="customMwcInputContainer">
|
|
54
|
+
<span style="min-width: 180px"><or-translate value="dashboard.numberOfRows"></or-translate></span>
|
|
55
|
+
<or-mwc-input type="${InputType.SELECT}" .options="${[10, 25, 100]}" .value="${this.widgetConfig.tableSize}"
|
|
56
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => this.onTableSizeSelect(ev)}"
|
|
57
|
+
></or-mwc-input>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</settings-panel>
|
|
61
|
+
</div>
|
|
62
|
+
`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected onAssetTypeSelect(ev: AssetTypeSelectEvent) {
|
|
66
|
+
this.widgetConfig.assetType = ev.detail;
|
|
67
|
+
this.widgetConfig.assetIds = [];
|
|
68
|
+
this.widgetConfig.attributeNames = [];
|
|
69
|
+
this.notifyConfigUpdate();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected onAssetIdsSelect(ev: AssetIdsSelectEvent) {
|
|
73
|
+
this.widgetConfig.assetIds = ev.detail as string[];
|
|
74
|
+
this.notifyConfigUpdate();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected onAttributesSelect(ev: AttributeNamesSelectEvent) {
|
|
78
|
+
this.widgetConfig.attributeNames = ev.detail as string[];
|
|
79
|
+
this.notifyConfigUpdate();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
protected onTableSizeSelect(ev: OrInputChangedEvent) {
|
|
83
|
+
const value = ev.detail.value || 10;
|
|
84
|
+
this.widgetConfig.tableSize = value;
|
|
85
|
+
if(value !== 10) {
|
|
86
|
+
this.widgetConfig.tableOptions = [value];
|
|
87
|
+
} else {
|
|
88
|
+
this.widgetConfig.tableOptions = [10, 25, 100]
|
|
89
|
+
}
|
|
90
|
+
this.notifyConfigUpdate();
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/style.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { css, unsafeCSS } from "lit";
|
|
2
|
+
import {DefaultColor1, DefaultColor2, DefaultColor4, DefaultColor5} from "@openremote/core";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// language=CSS
|
|
6
|
+
export const style = css`
|
|
7
|
+
|
|
8
|
+
:host {
|
|
9
|
+
--internal-or-asset-tree-header-color: var(--or-asset-tree-header-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)}));
|
|
10
|
+
--internal-or-asset-tree-header-text-color: var(--or-asset-tree-header-text-color, var(--or-app-color7, ${unsafeCSS(DefaultColor1)}));
|
|
11
|
+
--internal-or-asset-tree-header-menu-background-color: var(--or-asset-tree-header-menu-background-color, var(--internal-or-asset-tree-header-text-color));
|
|
12
|
+
--internal-or-asset-tree-header-menu-text-color: var(--or-asset-tree-header-menu-text-color, inherit);
|
|
13
|
+
--internal-or-asset-tree-header-height: var(--or-asset-tree-header-height, 48px);
|
|
14
|
+
--internal-or-asset-tree-background-color: var(--or-asset-tree-background-color, var(--or-app-color1, ${unsafeCSS(DefaultColor1)}));
|
|
15
|
+
--internal-or-asset-tree-text-color: var(--or-asset-tree-text-color, inherit);
|
|
16
|
+
--internal-or-asset-tree-item-height: var(--or-asset-tree-item-height, 24px);
|
|
17
|
+
--internal-or-asset-tree-item-padding: var(--or-asset-tree-item-padding, 10px);
|
|
18
|
+
--internal-or-asset-tree-selected-background-color: var(--or-asset-tree-selected-background-color, var(--or-app-color2, ${unsafeCSS(DefaultColor2)}));
|
|
19
|
+
--internal-or-asset-tree-selected-color: var(--or-asset-tree-selected-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)}));
|
|
20
|
+
--internal-or-asset-tree-button-color: var(--or-asset-tree-button-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)}));
|
|
21
|
+
--internal-or-asset-tree-line-color: var(--or-asset-tree-line-color, var(--or-app-color5, ${unsafeCSS(DefaultColor5)}));
|
|
22
|
+
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
height: 100%;
|
|
26
|
+
width: 100%;
|
|
27
|
+
background-color: var(--internal-or-asset-tree-background-color);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@media only screen and (max-width: 640px){
|
|
31
|
+
.hideMobile {
|
|
32
|
+
display: none !important;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
@media only screen and (min-width: 641px){
|
|
36
|
+
.showMobile {
|
|
37
|
+
display: none !important;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#container {
|
|
42
|
+
display: flex;
|
|
43
|
+
width: 100%;
|
|
44
|
+
height: 100%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#menu-header {
|
|
48
|
+
background-color: var(--internal-or-asset-tree-header-color);
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
width: 100%;
|
|
52
|
+
height: var(--internal-or-asset-tree-header-height);
|
|
53
|
+
border-bottom: 1px solid var(--or-app-color5, ${unsafeCSS(DefaultColor5)});
|
|
54
|
+
z-index: 3;
|
|
55
|
+
line-height: var(--internal-or-asset-tree-header-height);
|
|
56
|
+
color: var(--internal-or-asset-tree-header-text-color);
|
|
57
|
+
--or-icon-fill: var(--internal-or-asset-tree-header-text-color);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#title-container {
|
|
61
|
+
flex: 1;
|
|
62
|
+
flex-direction: row;
|
|
63
|
+
text-transform: capitalize;
|
|
64
|
+
padding-left: 14px;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
text-overflow: ellipsis;
|
|
67
|
+
white-space: nowrap;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#title {
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
font-size: 16px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.expandableHeader {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
padding: 12px;
|
|
79
|
+
background: none;
|
|
80
|
+
border-top: 1px solid var(--or-app-color5, ${unsafeCSS(DefaultColor5)});
|
|
81
|
+
border-right: none;
|
|
82
|
+
border-bottom: none;
|
|
83
|
+
border-left: none;
|
|
84
|
+
border-radius: 0;
|
|
85
|
+
width: 100%;
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
font-weight: 700;
|
|
88
|
+
line-height: 1em;
|
|
89
|
+
color: var(--internal-or-asset-viewer-title-text-color);
|
|
90
|
+
flex: 0 0 auto;
|
|
91
|
+
}
|
|
92
|
+
.expandableHeader > or-icon {
|
|
93
|
+
--or-icon-height: 20px;
|
|
94
|
+
--or-icon-width: 20px;
|
|
95
|
+
}
|
|
96
|
+
.panel-title {
|
|
97
|
+
text-transform: uppercase;
|
|
98
|
+
font-weight: bolder;
|
|
99
|
+
line-height: 1em;
|
|
100
|
+
color: var(--internal-or-asset-viewer-title-text-color);
|
|
101
|
+
/*margin-bottom: 20px;*/
|
|
102
|
+
flex: 0 0 auto;
|
|
103
|
+
}
|
|
104
|
+
`
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {OrWidget} from "./or-widget";
|
|
2
|
+
import {Asset, AssetQuery, Attribute, AttributeRef} from "@openremote/model";
|
|
3
|
+
import { state } from "lit/decorators.js";
|
|
4
|
+
import manager from "@openremote/core";
|
|
5
|
+
import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar";
|
|
6
|
+
import {WidgetConfig} from "./widget-config";
|
|
7
|
+
import {WidgetSettings} from "./widget-settings";
|
|
8
|
+
import { CSSResult } from "lit";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* OrAssetWidget class
|
|
12
|
+
*
|
|
13
|
+
* It is an extension on base class OrWidget,
|
|
14
|
+
* where some asset-specific methods such as fetching, are already defined for ease of use.
|
|
15
|
+
* For example it is used in the chart-, gauge- and kpi widget
|
|
16
|
+
* */
|
|
17
|
+
export abstract class OrAssetWidget extends OrWidget {
|
|
18
|
+
|
|
19
|
+
@state() // cached assets
|
|
20
|
+
protected loadedAssets: Asset[] = [];
|
|
21
|
+
|
|
22
|
+
@state() // cached attribute list; [asset index of the loadedAssets array, attribute]
|
|
23
|
+
protected assetAttributes: [number, Attribute<any>][] = [];
|
|
24
|
+
|
|
25
|
+
static get styles(): CSSResult[] {
|
|
26
|
+
return [...super.styles];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fetching the assets according to the AttributeRef[] input in DashboardWidget if required.
|
|
30
|
+
protected async fetchAssets(attributeRefs: AttributeRef[] = []) {
|
|
31
|
+
return fetchAssetsByAttributeRef(attributeRefs);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected async queryAssets(assetQuery: AssetQuery) {
|
|
35
|
+
return fetchAssets(assetQuery);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected isAssetLoaded(assetId: string) {
|
|
39
|
+
return isAssetIdLoaded(this.loadedAssets, assetId);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected isAttributeRefLoaded(attributeRef: AttributeRef) {
|
|
43
|
+
return isAssetIdLoaded(this.loadedAssets, attributeRef.id!);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* AssetWidgetSettings class
|
|
50
|
+
*
|
|
51
|
+
* It is an extension on base class WidgetSettings
|
|
52
|
+
* where some asset-specific methods such as fetching, are already defined for ease of use.
|
|
53
|
+
* For example it is used in the chart-, gauge- and kpi widget settings
|
|
54
|
+
* */
|
|
55
|
+
export abstract class AssetWidgetSettings extends WidgetSettings {
|
|
56
|
+
|
|
57
|
+
@state() // cached assets
|
|
58
|
+
protected loadedAssets: Asset[] = [];
|
|
59
|
+
|
|
60
|
+
protected async fetchAssets(attributeRefs: AttributeRef[] = []) {
|
|
61
|
+
return fetchAssetsByAttributeRef(attributeRefs);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected async queryAssets(assetQuery: AssetQuery) {
|
|
65
|
+
return fetchAssets(assetQuery);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected isAssetLoaded(assetId: string) {
|
|
69
|
+
return isAssetIdLoaded(this.loadedAssets, assetId)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected isAttributeRefLoaded(attributeRef: AttributeRef) {
|
|
73
|
+
return isAssetIdLoaded(this.loadedAssets, attributeRef.id!);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/* ---------------------------------------------------------- */
|
|
81
|
+
|
|
82
|
+
// GENERIC FUNCTIONS
|
|
83
|
+
|
|
84
|
+
// Simple async function for fetching assets by attributeRefs
|
|
85
|
+
async function fetchAssetsByAttributeRef(attributeRefs: AttributeRef[] = []) {
|
|
86
|
+
const assetIds = attributeRefs.map(ar => ar.id!);
|
|
87
|
+
const attributeNames = attributeRefs.map(ar => ar.name!);
|
|
88
|
+
return fetchAssets({
|
|
89
|
+
ids: attributeRefs?.map((x: AttributeRef) => x.id) as string[],
|
|
90
|
+
select: {
|
|
91
|
+
attributes: attributeRefs?.map((x: AttributeRef) => x.name) as string[]
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function fetchAssets(assetQuery: AssetQuery) {
|
|
97
|
+
let assets: Asset[] = [];
|
|
98
|
+
assetQuery.realm = { name: manager.displayRealm };
|
|
99
|
+
await manager.rest.api.AssetResource.queryAssets(assetQuery).then(response => {
|
|
100
|
+
assets = response.data;
|
|
101
|
+
}).catch((reason) => {
|
|
102
|
+
console.error(reason);
|
|
103
|
+
showSnackbar(undefined, "errorOccurred");
|
|
104
|
+
});
|
|
105
|
+
return assets;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isAssetIdLoaded(loadedAssets: Asset[] | undefined, assetId: string) {
|
|
109
|
+
return loadedAssets?.find(asset => asset.id === assetId) !== undefined;
|
|
110
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {CSSResult, LitElement, TemplateResult } from "lit";
|
|
2
|
+
import { property } from "lit/decorators.js";
|
|
3
|
+
import {WidgetConfig} from "./widget-config";
|
|
4
|
+
import {WidgetSettings} from "./widget-settings";
|
|
5
|
+
|
|
6
|
+
export interface WidgetManifest {
|
|
7
|
+
displayName: string,
|
|
8
|
+
displayIcon: string,
|
|
9
|
+
minColumnWidth?: number,
|
|
10
|
+
minColumnHeight?: number,
|
|
11
|
+
minPixelWidth?: number,
|
|
12
|
+
minPixelHeight?: number
|
|
13
|
+
getContentHtml(config: WidgetConfig): OrWidget,
|
|
14
|
+
getSettingsHtml(config: WidgetConfig): WidgetSettings,
|
|
15
|
+
getDefaultConfig(): WidgetConfig
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Main OrWidget class where all widgets extend their functionality on.
|
|
19
|
+
// It contains several methods used for rendering by the parent component; OrDashboardWidget
|
|
20
|
+
export abstract class OrWidget extends LitElement {
|
|
21
|
+
|
|
22
|
+
protected static manifest: WidgetManifest;
|
|
23
|
+
|
|
24
|
+
@property({type: Object})
|
|
25
|
+
protected readonly widgetConfig!: WidgetConfig;
|
|
26
|
+
|
|
27
|
+
constructor(config: WidgetConfig) {
|
|
28
|
+
super();
|
|
29
|
+
this.widgetConfig = config;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get styles(): CSSResult[] {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* --------------------------- */
|
|
37
|
+
|
|
38
|
+
static getManifest(): WidgetManifest {
|
|
39
|
+
if (!this.manifest) {
|
|
40
|
+
throw new Error(`No manifest present on ${this.name}`);
|
|
41
|
+
}
|
|
42
|
+
return this.manifest;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Method used for refreshing the content in a widget.
|
|
46
|
+
// This can be customized to lower the performance- and/or visual impact of a refresh.
|
|
47
|
+
public abstract refreshContent(force: boolean): void;
|
|
48
|
+
|
|
49
|
+
// WebComponent lifecycle method to render HTML content
|
|
50
|
+
protected abstract render(): TemplateResult;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/* ------------------------------------- */
|
|
54
|
+
|
|
55
|
+
public getDisplayName?: () => string | undefined;
|
|
56
|
+
|
|
57
|
+
public getEditMode?: () => boolean;
|
|
58
|
+
|
|
59
|
+
public getWidgetLocation?: () => { x?: number, y?: number, h?: number, w?: number }
|
|
60
|
+
}
|