iobroker.mywebui 1.37.31 → 1.37.33

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.31",
4
+ "version": "1.37.33",
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.31",
3
+ "version": "1.37.33",
4
4
  "description": "ioBroker mywebui - Custom edited mywebui by gokturk413",
5
5
  "type": "module",
6
6
  "main": "dist/backend/main.js",
@@ -388,231 +388,72 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
388
388
 
389
389
  async _updateVisibilityPanel() {
390
390
  const visibilityDock = this._getDomElement('visibilityDock');
391
- if (!visibilityDock) {
392
- console.warn('⚠️ [Visibility] visibilityDock not found');
393
- return;
394
- }
395
-
391
+ if (!visibilityDock) return;
392
+
396
393
  const content = visibilityDock.querySelector('#visibility-panel-content');
397
- if (!content) {
398
- console.warn('⚠️ [Visibility] Panel content not found');
399
- return;
400
- }
401
-
402
- // Use _selectedItems instead of selectedElements
394
+ if (!content) return;
395
+
403
396
  const selectedItems = this.propertyGrid.propertyGrid._selectedItems;
404
-
405
- console.log('🔍 [Visibility] Update called, selected:', selectedItems?.length || 0);
406
-
397
+
407
398
  if (!selectedItems || selectedItems.length !== 1) {
408
399
  content.innerHTML = '<p style="color:#999;font-style:italic;">Select an element to configure visibility...</p>';
409
- console.log('👁️ [Visibility] No selection');
410
400
  return;
411
401
  }
412
-
413
- console.log('👁️ [Visibility] Rendering UI for selected element...');
414
-
415
- const element = selectedItems[0].element;
416
-
417
- // Get current config from separate attributes
418
- let config = {
419
- enabled: element.getAttribute('data-visibility-enabled') === 'true',
420
- objectId: element.getAttribute('data-visibility-signal') || '',
421
- condition: element.getAttribute('data-visibility-condition') || '==',
422
- conditionValue: element.getAttribute('data-visibility-value') || '',
423
- action: element.getAttribute('data-visibility-action') || 'hide',
424
- groups: element.getAttribute('data-visibility-groups')?.split(',').filter(g => g) || []
425
- };
426
-
427
- const updateConfig = (key, value) => {
428
- config[key] = value;
429
-
430
- // Use DesignItem's setAttribute to properly persist in HTML
431
- const designItem = selectedItems[0];
432
-
433
- designItem.setAttribute('data-visibility-enabled', config.enabled ? 'true' : 'false');
434
- if (config.objectId) {
435
- designItem.setAttribute('data-visibility-signal', config.objectId);
436
- } else {
437
- designItem.removeAttribute('data-visibility-signal');
438
- }
439
- if (config.condition) {
440
- designItem.setAttribute('data-visibility-condition', config.condition);
441
- }
442
- if (config.conditionValue) {
443
- designItem.setAttribute('data-visibility-value', config.conditionValue);
444
- }
445
- if (config.action) {
446
- designItem.setAttribute('data-visibility-action', config.action);
447
- }
448
- if (config.groups && config.groups.length > 0) {
449
- designItem.setAttribute('data-visibility-groups', config.groups.join(','));
450
- } else {
451
- designItem.removeAttribute('data-visibility-groups');
452
- }
453
-
454
- console.log('🔒 [Visibility] Config updated via DesignItem:', config);
455
- };
456
-
402
+
403
+ const designItem = selectedItems[0];
404
+ const element = designItem.element;
405
+
406
+ // Read existing visible binding from bind-visible: attribute
407
+ const existingBindingAttr = element.getAttribute('bind-visible:');
408
+ let existingBinding = null;
409
+ if (existingBindingAttr != null) {
410
+ const parsed = bindingsHelper.parseBinding(element, 'bind-visible:', existingBindingAttr, 'visible', 'bind-visible:');
411
+ if (parsed) existingBinding = parsed[1];
412
+ }
413
+
457
414
  content.innerHTML = '';
458
-
459
- // Enable checkbox
460
- const enableDiv = document.createElement('div');
461
- enableDiv.style.cssText = 'margin-bottom:10px;';
462
- const enableLabel = document.createElement('label');
463
- enableLabel.style.cssText = 'display:flex;align-items:center;gap:5px;cursor:pointer;font-size:12px;';
464
- const enableCheck = document.createElement('input');
465
- enableCheck.type = 'checkbox';
466
- enableCheck.checked = config.enabled || false;
467
- enableCheck.onchange = () => updateConfig('enabled', enableCheck.checked);
468
- enableLabel.appendChild(enableCheck);
469
- enableLabel.appendChild(document.createTextNode('Enable Visibility Control'));
470
- enableDiv.appendChild(enableLabel);
471
- content.appendChild(enableDiv);
472
-
473
- // Object ID with IOB selector button
474
- const objIdField = this._createFieldWithButton('Object ID:', 'text', config.objectId || '', '0_userdata.0.test',
475
- (val) => updateConfig('objectId', val));
476
- content.appendChild(objIdField);
477
-
478
- // Condition
479
- const condDiv = document.createElement('div');
480
- condDiv.style.cssText = 'margin-bottom:10px;';
481
- const condLabel = document.createElement('label');
482
- condLabel.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
483
- condLabel.textContent = 'Condition:';
484
- const condSelect = document.createElement('select');
485
- condSelect.style.cssText = 'width:100%;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:3px;';
486
- condSelect.innerHTML = '<option>==</option><option>!=</option><option>&gt;</option><option>&lt;</option><option>&gt;=</option><option>&lt;=</option>';
487
- condSelect.value = config.condition || '==';
488
- condSelect.onchange = () => updateConfig('condition', condSelect.value);
489
- condDiv.appendChild(condLabel);
490
- condDiv.appendChild(condSelect);
491
- content.appendChild(condDiv);
492
-
493
- // Value for condition
494
- content.appendChild(this._createField('Value for condition:', 'text', config.conditionValue || '', '1',
495
- (val) => updateConfig('conditionValue', val)));
496
-
497
- // User Groups
498
- const groupsDiv = document.createElement('div');
499
- groupsDiv.style.cssText = 'margin-bottom:10px;';
500
- const groupsLabel = document.createElement('label');
501
- groupsLabel.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
502
- groupsLabel.textContent = 'Only for groups:';
503
- groupsDiv.appendChild(groupsLabel);
504
-
505
- const groupsList = document.createElement('div');
506
- groupsList.style.cssText = 'max-height:100px;overflow-y:auto;border:1px solid #ccc;padding:6px;background:#fff;border-radius:3px;';
507
-
508
- const userGroups = await iobrokerHandler.getUserGroups();
509
- const selectedGroups = config.groups || [];
510
-
511
- userGroups.forEach(group => {
512
- const groupLabel = document.createElement('label');
513
- groupLabel.style.cssText = 'display:flex;align-items:center;gap:5px;font-size:11px;padding:3px;cursor:pointer;';
514
- const groupCheck = document.createElement('input');
515
- groupCheck.type = 'checkbox';
516
- groupCheck.checked = selectedGroups.includes(group.id);
517
- groupCheck.onchange = () => {
518
- let groups = config.groups || [];
519
- if (groupCheck.checked) {
520
- if (!groups.includes(group.id)) groups.push(group.id);
521
- } else {
522
- groups = groups.filter(g => g !== group.id);
523
- }
524
- updateConfig('groups', groups);
525
- };
526
- groupLabel.appendChild(groupCheck);
527
- groupLabel.appendChild(document.createTextNode(group.name));
528
- groupsList.appendChild(groupLabel);
529
- });
530
-
531
- groupsDiv.appendChild(groupsList);
532
- content.appendChild(groupsDiv);
533
-
534
- // Action
535
- const actionDiv = document.createElement('div');
536
- actionDiv.style.cssText = 'margin-bottom:5px;';
537
- const actionLabel = document.createElement('label');
538
- actionLabel.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
539
- actionLabel.textContent = 'If user not in group:';
540
- const actionSelect = document.createElement('select');
541
- actionSelect.style.cssText = 'width:100%;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:3px;';
542
- actionSelect.innerHTML = '<option value="hide">hide</option><option value="disable">disable</option>';
543
- actionSelect.value = config.action || 'hide';
544
- actionSelect.onchange = () => updateConfig('action', actionSelect.value);
545
- actionDiv.appendChild(actionLabel);
546
- actionDiv.appendChild(actionSelect);
547
- content.appendChild(actionDiv);
548
- }
549
-
550
- _createField(label, type, value, placeholder, onChange) {
551
- const div = document.createElement('div');
552
- div.style.cssText = 'margin-bottom:10px;';
553
- const labelEl = document.createElement('label');
554
- labelEl.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
555
- labelEl.textContent = label;
556
- const input = document.createElement('input');
557
- input.type = type;
558
- input.value = value;
559
- input.placeholder = placeholder;
560
- input.style.cssText = 'width:100%;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:3px;';
561
- input.oninput = () => onChange(input.value);
562
- div.appendChild(labelEl);
563
- div.appendChild(input);
564
- return div;
565
- }
566
-
567
- _createFieldWithButton(label, type, value, placeholder, onChange) {
568
- const div = document.createElement('div');
569
- div.style.cssText = 'margin-bottom:10px;';
570
- const labelEl = document.createElement('label');
571
- labelEl.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
572
- labelEl.textContent = label;
573
-
574
- // Container for input + button
575
- const inputContainer = document.createElement('div');
576
- inputContainer.style.cssText = 'display:flex;gap:5px;';
577
-
578
- const input = document.createElement('input');
579
- input.type = type;
580
- input.value = value;
581
- input.placeholder = placeholder;
582
- input.style.cssText = 'flex:1;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:3px;';
583
- input.oninput = () => onChange(input.value);
584
-
585
- // IOB Button
586
- const btn = document.createElement('button');
587
- btn.textContent = 'IOB';
588
- btn.title = 'ioBroker object selector';
589
- btn.style.cssText = 'padding:6px 12px;font-size:11px;font-weight:bold;background:#2196f3;color:#fff;border:none;border-radius:3px;cursor:pointer;white-space:nowrap;';
590
- btn.onmouseover = () => btn.style.background = '#1976d2';
591
- btn.onmouseout = () => btn.style.background = '#2196f3';
592
- btn.onclick = async () => {
593
- try {
594
- const res = await openSelectIdDialog({
595
- host: window.iobrokerHost,
596
- port: window.iobrokerPort,
597
- protocol: window.location.protocol,
598
- language: 'en',
599
- selected: input.value,
600
- allowAll: true
601
- });
602
- if (res) {
603
- input.value = res;
604
- onChange(res);
605
- }
606
- } catch (err) {
607
- console.error('❌ [Visibility] Object selector error:', err);
608
- }
415
+
416
+ // Property row: label + current signal + bind button + clear button
417
+ const row = document.createElement('div');
418
+ row.style.cssText = 'display:flex;align-items:center;gap:6px;padding:4px 0;';
419
+
420
+ const label = document.createElement('span');
421
+ label.textContent = 'Visibility';
422
+ label.style.cssText = 'font-size:12px;font-weight:600;flex:1;';
423
+ row.appendChild(label);
424
+
425
+ if (existingBinding) {
426
+ const statusSpan = document.createElement('span');
427
+ const sig = existingBinding.signal || (existingBinding.expression ? 'expr' : '?');
428
+ statusSpan.textContent = sig;
429
+ statusSpan.title = JSON.stringify(existingBinding);
430
+ statusSpan.style.cssText = 'font-size:10px;color:#555;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
431
+ row.appendChild(statusSpan);
432
+ }
433
+
434
+ const bindBtn = document.createElement('button');
435
+ bindBtn.textContent = '□';
436
+ bindBtn.title = 'Open binding editor';
437
+ bindBtn.style.cssText = 'width:20px;height:20px;padding:0;font-size:13px;line-height:1;border:1px solid #888;background:#f0f0f0;cursor:pointer;flex-shrink:0;';
438
+ bindBtn.onclick = () => {
439
+ const property = { name: 'hidden', propertyType: 'visible' };
440
+ serviceContainer.config.openBindingsEditor(property, [designItem], existingBinding, 'visible');
609
441
  };
610
-
611
- inputContainer.appendChild(input);
612
- inputContainer.appendChild(btn);
613
- div.appendChild(labelEl);
614
- div.appendChild(inputContainer);
615
- return div;
442
+ row.appendChild(bindBtn);
443
+
444
+ if (existingBinding) {
445
+ const clearBtn = document.createElement('button');
446
+ clearBtn.textContent = '✕';
447
+ clearBtn.title = 'Remove binding';
448
+ clearBtn.style.cssText = 'width:20px;height:20px;padding:0;font-size:11px;line-height:1;border:1px solid #c66;background:#fee;cursor:pointer;color:#c00;flex-shrink:0;';
449
+ clearBtn.onclick = () => {
450
+ designItem.removeAttribute('bind-visible:');
451
+ this._updateVisibilityPanel();
452
+ };
453
+ row.appendChild(clearBtn);
454
+ }
455
+
456
+ content.appendChild(row);
616
457
  }
617
458
  /* Move to a Dock Spawn Helper */
618
459
  activateDockById(name) {
@@ -3,15 +3,11 @@ import { iobrokerHandler } from "../common/IobrokerHandler.js";
3
3
 
4
4
  export class IobrokerWebuiPropertyGrid extends VisualizationPropertyGrid {
5
5
  saveCallback;
6
- _visibilityPanel;
7
- _visibilityPanelCreated = false;
8
6
  _screenVisibilityPanel;
9
7
  _screenVisibilityPanelCreated = false;
10
-
8
+
11
9
  constructor() {
12
10
  super();
13
-
14
- // Watch for property grid changes without breaking parent
15
11
  this._setupScreenVisibilityObserver();
16
12
  }
17
13
 
@@ -47,166 +43,6 @@ export class IobrokerWebuiPropertyGrid extends VisualizationPropertyGrid {
47
43
  }, 200); // Check every 200ms
48
44
  }
49
45
 
50
- _ensureVisibilityPanel() {
51
- if (!this._visibilityPanelCreated) {
52
- this._visibilityPanel = document.createElement('div');
53
- this._visibilityPanel.id = 'visibility-panel';
54
- this._visibilityPanel.style.cssText = 'padding:10px;border-top:2px solid #ccc;background:#fff;';
55
- this._visibilityPanel.innerHTML = `
56
- <h3 style="margin:5px 0 10px 0;font-size:13px;font-weight:bold;">🔒 Visibility</h3>
57
- <div id="visibility-content"></div>
58
- `;
59
-
60
- // Find property grid container and append
61
- const container = this.shadowRoot || this;
62
- if (container.querySelector) {
63
- const target = container.querySelector('.property-grid-container') || container;
64
- target.appendChild(this._visibilityPanel);
65
- } else {
66
- this.appendChild(this._visibilityPanel);
67
- }
68
-
69
- this._visibilityPanelCreated = true;
70
- console.log('✅ [Visibility] Panel created and appended');
71
- }
72
- }
73
-
74
- set selectedElements(value) {
75
- super.selectedElements = value;
76
-
77
- // Ensure panel exists
78
- setTimeout(() => {
79
- this._ensureVisibilityPanel();
80
- this._updateVisibilityPanel(value);
81
- }, 50);
82
- }
83
-
84
- async _updateVisibilityPanel(elements) {
85
- if (!this._visibilityPanel) {
86
- return;
87
- }
88
-
89
- if (!elements || elements.length !== 1) {
90
- this._visibilityPanel.style.display = 'none';
91
- return;
92
- }
93
-
94
- const element = elements[0].element;
95
- this._visibilityPanel.style.display = 'block';
96
-
97
- const content = this._visibilityPanel.querySelector('#visibility-content');
98
- content.innerHTML = '';
99
-
100
- // Get current config
101
- const visibilityAttr = element.getAttribute('data-visibility');
102
- let config = {};
103
- if (visibilityAttr) {
104
- try {
105
- config = JSON.parse(visibilityAttr);
106
- } catch (err) {}
107
- }
108
-
109
- // Create UI
110
- const updateConfig = (key, value) => {
111
- config[key] = value;
112
- element.setAttribute('data-visibility', JSON.stringify(config));
113
- console.log('🔒 [Visibility] Updated:', config);
114
- };
115
-
116
- // Enable checkbox
117
- const enableContainer = document.createElement('label');
118
- enableContainer.style.cssText = 'display:flex;align-items:center;gap:5px;margin-bottom:10px;cursor:pointer;';
119
- const enableCheck = document.createElement('input');
120
- enableCheck.type = 'checkbox';
121
- enableCheck.checked = config.enabled || false;
122
- enableCheck.onchange = () => updateConfig('enabled', enableCheck.checked);
123
- enableContainer.appendChild(enableCheck);
124
- enableContainer.appendChild(document.createTextNode('Enable Visibility Control'));
125
- content.appendChild(enableContainer);
126
-
127
- // Object ID
128
- const objContainer = document.createElement('div');
129
- objContainer.style.cssText = 'margin-bottom:10px;';
130
- objContainer.innerHTML = '<label style="font-size:11px;font-weight:bold;display:block;margin-bottom:3px;">Object ID:</label>';
131
- const objInput = document.createElement('input');
132
- objInput.type = 'text';
133
- objInput.value = config.objectId || '';
134
- objInput.placeholder = '0_userdata.0.test';
135
- objInput.style.cssText = 'width:100%;padding:5px;font-size:11px;';
136
- objInput.oninput = () => updateConfig('objectId', objInput.value);
137
- objContainer.appendChild(objInput);
138
- content.appendChild(objContainer);
139
-
140
- // Condition
141
- const condContainer = document.createElement('div');
142
- condContainer.style.cssText = 'margin-bottom:10px;';
143
- condContainer.innerHTML = '<label style="font-size:11px;font-weight:bold;display:block;margin-bottom:3px;">Condition:</label>';
144
- const condSelect = document.createElement('select');
145
- condSelect.style.cssText = 'width:100%;padding:5px;font-size:11px;';
146
- condSelect.innerHTML = '<option>==</option><option>!=</option><option>&gt;</option><option>&lt;</option><option>&gt;=</option><option>&lt;=</option>';
147
- condSelect.value = config.condition || '==';
148
- condSelect.onchange = () => updateConfig('condition', condSelect.value);
149
- condContainer.appendChild(condSelect);
150
- content.appendChild(condContainer);
151
-
152
- // Value
153
- const valContainer = document.createElement('div');
154
- valContainer.style.cssText = 'margin-bottom:10px;';
155
- valContainer.innerHTML = '<label style="font-size:11px;font-weight:bold;display:block;margin-bottom:3px;">Value:</label>';
156
- const valInput = document.createElement('input');
157
- valInput.type = 'text';
158
- valInput.value = config.conditionValue || '';
159
- valInput.placeholder = '1';
160
- valInput.style.cssText = 'width:100%;padding:5px;font-size:11px;';
161
- valInput.oninput = () => updateConfig('conditionValue', valInput.value);
162
- valContainer.appendChild(valInput);
163
- content.appendChild(valContainer);
164
-
165
- // User Groups
166
- const groupsContainer = document.createElement('div');
167
- groupsContainer.style.cssText = 'margin-bottom:10px;';
168
- groupsContainer.innerHTML = '<label style="font-size:11px;font-weight:bold;display:block;margin-bottom:3px;">Only for groups:</label>';
169
- const groupsList = document.createElement('div');
170
- groupsList.style.cssText = 'max-height:100px;overflow-y:auto;border:1px solid #ccc;padding:5px;background:#f9f9f9;';
171
-
172
- const userGroups = await iobrokerHandler.getUserGroups();
173
- const selectedGroups = config.groups || [];
174
-
175
- userGroups.forEach(group => {
176
- const label = document.createElement('label');
177
- label.style.cssText = 'display:flex;align-items:center;gap:5px;font-size:11px;padding:2px;cursor:pointer;';
178
- const checkbox = document.createElement('input');
179
- checkbox.type = 'checkbox';
180
- checkbox.checked = selectedGroups.includes(group.id);
181
- checkbox.onchange = () => {
182
- let groups = config.groups || [];
183
- if (checkbox.checked) {
184
- if (!groups.includes(group.id)) groups.push(group.id);
185
- } else {
186
- groups = groups.filter(g => g !== group.id);
187
- }
188
- updateConfig('groups', groups);
189
- };
190
- label.appendChild(checkbox);
191
- label.appendChild(document.createTextNode(group.name));
192
- groupsList.appendChild(label);
193
- });
194
-
195
- groupsContainer.appendChild(groupsList);
196
- content.appendChild(groupsContainer);
197
-
198
- // Action
199
- const actionContainer = document.createElement('div');
200
- actionContainer.innerHTML = '<label style="font-size:11px;font-weight:bold;display:block;margin-bottom:3px;">If not in group:</label>';
201
- const actionSelect = document.createElement('select');
202
- actionSelect.style.cssText = 'width:100%;padding:5px;font-size:11px;';
203
- actionSelect.innerHTML = '<option value="hide">Hide</option><option value="disable">Disable</option>';
204
- actionSelect.value = config.action || 'hide';
205
- actionSelect.onchange = () => updateConfig('action', actionSelect.value);
206
- actionContainer.appendChild(actionSelect);
207
- content.appendChild(actionContainer);
208
- }
209
-
210
46
  _ensureScreenVisibilityPanel() {
211
47
  if (!this._screenVisibilityPanelCreated) {
212
48
  this._screenVisibilityPanel = document.createElement('div');
@@ -37,12 +37,6 @@ export class IobrokerWebuiVisibilityPropertiesService {
37
37
  type: 'boolean',
38
38
  service: this,
39
39
  propertyType: PropertyType.propertyAndAttribute
40
- },
41
- {
42
- name: 'data-visibility',
43
- type: 'string',
44
- service: this,
45
- propertyType: PropertyType.propertyAndAttribute
46
40
  }
47
41
  ];
48
42