iobroker.mywebui 1.42.45 → 1.42.47
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.mywebui",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.47",
|
|
4
4
|
"description": "ioBroker mywebui - Custom edited mywebui by gokturk413 with 3D Editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/backend/main.js",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@iobroker/adapter-core": "^3.3.2",
|
|
70
70
|
"@types/node": "^24.7.2",
|
|
71
|
+
"esbuild": "^0.28.1",
|
|
71
72
|
"three": "^0.184.0"
|
|
72
73
|
},
|
|
73
74
|
"devDependencies": {
|
|
@@ -112,4 +113,4 @@
|
|
|
112
113
|
"typescript-json-schema": "^0.65.1",
|
|
113
114
|
"wunderbaum": "0.13.0"
|
|
114
115
|
}
|
|
115
|
-
}
|
|
116
|
+
}
|
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
import { BaseCustomWebComponentConstructorAppend, css, html } from "@gokturk413/base-custom-webcomponent";
|
|
2
2
|
|
|
3
|
-
const EDITOR_BASE = new URL('../../../3d-editor/', import.meta.url).href;
|
|
4
|
-
|
|
5
3
|
export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponentConstructorAppend {
|
|
6
4
|
|
|
7
5
|
static template = html`
|
|
8
6
|
<div id="root">
|
|
9
|
-
<!-- Sub-tab bar -->
|
|
10
|
-
<div id="subBar">
|
|
11
|
-
<span class="stab active" data-sub="scene">Scene</span>
|
|
12
|
-
<span class="stab" data-sub="object">Object</span>
|
|
13
|
-
<span class="stab" data-sub="geom">Geom</span>
|
|
14
|
-
<span class="stab" data-sub="mater">Mater</span>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
<!-- Scene tab -->
|
|
18
|
-
<div id="subScene" class="spanel active"></div>
|
|
19
7
|
|
|
20
|
-
<!--
|
|
21
|
-
<div id="
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
<div id="bindRows"></div>
|
|
8
|
+
<!-- Selected object header -->
|
|
9
|
+
<div id="objHeader">
|
|
10
|
+
<span id="objType" class="htype"></span>
|
|
11
|
+
<span id="objName" class="hname">No object selected</span>
|
|
25
12
|
</div>
|
|
26
13
|
|
|
27
|
-
<!--
|
|
28
|
-
<div
|
|
29
|
-
|
|
30
|
-
<!-- Mater tab -->
|
|
31
|
-
<div id="subMater" class="spanel" style="display:none;"></div>
|
|
14
|
+
<!-- Section: 3D Bindings -->
|
|
15
|
+
<div class="sechdr">3D BINDINGS (IOBROKER STATES)</div>
|
|
16
|
+
<div id="bindRows"></div>
|
|
32
17
|
|
|
33
18
|
<!-- Context menu -->
|
|
34
19
|
<div id="ctxMenu">
|
|
@@ -40,12 +25,12 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
40
25
|
<!-- Binding dialog -->
|
|
41
26
|
<div id="bdOverlay">
|
|
42
27
|
<div id="bdBox">
|
|
43
|
-
<div id="bdTitle">Edit Binding of '<span id="bdPropLabel"></span>'
|
|
28
|
+
<div id="bdTitle">Edit Binding of '<span id="bdPropLabel"></span>'</div>
|
|
44
29
|
|
|
45
30
|
<div class="bdRow">
|
|
46
31
|
<div class="bdLbl">objects</div>
|
|
47
|
-
<div style="display:flex;gap:3px;
|
|
48
|
-
<input id="bdObj" type="text" class="bdInput" style="flex:1;" placeholder="state
|
|
32
|
+
<div style="display:flex;gap:3px;">
|
|
33
|
+
<input id="bdObj" type="text" class="bdInput" style="flex:1;" placeholder="mywebui.0.state.value" autocomplete="off"/>
|
|
49
34
|
<button class="bdBtn sm" id="bdObjX">X</button>
|
|
50
35
|
<button class="bdBtn sm" id="bdObjBrowse">...</button>
|
|
51
36
|
<button class="bdBtn sm accent" id="bdObjIOB">IOB</button>
|
|
@@ -53,20 +38,20 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
53
38
|
</div>
|
|
54
39
|
|
|
55
40
|
<div class="bdRow" style="display:flex;align-items:center;gap:8px;">
|
|
56
|
-
<span class="bdLbl" style="margin-bottom:0;">type :</span>
|
|
41
|
+
<span class="bdLbl" style="margin-bottom:0;min-width:40px;">type :</span>
|
|
57
42
|
<select id="bdType" class="bdSel">
|
|
58
43
|
<option value="signal">signal</option>
|
|
59
|
-
<option value="ignore"
|
|
44
|
+
<option value="ignore">ignore</option>
|
|
60
45
|
<option value="css">css</option>
|
|
61
46
|
<option value="attribute">attribute</option>
|
|
62
47
|
</select>
|
|
63
48
|
</div>
|
|
64
49
|
|
|
65
|
-
<div class="bdRow
|
|
50
|
+
<div class="bdRow bdChkRow">
|
|
66
51
|
<input id="bdTwoWay" type="checkbox"/>
|
|
67
52
|
<span class="bdChkLbl">two way binding</span>
|
|
68
53
|
</div>
|
|
69
|
-
<div class="bdRow
|
|
54
|
+
<div class="bdRow bdChkRow">
|
|
70
55
|
<input id="bdInvert" type="checkbox"/>
|
|
71
56
|
<span class="bdChkLbl">invert logic</span>
|
|
72
57
|
</div>
|
|
@@ -80,24 +65,24 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
80
65
|
</div>
|
|
81
66
|
|
|
82
67
|
<div class="bdRow">
|
|
83
|
-
<
|
|
84
|
-
<input id="bdWriteBack" type="text" class="bdInput"
|
|
68
|
+
<div class="bdLbl">write back signal :</div>
|
|
69
|
+
<input id="bdWriteBack" type="text" class="bdInput"/>
|
|
85
70
|
</div>
|
|
86
71
|
|
|
87
72
|
<div class="bdRow">
|
|
88
|
-
<
|
|
73
|
+
<div class="bdLbl">formula write back (two way)</div>
|
|
89
74
|
<textarea id="bdFormulaWB" class="bdTa" rows="2" placeholder="val"></textarea>
|
|
90
75
|
</div>
|
|
91
76
|
|
|
92
77
|
<div class="bdRow">
|
|
93
|
-
<
|
|
94
|
-
<table
|
|
78
|
+
<div class="bdLbl">converter:</div>
|
|
79
|
+
<table class="bdTable">
|
|
95
80
|
<thead><tr><th>condition</th><th>value</th></tr></thead>
|
|
96
81
|
<tbody id="bdConvBody"></tbody>
|
|
97
82
|
</table>
|
|
98
83
|
<div style="display:flex;gap:4px;margin-top:4px;justify-content:flex-end;">
|
|
99
|
-
<button class="bdBtn" id="bdConvAdd">add</button>
|
|
100
|
-
<button class="bdBtn" id="bdConvRemove">remove</button>
|
|
84
|
+
<button class="bdBtn sm" id="bdConvAdd">add</button>
|
|
85
|
+
<button class="bdBtn sm" id="bdConvRemove">remove</button>
|
|
101
86
|
</div>
|
|
102
87
|
</div>
|
|
103
88
|
|
|
@@ -112,159 +97,225 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
112
97
|
|
|
113
98
|
static style = css`
|
|
114
99
|
:host { display:block;width:100%;height:100%;overflow:hidden; }
|
|
115
|
-
#root { width:100%;height:100%;display:flex;flex-direction:column;overflow:hidden;background:#1a1a1a;color:#ccc;font-family:Helvetica,Arial,sans-serif;font-size:12px;position:relative; }
|
|
116
100
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
101
|
+
#root {
|
|
102
|
+
width:100%;height:100%;
|
|
103
|
+
display:flex;flex-direction:column;
|
|
104
|
+
overflow:hidden;
|
|
105
|
+
background:#1e1e1e;
|
|
106
|
+
color:#d4d4d4;
|
|
107
|
+
font-family:Segoe UI,Helvetica,Arial,sans-serif;
|
|
108
|
+
font-size:12px;
|
|
109
|
+
position:relative;
|
|
110
|
+
}
|
|
122
111
|
|
|
123
|
-
/*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
112
|
+
/* Selected object header */
|
|
113
|
+
#objHeader {
|
|
114
|
+
display:flex;align-items:center;gap:6px;
|
|
115
|
+
padding:5px 8px;
|
|
116
|
+
background:#252526;
|
|
117
|
+
border-bottom:1px solid #3c3c3c;
|
|
118
|
+
flex-shrink:0;
|
|
119
|
+
min-height:26px;
|
|
120
|
+
}
|
|
121
|
+
.htype {
|
|
122
|
+
font-size:10px;color:#569cd6;
|
|
123
|
+
background:#1e1e1e;border:1px solid #3c3c3c;
|
|
124
|
+
padding:1px 5px;border-radius:2px;
|
|
125
|
+
white-space:nowrap;
|
|
126
|
+
}
|
|
127
|
+
.hname { font-size:11px;color:#9cdcfe;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
|
|
128
128
|
|
|
129
129
|
/* Section header */
|
|
130
|
-
.sechdr {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
.sechdr {
|
|
131
|
+
background:#252526;
|
|
132
|
+
color:#9cdcfe;
|
|
133
|
+
padding:4px 8px;
|
|
134
|
+
font-size:10px;font-weight:700;
|
|
135
|
+
text-transform:uppercase;
|
|
136
|
+
letter-spacing:.6px;
|
|
137
|
+
border-bottom:1px solid #3c3c3c;
|
|
138
|
+
flex-shrink:0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Binding rows */
|
|
142
|
+
#bindRows { flex:1;overflow-y:auto;overflow-x:hidden; }
|
|
143
|
+
|
|
144
|
+
.brow {
|
|
145
|
+
display:flex;align-items:center;
|
|
146
|
+
padding:0 6px;
|
|
147
|
+
height:22px;
|
|
148
|
+
border-bottom:1px solid #2a2a2a;
|
|
149
|
+
gap:0;
|
|
150
|
+
}
|
|
151
|
+
.brow:hover { background:#2a2d2e; }
|
|
152
|
+
|
|
153
|
+
/* □ square binding button */
|
|
154
|
+
.bsq {
|
|
155
|
+
width:13px;height:13px;min-width:13px;
|
|
156
|
+
border:1px solid #4d4d4d;
|
|
157
|
+
background:transparent;
|
|
158
|
+
cursor:pointer;
|
|
159
|
+
display:flex;align-items:center;justify-content:center;
|
|
160
|
+
font-size:7px;color:transparent;
|
|
161
|
+
flex-shrink:0;
|
|
162
|
+
margin-right:5px;
|
|
163
|
+
padding:0;
|
|
164
|
+
outline:none;
|
|
165
|
+
}
|
|
138
166
|
.bsq:hover { border-color:#4ec9b0; }
|
|
139
|
-
.bsq.bound {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
167
|
+
.bsq.bound {
|
|
168
|
+
background:#0e639c;
|
|
169
|
+
border-color:#1177bb;
|
|
170
|
+
color:#fff;
|
|
171
|
+
font-size:8px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Property label */
|
|
175
|
+
.blbl {
|
|
176
|
+
width:88px;min-width:88px;
|
|
177
|
+
color:#d4d4d4;
|
|
178
|
+
font-size:11px;
|
|
179
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
180
|
+
flex-shrink:0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Signal value display */
|
|
184
|
+
.bval {
|
|
185
|
+
flex:1;
|
|
186
|
+
font-size:10px;
|
|
187
|
+
font-family:Consolas,monospace;
|
|
188
|
+
overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
|
|
189
|
+
padding:0 3px;
|
|
190
|
+
color:#4ec9b0;
|
|
191
|
+
}
|
|
144
192
|
.bval.none { color:#555; }
|
|
145
193
|
|
|
146
194
|
/* Context menu */
|
|
147
|
-
#ctxMenu {
|
|
195
|
+
#ctxMenu {
|
|
196
|
+
display:none;position:fixed;z-index:9999;
|
|
197
|
+
background:#252526;border:1px solid #454545;
|
|
198
|
+
min-width:130px;
|
|
199
|
+
box-shadow:2px 4px 12px rgba(0,0,0,.7);
|
|
200
|
+
}
|
|
148
201
|
#ctxMenu.open { display:block; }
|
|
149
|
-
.cmItem { padding:5px
|
|
150
|
-
.cmItem:hover { background:#
|
|
151
|
-
|
|
152
|
-
/* Binding dialog
|
|
153
|
-
#bdOverlay {
|
|
202
|
+
.cmItem { padding:5px 14px;cursor:pointer;font-size:11px;color:#d4d4d4; }
|
|
203
|
+
.cmItem:hover { background:#094771;color:#fff; }
|
|
204
|
+
|
|
205
|
+
/* Binding dialog */
|
|
206
|
+
#bdOverlay {
|
|
207
|
+
display:none;position:absolute;inset:0;
|
|
208
|
+
background:rgba(0,0,0,.72);
|
|
209
|
+
z-index:500;
|
|
210
|
+
align-items:center;justify-content:center;
|
|
211
|
+
}
|
|
154
212
|
#bdOverlay.open { display:flex; }
|
|
155
|
-
|
|
156
|
-
#
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
213
|
+
|
|
214
|
+
#bdBox {
|
|
215
|
+
background:#252526;
|
|
216
|
+
border:1px solid #454545;
|
|
217
|
+
width:320px;max-height:88%;
|
|
218
|
+
overflow-y:auto;
|
|
219
|
+
box-shadow:4px 6px 20px rgba(0,0,0,.8);
|
|
220
|
+
}
|
|
221
|
+
#bdTitle {
|
|
222
|
+
background:#37373d;
|
|
223
|
+
padding:7px 10px;
|
|
224
|
+
font-size:11px;font-weight:600;
|
|
225
|
+
color:#ccc;
|
|
226
|
+
border-bottom:1px solid #454545;
|
|
227
|
+
}
|
|
228
|
+
.bdRow { padding:5px 10px 2px; }
|
|
229
|
+
.bdChkRow { display:flex;align-items:center;gap:8px;padding:3px 10px; }
|
|
230
|
+
.bdLbl { color:#888;font-size:10px;margin-bottom:3px;display:block; }
|
|
231
|
+
.bdChkLbl { font-size:11px;color:#d4d4d4; }
|
|
232
|
+
.bdInput {
|
|
233
|
+
width:100%;
|
|
234
|
+
background:#3c3c3c;border:1px solid #555;
|
|
235
|
+
color:#d4d4d4;padding:3px 5px;
|
|
236
|
+
font-size:11px;box-sizing:border-box;
|
|
237
|
+
outline:none;
|
|
238
|
+
}
|
|
239
|
+
.bdInput:focus { border-color:#4ec9b0; }
|
|
240
|
+
.bdSel {
|
|
241
|
+
background:#3c3c3c;border:1px solid #555;
|
|
242
|
+
color:#d4d4d4;padding:2px 4px;font-size:11px;
|
|
243
|
+
outline:none;
|
|
244
|
+
}
|
|
245
|
+
.bdTa {
|
|
246
|
+
width:100%;background:#3c3c3c;border:1px solid #555;
|
|
247
|
+
color:#d4d4d4;padding:3px 5px;font-size:11px;
|
|
248
|
+
font-family:Consolas,monospace;
|
|
249
|
+
box-sizing:border-box;resize:vertical;
|
|
250
|
+
outline:none;
|
|
251
|
+
}
|
|
252
|
+
.bdTa:focus { border-color:#4ec9b0; }
|
|
253
|
+
.bdTable { width:100%;border-collapse:collapse;font-size:10px;color:#d4d4d4; }
|
|
254
|
+
.bdTable th { background:#1e1e1e;padding:2px 6px;text-align:left;color:#888;border:1px solid #3c3c3c; }
|
|
255
|
+
.bdTable td { padding:1px 3px;border:1px solid #3c3c3c; }
|
|
256
|
+
.bdTable td input { width:100%;background:#3c3c3c;border:none;color:#d4d4d4;padding:2px 3px;font-size:10px;outline:none; }
|
|
257
|
+
.bdBtn {
|
|
258
|
+
padding:3px 10px;
|
|
259
|
+
background:#3c3c3c;border:1px solid #555;
|
|
260
|
+
color:#d4d4d4;cursor:pointer;font-size:11px;
|
|
261
|
+
}
|
|
170
262
|
.bdBtn:hover { border-color:#999; }
|
|
171
263
|
.bdBtn.sm { padding:2px 6px;font-size:10px; }
|
|
172
264
|
.bdBtn.accent { background:#0e639c;border-color:#1177bb;color:#fff; }
|
|
173
265
|
.bdBtn.accent-ok { background:#0e639c;border-color:#1177bb;color:#fff; }
|
|
174
|
-
#bdFoot {
|
|
266
|
+
#bdFoot {
|
|
267
|
+
display:flex;justify-content:flex-end;gap:6px;
|
|
268
|
+
padding:8px 10px;border-top:1px solid #3c3c3c;
|
|
269
|
+
}
|
|
175
270
|
`;
|
|
176
271
|
|
|
177
|
-
_editor
|
|
178
|
-
_editorHost
|
|
179
|
-
_bindings
|
|
272
|
+
_editor = null;
|
|
273
|
+
_editorHost = null;
|
|
274
|
+
_bindings = {};
|
|
180
275
|
_selectedObj = null;
|
|
181
|
-
_ctxTarget
|
|
276
|
+
_ctxTarget = null;
|
|
182
277
|
|
|
183
278
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
184
279
|
|
|
185
280
|
async connect(editor, editorHost) {
|
|
186
|
-
this._editor
|
|
281
|
+
this._editor = editor;
|
|
187
282
|
this._editorHost = editorHost;
|
|
188
|
-
this._bindings
|
|
283
|
+
this._bindings = editorHost?.sceneData?.bindings ?? {};
|
|
189
284
|
|
|
190
|
-
await this._injectThreeCSS();
|
|
191
|
-
await this._mountThreePanels();
|
|
192
|
-
this._setupSubTabs();
|
|
193
285
|
this._buildBindingRows();
|
|
194
286
|
this._setupContextMenu();
|
|
195
287
|
this._setupBindingDialog();
|
|
196
288
|
|
|
197
289
|
editor.signals.objectSelected.add(obj => {
|
|
198
290
|
this._selectedObj = obj;
|
|
291
|
+
this._refreshHeader();
|
|
199
292
|
this._refreshBindRows();
|
|
200
293
|
});
|
|
201
294
|
editor.signals.objectChanged.add(obj => {
|
|
202
295
|
if (obj && obj === this._selectedObj) this._refreshBindRows();
|
|
203
296
|
});
|
|
204
297
|
|
|
298
|
+
this._refreshHeader();
|
|
205
299
|
this._refreshBindRows();
|
|
206
300
|
}
|
|
207
301
|
|
|
208
302
|
getBindings() { return this._bindings; }
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// ── Mount Three.js sidebar panels ──────────────────────────────────────────
|
|
227
|
-
|
|
228
|
-
async _mountThreePanels() {
|
|
229
|
-
const base = EDITOR_BASE + 'js/';
|
|
230
|
-
const ed = this._editor;
|
|
231
|
-
const [
|
|
232
|
-
{ SidebarScene },
|
|
233
|
-
{ SidebarObject },
|
|
234
|
-
{ SidebarGeometry },
|
|
235
|
-
{ SidebarMaterial },
|
|
236
|
-
] = await Promise.all([
|
|
237
|
-
import(/* @vite-ignore */ base + 'Sidebar.Scene.js'),
|
|
238
|
-
import(/* @vite-ignore */ base + 'Sidebar.Object.js'),
|
|
239
|
-
import(/* @vite-ignore */ base + 'Sidebar.Geometry.js'),
|
|
240
|
-
import(/* @vite-ignore */ base + 'Sidebar.Material.js'),
|
|
241
|
-
]);
|
|
242
|
-
|
|
243
|
-
this._getDomElement('subScene').appendChild(new SidebarScene(ed).dom);
|
|
244
|
-
this._getDomElement('objThreeWrap').appendChild(new SidebarObject(ed).dom);
|
|
245
|
-
this._getDomElement('subGeom').appendChild(new SidebarGeometry(ed).dom);
|
|
246
|
-
this._getDomElement('subMater').appendChild(new SidebarMaterial(ed).dom);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ── Sub-tabs ────────────────────────────────────────────────────────────────
|
|
250
|
-
|
|
251
|
-
_setupSubTabs() {
|
|
252
|
-
const bar = this._getDomElement('subBar');
|
|
253
|
-
const panels = {
|
|
254
|
-
scene: this._getDomElement('subScene'),
|
|
255
|
-
object: this._getDomElement('subObject'),
|
|
256
|
-
geom: this._getDomElement('subGeom'),
|
|
257
|
-
mater: this._getDomElement('subMater'),
|
|
258
|
-
};
|
|
259
|
-
bar?.querySelectorAll('.stab').forEach(btn => {
|
|
260
|
-
btn.addEventListener('click', () => {
|
|
261
|
-
bar.querySelectorAll('.stab').forEach(b => b.classList.remove('active'));
|
|
262
|
-
btn.classList.add('active');
|
|
263
|
-
Object.values(panels).forEach(p => { if (p) p.style.display = 'none'; });
|
|
264
|
-
const p = panels[btn.dataset.sub];
|
|
265
|
-
if (p) p.style.display = btn.dataset.sub === 'object' ? 'flex' : 'block';
|
|
266
|
-
});
|
|
267
|
-
});
|
|
303
|
+
setBindings(b) { this._bindings = b || {}; this._refreshBindRows(); }
|
|
304
|
+
|
|
305
|
+
// ── Header ─────────────────────────────────────────────────────────────────
|
|
306
|
+
|
|
307
|
+
_refreshHeader() {
|
|
308
|
+
const obj = this._selectedObj;
|
|
309
|
+
const typeEl = this._getDomElement('objType');
|
|
310
|
+
const nameEl = this._getDomElement('objName');
|
|
311
|
+
if (!typeEl || !nameEl) return;
|
|
312
|
+
if (!obj) {
|
|
313
|
+
typeEl.textContent = '';
|
|
314
|
+
nameEl.textContent = 'No object selected';
|
|
315
|
+
} else {
|
|
316
|
+
typeEl.textContent = obj.type || '';
|
|
317
|
+
nameEl.textContent = obj.name || obj.uuid?.slice(0, 8) || '—';
|
|
318
|
+
}
|
|
268
319
|
}
|
|
269
320
|
|
|
270
321
|
// ── Bindable properties ─────────────────────────────────────────────────────
|
|
@@ -288,60 +339,44 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
288
339
|
const cont = this._getDomElement('bindRows');
|
|
289
340
|
if (!cont) return;
|
|
290
341
|
cont.innerHTML = '';
|
|
291
|
-
|
|
342
|
+
for (const { key, label } of this._BINDABLE) {
|
|
292
343
|
const row = document.createElement('div');
|
|
293
344
|
row.className = 'brow';
|
|
294
345
|
row.dataset.key = key;
|
|
295
346
|
|
|
296
|
-
// □ square button (left of label)
|
|
297
347
|
const sq = document.createElement('button');
|
|
298
348
|
sq.className = 'bsq';
|
|
299
|
-
sq.title =
|
|
300
|
-
|
|
301
|
-
sq.addEventListener('
|
|
302
|
-
|
|
303
|
-
this._showCtxMenu(e, key);
|
|
304
|
-
});
|
|
305
|
-
sq.addEventListener('click', e => {
|
|
306
|
-
e.preventDefault();
|
|
307
|
-
this._showCtxMenu(e, key);
|
|
308
|
-
});
|
|
349
|
+
sq.title = key;
|
|
350
|
+
const onClick = e => { e.preventDefault(); this._showCtxMenu(e, key); };
|
|
351
|
+
sq.addEventListener('click', onClick);
|
|
352
|
+
sq.addEventListener('contextmenu', onClick);
|
|
309
353
|
|
|
310
|
-
// property name
|
|
311
354
|
const lbl = document.createElement('span');
|
|
312
355
|
lbl.className = 'blbl';
|
|
313
356
|
lbl.textContent = label;
|
|
314
357
|
|
|
315
|
-
// current signal display
|
|
316
358
|
const val = document.createElement('span');
|
|
317
359
|
val.className = 'bval none';
|
|
318
|
-
val.textContent = '';
|
|
319
360
|
|
|
320
361
|
row.appendChild(sq);
|
|
321
362
|
row.appendChild(lbl);
|
|
322
363
|
row.appendChild(val);
|
|
323
364
|
cont.appendChild(row);
|
|
324
|
-
}
|
|
365
|
+
}
|
|
325
366
|
}
|
|
326
367
|
|
|
327
368
|
_refreshBindRows() {
|
|
328
369
|
const cont = this._getDomElement('bindRows');
|
|
329
370
|
if (!cont) return;
|
|
330
371
|
const uuid = this._selectedObj?.uuid;
|
|
331
|
-
cont.querySelectorAll('.brow')
|
|
372
|
+
for (const row of cont.querySelectorAll('.brow')) {
|
|
332
373
|
const key = row.dataset.key;
|
|
333
374
|
const sq = row.querySelector('.bsq');
|
|
334
375
|
const val = row.querySelector('.bval');
|
|
335
376
|
const bind = uuid ? this._bindings[uuid]?.[key] : null;
|
|
336
|
-
|
|
337
|
-
if (val) {
|
|
338
|
-
|
|
339
|
-
val.className = 'bval' + (bind?.signal ? '' : ' none');
|
|
340
|
-
}
|
|
341
|
-
if (sq) {
|
|
342
|
-
sq.className = 'bsq' + (bind?.signal ? ' bound' : '');
|
|
343
|
-
}
|
|
344
|
-
});
|
|
377
|
+
if (sq) sq.className = 'bsq' + (bind?.signal ? ' bound' : '');
|
|
378
|
+
if (val) { val.textContent = bind?.signal || ''; val.className = 'bval' + (bind?.signal ? '' : ' none'); }
|
|
379
|
+
}
|
|
345
380
|
}
|
|
346
381
|
|
|
347
382
|
// ── Context menu ────────────────────────────────────────────────────────────
|
|
@@ -349,33 +384,25 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
349
384
|
_setupContextMenu() {
|
|
350
385
|
const menu = this._getDomElement('ctxMenu');
|
|
351
386
|
if (!menu) return;
|
|
352
|
-
|
|
353
387
|
menu.addEventListener('click', e => {
|
|
354
388
|
const item = e.target.closest('.cmItem');
|
|
355
389
|
if (!item) return;
|
|
356
|
-
const action = item.dataset.action;
|
|
357
390
|
menu.classList.remove('open');
|
|
358
|
-
|
|
391
|
+
const action = item.dataset.action;
|
|
359
392
|
if (action === 'clear') this._clearBinding(this._ctxTarget);
|
|
360
393
|
if (action === 'editbind') this._openBindDialog(this._ctxTarget);
|
|
361
394
|
if (action === 'edittext') this._openTextEditor(this._ctxTarget);
|
|
362
395
|
});
|
|
363
|
-
|
|
364
|
-
// close on outside click
|
|
365
396
|
document.addEventListener('click', e => {
|
|
366
397
|
if (!menu.contains(e.target)) menu.classList.remove('open');
|
|
367
398
|
});
|
|
368
399
|
}
|
|
369
400
|
|
|
370
401
|
_showCtxMenu(e, key) {
|
|
371
|
-
if (!this._selectedObj) {
|
|
372
|
-
alert('Select a 3D object first.');
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
402
|
+
if (!this._selectedObj) { alert('Select a 3D object first.'); return; }
|
|
375
403
|
this._ctxTarget = { uuid: this._selectedObj.uuid, key };
|
|
376
404
|
const menu = this._getDomElement('ctxMenu');
|
|
377
405
|
if (!menu) return;
|
|
378
|
-
// position near click
|
|
379
406
|
menu.style.left = e.clientX + 'px';
|
|
380
407
|
menu.style.top = e.clientY + 'px';
|
|
381
408
|
menu.classList.add('open');
|
|
@@ -386,21 +413,18 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
386
413
|
delete this._bindings[uuid]?.[key];
|
|
387
414
|
if (this._bindings[uuid] && !Object.keys(this._bindings[uuid]).length)
|
|
388
415
|
delete this._bindings[uuid];
|
|
389
|
-
this._syncHost();
|
|
390
|
-
this._refreshBindRows();
|
|
416
|
+
this._syncHost(); this._refreshBindRows();
|
|
391
417
|
}
|
|
392
418
|
|
|
393
419
|
_openTextEditor({ uuid, key } = {}) {
|
|
394
420
|
if (!uuid || !key) return;
|
|
395
421
|
const ex = this._bindings[uuid]?.[key];
|
|
396
|
-
const
|
|
397
|
-
const nv = window.prompt(`State ID for "${key}":`, val);
|
|
422
|
+
const nv = window.prompt(`State ID for "${key}":`, ex?.signal || '');
|
|
398
423
|
if (nv === null) return;
|
|
399
424
|
if (!nv.trim()) { this._clearBinding({ uuid, key }); return; }
|
|
400
425
|
if (!this._bindings[uuid]) this._bindings[uuid] = {};
|
|
401
|
-
this._bindings[uuid][key] = { signal: nv.trim(), formula: ex?.formula || 'val'
|
|
402
|
-
this._syncHost();
|
|
403
|
-
this._refreshBindRows();
|
|
426
|
+
this._bindings[uuid][key] = { ...(ex || {}), signal: nv.trim(), formula: ex?.formula || 'val' };
|
|
427
|
+
this._syncHost(); this._refreshBindRows();
|
|
404
428
|
}
|
|
405
429
|
|
|
406
430
|
// ── Binding dialog ──────────────────────────────────────────────────────────
|
|
@@ -408,36 +432,22 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
408
432
|
_setupBindingDialog() {
|
|
409
433
|
const ov = this._getDomElement('bdOverlay');
|
|
410
434
|
|
|
411
|
-
this._getDomElement('bdCancel')?.addEventListener('click', () =>
|
|
412
|
-
ov?.classList.remove('open');
|
|
413
|
-
});
|
|
435
|
+
this._getDomElement('bdCancel')?.addEventListener('click', () => ov?.classList.remove('open'));
|
|
414
436
|
|
|
415
437
|
this._getDomElement('bdObjX')?.addEventListener('click', () => {
|
|
416
|
-
const inp = this._getDomElement('bdObj');
|
|
417
|
-
if (inp) inp.value = '';
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
this._getDomElement('bdObjBrowse')?.addEventListener('click', async () => {
|
|
421
|
-
try {
|
|
422
|
-
const { openSelectIdDialog } = await import('@iobroker/webcomponent-selectid-dialog/dist/selectIdHelper.js');
|
|
423
|
-
const connection = (await import('../common/IobrokerHandler.js')).iobrokerHandler.connection;
|
|
424
|
-
const id = await openSelectIdDialog(connection, null, false);
|
|
425
|
-
if (id) { const inp = this._getDomElement('bdObj'); if (inp) inp.value = id; }
|
|
426
|
-
} catch (_) {}
|
|
438
|
+
const inp = this._getDomElement('bdObj'); if (inp) inp.value = '';
|
|
427
439
|
});
|
|
428
440
|
|
|
429
|
-
|
|
441
|
+
const browse = async () => {
|
|
430
442
|
try {
|
|
431
443
|
const { openSelectIdDialog } = await import('@iobroker/webcomponent-selectid-dialog/dist/selectIdHelper.js');
|
|
432
|
-
const
|
|
433
|
-
const id = await openSelectIdDialog(connection, null, false);
|
|
444
|
+
const { iobrokerHandler } = await import('../common/IobrokerHandler.js');
|
|
445
|
+
const id = await openSelectIdDialog(iobrokerHandler.connection, null, false);
|
|
434
446
|
if (id) { const inp = this._getDomElement('bdObj'); if (inp) inp.value = id; }
|
|
435
447
|
} catch (_) {}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
this._getDomElement('
|
|
439
|
-
// historic signals - show simple list if available
|
|
440
|
-
});
|
|
448
|
+
};
|
|
449
|
+
this._getDomElement('bdObjBrowse')?.addEventListener('click', browse);
|
|
450
|
+
this._getDomElement('bdObjIOB')?.addEventListener('click', browse);
|
|
441
451
|
|
|
442
452
|
this._getDomElement('bdConvAdd')?.addEventListener('click', () => {
|
|
443
453
|
const tbody = this._getDomElement('bdConvBody');
|
|
@@ -449,9 +459,7 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
449
459
|
|
|
450
460
|
this._getDomElement('bdConvRemove')?.addEventListener('click', () => {
|
|
451
461
|
const tbody = this._getDomElement('bdConvBody');
|
|
452
|
-
|
|
453
|
-
const last = tbody.querySelector('tr:last-child');
|
|
454
|
-
if (last) last.remove();
|
|
462
|
+
tbody?.querySelector('tr:last-child')?.remove();
|
|
455
463
|
});
|
|
456
464
|
|
|
457
465
|
this._getDomElement('bdOk')?.addEventListener('click', () => {
|
|
@@ -463,46 +471,42 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
463
471
|
const twoWay = this._getDomElement('bdTwoWay')?.checked ?? false;
|
|
464
472
|
const invert = this._getDomElement('bdInvert')?.checked ?? false;
|
|
465
473
|
const type = this._getDomElement('bdType')?.value || 'signal';
|
|
466
|
-
|
|
467
|
-
// collect converter rows
|
|
468
474
|
const converter = [];
|
|
469
475
|
this._getDomElement('bdConvBody')?.querySelectorAll('tr').forEach(tr => {
|
|
470
|
-
const
|
|
471
|
-
if (
|
|
472
|
-
converter.push({ condition:
|
|
476
|
+
const ins = tr.querySelectorAll('input');
|
|
477
|
+
if (ins[0]?.value || ins[1]?.value)
|
|
478
|
+
converter.push({ condition: ins[0]?.value || '', value: ins[1]?.value || '' });
|
|
473
479
|
});
|
|
474
480
|
|
|
475
|
-
if (uuid && key
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
481
|
+
if (uuid && key) {
|
|
482
|
+
if (signal) {
|
|
483
|
+
if (!this._bindings[uuid]) this._bindings[uuid] = {};
|
|
484
|
+
this._bindings[uuid][key] = { signal, formula, twoWay, invert, type, writeBack, formulaWB, converter };
|
|
485
|
+
} else {
|
|
486
|
+
this._clearBinding({ uuid, key });
|
|
487
|
+
}
|
|
488
|
+
this._syncHost(); this._refreshBindRows();
|
|
482
489
|
}
|
|
483
|
-
|
|
484
490
|
ov?.classList.remove('open');
|
|
485
491
|
});
|
|
486
492
|
}
|
|
487
493
|
|
|
488
494
|
_openBindDialog({ uuid, key } = {}) {
|
|
489
495
|
if (!uuid || !key) return;
|
|
490
|
-
const ex
|
|
491
|
-
|
|
492
|
-
const getEl = id => this._getDomElement(id);
|
|
496
|
+
const ex = this._bindings[uuid]?.[key];
|
|
493
497
|
const label = this._BINDABLE.find(b => b.key === key)?.label || key;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
const tbody =
|
|
498
|
+
const g = id => this._getDomElement(id);
|
|
499
|
+
|
|
500
|
+
g('bdPropLabel').textContent = label;
|
|
501
|
+
g('bdObj').value = ex?.signal || '';
|
|
502
|
+
g('bdFormula').value = ex?.formula || 'val';
|
|
503
|
+
g('bdFormulaWB').value = ex?.formulaWB || '';
|
|
504
|
+
g('bdWriteBack').value = ex?.writeBack || '';
|
|
505
|
+
g('bdTwoWay').checked = ex?.twoWay || false;
|
|
506
|
+
g('bdInvert').checked = ex?.invert || false;
|
|
507
|
+
if (g('bdType')) g('bdType').value = ex?.type || 'signal';
|
|
508
|
+
|
|
509
|
+
const tbody = g('bdConvBody');
|
|
506
510
|
if (tbody) {
|
|
507
511
|
tbody.innerHTML = '';
|
|
508
512
|
(ex?.converter || []).forEach(row => {
|
|
@@ -513,7 +517,7 @@ export class IobrokerWebui3DScreenPropertiesPanel extends BaseCustomWebComponent
|
|
|
513
517
|
}
|
|
514
518
|
|
|
515
519
|
this._ctxTarget = { uuid, key };
|
|
516
|
-
|
|
520
|
+
g('bdOverlay')?.classList.add('open');
|
|
517
521
|
}
|
|
518
522
|
|
|
519
523
|
_syncHost() {
|
|
@@ -19,6 +19,7 @@ import { CommandHandling } from './CommandHandling.js';
|
|
|
19
19
|
import propertiesTypeInfo from "../generated/Properties.json" with { type: 'json' };
|
|
20
20
|
import "../runtime/controls.js";
|
|
21
21
|
import "./IobrokerWebuiSolutionExplorer.js";
|
|
22
|
+
import "./IobrokerWebuiWidgetGallery.js";
|
|
22
23
|
import "./IobrokerWebuiMonacoEditor.js";
|
|
23
24
|
import "./IobrokerWebuiEventAssignment.js";
|
|
24
25
|
import "./IobrokerWebuiPropertyGrid.js";
|
|
@@ -88,10 +89,14 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
88
89
|
<iobroker-webui-solution-explorer id="solutionExplorer"></iobroker-solution-explorer>
|
|
89
90
|
</div>
|
|
90
91
|
|
|
91
|
-
<div title="outline" dock-spawn-dock-type="down" dock-spawn-dock-to="treeUpper" dock-spawn-dock-ratio="0.33"
|
|
92
|
+
<div id="outlineDock" title="outline" dock-spawn-dock-type="down" dock-spawn-dock-to="treeUpper" dock-spawn-dock-ratio="0.33"
|
|
92
93
|
style="overflow: hidden; width: 100%;">
|
|
93
94
|
<node-projects-tree-view-extended name="tree" id="treeViewExtended"></node-projects-tree-view-extended>
|
|
94
95
|
</div>
|
|
96
|
+
|
|
97
|
+
<div id="galleryDock" title="gallery" dock-spawn-dock-to="outlineDock" style="overflow: hidden; width: 100%;">
|
|
98
|
+
<iobroker-webui-widget-gallery id="widgetGallery"></iobroker-webui-widget-gallery>
|
|
99
|
+
</div>
|
|
95
100
|
|
|
96
101
|
<div id="attributeDock" title="Properties" dock-spawn-dock-type="right" dock-spawn-dock-ratio="0.2">
|
|
97
102
|
<node-projects-web-component-designer-property-grid-with-header id="propertyGrid"></node-projects-web-component-designer-property-grid-with-header>
|
|
@@ -108,7 +113,7 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
108
113
|
<div id="visibilityDock" title="Visibility" style="overflow: auto; width: 100%;" dock-spawn-dock-to="attributeDock">
|
|
109
114
|
</div>
|
|
110
115
|
|
|
111
|
-
<div id="effectsDock" title="
|
|
116
|
+
<div id="effectsDock" title="Effects" style="overflow: auto; width: 100%;" dock-spawn-dock-to="attributeDock">
|
|
112
117
|
</div>
|
|
113
118
|
|
|
114
119
|
<div id="animationsDock" title="Animations" style="overflow: auto; width: 100%;" dock-spawn-dock-to="attributeDock">
|
|
@@ -156,6 +161,8 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
156
161
|
this.eventsAssignment = this._getDomElement('eventsList');
|
|
157
162
|
this.refactorView = this._getDomElement('refactorView');
|
|
158
163
|
this.translationEditor = this._getDomElement('translationEditor');
|
|
164
|
+
this.widgetGallery = this._getDomElement('widgetGallery');
|
|
165
|
+
this.widgetGallery.serviceContainer = serviceContainer;
|
|
159
166
|
this.settingsEditor = this._getDomElement('settingsEditor');
|
|
160
167
|
this.settingsEditor.getTypeInfo = (obj, type) => typeInfoFromJsonSchema(propertiesTypeInfo, obj, type);
|
|
161
168
|
this.settingsEditor.propertyChanged.on((prp) => {
|
|
@@ -313,7 +320,7 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
313
320
|
|
|
314
321
|
setTimeout(makeSetup('visibilityDock', () => this._setupVisibilityPanel(), 'Visibility'), 1000);
|
|
315
322
|
setTimeout(makeSetup('animationsDock', () => this._setupAnimationsPanel(), 'Animations'), 1200);
|
|
316
|
-
setTimeout(makeSetup('effectsDock', () => this._setupEffectsPanel(), '
|
|
323
|
+
setTimeout(makeSetup('effectsDock', () => this._setupEffectsPanel(), 'Effects'), 1400);
|
|
317
324
|
}
|
|
318
325
|
|
|
319
326
|
_setupVisibilityPanel() {
|
|
@@ -1430,6 +1437,14 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
1430
1437
|
cfgList.forEach((cfg, i) => content.appendChild(buildEffectBlock(cfg, i)));
|
|
1431
1438
|
}
|
|
1432
1439
|
|
|
1440
|
+
// shows the live rendered widgets of a tree folder in the gallery panel
|
|
1441
|
+
showWidgetGallery(data, title) {
|
|
1442
|
+
this.widgetGallery?.showForSource(data, title);
|
|
1443
|
+
try {
|
|
1444
|
+
this.activateDockById('galleryDock');
|
|
1445
|
+
}
|
|
1446
|
+
catch (e) { }
|
|
1447
|
+
}
|
|
1433
1448
|
/* Move to a Dock Spawn Helper */
|
|
1434
1449
|
activateDockById(name) {
|
|
1435
1450
|
this.activateDock(this._getDomElement(name));
|
|
@@ -2136,46 +2151,30 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
2136
2151
|
|
|
2137
2152
|
// ── 3D Screen Properties Mode ──────────────────────────────────────────────
|
|
2138
2153
|
|
|
2139
|
-
_3dPropsActive
|
|
2140
|
-
_3dPropsPanel
|
|
2141
|
-
_3dSettPanel
|
|
2142
|
-
_3dProjPanel = null;
|
|
2154
|
+
_3dPropsActive = false;
|
|
2155
|
+
_3dPropsPanel = null;
|
|
2156
|
+
_3dSettPanel = null;
|
|
2143
2157
|
|
|
2144
2158
|
_activate3DPropertiesMode(editor3DEl) {
|
|
2145
2159
|
if (this._3dPropsActive) return;
|
|
2146
2160
|
this._3dPropsActive = true;
|
|
2147
2161
|
|
|
2148
|
-
// Helper: hide existing children and track state
|
|
2149
|
-
const hideChildren = (el) => {
|
|
2150
|
-
for (const c of el.children) {
|
|
2151
|
-
c.__3d_prev_display = c.style.display;
|
|
2152
|
-
c.style.display = 'none';
|
|
2153
|
-
}
|
|
2154
|
-
};
|
|
2155
|
-
// Helper: restore
|
|
2156
|
-
const showChildren = (el) => {
|
|
2157
|
-
for (const c of el.children) {
|
|
2158
|
-
if (c.__3d_prev_display !== undefined) {
|
|
2159
|
-
c.style.display = c.__3d_prev_display;
|
|
2160
|
-
delete c.__3d_prev_display;
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
};
|
|
2164
|
-
|
|
2165
2162
|
const attrDock = this._getDomElement('attributeDock');
|
|
2166
2163
|
const settiDock = this._getDomElement('settingsDock');
|
|
2167
|
-
const projDock = this._getDomElement('effectsDock'); // reuse effectsDock for Project
|
|
2168
2164
|
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2165
|
+
// Hide existing children of attributeDock and settingsDock
|
|
2166
|
+
const hideChildren = el => {
|
|
2167
|
+
if (!el) return;
|
|
2168
|
+
for (const c of el.children) { c.__3d_prev_display = c.style.display; c.style.display = 'none'; }
|
|
2169
|
+
};
|
|
2170
|
+
hideChildren(attrDock);
|
|
2171
|
+
hideChildren(settiDock);
|
|
2172
2172
|
|
|
2173
2173
|
const tryConnect = () => {
|
|
2174
2174
|
if (!editor3DEl._editor) { setTimeout(tryConnect, 200); return; }
|
|
2175
2175
|
const ed = editor3DEl._editor;
|
|
2176
|
-
const base = new URL('../../../3d-editor/js/', import.meta.url).href;
|
|
2177
2176
|
|
|
2178
|
-
// ── attributeDock:
|
|
2177
|
+
// ── attributeDock: 3D Bindings panel ──────────────────────────
|
|
2179
2178
|
if (attrDock) {
|
|
2180
2179
|
const propPanel = document.createElement('iobroker-webui-3dscreen-properties');
|
|
2181
2180
|
propPanel.id = '__3d_attr';
|
|
@@ -2185,112 +2184,106 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
2185
2184
|
this._3dPropsPanel = propPanel;
|
|
2186
2185
|
}
|
|
2187
2186
|
|
|
2188
|
-
// ── settingsDock:
|
|
2187
|
+
// ── settingsDock: Screen Visibility Control ────────────────────
|
|
2189
2188
|
if (settiDock) {
|
|
2190
2189
|
const wrap = document.createElement('div');
|
|
2191
2190
|
wrap.id = '__3d_setti';
|
|
2192
|
-
wrap.style.cssText = 'width:100%;height:100%;overflow:auto;
|
|
2193
|
-
|
|
2194
|
-
Promise.all([
|
|
2195
|
-
import(/* @vite-ignore */ base + 'Sidebar.Settings.js'),
|
|
2196
|
-
]).then(([{ SidebarSettings }]) => {
|
|
2197
|
-
const settPanel = new SidebarSettings(ed);
|
|
2198
|
-
settPanel.dom.style.cssText = 'padding:8px;';
|
|
2199
|
-
wrap.appendChild(settPanel.dom);
|
|
2200
|
-
|
|
2201
|
-
// ── Visibility section ─────────────────────────────────
|
|
2202
|
-
const visDiv = document.createElement('div');
|
|
2203
|
-
visDiv.style.cssText = 'border-top:1px solid #3c3c3c;margin-top:8px;padding:10px;';
|
|
2204
|
-
visDiv.innerHTML = `
|
|
2205
|
-
<div style="color:#9cdcfe;font-size:10px;font-weight:bold;text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;">
|
|
2206
|
-
Screen Visibility Control
|
|
2207
|
-
</div>
|
|
2208
|
-
<label style="display:flex;align-items:center;gap:8px;margin-bottom:8px;cursor:pointer;font-size:11px;">
|
|
2209
|
-
<input type="checkbox" id="__3d_vis_en" />
|
|
2210
|
-
Enable Visibility Control
|
|
2211
|
-
</label>
|
|
2212
|
-
<div style="font-size:11px;color:#888;margin-bottom:4px;">Only for groups:</div>
|
|
2213
|
-
<div id="__3d_vis_groups" style="border:1px solid #444;padding:6px;max-height:100px;overflow-y:auto;background:#111;font-size:11px;margin-bottom:8px;">
|
|
2214
|
-
<span style="color:#555;">Loading...</span>
|
|
2215
|
-
</div>
|
|
2216
|
-
<div style="font-size:11px;color:#888;margin-bottom:4px;">If not in group:</div>
|
|
2217
|
-
<select id="__3d_vis_act" style="width:100%;background:#3c3c3c;border:1px solid #555;color:#ccc;padding:4px;font-size:11px;margin-bottom:8px;">
|
|
2218
|
-
<option value="hide">Show "Access Denied"</option>
|
|
2219
|
-
<option value="redirect">Redirect to screen</option>
|
|
2220
|
-
</select>
|
|
2221
|
-
<div id="__3d_vis_rdiv" style="display:none;">
|
|
2222
|
-
<div style="font-size:11px;color:#888;margin-bottom:4px;">Redirect screen:</div>
|
|
2223
|
-
<input id="__3d_vis_redir" type="text" style="width:100%;background:#3c3c3c;border:1px solid #555;color:#ccc;padding:4px;font-size:11px;box-sizing:border-box;" />
|
|
2224
|
-
</div>
|
|
2225
|
-
`;
|
|
2226
|
-
|
|
2227
|
-
const settings = editor3DEl.sceneData?.settings ?? {};
|
|
2228
|
-
editor3DEl.sceneData = editor3DEl.sceneData ?? {};
|
|
2229
|
-
editor3DEl.sceneData.settings = settings;
|
|
2230
|
-
if (!settings.visibilityEnabled) settings.visibilityEnabled = false;
|
|
2231
|
-
if (!settings.visibilityGroups) settings.visibilityGroups = [];
|
|
2232
|
-
if (!settings.visibilityAction) settings.visibilityAction = 'hide';
|
|
2233
|
-
if (!settings.visibilityRedirectScreen) settings.visibilityRedirectScreen = '';
|
|
2234
|
-
|
|
2235
|
-
const save = () => {};
|
|
2236
|
-
const enEl = visDiv.querySelector('#__3d_vis_en');
|
|
2237
|
-
const actEl = visDiv.querySelector('#__3d_vis_act');
|
|
2238
|
-
const rdDiv = visDiv.querySelector('#__3d_vis_rdiv');
|
|
2239
|
-
const rdEl = visDiv.querySelector('#__3d_vis_redir');
|
|
2240
|
-
|
|
2241
|
-
if (enEl) { enEl.checked = settings.visibilityEnabled; enEl.addEventListener('change', e => { settings.visibilityEnabled = e.target.checked; }); }
|
|
2242
|
-
if (actEl) { actEl.value = settings.visibilityAction; actEl.addEventListener('change', e => { settings.visibilityAction = e.target.value; if (rdDiv) rdDiv.style.display = e.target.value === 'redirect' ? 'block' : 'none'; }); }
|
|
2243
|
-
if (rdDiv) rdDiv.style.display = settings.visibilityAction === 'redirect' ? 'block' : 'none';
|
|
2244
|
-
if (rdEl) { rdEl.value = settings.visibilityRedirectScreen || ''; rdEl.addEventListener('input', e => { settings.visibilityRedirectScreen = e.target.value; }); }
|
|
2245
|
-
|
|
2246
|
-
// Load groups
|
|
2247
|
-
const grpEl = visDiv.querySelector('#__3d_vis_groups');
|
|
2248
|
-
iobrokerHandler.getUserGroups?.().then(groups => {
|
|
2249
|
-
if (!grpEl) return;
|
|
2250
|
-
if (!groups?.length) { grpEl.innerHTML = '<span style="color:#555;">No groups</span>'; return; }
|
|
2251
|
-
grpEl.innerHTML = '';
|
|
2252
|
-
groups.forEach(g => {
|
|
2253
|
-
const id = typeof g === 'string' ? g : (g.id ?? g._id ?? g.name ?? g);
|
|
2254
|
-
const lbl = typeof g === 'string' ? g : (g.name ?? id);
|
|
2255
|
-
const chk = (settings.visibilityGroups || []).includes(id);
|
|
2256
|
-
const row = document.createElement('label');
|
|
2257
|
-
row.style.cssText = 'display:flex;align-items:center;gap:6px;padding:2px 0;cursor:pointer;';
|
|
2258
|
-
row.innerHTML = `<input type="checkbox" ${chk ? 'checked' : ''} />${lbl}`;
|
|
2259
|
-
row.querySelector('input').addEventListener('change', ev => {
|
|
2260
|
-
const arr = settings.visibilityGroups || [];
|
|
2261
|
-
if (ev.target.checked) { if (!arr.includes(id)) arr.push(id); }
|
|
2262
|
-
else { const i = arr.indexOf(id); if (i >= 0) arr.splice(i, 1); }
|
|
2263
|
-
settings.visibilityGroups = arr;
|
|
2264
|
-
});
|
|
2265
|
-
grpEl.appendChild(row);
|
|
2266
|
-
});
|
|
2267
|
-
}).catch(() => { if (grpEl) grpEl.innerHTML = '<span style="color:#555;">—</span>'; });
|
|
2268
|
-
|
|
2269
|
-
wrap.appendChild(visDiv);
|
|
2270
|
-
});
|
|
2271
|
-
|
|
2191
|
+
wrap.style.cssText = 'width:100%;height:100%;overflow-y:auto;box-sizing:border-box;';
|
|
2192
|
+
this._build3DVisibilityPanel(wrap, editor3DEl);
|
|
2272
2193
|
settiDock.appendChild(wrap);
|
|
2273
2194
|
this._3dSettPanel = wrap;
|
|
2274
2195
|
}
|
|
2275
|
-
|
|
2276
|
-
// ── effectsDock: SidebarProject (Geometries / Materials / Textures) ──
|
|
2277
|
-
if (projDock) {
|
|
2278
|
-
const wrap = document.createElement('div');
|
|
2279
|
-
wrap.id = '__3d_proj';
|
|
2280
|
-
wrap.style.cssText = 'width:100%;height:100%;overflow:auto;background:#1a1a1a;';
|
|
2281
|
-
import(/* @vite-ignore */ base + 'Sidebar.Project.js').then(({ SidebarProject }) => {
|
|
2282
|
-
const p = new SidebarProject(ed);
|
|
2283
|
-
p.dom.style.cssText = 'padding:8px;';
|
|
2284
|
-
wrap.appendChild(p.dom);
|
|
2285
|
-
});
|
|
2286
|
-
projDock.appendChild(wrap);
|
|
2287
|
-
this._3dProjPanel = wrap;
|
|
2288
|
-
}
|
|
2289
2196
|
};
|
|
2290
2197
|
|
|
2291
2198
|
tryConnect();
|
|
2292
2199
|
}
|
|
2293
2200
|
|
|
2201
|
+
_build3DVisibilityPanel(container, editor3DEl) {
|
|
2202
|
+
const sceneData = editor3DEl.sceneData ?? {};
|
|
2203
|
+
editor3DEl.sceneData = sceneData;
|
|
2204
|
+
const settings = sceneData.settings ?? {};
|
|
2205
|
+
sceneData.settings = settings;
|
|
2206
|
+
if (!settings.visibilityEnabled) settings.visibilityEnabled = false;
|
|
2207
|
+
if (!settings.visibilityGroups) settings.visibilityGroups = [];
|
|
2208
|
+
if (!settings.visibilityAction) settings.visibilityAction = 'hide';
|
|
2209
|
+
if (!settings.visibilityRedirectScreen) settings.visibilityRedirectScreen = '';
|
|
2210
|
+
|
|
2211
|
+
container.innerHTML = `
|
|
2212
|
+
<div style="padding:15px;border:3px solid #2196F3;background:linear-gradient(135deg,#e3f2fd 0%,#bbdefb 100%);margin:10px;border-radius:8px;box-shadow:0 4px 6px rgba(0,0,0,.1);">
|
|
2213
|
+
<h3 style="margin:0 0 15px;font-size:16px;font-weight:bold;color:#1565C0;">🔒 Screen Visibility Control</h3>
|
|
2214
|
+
<div id="__3dvis_content"></div>
|
|
2215
|
+
</div>`;
|
|
2216
|
+
|
|
2217
|
+
const content = container.querySelector('#__3dvis_content');
|
|
2218
|
+
|
|
2219
|
+
content.innerHTML = `
|
|
2220
|
+
<div style="padding:10px;background:#fff3cd;border:1px solid #ffc107;border-radius:4px;margin-bottom:15px;font-size:12px;color:#856404;">
|
|
2221
|
+
💡 <strong>Tip:</strong> Don't forget to save (Ctrl+S) after changing settings!
|
|
2222
|
+
</div>
|
|
2223
|
+
<div style="margin-bottom:15px;">
|
|
2224
|
+
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;font-weight:500;">
|
|
2225
|
+
<input type="checkbox" id="__3dv_en" ${settings.visibilityEnabled ? 'checked' : ''} style="width:16px;height:16px;cursor:pointer;"/>
|
|
2226
|
+
<span>Enable Screen Visibility Control</span>
|
|
2227
|
+
</label>
|
|
2228
|
+
</div>
|
|
2229
|
+
<div style="margin:15px 0;">
|
|
2230
|
+
<label style="font-size:13px;font-weight:600;display:block;margin-bottom:10px;color:#555;">Only for groups:</label>
|
|
2231
|
+
<div id="__3dv_groups" style="border:1px solid #ccc;padding:10px;background:#fff;border-radius:4px;max-height:150px;overflow-y:auto;">
|
|
2232
|
+
<p style="font-size:12px;color:#999;">⏳ Loading groups...</p>
|
|
2233
|
+
</div>
|
|
2234
|
+
</div>
|
|
2235
|
+
<div style="margin:15px 0;">
|
|
2236
|
+
<label style="font-size:13px;font-weight:600;display:block;margin-bottom:8px;color:#555;">If user not in group:</label>
|
|
2237
|
+
<select id="__3dv_act" style="width:100%;padding:8px;font-size:13px;border:1px solid #ccc;border-radius:4px;background:#fff;">
|
|
2238
|
+
<option value="hide" ${settings.visibilityAction === 'hide' ? 'selected' : ''}>Show "Access Denied" message</option>
|
|
2239
|
+
<option value="redirect" ${settings.visibilityAction === 'redirect' ? 'selected' : ''}>Redirect to another screen</option>
|
|
2240
|
+
</select>
|
|
2241
|
+
</div>
|
|
2242
|
+
<div id="__3dv_rdiv" style="margin:15px 0;display:${settings.visibilityAction === 'redirect' ? 'block' : 'none'};">
|
|
2243
|
+
<label style="font-size:13px;font-weight:600;display:block;margin-bottom:8px;color:#555;">Redirect to screen:</label>
|
|
2244
|
+
<input type="text" id="__3dv_redir" placeholder="Enter screen name"
|
|
2245
|
+
value="${settings.visibilityRedirectScreen || ''}"
|
|
2246
|
+
style="width:100%;padding:8px;font-size:13px;border:1px solid #ccc;border-radius:4px;background:#fff;box-sizing:border-box;"/>
|
|
2247
|
+
</div>`;
|
|
2248
|
+
|
|
2249
|
+
content.querySelector('#__3dv_en')?.addEventListener('change', e => { settings.visibilityEnabled = e.target.checked; });
|
|
2250
|
+
const actEl = content.querySelector('#__3dv_act');
|
|
2251
|
+
const rdDiv = content.querySelector('#__3dv_rdiv');
|
|
2252
|
+
actEl?.addEventListener('change', e => {
|
|
2253
|
+
settings.visibilityAction = e.target.value;
|
|
2254
|
+
if (rdDiv) rdDiv.style.display = e.target.value === 'redirect' ? 'block' : 'none';
|
|
2255
|
+
});
|
|
2256
|
+
content.querySelector('#__3dv_redir')?.addEventListener('input', e => { settings.visibilityRedirectScreen = e.target.value; });
|
|
2257
|
+
|
|
2258
|
+
// Load groups async
|
|
2259
|
+
const grpEl = content.querySelector('#__3dv_groups');
|
|
2260
|
+
iobrokerHandler.getUserGroups?.().then(groups => {
|
|
2261
|
+
if (!grpEl) return;
|
|
2262
|
+
if (!groups?.length) { grpEl.innerHTML = '<div style="padding:10px;color:#f44336;">⚠️ No groups found!</div>'; return; }
|
|
2263
|
+
let html = '';
|
|
2264
|
+
groups.forEach(g => {
|
|
2265
|
+
const id = g.id ?? g._id ?? g.name ?? g;
|
|
2266
|
+
const lbl = g.name ?? id;
|
|
2267
|
+
const chk = (settings.visibilityGroups || []).includes(id);
|
|
2268
|
+
html += `<div style="padding:8px;margin:4px 0;background:#f9f9f9;border-radius:3px;">
|
|
2269
|
+
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;">
|
|
2270
|
+
<input type="checkbox" ${chk ? 'checked' : ''} data-gid="${id}" style="width:16px;height:16px;cursor:pointer;"/>
|
|
2271
|
+
<span>${lbl}</span>
|
|
2272
|
+
</label></div>`;
|
|
2273
|
+
});
|
|
2274
|
+
grpEl.innerHTML = html;
|
|
2275
|
+
grpEl.querySelectorAll('input[data-gid]').forEach(cb => {
|
|
2276
|
+
cb.addEventListener('change', e => {
|
|
2277
|
+
const gid = cb.dataset.gid;
|
|
2278
|
+
let arr = settings.visibilityGroups || [];
|
|
2279
|
+
if (e.target.checked) { if (!arr.includes(gid)) arr.push(gid); }
|
|
2280
|
+
else arr = arr.filter(x => x !== gid);
|
|
2281
|
+
settings.visibilityGroups = arr;
|
|
2282
|
+
});
|
|
2283
|
+
});
|
|
2284
|
+
}).catch(() => { if (grpEl) grpEl.innerHTML = '<div style="padding:10px;color:#f44336;">⚠️ Error loading groups</div>'; });
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2294
2287
|
_deactivate3DPropertiesMode() {
|
|
2295
2288
|
if (!this._3dPropsActive) return;
|
|
2296
2289
|
this._3dPropsActive = false;
|
|
@@ -2307,13 +2300,11 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
2307
2300
|
}
|
|
2308
2301
|
};
|
|
2309
2302
|
|
|
2310
|
-
restore('attributeDock',
|
|
2311
|
-
restore('settingsDock',
|
|
2312
|
-
restore('effectsDock', this._3dProjPanel);
|
|
2303
|
+
restore('attributeDock', this._3dPropsPanel);
|
|
2304
|
+
restore('settingsDock', this._3dSettPanel);
|
|
2313
2305
|
|
|
2314
2306
|
this._3dPropsPanel = null;
|
|
2315
2307
|
this._3dSettPanel = null;
|
|
2316
|
-
this._3dProjPanel = null;
|
|
2317
2308
|
}
|
|
2318
2309
|
}
|
|
2319
2310
|
window.customElements.define('iobroker-webui-app-shell', IobrokerWebuiAppShell);
|