iobroker.mywebui 1.37.32 → 1.37.34

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.32",
4
+ "version": "1.37.34",
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.32",
3
+ "version": "1.37.34",
4
4
  "description": "ioBroker mywebui - Custom edited mywebui by gokturk413",
5
5
  "type": "module",
6
6
  "main": "dist/backend/main.js",
@@ -388,74 +388,97 @@ 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 = {
402
+
403
+ const designItem = selectedItems[0];
404
+ const element = designItem.element;
405
+
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
+ }
413
+
414
+ // Read group access control config from data attributes
415
+ const dataConfig = {
419
416
  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) || []
417
+ groups: element.getAttribute('data-visibility-groups')?.split(',').filter(g => g) || [],
418
+ action: element.getAttribute('data-visibility-action') || 'hide'
425
419
  };
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');
420
+
421
+ content.innerHTML = '';
422
+
423
+ // --- BINDING ROW ---
424
+ const bindRow = document.createElement('div');
425
+ bindRow.style.cssText = 'display:flex;align-items:center;gap:6px;padding:4px 0;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #ddd;';
426
+
427
+ const bindLabel = document.createElement('span');
428
+ bindLabel.textContent = 'Visibility';
429
+ bindLabel.style.cssText = 'font-size:12px;font-weight:600;flex:1;';
430
+ bindRow.appendChild(bindLabel);
431
+
432
+ if (existingBinding) {
433
+ const sig = existingBinding.signal || (existingBinding.expression ? 'expr' : '?');
434
+ const statusSpan = document.createElement('span');
435
+ statusSpan.textContent = sig;
436
+ statusSpan.title = JSON.stringify(existingBinding);
437
+ statusSpan.style.cssText = 'font-size:10px;color:#555;max-width:90px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
438
+ bindRow.appendChild(statusSpan);
439
+ }
440
+
441
+ const bindBtn = document.createElement('button');
442
+ bindBtn.textContent = '□';
443
+ bindBtn.title = 'Open binding editor';
444
+ bindBtn.style.cssText = existingBinding
445
+ ? 'width:20px;height:20px;padding:0;font-size:13px;line-height:1;border:1px solid #888;background:#ff0;cursor:pointer;flex-shrink:0;'
446
+ : 'width:20px;height:20px;padding:0;font-size:13px;line-height:1;border:1px solid #888;background:#f0f0f0;cursor:pointer;flex-shrink:0;';
447
+ bindBtn.onclick = () => {
448
+ const property = { name: 'hidden', propertyType: 'propertyAndAttribute' };
449
+ serviceContainer.config.openBindingsEditor(property, [designItem], existingBinding, 'property');
450
+ };
451
+ bindRow.appendChild(bindBtn);
452
+
453
+ if (existingBinding) {
454
+ const clearBtn = document.createElement('button');
455
+ clearBtn.textContent = '✕';
456
+ clearBtn.title = 'Remove binding';
457
+ 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;';
458
+ clearBtn.onclick = () => {
459
+ designItem.removeAttribute('bind-prop:hidden');
460
+ this._updateVisibilityPanel();
461
+ };
462
+ bindRow.appendChild(clearBtn);
463
+ }
464
+
465
+ content.appendChild(bindRow);
466
+
467
+ // --- GROUP ACCESS CONTROL ---
468
+ const updateGroupConfig = (key, value) => {
469
+ dataConfig[key] = value;
470
+ if (key === 'enabled') {
471
+ if (value) designItem.setAttribute('data-visibility-enabled', 'true');
472
+ else designItem.removeAttribute('data-visibility-enabled');
473
+ } else if (key === 'groups') {
474
+ if (value && value.length > 0) designItem.setAttribute('data-visibility-groups', value.join(','));
475
+ else designItem.removeAttribute('data-visibility-groups');
476
+ } else if (key === 'action') {
477
+ if (value) designItem.setAttribute('data-visibility-action', value);
478
+ else designItem.removeAttribute('data-visibility-action');
452
479
  }
453
-
454
- console.log('🔒 [Visibility] Config updated via DesignItem:', config);
455
480
  };
456
-
457
- content.innerHTML = '';
458
-
481
+
459
482
  // Enable checkbox
460
483
  const enableDiv = document.createElement('div');
461
484
  enableDiv.style.cssText = 'margin-bottom:10px;';
