@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.
Files changed (167) hide show
  1. package/README.md +156 -0
  2. package/build.gradle +15 -0
  3. package/lib/controls/dashboard-refresh-controls.d.ts +17 -0
  4. package/lib/controls/dashboard-refresh-controls.js +17 -0
  5. package/lib/controls/dashboard-refresh-controls.js.map +1 -0
  6. package/lib/index.d.ts +71 -0
  7. package/lib/index.js +285 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/or-dashboard-boardsettings.d.ts +19 -0
  10. package/lib/or-dashboard-boardsettings.js +121 -0
  11. package/lib/or-dashboard-boardsettings.js.map +1 -0
  12. package/lib/or-dashboard-browser.d.ts +9 -0
  13. package/lib/or-dashboard-browser.js +35 -0
  14. package/lib/or-dashboard-browser.js.map +1 -0
  15. package/lib/or-dashboard-engine.d.ts +4 -0
  16. package/lib/or-dashboard-engine.js +1 -0
  17. package/lib/or-dashboard-engine.js.map +1 -0
  18. package/lib/or-dashboard-keyhandler.d.ts +6 -0
  19. package/lib/or-dashboard-keyhandler.js +1 -0
  20. package/lib/or-dashboard-keyhandler.js.map +1 -0
  21. package/lib/or-dashboard-preview.d.ts +70 -0
  22. package/lib/or-dashboard-preview.js +155 -0
  23. package/lib/or-dashboard-preview.js.map +1 -0
  24. package/lib/or-dashboard-settingspanel.d.ts +44 -0
  25. package/lib/or-dashboard-settingspanel.js +216 -0
  26. package/lib/or-dashboard-settingspanel.js.map +1 -0
  27. package/lib/or-dashboard-tree.d.ts +25 -0
  28. package/lib/or-dashboard-tree.js +62 -0
  29. package/lib/or-dashboard-tree.js.map +1 -0
  30. package/lib/or-dashboard-widget.d.ts +18 -0
  31. package/lib/or-dashboard-widget.js +15 -0
  32. package/lib/or-dashboard-widget.js.map +1 -0
  33. package/lib/or-dashboard-widgetcontainer.d.ts +23 -0
  34. package/lib/or-dashboard-widgetcontainer.js +20 -0
  35. package/lib/or-dashboard-widgetcontainer.js.map +1 -0
  36. package/lib/or-dashboard-widgetsettings.d.ts +15 -0
  37. package/lib/or-dashboard-widgetsettings.js +16 -0
  38. package/lib/or-dashboard-widgetsettings.js.map +1 -0
  39. package/lib/panels/assettypes-panel.d.ts +44 -0
  40. package/lib/panels/assettypes-panel.js +51 -0
  41. package/lib/panels/assettypes-panel.js.map +1 -0
  42. package/lib/panels/attributes-panel.d.ts +41 -0
  43. package/lib/panels/attributes-panel.js +126 -0
  44. package/lib/panels/attributes-panel.js.map +1 -0
  45. package/lib/panels/thresholds-panel.d.ts +39 -0
  46. package/lib/panels/thresholds-panel.js +129 -0
  47. package/lib/panels/thresholds-panel.js.map +1 -0
  48. package/lib/service/dashboard-service.d.ts +13 -0
  49. package/lib/service/dashboard-service.js +1 -0
  50. package/lib/service/dashboard-service.js.map +1 -0
  51. package/lib/service/widget-service.d.ts +8 -0
  52. package/lib/service/widget-service.js +1 -0
  53. package/lib/service/widget-service.js.map +1 -0
  54. package/lib/settings/attribute-input-settings.d.ts +13 -0
  55. package/lib/settings/attribute-input-settings.js +36 -0
  56. package/lib/settings/attribute-input-settings.js.map +1 -0
  57. package/lib/settings/chart-settings.d.ts +30 -0
  58. package/lib/settings/chart-settings.js +144 -0
  59. package/lib/settings/chart-settings.js.map +1 -0
  60. package/lib/settings/gauge-settings.d.ts +15 -0
  61. package/lib/settings/gauge-settings.js +37 -0
  62. package/lib/settings/gauge-settings.js.map +1 -0
  63. package/lib/settings/image-settings.d.ts +17 -0
  64. package/lib/settings/image-settings.js +52 -0
  65. package/lib/settings/image-settings.js.map +1 -0
  66. package/lib/settings/kpi-settings.d.ts +15 -0
  67. package/lib/settings/kpi-settings.js +47 -0
  68. package/lib/settings/kpi-settings.js.map +1 -0
  69. package/lib/settings/map-settings.d.ts +21 -0
  70. package/lib/settings/map-settings.js +72 -0
  71. package/lib/settings/map-settings.js.map +1 -0
  72. package/lib/settings/table-settings.d.ts +14 -0
  73. package/lib/settings/table-settings.js +33 -0
  74. package/lib/settings/table-settings.js.map +1 -0
  75. package/lib/style.d.ts +1 -0
  76. package/lib/style.js +99 -0
  77. package/lib/style.js.map +1 -0
  78. package/lib/util/or-asset-widget.d.ts +20 -0
  79. package/lib/util/or-asset-widget.js +1 -0
  80. package/lib/util/or-asset-widget.js.map +1 -0
  81. package/lib/util/or-widget.d.ts +31 -0
  82. package/lib/util/or-widget.js +1 -0
  83. package/lib/util/or-widget.js.map +1 -0
  84. package/lib/util/settings-panel.d.ts +9 -0
  85. package/lib/util/settings-panel.js +56 -0
  86. package/lib/util/settings-panel.js.map +1 -0
  87. package/lib/util/widget-config.d.ts +2 -0
  88. package/lib/util/widget-config.js +1 -0
  89. package/lib/util/widget-config.js.map +1 -0
  90. package/lib/util/widget-settings.d.ts +23 -0
  91. package/lib/util/widget-settings.js +1 -0
  92. package/lib/util/widget-settings.js.map +1 -0
  93. package/lib/widgets/attribute-input-widget.d.ts +24 -0
  94. package/lib/widgets/attribute-input-widget.js +31 -0
  95. package/lib/widgets/attribute-input-widget.js.map +1 -0
  96. package/lib/widgets/chart-widget.d.ts +25 -0
  97. package/lib/widgets/chart-widget.js +15 -0
  98. package/lib/widgets/chart-widget.js.map +1 -0
  99. package/lib/widgets/gauge-widget.d.ts +22 -0
  100. package/lib/widgets/gauge-widget.js +12 -0
  101. package/lib/widgets/gauge-widget.js.map +1 -0
  102. package/lib/widgets/image-widget.d.ts +25 -0
  103. package/lib/widgets/image-widget.js +54 -0
  104. package/lib/widgets/image-widget.js.map +1 -0
  105. package/lib/widgets/kpi-widget.d.ts +21 -0
  106. package/lib/widgets/kpi-widget.js +8 -0
  107. package/lib/widgets/kpi-widget.js.map +1 -0
  108. package/lib/widgets/map-widget.d.ts +39 -0
  109. package/lib/widgets/map-widget.js +9 -0
  110. package/lib/widgets/map-widget.js.map +1 -0
  111. package/lib/widgets/or-base-widget.d.ts +15 -0
  112. package/lib/widgets/or-base-widget.js +1 -0
  113. package/lib/widgets/or-base-widget.js.map +1 -0
  114. package/lib/widgets/or-chart-widget.d.ts +36 -0
  115. package/lib/widgets/or-chart-widget.js +112 -0
  116. package/lib/widgets/or-chart-widget.js.map +1 -0
  117. package/lib/widgets/or-gauge-widget.d.ts +46 -0
  118. package/lib/widgets/or-gauge-widget.js +60 -0
  119. package/lib/widgets/or-gauge-widget.js.map +1 -0
  120. package/lib/widgets/or-kpi-widget.d.ts +44 -0
  121. package/lib/widgets/or-kpi-widget.js +63 -0
  122. package/lib/widgets/or-kpi-widget.js.map +1 -0
  123. package/lib/widgets/or-map-widget.d.ts +49 -0
  124. package/lib/widgets/or-map-widget.js +75 -0
  125. package/lib/widgets/or-map-widget.js.map +1 -0
  126. package/lib/widgets/table-widget.d.ts +25 -0
  127. package/lib/widgets/table-widget.js +12 -0
  128. package/lib/widgets/table-widget.js.map +1 -0
  129. package/package.json +32 -0
  130. package/src/controls/dashboard-refresh-controls.ts +100 -0
  131. package/src/index.ts +731 -0
  132. package/src/or-dashboard-boardsettings.ts +249 -0
  133. package/src/or-dashboard-browser.ts +160 -0
  134. package/src/or-dashboard-engine.ts +17 -0
  135. package/src/or-dashboard-keyhandler.ts +25 -0
  136. package/src/or-dashboard-preview.ts +713 -0
  137. package/src/or-dashboard-tree.ts +304 -0
  138. package/src/or-dashboard-widgetcontainer.ts +155 -0
  139. package/src/or-dashboard-widgetsettings.ts +91 -0
  140. package/src/panels/assettypes-panel.ts +311 -0
  141. package/src/panels/attributes-panel.ts +304 -0
  142. package/src/panels/thresholds-panel.ts +285 -0
  143. package/src/service/dashboard-service.ts +89 -0
  144. package/src/service/widget-service.ts +48 -0
  145. package/src/settings/attribute-input-settings.ts +79 -0
  146. package/src/settings/chart-settings.ts +306 -0
  147. package/src/settings/gauge-settings.ts +93 -0
  148. package/src/settings/image-settings.ts +175 -0
  149. package/src/settings/kpi-settings.ts +106 -0
  150. package/src/settings/map-settings.ts +185 -0
  151. package/src/settings/table-settings.ts +92 -0
  152. package/src/style.ts +104 -0
  153. package/src/util/or-asset-widget.ts +110 -0
  154. package/src/util/or-widget.ts +60 -0
  155. package/src/util/settings-panel.ts +93 -0
  156. package/src/util/widget-config.ts +2 -0
  157. package/src/util/widget-settings.ts +58 -0
  158. package/src/widgets/attribute-input-widget.ts +143 -0
  159. package/src/widgets/chart-widget.ts +203 -0
  160. package/src/widgets/gauge-widget.ts +111 -0
  161. package/src/widgets/image-widget.ts +180 -0
  162. package/src/widgets/kpi-widget.ts +97 -0
  163. package/src/widgets/map-widget.ts +187 -0
  164. package/src/widgets/table-widget.ts +157 -0
  165. package/tsconfig.json +15 -0
  166. package/tsconfig.tsbuildinfo +1 -0
  167. 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
+ }