iobroker.mywebui 1.37.36 → 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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "mywebui",
4
- "version": "1.37.36",
4
+ "version": "1.37.37",
5
5
  "titleLang": {
6
6
  "en": "mywebui",
7
7
  "de": "mywebui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.mywebui",
3
- "version": "1.37.36",
3
+ "version": "1.37.37",
4
4
  "description": "ioBroker mywebui - Custom edited mywebui by gokturk413",
5
5
  "type": "module",
6
6
  "main": "dist/backend/main.js",
@@ -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 binding from bind-prop:hidden attribute
407
- const bindAttr = element.getAttribute('bind-prop:hidden');
408
- let existingBinding = null;
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 = existingBinding
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
- // Left-click: open binding editor (pass null so openBindingsEditor doesn't reject on service check)
436
- bindBtn.onclick = () => {
437
- const property = { name: 'hidden', propertyType: 'propertyAndAttribute' };
438
- serviceContainer.config.openBindingsEditor(property, [designItem], null, 'property');
439
- };
460
+ bindBtn.onclick = openVisibilityBindingEditor;
440
461
 
441
- // Right-click: context menu (like Properties panel)
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], null, '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
- }, !existingBinding);
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 (existingBinding) {
486
- const sig = existingBinding.signal || (existingBinding.expression ? 'expr' : '?');
504
+ if (existingSignal) {
487
505
  const statusSpan = document.createElement('span');
488
- statusSpan.textContent = sig;
489
- statusSpan.title = JSON.stringify(existingBinding);
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
- async #checkAndApply(element, visibilityConfig) {
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 result = await iobrokerHandler.checkVisibility(visibilityConfig);
11
-
12
- if (!result.visible) {
13
- element.style.display = 'none';
14
- element.style.visibility = 'hidden';
15
- } else {
16
- const orig = this.#originalDisplayStyles.get(element) || '';
17
- element.style.display = orig;
18
- element.style.visibility = '';
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 (!result.enabled) {
22
- element.style.pointerEvents = 'none';
23
- element.style.opacity = '0.5';
24
- } else {
25
- element.style.pointerEvents = '';
26
- element.style.opacity = '';
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,11 +72,11 @@ 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
  }
@@ -58,20 +93,24 @@ class VisibilityService {
58
93
  }
59
94
 
60
95
  async scanAndApply(container = document.body) {
61
- const elements = container.querySelectorAll('[data-visibility-enabled="true"]');
62
- for (const element of elements) {
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);
63
104
  try {
64
105
  const visibilityConfig = {
65
106
  enabled: true,
66
- objectId: element.getAttribute('data-visibility-signal'),
67
- condition: element.getAttribute('data-visibility-condition') || '==',
68
- conditionValue: element.getAttribute('data-visibility-value') || '',
107
+ objectId: element.getAttribute('data-visibility-signal') || null,
69
108
  action: element.getAttribute('data-visibility-action') || 'hide',
70
109
  groups: element.getAttribute('data-visibility-groups')?.split(',').filter(g => g) || []
71
110
  };
72
111
  await this.applyVisibility(element, visibilityConfig);
73
112
  } catch (err) {
74
- console.error('[Visibility] Error parsing visibility config:', err);
113
+ console.error('[Visibility] Error:', err);
75
114
  }
76
115
  }
77
116
  }