iobroker.mywebui 1.37.35 → 1.37.37
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/io-package.json
CHANGED
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@ import { IobrokerWebuiScreenEditor } from './IobrokerWebuiScreenEditor.js';
|
|
|
27
27
|
import { IobrokerWebuiConfirmationWrapper } from './IobrokerWebuiConfirmationWrapper.js';
|
|
28
28
|
import { getPanelContainerForElement } from './DockHelper.js';
|
|
29
29
|
import { IobrokerWebuiPropertyGrid } from './IobrokerWebuiPropertyGrid.js';
|
|
30
|
+
import { IobrokerWebuiBindingsEditor } from './IobrokerWebuiBindingsEditor.js';
|
|
30
31
|
import { typeInfoFromJsonSchema } from '@gokturk413/propertygrid.webcomponent';
|
|
31
32
|
import { openSelectIdDialog } from "@iobroker/webcomponent-selectid-dialog/dist/selectIdHelper.js";
|
|
32
33
|
import "./IobrokerWebuiTranslationEditor.js";
|
|
@@ -403,13 +404,9 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
403
404
|
const designItem = selectedItems[0];
|
|
404
405
|
const element = designItem.element;
|
|
405
406
|
|
|
406
|
-
// Read existing
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
if (bindAttr != null) {
|
|
410
|
-
const parsed = bindingsHelper.parseBinding(element, 'bind-prop:hidden', bindAttr, 'property', 'bind-prop:');
|
|
411
|
-
if (parsed) existingBinding = parsed[1];
|
|
412
|
-
}
|
|
407
|
+
// Read existing signal from data-visibility-signal (NOT bind-prop:hidden)
|
|
408
|
+
const existingSignal = element.getAttribute('data-visibility-signal') || null;
|
|
409
|
+
const existingBinding = existingSignal ? { signal: existingSignal, target: 'property' } : null;
|
|
413
410
|
|
|
414
411
|
// Read group access control config from data attributes
|
|
415
412
|
const dataConfig = {
|
|
@@ -420,6 +417,34 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
420
417
|
|
|
421
418
|
content.innerHTML = '';
|
|
422
419
|
|
|
420
|
+
// Helper: open IobrokerWebuiBindingsEditor directly (pre-populates with existingBinding, saves to data-visibility-signal)
|
|
421
|
+
const openVisibilityBindingEditor = () => {
|
|
422
|
+
const dynEdt = new IobrokerWebuiBindingsEditor(
|
|
423
|
+
{ name: 'visibility', propertyType: 'propertyAndAttribute' },
|
|
424
|
+
existingBinding,
|
|
425
|
+
'property',
|
|
426
|
+
serviceContainer,
|
|
427
|
+
designItem.instanceServiceContainer,
|
|
428
|
+
this
|
|
429
|
+
);
|
|
430
|
+
const cw = new IobrokerWebuiConfirmationWrapper();
|
|
431
|
+
cw.title = 'Edit Visibility Signal Binding';
|
|
432
|
+
cw.appendChild(dynEdt);
|
|
433
|
+
const dlg = this.openDialog(cw, { x: 200, y: 200, width: 700, height: 460 });
|
|
434
|
+
cw.cancelClicked.on(() => dlg.close());
|
|
435
|
+
cw.okClicked.on(() => {
|
|
436
|
+
dlg.close();
|
|
437
|
+
const signal = dynEdt.objectNames;
|
|
438
|
+
if (signal) {
|
|
439
|
+
designItem.setAttribute('data-visibility-signal', signal);
|
|
440
|
+
} else {
|
|
441
|
+
designItem.removeAttribute('data-visibility-signal');
|
|
442
|
+
}
|
|
443
|
+
designItem.removeAttribute('bind-prop:hidden'); // clean up old approach
|
|
444
|
+
this._updateVisibilityPanel();
|
|
445
|
+
});
|
|
446
|
+
};
|
|
447
|
+
|
|
423
448
|
// --- BINDING ROW ---
|
|
424
449
|
const bindRow = document.createElement('div');
|
|
425
450
|
bindRow.style.cssText = 'display:flex;align-items:center;gap:6px;padding:4px 0;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #ddd;';
|
|
@@ -428,17 +453,13 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
428
453
|
const bindBtn = document.createElement('button');
|
|
429
454
|
bindBtn.textContent = '□';
|
|
430
455
|
bindBtn.title = 'Right-click for binding options';
|
|
431
|
-
bindBtn.style.cssText =
|
|
456
|
+
bindBtn.style.cssText = existingSignal
|
|
432
457
|
? 'width:16px;height:16px;padding:0;font-size:11px;line-height:1;border:1px solid #888;background:#ffd700;cursor:pointer;flex-shrink:0;'
|
|
433
458
|
: 'width:16px;height:16px;padding:0;font-size:11px;line-height:1;border:1px solid #888;background:#f0f0f0;cursor:pointer;flex-shrink:0;';
|
|
434
459
|
|
|
435
|
-
|
|
436
|
-
bindBtn.onclick = () => {
|
|
437
|
-
const property = { name: 'hidden', propertyType: 'propertyAndAttribute' };
|
|
438
|
-
serviceContainer.config.openBindingsEditor(property, [designItem], existingBinding, 'property');
|
|
439
|
-
};
|
|
460
|
+
bindBtn.onclick = openVisibilityBindingEditor;
|
|
440
461
|
|
|
441
|
-
// Right-click: context menu
|
|
462
|
+
// Right-click: context menu
|
|
442
463
|
bindBtn.oncontextmenu = (e) => {
|
|
443
464
|
e.preventDefault();
|
|
444
465
|
const existing = document.getElementById('__vis-ctx-menu');
|
|
@@ -461,14 +482,12 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
461
482
|
menu.appendChild(item);
|
|
462
483
|
};
|
|
463
484
|
|
|
464
|
-
addItem('edit binding',
|
|
465
|
-
const property = { name: 'hidden', propertyType: 'propertyAndAttribute' };
|
|
466
|
-
serviceContainer.config.openBindingsEditor(property, [designItem], existingBinding, 'property');
|
|
467
|
-
});
|
|
485
|
+
addItem('edit binding', openVisibilityBindingEditor);
|
|
468
486
|
addItem('clear binding', () => {
|
|
487
|
+
designItem.removeAttribute('data-visibility-signal');
|
|
469
488
|
designItem.removeAttribute('bind-prop:hidden');
|
|
470
489
|
this._updateVisibilityPanel();
|
|
471
|
-
}, !
|
|
490
|
+
}, !existingSignal);
|
|
472
491
|
|
|
473
492
|
document.body.appendChild(menu);
|
|
474
493
|
const close = (ev) => { if (!menu.contains(ev.target)) { menu.remove(); document.removeEventListener('mousedown', close); } };
|
|
@@ -482,11 +501,10 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
|
|
|
482
501
|
bindLabel.style.cssText = 'font-size:12px;font-weight:600;flex:1;';
|
|
483
502
|
bindRow.appendChild(bindLabel);
|
|
484
503
|
|
|
485
|
-
if (
|
|
486
|
-
const sig = existingBinding.signal || (existingBinding.expression ? 'expr' : '?');
|
|
504
|
+
if (existingSignal) {
|
|
487
505
|
const statusSpan = document.createElement('span');
|
|
488
|
-
statusSpan.textContent =
|
|
489
|
-
statusSpan.title =
|
|
506
|
+
statusSpan.textContent = existingSignal;
|
|
507
|
+
statusSpan.title = existingSignal;
|
|
490
508
|
statusSpan.style.cssText = 'font-size:10px;color:#aaa;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right;';
|
|
491
509
|
bindRow.appendChild(statusSpan);
|
|
492
510
|
}
|
|
@@ -5,26 +5,61 @@ class VisibilityService {
|
|
|
5
5
|
|
|
6
6
|
#originalDisplayStyles = new WeakMap();
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
#applyResult(element, result) {
|
|
9
|
+
if (!result.visible) {
|
|
10
|
+
element.style.display = 'none';
|
|
11
|
+
element.style.visibility = 'hidden';
|
|
12
|
+
} else {
|
|
13
|
+
const orig = this.#originalDisplayStyles.get(element) || '';
|
|
14
|
+
element.style.display = orig;
|
|
15
|
+
element.style.visibility = '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!result.enabled) {
|
|
19
|
+
element.style.pointerEvents = 'none';
|
|
20
|
+
element.style.opacity = '0.5';
|
|
21
|
+
} else {
|
|
22
|
+
element.style.pointerEvents = '';
|
|
23
|
+
element.style.opacity = '';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async #checkAndApply(element, visibilityConfig, currentStateVal) {
|
|
9
28
|
try {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
if
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
const action = visibilityConfig.action || 'hide';
|
|
30
|
+
|
|
31
|
+
// Step 1: Group access check (if groups configured)
|
|
32
|
+
if (visibilityConfig.groups && visibilityConfig.groups.length > 0) {
|
|
33
|
+
const groupResult = await iobrokerHandler.checkVisibility({
|
|
34
|
+
enabled: true,
|
|
35
|
+
groups: visibilityConfig.groups,
|
|
36
|
+
action: action
|
|
37
|
+
});
|
|
38
|
+
// If user is not in allowed groups, apply restriction and stop
|
|
39
|
+
if (!groupResult.visible || !groupResult.enabled) {
|
|
40
|
+
this.#applyResult(element, groupResult);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
19
43
|
}
|
|
20
44
|
|
|
21
|
-
if
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
// Step 2: Signal/state check (if objectId configured)
|
|
46
|
+
if (visibilityConfig.objectId) {
|
|
47
|
+
let stateVal = currentStateVal;
|
|
48
|
+
if (stateVal === undefined) {
|
|
49
|
+
const state = await iobrokerHandler.connection.getState(visibilityConfig.objectId);
|
|
50
|
+
stateVal = state?.val;
|
|
51
|
+
}
|
|
52
|
+
// Truthy state value → apply action
|
|
53
|
+
const isActive = stateVal !== null && stateVal !== undefined && stateVal !== false && stateVal !== 0 && stateVal !== '';
|
|
54
|
+
this.#applyResult(element, isActive
|
|
55
|
+
? { visible: action !== 'hide', enabled: action !== 'disable' }
|
|
56
|
+
: { visible: true, enabled: true }
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
27
59
|
}
|
|
60
|
+
|
|
61
|
+
// No restrictions active → show/enable
|
|
62
|
+
this.#applyResult(element, { visible: true, enabled: true });
|
|
28
63
|
} catch (err) {
|
|
29
64
|
console.error('[Visibility] Check failed:', err);
|
|
30
65
|
}
|
|
@@ -37,30 +72,45 @@ class VisibilityService {
|
|
|
37
72
|
this.#originalDisplayStyles.set(element, element.style.display || '');
|
|
38
73
|
}
|
|
39
74
|
|
|
40
|
-
await this.#checkAndApply(element, visibilityConfig);
|
|
75
|
+
await this.#checkAndApply(element, visibilityConfig, undefined);
|
|
41
76
|
|
|
42
77
|
if (visibilityConfig.objectId) {
|
|
43
|
-
await iobrokerHandler.subscribeState(visibilityConfig.objectId, () => {
|
|
44
|
-
this.#checkAndApply(element, visibilityConfig);
|
|
78
|
+
await iobrokerHandler.subscribeState(visibilityConfig.objectId, (_id, state) => {
|
|
79
|
+
this.#checkAndApply(element, visibilityConfig, state?.val);
|
|
45
80
|
});
|
|
46
81
|
}
|
|
47
82
|
}
|
|
48
83
|
|
|
84
|
+
removeVisibility(element) {
|
|
85
|
+
const originalDisplay = this.#originalDisplayStyles.get(element);
|
|
86
|
+
if (originalDisplay !== undefined) {
|
|
87
|
+
element.style.display = originalDisplay;
|
|
88
|
+
this.#originalDisplayStyles.delete(element);
|
|
89
|
+
}
|
|
90
|
+
element.style.visibility = '';
|
|
91
|
+
element.style.pointerEvents = '';
|
|
92
|
+
element.style.opacity = '';
|
|
93
|
+
}
|
|
94
|
+
|
|
49
95
|
async scanAndApply(container = document.body) {
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
// Collect all elements needing visibility handling (group control OR signal binding)
|
|
97
|
+
const groupElements = container.querySelectorAll('[data-visibility-enabled="true"]');
|
|
98
|
+
const signalElements = container.querySelectorAll('[data-visibility-signal]');
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
|
|
101
|
+
for (const element of [...groupElements, ...signalElements]) {
|
|
102
|
+
if (seen.has(element)) continue;
|
|
103
|
+
seen.add(element);
|
|
52
104
|
try {
|
|
53
105
|
const visibilityConfig = {
|
|
54
106
|
enabled: true,
|
|
55
|
-
objectId: element.getAttribute('data-visibility-signal'),
|
|
56
|
-
condition: element.getAttribute('data-visibility-condition') || '==',
|
|
57
|
-
conditionValue: element.getAttribute('data-visibility-value') || '',
|
|
107
|
+
objectId: element.getAttribute('data-visibility-signal') || null,
|
|
58
108
|
action: element.getAttribute('data-visibility-action') || 'hide',
|
|
59
109
|
groups: element.getAttribute('data-visibility-groups')?.split(',').filter(g => g) || []
|
|
60
110
|
};
|
|
61
111
|
await this.applyVisibility(element, visibilityConfig);
|
|
62
112
|
} catch (err) {
|
|
63
|
-
console.error('[Visibility] Error
|
|
113
|
+
console.error('[Visibility] Error:', err);
|
|
64
114
|
}
|
|
65
115
|
}
|
|
66
116
|
}
|