@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,249 @@
|
|
|
1
|
+
import {Dashboard, DashboardAccess, DashboardRefreshInterval, DashboardScalingPreset, DashboardScreenPreset} from "@openremote/model";
|
|
2
|
+
import {css, html, LitElement} from "lit";
|
|
3
|
+
import {customElement, property} from "lit/decorators.js";
|
|
4
|
+
import {InputType, OrInputChangedEvent} from "@openremote/or-mwc-components/or-mwc-input";
|
|
5
|
+
import {style} from './style';
|
|
6
|
+
import {i18next} from "@openremote/or-translate";
|
|
7
|
+
import {unsafeHTML} from 'lit/directives/unsafe-html.js';
|
|
8
|
+
import {when} from "lit/directives/when.js";
|
|
9
|
+
import {dashboardAccessToString, scalingPresetToString, sortScreenPresets} from ".";
|
|
10
|
+
|
|
11
|
+
//language=css
|
|
12
|
+
const boardSettingsStyling = css`
|
|
13
|
+
.label {
|
|
14
|
+
margin-bottom: 4px;
|
|
15
|
+
}
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
@customElement("or-dashboard-boardsettings")
|
|
19
|
+
export class OrDashboardBoardsettings extends LitElement {
|
|
20
|
+
|
|
21
|
+
@property()
|
|
22
|
+
protected readonly dashboard!: Dashboard;
|
|
23
|
+
|
|
24
|
+
@property()
|
|
25
|
+
protected readonly showPerms?: boolean;
|
|
26
|
+
|
|
27
|
+
/* ------------------------- */
|
|
28
|
+
|
|
29
|
+
static get styles() {
|
|
30
|
+
return [boardSettingsStyling, style]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
forceParentUpdate(force: boolean = false) {
|
|
34
|
+
this.dispatchEvent(new CustomEvent('update', {detail: {force: force}}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* -------------------------------- */
|
|
38
|
+
|
|
39
|
+
private setViewAccess(access: DashboardAccess) {
|
|
40
|
+
this.dashboard.viewAccess = access;
|
|
41
|
+
if (access === DashboardAccess.PRIVATE) {
|
|
42
|
+
this.dashboard.editAccess = DashboardAccess.PRIVATE; // if viewAccess changed to PRIVATE, make editAccess private as well.
|
|
43
|
+
}
|
|
44
|
+
this.requestUpdate();
|
|
45
|
+
this.forceParentUpdate(false);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private setEditAccess(access: DashboardAccess) {
|
|
49
|
+
this.dashboard.editAccess = access;
|
|
50
|
+
this.requestUpdate();
|
|
51
|
+
this.forceParentUpdate(false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private setBreakpoint(presetIndex: number, value: number) {
|
|
55
|
+
this.dashboard.template!.screenPresets![presetIndex].breakpoint = value;
|
|
56
|
+
this.requestUpdate();
|
|
57
|
+
this.forceParentUpdate(true);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private setRefreshInterval(interval: DashboardRefreshInterval) {
|
|
61
|
+
this.dashboard.template!.refreshInterval = interval;
|
|
62
|
+
this.requestUpdate();
|
|
63
|
+
this.forceParentUpdate(false);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
protected render() {
|
|
67
|
+
if (this.dashboard.template?.screenPresets != null) {
|
|
68
|
+
const screenPresets = sortScreenPresets(this.dashboard.template.screenPresets, true);
|
|
69
|
+
const viewAccessOptions = [DashboardAccess.PRIVATE, DashboardAccess.SHARED, DashboardAccess.PUBLIC].map((access) => ({key: access, value: dashboardAccessToString(access)}));
|
|
70
|
+
const editAccessOptions = [DashboardAccess.PRIVATE, DashboardAccess.SHARED].map((access) => ({key: access, value: dashboardAccessToString(access)}));
|
|
71
|
+
const refreshIntervalOptions = [DashboardRefreshInterval.OFF, DashboardRefreshInterval.ONE_MIN, DashboardRefreshInterval.FIVE_MIN, DashboardRefreshInterval.QUARTER, DashboardRefreshInterval.ONE_HOUR].map(interval => ({key: interval, value: `dashboard.interval.${interval.toLowerCase()}`}))
|
|
72
|
+
const scalingPresets: { key: DashboardScalingPreset, value: string }[] = [];
|
|
73
|
+
[DashboardScalingPreset.KEEP_LAYOUT, DashboardScalingPreset.WRAP_TO_SINGLE_COLUMN, /*DashboardScalingPreset.REDIRECT,*/ DashboardScalingPreset.BLOCK_DEVICE].forEach((preset: DashboardScalingPreset) => {
|
|
74
|
+
scalingPresets.push({key: preset, value: scalingPresetToString(preset)});
|
|
75
|
+
});
|
|
76
|
+
return html`
|
|
77
|
+
|
|
78
|
+
<!-- Permissions panel, to set who can view/edit the selected dashboard -->
|
|
79
|
+
${when(this.showPerms, () => html`
|
|
80
|
+
<settings-panel displayName="permissions" expanded="${true}">
|
|
81
|
+
<div>
|
|
82
|
+
<div style="margin-bottom: 24px;">
|
|
83
|
+
<div class="label">
|
|
84
|
+
${html`<span>${unsafeHTML(i18next.t('dashboard.whoCanView').toString())}</span>`}
|
|
85
|
+
</div>
|
|
86
|
+
<or-mwc-input class="permissionInput" comfortable type="${InputType.SELECT}" style="width: 100%;"
|
|
87
|
+
.options="${viewAccessOptions.map((access) => access.value)}"
|
|
88
|
+
.value="${dashboardAccessToString(this.dashboard.viewAccess!)}"
|
|
89
|
+
@or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
90
|
+
this.setViewAccess(viewAccessOptions.find((access) => access.value == event.detail.value)?.key!);
|
|
91
|
+
}}"
|
|
92
|
+
></or-mwc-input>
|
|
93
|
+
</div>
|
|
94
|
+
<div style="margin-bottom: 24px;">
|
|
95
|
+
<div class="label">
|
|
96
|
+
${html`<span>${unsafeHTML(i18next.t('dashboard.whoCanEdit').toString())}</span>`}
|
|
97
|
+
</div>
|
|
98
|
+
<or-mwc-input class="permissionInput" comfortable type="${InputType.SELECT}" style="width: 100%;"
|
|
99
|
+
.disabled="${(this.dashboard.viewAccess == DashboardAccess.PRIVATE)}"
|
|
100
|
+
.options="${editAccessOptions.map((access) => access.value)}"
|
|
101
|
+
.value="${dashboardAccessToString(this.dashboard.editAccess!)}"
|
|
102
|
+
@or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
103
|
+
this.setEditAccess(editAccessOptions.find((access) => access.value == event.detail.value)?.key!);
|
|
104
|
+
}}"
|
|
105
|
+
></or-mwc-input>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</settings-panel>
|
|
109
|
+
`)}
|
|
110
|
+
|
|
111
|
+
<!-- Layout panel, with options such as 'amount of columns' -->
|
|
112
|
+
<settings-panel displayName="layout" expanded="${true}">
|
|
113
|
+
<div>
|
|
114
|
+
|
|
115
|
+
<!-- Number of Columns control -->
|
|
116
|
+
<div style="margin-bottom: 24px; display: flex; align-items: center;">
|
|
117
|
+
<span style="min-width: 180px;"><or-translate value="dashboard.numberOfColumns"></or-translate></span>
|
|
118
|
+
<or-mwc-input type="${InputType.NUMBER}" comfortable .value="${this.dashboard.template.columns}" min="1" max="24" @or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
119
|
+
if (this.dashboard.template != null && event.detail.value as number <= 24 && event.detail.value as number >= 1) {
|
|
120
|
+
this.dashboard.template.columns = event.detail.value as number;
|
|
121
|
+
this.forceParentUpdate(true);
|
|
122
|
+
}
|
|
123
|
+
}}"></or-mwc-input>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- Scaling preset -->
|
|
127
|
+
${screenPresets.length === 1 ? html`
|
|
128
|
+
${this.scalingPresetTemplate(screenPresets, scalingPresets)}
|
|
129
|
+
` : undefined}
|
|
130
|
+
|
|
131
|
+
<!-- Screen preset -->
|
|
132
|
+
${screenPresets.length === 1 ? html`
|
|
133
|
+
${this.screenPresetTemplate(screenPresets, ['Mobile breakpoint'])}
|
|
134
|
+
` : undefined}
|
|
135
|
+
|
|
136
|
+
<!-- Max Screen Width control-->
|
|
137
|
+
<div style="margin-bottom: 24px; display: flex; align-items: center; justify-content: space-between;">
|
|
138
|
+
<span style="min-width: 150px;"><or-translate value="dashboard.maxScreenWidth"></or-translate></span>
|
|
139
|
+
<div>
|
|
140
|
+
<or-mwc-input type="${InputType.NUMBER}" comfortable .value="${this.dashboard.template.maxScreenWidth}" style="width: 70px;"
|
|
141
|
+
@or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
142
|
+
if (this.dashboard.template != null) {
|
|
143
|
+
this.dashboard.template.maxScreenWidth = event.detail.value as number;
|
|
144
|
+
this.forceParentUpdate(true);
|
|
145
|
+
}
|
|
146
|
+
}}">
|
|
147
|
+
</or-mwc-input>
|
|
148
|
+
<span style="margin-left: 2px;">px</span>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<!-- "Set to mobile" preset button, for ease of use -->
|
|
153
|
+
<div>
|
|
154
|
+
<or-mwc-input type="${InputType.BUTTON}" comfortable label="dashboard.setToMobilePreset"
|
|
155
|
+
@or-mwc-input-changed="${() => {
|
|
156
|
+
this.setToMobilePreset();
|
|
157
|
+
}}">
|
|
158
|
+
</or-mwc-input>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</settings-panel>
|
|
162
|
+
|
|
163
|
+
<!-- Data management options -->
|
|
164
|
+
<settings-panel displayName="dashboard.dataManagement" expanded="${true}">
|
|
165
|
+
<div>
|
|
166
|
+
<div class="label">
|
|
167
|
+
<span><or-translate value="dashboard.defaultRefreshInterval"></or-translate></span>
|
|
168
|
+
</div>
|
|
169
|
+
<or-mwc-input type="${InputType.SELECT}" comfortable .options="${refreshIntervalOptions.map(o => o.value)}" style="width: 100%;"
|
|
170
|
+
.value="${`dashboard.interval.${this.dashboard.template.refreshInterval?.toLowerCase() || `off`}`}"
|
|
171
|
+
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => {
|
|
172
|
+
const intervalEntry = refreshIntervalOptions.find(o => o.value === ev.detail.value);
|
|
173
|
+
if(intervalEntry) {
|
|
174
|
+
this.setRefreshInterval(intervalEntry.key)
|
|
175
|
+
}
|
|
176
|
+
}}">
|
|
177
|
+
</or-mwc-input>
|
|
178
|
+
</div>
|
|
179
|
+
</settings-panel>
|
|
180
|
+
|
|
181
|
+
`;
|
|
182
|
+
} else {
|
|
183
|
+
return html`
|
|
184
|
+
<div style="padding: 24px;">
|
|
185
|
+
<span><or-translate value="errorOccurred"></or-translate></span><br/>
|
|
186
|
+
<span><or-translate value="noDashboardFound"></or-translate></span>
|
|
187
|
+
</div>
|
|
188
|
+
`
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Button to switch to a mobile preset of layout settings.
|
|
193
|
+
// TODO: Make these values not hardcoded anymore, and depend on the getDefault functions in or-dashboard-tree (that probably should move to somewhere else)
|
|
194
|
+
setToMobilePreset() {
|
|
195
|
+
this.dashboard.template!.columns = 4;
|
|
196
|
+
this.dashboard.template!.screenPresets![0].scalingPreset = DashboardScalingPreset.KEEP_LAYOUT;
|
|
197
|
+
this.dashboard.template!.maxScreenWidth = 700;
|
|
198
|
+
this.requestUpdate();
|
|
199
|
+
this.forceParentUpdate(true);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
/* ======================== */
|
|
204
|
+
|
|
205
|
+
scalingPresetTemplate(screenPresets: DashboardScreenPreset[], scalingPresets: { key: DashboardScalingPreset, value: string }[]) {
|
|
206
|
+
return html`
|
|
207
|
+
${screenPresets.map((preset) => {
|
|
208
|
+
return html`
|
|
209
|
+
<div style="margin-bottom: ${screenPresets.length > 1 ? '24px' : '16px'}">
|
|
210
|
+
<div class="label">
|
|
211
|
+
${html`<span>${unsafeHTML(i18next.t("dashboard.onScreenMyBoardShould").replace("{{size}}", ("<b>" + i18next.t(preset.displayName!) + "</b>")))}</span>`}
|
|
212
|
+
</div>
|
|
213
|
+
<or-mwc-input class="displayInput" type="${InputType.SELECT}" comfortable style="width: 100%;"
|
|
214
|
+
.options="${scalingPresets.map((x) => x.value)}"
|
|
215
|
+
.value="${scalingPresets.find((p) => p.key == preset.scalingPreset)?.value}"
|
|
216
|
+
@or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
217
|
+
preset.scalingPreset = scalingPresets.find((p) => p.value == event.detail.value)?.key;
|
|
218
|
+
this.forceParentUpdate(true);
|
|
219
|
+
this.requestUpdate();
|
|
220
|
+
}}"
|
|
221
|
+
></or-mwc-input>
|
|
222
|
+
</div>
|
|
223
|
+
`
|
|
224
|
+
})}
|
|
225
|
+
`
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
screenPresetTemplate(screenPresets: DashboardScreenPreset[], customLabels?: string[]) {
|
|
229
|
+
return html`
|
|
230
|
+
${screenPresets.map((preset, index) => {
|
|
231
|
+
return html`
|
|
232
|
+
<div style="margin-bottom: 12px; display: flex; align-items: center; justify-content: space-between;">
|
|
233
|
+
<span style="min-width: 140px;">${customLabels ? customLabels[index] : (preset.displayName + " " + i18next.t('screen'))}</span>
|
|
234
|
+
<div>
|
|
235
|
+
<span style="margin-right: 4px;">${(screenPresets.indexOf(preset) == 0) ? '>' : '<'}</span>
|
|
236
|
+
<or-mwc-input type="${InputType.NUMBER}" comfortable .disabled="${(screenPresets.length > 1 && screenPresets.indexOf(preset) == 0)}" style="width: 70px;"
|
|
237
|
+
.value="${(screenPresets.length > 1 && screenPresets.indexOf(preset) == 0 ? screenPresets[1].breakpoint : preset.breakpoint)}"
|
|
238
|
+
@or-mwc-input-changed="${(event: OrInputChangedEvent) => {
|
|
239
|
+
this.setBreakpoint(screenPresets.indexOf(preset), event.detail.value)
|
|
240
|
+
}}"
|
|
241
|
+
></or-mwc-input>
|
|
242
|
+
<span style="margin-left: 2px;">px</span>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
`
|
|
246
|
+
})}
|
|
247
|
+
`
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {GridStack, GridStackNode } from "gridstack";
|
|
2
|
+
import {css, html, LitElement, unsafeCSS } from "lit";
|
|
3
|
+
import { customElement, state} from "lit/decorators.js";
|
|
4
|
+
import {style} from "./style";
|
|
5
|
+
import {widgetTypes} from "./index";
|
|
6
|
+
|
|
7
|
+
// TODO: Add webpack/rollup to build so consumers aren't forced to use the same tooling
|
|
8
|
+
const gridcss = require('gridstack/dist/gridstack.min.css');
|
|
9
|
+
const extracss = require('gridstack/dist/gridstack-extra.css');
|
|
10
|
+
|
|
11
|
+
//language=css
|
|
12
|
+
const browserStyling = css`
|
|
13
|
+
#sidebar {
|
|
14
|
+
display: grid;
|
|
15
|
+
padding: 16px;
|
|
16
|
+
}
|
|
17
|
+
#sidebarElement, #sidebarBgElement {
|
|
18
|
+
grid-column: 1;
|
|
19
|
+
grid-row: 1;
|
|
20
|
+
}
|
|
21
|
+
.grid-stack-item {
|
|
22
|
+
cursor: grab;
|
|
23
|
+
}
|
|
24
|
+
.sidebarItem {
|
|
25
|
+
height: 100%;
|
|
26
|
+
background: white;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
border: 1px solid #E0E0E0;
|
|
32
|
+
border-radius: 8px;
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
--or-icon-width: 36px;
|
|
37
|
+
}
|
|
38
|
+
.itemText {
|
|
39
|
+
margin-top: 10px;
|
|
40
|
+
}
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
@customElement("or-dashboard-browser")
|
|
44
|
+
export class OrDashboardBrowser extends LitElement {
|
|
45
|
+
|
|
46
|
+
@state()
|
|
47
|
+
private sidebarGrid?: GridStack;
|
|
48
|
+
|
|
49
|
+
@state()
|
|
50
|
+
private backgroundGrid?: GridStack;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
static get styles() {
|
|
54
|
+
return [unsafeCSS(gridcss), unsafeCSS(extracss), browserStyling, style];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
super();
|
|
59
|
+
this.updateComplete.then(() => {
|
|
60
|
+
this.renderGrid();
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* --------------------------------- */
|
|
65
|
+
|
|
66
|
+
protected renderGrid() {
|
|
67
|
+
const sidebarElement = this.shadowRoot?.getElementById("sidebarElement");
|
|
68
|
+
const coords: Array<[number, number]> = new Array<[number, number]>([0,0],[2,0], [0,2], [2,2], [0,4], [2,4], [0,6], [2,6], [0,8], [2,8]) // TODO: make this unlimited possibilities with a formula
|
|
69
|
+
const sidebarItems: any[] = Array.from(widgetTypes)
|
|
70
|
+
.sort((a, b) => a[1].displayName.localeCompare(b[1].displayName))
|
|
71
|
+
.map((typeArr, index) => {
|
|
72
|
+
return {x: coords[index][0], y: coords[index][1], w: 2, h: 2, widgetTypeId: typeArr[0], locked: true, content: `<div class="sidebarItem"><or-icon icon="${typeArr[1].displayIcon}"></or-icon><span class="itemText">${typeArr[1].displayName}</span>`}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Setting Sidebar height depending on sidebarItems
|
|
76
|
+
let sidebarHeight = 0;
|
|
77
|
+
sidebarItems.forEach((item) => {
|
|
78
|
+
if((item.y + item.h) > sidebarHeight) {
|
|
79
|
+
sidebarHeight = (item.y + item.h);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let newSidebarHeight = 0;
|
|
84
|
+
if (sidebarItems.length % 2 !== 0) {
|
|
85
|
+
// If sidebarHeight is based on nr of widgets in array & is not even add 1
|
|
86
|
+
newSidebarHeight = sidebarItems.length + 1
|
|
87
|
+
} else {
|
|
88
|
+
newSidebarHeight = sidebarItems.length
|
|
89
|
+
}
|
|
90
|
+
// Creation of the sidebarGrid. Only loads items if already existing
|
|
91
|
+
if(this.sidebarGrid !== undefined) {
|
|
92
|
+
this.sidebarGrid.removeAll();
|
|
93
|
+
this.sidebarGrid.load(sidebarItems);
|
|
94
|
+
} else {
|
|
95
|
+
this.sidebarGrid = GridStack.init({
|
|
96
|
+
acceptWidgets: false,
|
|
97
|
+
column: 4,
|
|
98
|
+
cellHeight: 67,
|
|
99
|
+
disableOneColumnMode: true,
|
|
100
|
+
disableResize: true,
|
|
101
|
+
draggable: {
|
|
102
|
+
appendTo: 'parent'
|
|
103
|
+
},
|
|
104
|
+
margin: 8,
|
|
105
|
+
row: newSidebarHeight
|
|
106
|
+
|
|
107
|
+
// @ts-ignore typechecking, because we can only provide an HTMLElement (which GridHTMLElement inherits)
|
|
108
|
+
}, sidebarElement);
|
|
109
|
+
|
|
110
|
+
this.sidebarGrid.load(sidebarItems);
|
|
111
|
+
|
|
112
|
+
// If an item gets dropped on the main grid, the dragged item needs to be reset to the sidebar.
|
|
113
|
+
// This is done by just loading the initial/original widget back in the sidebar.
|
|
114
|
+
// @ts-ignore typechecking since we assume they are not undefined
|
|
115
|
+
this.sidebarGrid.on('removed', (_event: Event, items: GridStackNode[]) => {
|
|
116
|
+
if(items.length === 1) {
|
|
117
|
+
const filteredItems = sidebarItems.filter(widget => {
|
|
118
|
+
return (widget.content === items[0].content);
|
|
119
|
+
});
|
|
120
|
+
this.sidebarGrid?.load([filteredItems[0]]);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// On any change, recreate the grid to prevent users moving Sidebar items around.
|
|
125
|
+
// @ts-ignore typechecking since we assume they are not undefined
|
|
126
|
+
this.sidebarGrid.on('change', (_event: Event, _items: GridStackNode[]) => {
|
|
127
|
+
this.renderGrid();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
// Seperate Static Background grid (to make it look like the items duplicate)
|
|
133
|
+
const sidebarBgElement = this.shadowRoot?.getElementById("sidebarBgElement");
|
|
134
|
+
if(this.backgroundGrid != undefined) {
|
|
135
|
+
this.backgroundGrid.removeAll();
|
|
136
|
+
this.backgroundGrid.load(sidebarItems);
|
|
137
|
+
|
|
138
|
+
} else {
|
|
139
|
+
this.backgroundGrid = GridStack.init({
|
|
140
|
+
staticGrid: true,
|
|
141
|
+
column: 4,
|
|
142
|
+
cellHeight: 67,
|
|
143
|
+
disableOneColumnMode: true,
|
|
144
|
+
margin: 8
|
|
145
|
+
|
|
146
|
+
// @ts-ignore typechecking, because we can only provide an HTMLElement (which GridHTMLElement inherits)
|
|
147
|
+
}, sidebarBgElement);
|
|
148
|
+
this.backgroundGrid.load(sidebarItems);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
protected render() {
|
|
153
|
+
return html`
|
|
154
|
+
<div id="sidebar">
|
|
155
|
+
<div id="sidebarElement" class="grid-stack" style="width: 100%; z-index: 3;"></div>
|
|
156
|
+
<div id="sidebarBgElement" class="grid-stack" style="width: 100%; z-index: 2"></div>
|
|
157
|
+
</div>
|
|
158
|
+
`
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GridStackEngine, GridStackNode, GridStackMoveOpts } from 'gridstack';
|
|
2
|
+
|
|
3
|
+
export class OrDashboardEngine extends GridStackEngine {
|
|
4
|
+
|
|
5
|
+
// Cancelling move when it collides with the same widget.
|
|
6
|
+
// Apparently during some rerenders, the widget moved downwards after colliding with itself.
|
|
7
|
+
// Now cancelling the movement when the IDs are the same.
|
|
8
|
+
public moveNode(node: GridStackNode, o: GridStackMoveOpts): boolean {
|
|
9
|
+
if(o.skip && o.skip.id == node.id) {
|
|
10
|
+
o.x = o.skip.x;
|
|
11
|
+
o.y = o.skip.y;
|
|
12
|
+
o.w = o.skip.w;
|
|
13
|
+
o.h = o.skip.h;
|
|
14
|
+
}
|
|
15
|
+
return super.moveNode(node, o);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
|
|
3
|
+
// Inspired by or-rules flow editor Input class
|
|
4
|
+
export class DashboardKeyEmitter extends EventEmitter {
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
window.addEventListener("keydown", this.onkeydown);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* ------------------------------------- */
|
|
12
|
+
|
|
13
|
+
private onkeydown = (e: KeyboardEvent) => {
|
|
14
|
+
if(e.key == 'Delete') {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
this.emit('delete', e);
|
|
17
|
+
} else if(e.key == 'Escape') {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
this.emit('deselect', e);
|
|
20
|
+
} else if(e.key == 's' && e.ctrlKey) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
this.emit('save', e);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|