@@ -463,51 +486,27 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
463
486
  enableLabel.style.cssText = 'display:flex;align-items:center;gap:5px;cursor:pointer;font-size:12px;';
464
487
  const enableCheck = document.createElement('input');
465
488
  enableCheck.type = 'checkbox';
466
- enableCheck.checked = config.enabled || false;
467
- enableCheck.onchange = () => updateConfig('enabled', enableCheck.checked);
489
+ enableCheck.checked = dataConfig.enabled || false;
490
+ enableCheck.onchange = () => updateGroupConfig('enabled', enableCheck.checked);
468
491
  enableLabel.appendChild(enableCheck);
469
- enableLabel.appendChild(document.createTextNode('Enable Visibility Control'));
492
+ enableLabel.appendChild(document.createTextNode('Enable Group Visibility Control'));
470
493
  enableDiv.appendChild(enableLabel);
471
494
  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
495
+
496
+ // Groups
498
497
  const groupsDiv = document.createElement('div');
499
498
  groupsDiv.style.cssText = 'margin-bottom:10px;';
500
499
  const groupsLabel = document.createElement('label');
501
500
  groupsLabel.style.cssText = 'font-size:11px;font-weight:600;display:block;margin-bottom:3px;color:#555;';
502
501
  groupsLabel.textContent = 'Only for groups:';
503
502
  groupsDiv.appendChild(groupsLabel);
504
-
503
+
505
504
  const groupsList = document.createElement('div');
506
505
  groupsList.style.cssText = 'max-height:100px;overflow-y:auto;border:1px solid #ccc;padding:6px;background:#fff;border-radius:3px;';
507
-
506
+
508
507
  const userGroups = await iobrokerHandler.getUserGroups();
509
- const selectedGroups = config.groups || [];
510
-
508
+ const selectedGroups = dataConfig.groups || [];
509
+
511
510
  userGroups.forEach(group => {
512
511
  const groupLabel = document.createElement('label');
513
512
  groupLabel.style.cssText = 'display:flex;align-items:center;gap:5px;font-size:11px;padding:3px;cursor:pointer;';
@@ -515,22 +514,22 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
515
514
  groupCheck.type = 'checkbox';
516
515
  groupCheck.checked = selectedGroups.includes(group.id);
517
516
  groupCheck.onchange = () => {
518
- let groups = config.groups || [];
517
+ let groups = [...dataConfig.groups];
519
518
  if (groupCheck.checked) {
520
519
  if (!groups.includes(group.id)) groups.push(group.id);
521
520
  } else {
522
521
  groups = groups.filter(g => g !== group.id);
523
522
  }
524
- updateConfig('groups', groups);
523
+ updateGroupConfig('groups', groups);
525
524
  };
526
525
  groupLabel.appendChild(groupCheck);
527
526
  groupLabel.appendChild(document.createTextNode(group.name));
528
527
  groupsList.appendChild(groupLabel);
529
528
  });
530
-
529
+
531
530
  groupsDiv.appendChild(groupsList);
532
531
  content.appendChild(groupsDiv);
533
-
532
+
534
533
  // Action
535
534
  const actionDiv = document.createElement('div');
536
535
  actionDiv.style.cssText = 'margin-bottom:5px;';
@@ -540,80 +539,12 @@ export class IobrokerWebuiAppShell extends BaseCustomWebComponentConstructorAppe
540
539
  const actionSelect = document.createElement('select');
541
540
  actionSelect.style.cssText = 'width:100%;padding:6px;font-size:12px;border:1px solid #ccc;border-radius:3px;';
542
541
  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);
542
+ actionSelect.value = dataConfig.action || 'hide';
543
+ actionSelect.onchange = () => updateGroupConfig('action', actionSelect.value);
545
544
  actionDiv.appendChild(actionLabel);
546
545
  actionDiv.appendChild(actionSelect);
547
546
  content.appendChild(actionDiv);
548
547
  }
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
- }
609
- };
610
-
611
- inputContainer.appendChild(input);
612
- inputContainer.appendChild(btn);
613
- div.appendChild(labelEl);
614
- div.appendChild(inputContainer);
615
- return div;
616
- }
617
548
  /* Move to a Dock Spawn Helper */
618
549
  activateDockById(name) {
619
550
  this.activateDock(this._getDomElement(name));