iobroker.mywebui 1.82.0 → 1.83.0

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.
Files changed (245) hide show
  1. package/default-controls/controls/Language/LanguageSelector.control +1 -1
  2. package/default-controls/controls/Language/LanguageSelector2.control +1 -1
  3. package/default-controls/controls/WebuiConnectionMonitor1.control +1 -1
  4. package/default-controls/controls/levelbars/level_bar.control +1 -1
  5. package/default-controls/controls/sidebar/menuitems.control +1 -1
  6. package/default-controls/controls/sidebar/modern-sidebar.control +1 -1
  7. package/default-controls/controls/sidebar/sidebarcontrol.control +1 -1
  8. package/default-controls/controls/svetofor.control +1 -1
  9. package/default-controls/controls/switches/switch1.control +1 -1
  10. package/dist/backend/3d-editor-integration.js +1 -1
  11. package/dist/backend/ImportmapCreator.js +1 -1
  12. package/dist/backend/UploadHelper.js +1 -1
  13. package/dist/backend/main.js +1 -1
  14. package/io-package.json +599 -599
  15. package/package.json +2 -2
  16. package/scripts/_theme_all.py +50 -0
  17. package/www/3d-editor/js/Animation.js +1 -1
  18. package/www/3d-editor/js/AnimationResizer.js +1 -1
  19. package/www/3d-editor/js/Command.js +1 -1
  20. package/www/3d-editor/js/Config.js +1 -1
  21. package/www/3d-editor/js/Editor.js +1 -1
  22. package/www/3d-editor/js/EditorControls.js +1 -1
  23. package/www/3d-editor/js/GLTFImportDialog.js +1 -1
  24. package/www/3d-editor/js/History.js +1 -1
  25. package/www/3d-editor/js/Loader.js +1 -1
  26. package/www/3d-editor/js/LoaderUtils.js +1 -1
  27. package/www/3d-editor/js/Menubar.Add.js +1 -1
  28. package/www/3d-editor/js/Menubar.Edit.js +1 -1
  29. package/www/3d-editor/js/Menubar.File.js +1 -1
  30. package/www/3d-editor/js/Menubar.Help.js +1 -1
  31. package/www/3d-editor/js/Menubar.Render.js +1 -1
  32. package/www/3d-editor/js/Menubar.Status.js +1 -1
  33. package/www/3d-editor/js/Menubar.View.js +1 -1
  34. package/www/3d-editor/js/Menubar.js +1 -1
  35. package/www/3d-editor/js/Player.js +1 -1
  36. package/www/3d-editor/js/Resizer.js +1 -1
  37. package/www/3d-editor/js/Script.js +1 -1
  38. package/www/3d-editor/js/Selector.js +1 -1
  39. package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -1
  40. package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -1
  41. package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -1
  42. package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -1
  43. package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -1
  44. package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -1
  45. package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -1
  46. package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -1
  47. package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -1
  48. package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -1
  49. package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -1
  50. package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -1
  51. package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -1
  52. package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -1
  53. package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -1
  54. package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -1
  55. package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -1
  56. package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -1
  57. package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -1
  58. package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -1
  59. package/www/3d-editor/js/Sidebar.Geometry.js +1 -1
  60. package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -1
  61. package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -1
  62. package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -1
  63. package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -1
  64. package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -1
  65. package/www/3d-editor/js/Sidebar.Material.Program.js +1 -1
  66. package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -1
  67. package/www/3d-editor/js/Sidebar.Material.js +1 -1
  68. package/www/3d-editor/js/Sidebar.Object.js +1 -1
  69. package/www/3d-editor/js/Sidebar.Project.App.js +1 -1
  70. package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -1
  71. package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -1
  72. package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -1
  73. package/www/3d-editor/js/Sidebar.Project.js +1 -1
  74. package/www/3d-editor/js/Sidebar.Properties.js +1 -1
  75. package/www/3d-editor/js/Sidebar.Scene.js +1 -1
  76. package/www/3d-editor/js/Sidebar.Script.js +1 -1
  77. package/www/3d-editor/js/Sidebar.Settings.History.js +1 -1
  78. package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -1
  79. package/www/3d-editor/js/Sidebar.Settings.js +1 -1
  80. package/www/3d-editor/js/Sidebar.js +1 -1
  81. package/www/3d-editor/js/Storage.js +1 -1
  82. package/www/3d-editor/js/Strings.js +1 -1
  83. package/www/3d-editor/js/TextureParametersDialog.js +1 -1
  84. package/www/3d-editor/js/Toolbar.js +1 -1
  85. package/www/3d-editor/js/Viewport.Controls.js +1 -1
  86. package/www/3d-editor/js/Viewport.Info.js +1 -1
  87. package/www/3d-editor/js/Viewport.Pathtracer.js +1 -1
  88. package/www/3d-editor/js/Viewport.ViewHelper.js +1 -1
  89. package/www/3d-editor/js/Viewport.XR.js +1 -1
  90. package/www/3d-editor/js/Viewport.js +1 -1
  91. package/www/3d-editor/js/commands/AddObjectCommand.js +1 -1
  92. package/www/3d-editor/js/commands/AddScriptCommand.js +1 -1
  93. package/www/3d-editor/js/commands/Commands.js +1 -1
  94. package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -1
  95. package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -1
  96. package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -1
  97. package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -1
  98. package/www/3d-editor/js/commands/SetColorCommand.js +1 -1
  99. package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -1
  100. package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -1
  101. package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -1
  102. package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -1
  103. package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -1
  104. package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -1
  105. package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -1
  106. package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -1
  107. package/www/3d-editor/js/commands/SetPositionCommand.js +1 -1
  108. package/www/3d-editor/js/commands/SetRotationCommand.js +1 -1
  109. package/www/3d-editor/js/commands/SetScaleCommand.js +1 -1
  110. package/www/3d-editor/js/commands/SetSceneCommand.js +1 -1
  111. package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -1
  112. package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -1
  113. package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -1
  114. package/www/3d-editor/js/commands/SetUuidCommand.js +1 -1
  115. package/www/3d-editor/js/commands/SetValueCommand.js +1 -1
  116. package/www/dist/frontend/bundle/chunk-3C6XC7RW.js +1 -1
  117. package/www/dist/frontend/bundle/chunk-3WGNLIWJ.js +3 -3
  118. package/www/dist/frontend/bundle/chunk-3YK3Y2SJ.js +1 -1
  119. package/www/dist/frontend/bundle/chunk-43HEBO5G.js +3 -3
  120. package/www/dist/frontend/bundle/chunk-5MUGMRXU.js +1 -1
  121. package/www/dist/frontend/bundle/chunk-5UBRCTRX.js +3 -3
  122. package/www/dist/frontend/bundle/chunk-65A3PNMG.js +1 -1
  123. package/www/dist/frontend/bundle/chunk-7CWDQGAG.js +3 -3
  124. package/www/dist/frontend/bundle/chunk-7PT7IGUJ.js +2 -2
  125. package/www/dist/frontend/bundle/chunk-AI5PDLDE.js +3 -3
  126. package/www/dist/frontend/bundle/chunk-AWKVUUKN.js +3 -3
  127. package/www/dist/frontend/bundle/chunk-B6NXWGSB.js +1 -1
  128. package/www/dist/frontend/bundle/chunk-BHSHNRXW.js +1 -1
  129. package/www/dist/frontend/bundle/chunk-BVONDEOL.js +3 -3
  130. package/www/dist/frontend/bundle/chunk-CHDKROX5.js +2 -2
  131. package/www/dist/frontend/bundle/chunk-DNCD2F6X.js +2 -2
  132. package/www/dist/frontend/bundle/chunk-FHF6DL5U.js +1 -1
  133. package/www/dist/frontend/bundle/chunk-FO56TEZH.js +3 -3
  134. package/www/dist/frontend/bundle/chunk-FSRRPQMX.js +3 -3
  135. package/www/dist/frontend/bundle/chunk-GLQNGCFL.js +1 -1
  136. package/www/dist/frontend/bundle/chunk-JBDSDK2C.js +2 -2
  137. package/www/dist/frontend/bundle/chunk-JSC66ZLQ.js +1 -1
  138. package/www/dist/frontend/bundle/chunk-K6IV7LKW.js +1 -1
  139. package/www/dist/frontend/bundle/chunk-KY73TL6O.js +3 -3
  140. package/www/dist/frontend/bundle/chunk-M6VVTDVX.js +2 -2
  141. package/www/dist/frontend/bundle/chunk-MECWXZRN.js +1 -1
  142. package/www/dist/frontend/bundle/chunk-N2YIZTB2.js +1 -1
  143. package/www/dist/frontend/bundle/chunk-NQ7L7KNT.js +3 -3
  144. package/www/dist/frontend/bundle/chunk-P7EOR36H.js +1 -1
  145. package/www/dist/frontend/bundle/chunk-PJYK24KZ.js +1 -1
  146. package/www/dist/frontend/bundle/chunk-PYLAW6UA.js +3 -3
  147. package/www/dist/frontend/bundle/chunk-QJJGP23I.js +1 -1
  148. package/www/dist/frontend/bundle/chunk-QV4IBWXA.js +1 -1
  149. package/www/dist/frontend/bundle/chunk-R6IQDPAP.js +3 -3
  150. package/www/dist/frontend/bundle/chunk-RTTBZOQU.js +2 -2
  151. package/www/dist/frontend/bundle/chunk-S4TLNW5D.js +1 -1
  152. package/www/dist/frontend/bundle/chunk-SE6N4ZEN.js +1 -1
  153. package/www/dist/frontend/bundle/chunk-SQHXO2IT.js +3 -3
  154. package/www/dist/frontend/bundle/chunk-T4AIQBBH.js +3 -3
  155. package/www/dist/frontend/bundle/chunk-U5Q332OH.js +1 -1
  156. package/www/dist/frontend/bundle/chunk-UIP324PP.js +1 -1
  157. package/www/dist/frontend/bundle/chunk-V27IRYSL.js +1 -1
  158. package/www/dist/frontend/bundle/chunk-V4C5FGJL.js +3 -3
  159. package/www/dist/frontend/bundle/chunk-WED7CGWA.js +2 -2
  160. package/www/dist/frontend/bundle/chunk-WQ5H5LTG.js +3 -3
  161. package/www/dist/frontend/bundle/chunk-WU54NCEU.js +1 -1
  162. package/www/dist/frontend/bundle/chunk-XFO5FUCN.js +1 -1
  163. package/www/dist/frontend/bundle/chunk-XWAQ2BWM.js +3 -3
  164. package/www/dist/frontend/bundle/chunk-YTQCHNRQ.js +3 -3
  165. package/www/dist/frontend/bundle/chunk-YUSK4FN3.js +3 -3
  166. package/www/dist/frontend/bundle/chunk-Z34BSG5O.js +3 -3
  167. package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -1
  168. package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -1
  169. package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -1
  170. package/www/dist/frontend/bundle/runtime/controls.js +3 -3
  171. package/www/dist/frontend/bundle/runtime/init.js +3 -3
  172. package/www/dist/frontend/common/Common.globals.js +1 -1
  173. package/www/dist/frontend/common/IobrokerHandler.js +1 -1
  174. package/www/dist/frontend/common/Runtime.js +1 -1
  175. package/www/dist/frontend/config/CommandHandling.js +1 -1
  176. package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -1
  177. package/www/dist/frontend/config/DockHelper.js +1 -1
  178. package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -1
  179. package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +3 -3
  180. package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +3 -3
  181. package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +3 -3
  182. package/www/dist/frontend/config/IobrokerWebuiAppShell.js +3 -3
  183. package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -1
  184. package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +2 -2
  185. package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +3 -3
  186. package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -1
  187. package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -1
  188. package/www/dist/frontend/config/IobrokerWebuiIconsView.js +3 -3
  189. package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +3 -3
  190. package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -1
  191. package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -1
  192. package/www/dist/frontend/config/IobrokerWebuiScreensView.js +2 -2
  193. package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -1
  194. package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +2 -2
  195. package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -1
  196. package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +3 -3
  197. package/www/dist/frontend/helper/DialogHelper.js +2 -2
  198. package/www/dist/frontend/helper/EsprimaHelper.js +1 -1
  199. package/www/dist/frontend/helper/Helper.js +1 -1
  200. package/www/dist/frontend/helper/LayoutHelper.js +1 -1
  201. package/www/dist/frontend/helper/XmlHelper.js +1 -1
  202. package/www/dist/frontend/helper/ZipHelper.js +1 -1
  203. package/www/dist/frontend/runtime/AnimationService.js +1 -1
  204. package/www/dist/frontend/runtime/CustomControls.js +1 -1
  205. package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -1
  206. package/www/dist/frontend/runtime/HabPanelLikeMenu.js +3 -3
  207. package/www/dist/frontend/runtime/NavigationShell.js +1 -1
  208. package/www/dist/frontend/runtime/RuntimeTheme.js +1 -1
  209. package/www/dist/frontend/runtime/ScreenViewer.js +3 -3
  210. package/www/dist/frontend/runtime/TranslateableText.js +2 -2
  211. package/www/dist/frontend/runtime/VisibilityService.js +1 -1
  212. package/www/dist/frontend/runtime/controls.js +1 -1
  213. package/www/dist/frontend/runtime/init.js +1 -1
  214. package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -1
  215. package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -1
  216. package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -1
  217. package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -1
  218. package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -1
  219. package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -1
  220. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -1
  221. package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -1
  222. package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -1
  223. package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -1
  224. package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -1
  225. package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -1
  226. package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -1
  227. package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -1
  228. package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -1
  229. package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -1
  230. package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -1
  231. package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -1
  232. package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -1
  233. package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -1
  234. package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -1
  235. package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -1
  236. package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -1
  237. package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -1
  238. package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -1
  239. package/www/dist/frontend/widgets/OrthoFlowLineTool.js +1 -1
  240. package/www/dist/frontend/widgets/customElementsObserver.js +1 -1
  241. package/www/dist/frontend/widgets/importWidgetFiles.js +1 -1
  242. package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
  243. package/www/dist/frontend/widgets/layout-components.js +1 -1
  244. package/www/dist/frontend/widgets/layout-placement-services.js +1 -1
  245. package/www/dist/frontend/widgets/testElement.js +1 -1
@@ -1 +1 @@
1
- {"html":"<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:6px;padding:6px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n","style":":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid transparent;\n background: transparent;\n padding: 4px 8px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 2px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.12);\n transform: scale(1.08);\n}\n.lang-btn.active {\n border-color: #2196f3;\n background: rgba(33,150,243,0.18);\n box-shadow: 0 0 8px rgba(33,150,243,0.4);\n}\n.lang-flag { line-height: 1; }\n.lang-code {\n font-size: 10px;\n font-weight: bold;\n color: #ccc;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: #2196f3; }\n","script":"\nlet iobrokerHandler;\nlet _langChangedHandler;\nlet _translChangedHandler;\n\nconst FLAGS = {\n az: '🇦🇿',\n ru: '🇷🇺',\n en: '🇬🇧',\n de: '🇩🇪',\n fr: '🇫🇷',\n tr: '🇹🇷',\n uk: '🇺🇦',\n pl: '🇵🇱',\n es: '🇪🇸',\n it: '🇮🇹',\n zh: '🇨🇳',\n ar: '🇸🇦',\n pt: '🇵🇹',\n nl: '🇳🇱',\n};\n\nexport async function connectedCallback(instance) {\n const p = window.location.protocol;\n const h = window.location.hostname;\n const port = window.location.port;\n try {\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\n iobrokerHandler = mod.iobrokerHandler;\n } catch(e) {\n console.error('LanguageSelector: import failed', e);\n return;\n }\n await iobrokerHandler.waitForReady();\n _render(instance);\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\n}\n\nexport function disconnectedCallback(instance) {\n _langChangedHandler?.dispose();\n _translChangedHandler?.dispose();\n}\n\nfunction _render(instance) {\n const container = instance._getDomElement('lang-selector');\n container.innerHTML = '';\n const langs = Object.keys(iobrokerHandler.translations ?? {});\n if (!langs.length) { container.textContent = 'No translations'; return; }\n const size = parseInt(instance.size ?? 32);\n const dir = instance.direction ?? 'horizontal';\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\n for (const lang of langs) {\n const btn = document.createElement('button');\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\n btn.dataset.lang = lang;\n btn.title = lang.toUpperCase();\n const flag = document.createElement('span');\n flag.className = 'lang-flag';\n flag.style.fontSize = size + 'px';\n flag.textContent = FLAGS[lang] ?? '🌐';\n const code = document.createElement('span');\n code.className = 'lang-code';\n code.textContent = lang;\n btn.appendChild(flag);\n btn.appendChild(code);\n btn.onclick = () => { iobrokerHandler.setLanguage(lang); };\n container.appendChild(btn);\n }\n}\n\nfunction _updateActive(instance) {\n const container = instance._getDomElement('lang-selector');\n container.querySelectorAll('.lang-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\n });\n}\n","properties":{"size":{"type":"number","default":"32"},"direction":{"type":"string","default":"horizontal"}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
1
+ {"html": "<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:6px;padding:6px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n", "style": ":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid transparent;\n background: transparent;\n padding: 4px 8px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 2px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.12);\n transform: scale(1.08);\n}\n.lang-btn.active {\n border-color: var(--accent,#2196f3);\n background: rgba(33,150,243,0.18);\n box-shadow: 0 0 8px rgba(33,150,243,0.4);\n}\n.lang-flag { line-height: 1; }\n.lang-code {\n font-size: 10px;\n font-weight: bold;\n color: var(--ui-border,#ccc);\n letter-spacing: 0.5px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: var(--accent,#2196f3); }\n", "script": "\nlet iobrokerHandler;\nlet _langChangedHandler;\nlet _translChangedHandler;\n\nconst FLAGS = {\n az: '🇦🇿',\n ru: '🇷🇺',\n en: '🇬🇧',\n de: '🇩🇪',\n fr: '🇫🇷',\n tr: '🇹🇷',\n uk: '🇺🇦',\n pl: '🇵🇱',\n es: '🇪🇸',\n it: '🇮🇹',\n zh: '🇨🇳',\n ar: '🇸🇦',\n pt: '🇵🇹',\n nl: '🇳🇱',\n};\n\nexport async function connectedCallback(instance) {\n const p = window.location.protocol;\n const h = window.location.hostname;\n const port = window.location.port;\n try {\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\n iobrokerHandler = mod.iobrokerHandler;\n } catch(e) {\n console.error('LanguageSelector: import failed', e);\n return;\n }\n await iobrokerHandler.waitForReady();\n _render(instance);\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\n}\n\nexport function disconnectedCallback(instance) {\n _langChangedHandler?.dispose();\n _translChangedHandler?.dispose();\n}\n\nfunction _render(instance) {\n const container = instance._getDomElement('lang-selector');\n container.innerHTML = '';\n const langs = Object.keys(iobrokerHandler.translations ?? {});\n if (!langs.length) { container.textContent = 'No translations'; return; }\n const size = parseInt(instance.size ?? 32);\n const dir = instance.direction ?? 'horizontal';\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\n for (const lang of langs) {\n const btn = document.createElement('button');\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\n btn.dataset.lang = lang;\n btn.title = lang.toUpperCase();\n const flag = document.createElement('span');\n flag.className = 'lang-flag';\n flag.style.fontSize = size + 'px';\n flag.textContent = FLAGS[lang] ?? '🌐';\n const code = document.createElement('span');\n code.className = 'lang-code';\n code.textContent = lang;\n btn.appendChild(flag);\n btn.appendChild(code);\n btn.onclick = () => { iobrokerHandler.setLanguage(lang); };\n container.appendChild(btn);\n }\n}\n\nfunction _updateActive(instance) {\n const container = instance._getDomElement('lang-selector');\n container.querySelectorAll('.lang-btn').forEach(btn => {\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\n });\n}\n", "properties": {"size": {"type": "number", "default": "32"}, "direction": {"type": "string", "default": "horizontal"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
@@ -1 +1 @@
1
- {"html": "<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:8px;padding:8px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n", "style": ":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid rgba(255,255,255,0.1);\n background: rgba(255,255,255,0.05);\n padding: 6px 10px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 4px;\n min-width: 56px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.15);\n border-color: rgba(255,255,255,0.3);\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n}\n.lang-btn.active {\n border-color: #2196f3;\n background: rgba(33,150,243,0.2);\n box-shadow: 0 0 12px rgba(33,150,243,0.5);\n}\n.lang-flag {\n border-radius: 3px;\n overflow: hidden;\n display: block;\n box-shadow: 0 1px 4px rgba(0,0,0,0.4);\n}\n.lang-code {\n font-size: 11px;\n font-weight: 700;\n color: #aaa;\n letter-spacing: 1px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: #64b5f6; }\n", "script": "let iobrokerHandler;\r\nlet _langChangedHandler;\r\nlet _translChangedHandler;\r\nconst W = 36, H = 24;\r\n\r\nconst FLAGS = {\r\n az: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#0092BC\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#E8112D\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#509E2F\"/>\r\n <circle cx=\"18\" cy=\"12\" r=\"4.5\" fill=\"white\"/>\r\n <circle cx=\"19.5\" cy=\"12\" r=\"3.5\" fill=\"#E8112D\"/>\r\n <polygon fill=\"white\" points=\"22,10 23.5,12 22,14 24.5,12\"/>\r\n <polygon fill=\"white\" points=\"21.5,10.5 22.2,12 21.5,13.5 23.5,12\"/>\r\n </svg>`,\r\n ru: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#0039A6\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#D52B1E\"/>\r\n </svg>`,\r\n en: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#012169\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <rect x=\"15\" y=\"0\" width=\"6\" height=\"24\" fill=\"white\"/>\r\n <rect y=\"9\" width=\"36\" height=\"6\" fill=\"white\"/>\r\n <rect x=\"16\" y=\"0\" width=\"4\" height=\"24\" fill=\"#C8102E\"/>\r\n <rect y=\"10\" width=\"36\" height=\"4\" fill=\"#C8102E\"/>\r\n </svg>`,\r\n de: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#000000\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#DD0000\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#FFCE00\"/>\r\n </svg>`,\r\n fr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#ED2939\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#002395\"/>\r\n </svg>`,\r\n tr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#E30A17\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"6\" fill=\"white\"/>\r\n <circle cx=\"16\" cy=\"12\" r=\"4.5\" fill=\"#E30A17\"/>\r\n <polygon fill=\"white\" points=\"22,12 19.5,10.5 20.5,13.5 20.5,10.5 19.5,13.5\"/>\r\n <polygon fill=\"white\" transform=\"rotate(-18,20.5,12)\" points=\"20.5,8 21.2,10.2 23.5,10.2 21.7,11.5 22.4,13.7 20.5,12.5 18.6,13.7 19.3,11.5 17.5,10.2 19.8,10.2\"/>\r\n </svg>`,\r\n uk: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#005BBB\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#FFD500\"/>\r\n </svg>`,\r\n pl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#FFFFFF\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#DC143C\"/>\r\n </svg>`,\r\n es: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#AA151B\"/>\r\n <rect y=\"6\" width=\"36\" height=\"12\" fill=\"#F1BF00\"/>\r\n </svg>`,\r\n it: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#CE2B37\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#009246\"/>\r\n </svg>`,\r\n zh: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#DE2910\"/>\r\n <polygon fill=\"#FFDE00\" points=\"4,2 5,5.5 8.5,5.5 5.8,7.5 6.8,11 4,9 1.2,11 2.2,7.5 -0.5,5.5 3,5.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(30,12,3)\" points=\"12,1 12.5,2.5 14,2.5 12.8,3.3 13.3,4.8 12,4 10.7,4.8 11.2,3.3 10,2.5 11.5,2.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(-20,15,6)\" points=\"15,4 15.5,5.5 17,5.5 15.8,6.3 16.3,7.8 15,7 13.7,7.8 14.2,6.3 13,5.5 14.5,5.5\"/>\r\n </svg>`,\r\n ar: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#006C35\"/>\r\n <line x1=\"6\" y1=\"12\" x2=\"30\" y2=\"12\" stroke=\"white\" stroke-width=\"1.5\"/>\r\n <text x=\"18\" y=\"15\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-family=\"serif\">ع</text>\r\n </svg>`,\r\n pt: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#FF0000\"/>\r\n <rect width=\"14\" height=\"24\" fill=\"#006600\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"5\" fill=\"#FFFF00\" stroke=\"#006600\" stroke-width=\"0.8\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"3\" fill=\"#0033AA\"/>\r\n </svg>`,\r\n nl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#AE1C28\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#21468B\"/>\r\n </svg>`,\r\n};\r\n\r\nexport async function connectedCallback(instance) {\r\n const p = window.location.protocol;\r\n const h = window.location.hostname;\r\n const port = window.location.port;\r\n try {\r\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\r\n iobrokerHandler = mod.iobrokerHandler;\r\n } catch(e) {\r\n console.error('LanguageSelector: import failed', e);\r\n return;\r\n }\r\n await iobrokerHandler.waitForReady();\r\n _render(instance);\r\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\r\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: iobrokerHandler.language });\r\n}\r\n\r\nexport function disconnectedCallback(instance) {\r\n _langChangedHandler?.dispose();\r\n _translChangedHandler?.dispose();\r\n}\r\n\r\nfunction _render(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.innerHTML = '';\r\n const langs = Object.keys(iobrokerHandler.translations ?? {});\r\n if (!langs.length) {\r\n container.innerHTML = '<span style=\"color:#888;font-size:12px;\">No translations defined</span>';\r\n return;\r\n }\r\n const size = parseInt(instance.size ?? 36);\r\n const dir = instance.direction ?? 'horizontal';\r\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\r\n for (const lang of langs) {\r\n const btn = document.createElement('button');\r\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\r\n btn.dataset.lang = lang;\r\n btn.title = lang.toUpperCase();\r\n const flagWrap = document.createElement('span');\r\n flagWrap.className = 'lang-flag';\r\n const svgStr = FLAGS[lang];\r\n if (svgStr) {\r\n const scaled = svgStr.replace(/width=\"\\d+\"/, `width=\"${size}\"`).replace(/height=\"\\d+\"/, `height=\"${Math.round(size*2/3)}\"`);\r\n flagWrap.innerHTML = scaled;\r\n } else {\r\n flagWrap.style.cssText = `width:${size}px;height:${Math.round(size*2/3)}px;background:#555;border-radius:3px;display:flex;align-items:center;justify-content:center;`;\r\n flagWrap.textContent = lang.toUpperCase();\r\n }\r\n const code = document.createElement('span');\r\n code.className = 'lang-code';\r\n code.textContent = lang;\r\n btn.appendChild(flagWrap);\r\n btn.appendChild(code);\r\n btn.onclick = () => {\r\n iobrokerHandler.setLanguage(lang);\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: lang });\r\n };\r\n container.appendChild(btn);\r\n }\r\n}\r\n\r\nfunction _updateActive(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.querySelectorAll('.lang-btn').forEach(btn => {\r\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\r\n });\r\n}\r\n", "properties": {"size": {"type": "number", "default": "36"}, "direction": {"type": "string", "default": "horizontal"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "useGlobalStyle": "false"}}
1
+ {"html": "<div id=\"lang-selector\" style=\"display:flex;flex-wrap:wrap;gap:8px;padding:8px;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;\"></div>\n", "style": ":host {\n display: block;\n width: 100%;\n height: 100%;\n background: transparent;\n}\n* { box-sizing: border-box; }\n.lang-btn {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n border-radius: 8px;\n border: 2px solid rgba(255,255,255,0.1);\n background: rgba(255,255,255,0.05);\n padding: 6px 10px;\n transition: all 0.2s ease;\n user-select: none;\n gap: 4px;\n min-width: 56px;\n}\n.lang-btn:hover {\n background: rgba(255,255,255,0.15);\n border-color: rgba(255,255,255,0.3);\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n}\n.lang-btn.active {\n border-color: var(--accent,#2196f3);\n background: rgba(33,150,243,0.2);\n box-shadow: 0 0 12px rgba(33,150,243,0.5);\n}\n.lang-flag {\n border-radius: 3px;\n overflow: hidden;\n display: block;\n box-shadow: 0 1px 4px rgba(0,0,0,0.4);\n}\n.lang-code {\n font-size: 11px;\n font-weight: 700;\n color: #aaa;\n letter-spacing: 1px;\n text-transform: uppercase;\n}\n.lang-btn.active .lang-code { color: #64b5f6; }\n", "script": "let iobrokerHandler;\r\nlet _langChangedHandler;\r\nlet _translChangedHandler;\r\nconst W = 36, H = 24;\r\n\r\nconst FLAGS = {\r\n az: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#0092BC\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#E8112D\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#509E2F\"/>\r\n <circle cx=\"18\" cy=\"12\" r=\"4.5\" fill=\"white\"/>\r\n <circle cx=\"19.5\" cy=\"12\" r=\"3.5\" fill=\"#E8112D\"/>\r\n <polygon fill=\"white\" points=\"22,10 23.5,12 22,14 24.5,12\"/>\r\n <polygon fill=\"white\" points=\"21.5,10.5 22.2,12 21.5,13.5 23.5,12\"/>\r\n </svg>`,\r\n ru: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#0039A6\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#D52B1E\"/>\r\n </svg>`,\r\n en: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#012169\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"white\" stroke-width=\"4.8\"/>\r\n <line x1=\"0\" y1=\"0\" x2=\"36\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <line x1=\"36\" y1=\"0\" x2=\"0\" y2=\"24\" stroke=\"#C8102E\" stroke-width=\"3.2\"/>\r\n <rect x=\"15\" y=\"0\" width=\"6\" height=\"24\" fill=\"white\"/>\r\n <rect y=\"9\" width=\"36\" height=\"6\" fill=\"white\"/>\r\n <rect x=\"16\" y=\"0\" width=\"4\" height=\"24\" fill=\"#C8102E\"/>\r\n <rect y=\"10\" width=\"36\" height=\"4\" fill=\"#C8102E\"/>\r\n </svg>`,\r\n de: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#000000\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#DD0000\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#FFCE00\"/>\r\n </svg>`,\r\n fr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#ED2939\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#002395\"/>\r\n </svg>`,\r\n tr: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#E30A17\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"6\" fill=\"white\"/>\r\n <circle cx=\"16\" cy=\"12\" r=\"4.5\" fill=\"#E30A17\"/>\r\n <polygon fill=\"white\" points=\"22,12 19.5,10.5 20.5,13.5 20.5,10.5 19.5,13.5\"/>\r\n <polygon fill=\"white\" transform=\"rotate(-18,20.5,12)\" points=\"20.5,8 21.2,10.2 23.5,10.2 21.7,11.5 22.4,13.7 20.5,12.5 18.6,13.7 19.3,11.5 17.5,10.2 19.8,10.2\"/>\r\n </svg>`,\r\n uk: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#005BBB\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#FFD500\"/>\r\n </svg>`,\r\n pl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"12\" fill=\"#FFFFFF\"/>\r\n <rect y=\"12\" width=\"36\" height=\"12\" fill=\"#DC143C\"/>\r\n </svg>`,\r\n es: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#AA151B\"/>\r\n <rect y=\"6\" width=\"36\" height=\"12\" fill=\"#F1BF00\"/>\r\n </svg>`,\r\n it: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#CE2B37\"/>\r\n <rect width=\"24\" height=\"24\" fill=\"#FFFFFF\"/>\r\n <rect width=\"12\" height=\"24\" fill=\"#009246\"/>\r\n </svg>`,\r\n zh: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#DE2910\"/>\r\n <polygon fill=\"#FFDE00\" points=\"4,2 5,5.5 8.5,5.5 5.8,7.5 6.8,11 4,9 1.2,11 2.2,7.5 -0.5,5.5 3,5.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(30,12,3)\" points=\"12,1 12.5,2.5 14,2.5 12.8,3.3 13.3,4.8 12,4 10.7,4.8 11.2,3.3 10,2.5 11.5,2.5\"/>\r\n <polygon fill=\"#FFDE00\" transform=\"rotate(-20,15,6)\" points=\"15,4 15.5,5.5 17,5.5 15.8,6.3 16.3,7.8 15,7 13.7,7.8 14.2,6.3 13,5.5 14.5,5.5\"/>\r\n </svg>`,\r\n ar: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#006C35\"/>\r\n <line x1=\"6\" y1=\"12\" x2=\"30\" y2=\"12\" stroke=\"white\" stroke-width=\"1.5\"/>\r\n <text x=\"18\" y=\"15\" text-anchor=\"middle\" fill=\"white\" font-size=\"7\" font-family=\"serif\">ع</text>\r\n </svg>`,\r\n pt: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"24\" fill=\"#FF0000\"/>\r\n <rect width=\"14\" height=\"24\" fill=\"#006600\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"5\" fill=\"#FFFF00\" stroke=\"#006600\" stroke-width=\"0.8\"/>\r\n <circle cx=\"14\" cy=\"12\" r=\"3\" fill=\"#0033AA\"/>\r\n </svg>`,\r\n nl: `<svg viewBox=\"0 0 36 24\" xmlns=\"http://www.w3.org/2000/svg\" width=\"${W}\" height=\"${H}\">\r\n <rect width=\"36\" height=\"8\" fill=\"#AE1C28\"/>\r\n <rect y=\"8\" width=\"36\" height=\"8\" fill=\"#FFFFFF\"/>\r\n <rect y=\"16\" width=\"36\" height=\"8\" fill=\"#21468B\"/>\r\n </svg>`,\r\n};\r\n\r\nexport async function connectedCallback(instance) {\r\n const p = window.location.protocol;\r\n const h = window.location.hostname;\r\n const port = window.location.port;\r\n try {\r\n const mod = await import(`${p}//${h}:${port}/mywebui/dist/frontend/common/IobrokerHandler.js`);\r\n iobrokerHandler = mod.iobrokerHandler;\r\n } catch(e) {\r\n console.error('LanguageSelector: import failed', e);\r\n return;\r\n }\r\n await iobrokerHandler.waitForReady();\r\n _render(instance);\r\n _langChangedHandler = iobrokerHandler.languageChanged.on(() => _updateActive(instance));\r\n _translChangedHandler = iobrokerHandler.translationsChanged.on(() => _render(instance));\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: iobrokerHandler.language });\r\n}\r\n\r\nexport function disconnectedCallback(instance) {\r\n _langChangedHandler?.dispose();\r\n _translChangedHandler?.dispose();\r\n}\r\n\r\nfunction _render(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.innerHTML = '';\r\n const langs = Object.keys(iobrokerHandler.translations ?? {});\r\n if (!langs.length) {\r\n container.innerHTML = '<span style=\"color:#888;font-size:12px;\">No translations defined</span>';\r\n return;\r\n }\r\n const size = parseInt(instance.size ?? 36);\r\n const dir = instance.direction ?? 'horizontal';\r\n container.style.flexDirection = dir === 'vertical' ? 'column' : 'row';\r\n for (const lang of langs) {\r\n const btn = document.createElement('button');\r\n btn.className = 'lang-btn' + (lang === iobrokerHandler.language ? ' active' : '');\r\n btn.dataset.lang = lang;\r\n btn.title = lang.toUpperCase();\r\n const flagWrap = document.createElement('span');\r\n flagWrap.className = 'lang-flag';\r\n const svgStr = FLAGS[lang];\r\n if (svgStr) {\r\n const scaled = svgStr.replace(/width=\"\\d+\"/, `width=\"${size}\"`).replace(/height=\"\\d+\"/, `height=\"${Math.round(size*2/3)}\"`);\r\n flagWrap.innerHTML = scaled;\r\n } else {\r\n flagWrap.style.cssText = `width:${size}px;height:${Math.round(size*2/3)}px;background:#555;border-radius:3px;display:flex;align-items:center;justify-content:center;`;\r\n flagWrap.textContent = lang.toUpperCase();\r\n }\r\n const code = document.createElement('span');\r\n code.className = 'lang-code';\r\n code.textContent = lang;\r\n btn.appendChild(flagWrap);\r\n btn.appendChild(code);\r\n btn.onclick = () => {\r\n iobrokerHandler.setLanguage(lang);\r\n iobrokerHandler.setState('0_userdata.0.language.selectedlanguage', { val: lang });\r\n };\r\n container.appendChild(btn);\r\n }\r\n}\r\n\r\nfunction _updateActive(instance) {\r\n const container = instance._getDomElement('lang-selector');\r\n container.querySelectorAll('.lang-btn').forEach(btn => {\r\n btn.classList.toggle('active', btn.dataset.lang === iobrokerHandler.language);\r\n });\r\n}\r\n", "properties": {"size": {"type": "number", "default": "36"}, "direction": {"type": "string", "default": "horizontal"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "useGlobalStyle": "false"}}
@@ -1 +1 @@
1
- {"html":"<div id=\"monitor\" class=\"connection-monitor\"><div class=\"status-indicator\">\n <svg class=\"status-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle id=\"circle\" cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"check\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"cross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<span id=\"statusText\" class=\"status-text\"></span>\n</div><div class=\"pulse-ring\"></div></div>\n<!-- Popup Modal -->\n<div id=\"connectionPopup\" class=\"connection-popup\" style=\"display:none;\">\n <div class=\"popup-overlay\"></div>\n <div class=\"popup-content\">\n <div class=\"popup-icon-wrapper\">\n <svg id=\"popupIcon\" class=\"popup-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"popupCheck\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"popupCross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<div class=\"popup-pulse-ring\"></div>\n </div>\n <h2 id=\"popupTitle\" class=\"popup-title\"></h2>\n <p id=\"popupMessage\" class=\"popup-message\"></p>\n </div>\n</div>\n","style":":host {\n display: inline-block;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n* {\n box-sizing: border-box;\n}\n\n.connection-monitor {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 10px 16px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);\n backdrop-filter: blur(8px);\n transition: all 0.3s ease;\n border: 2px solid transparent;\n}\n\n.status-indicator {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.status-icon {\n width: 24px;\n height: 24px;\n transition: all 0.3s ease;\n}\n\n.status-text {\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n/* Bağlı vəziyyət (Yaşıl) */\n.connection-monitor.connected {\n background: rgba(16, 185, 129, 0.1);\n border-color: #10b981;\n}\n\n.connection-monitor.connected .status-icon {\n color: #10b981;\n}\n\n.connection-monitor.connected .status-text {\n color: #10b981;\n}\n\n.connection-monitor.connected .pulse-ring {\n animation: pulse-green 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Kəsilmiş vəziyyət (Qırmızı) */\n.connection-monitor.disconnected {\n background: rgba(239, 68, 68, 0.1);\n border-color: #ef4444;\n animation: shake 0.5s ease;\n}\n\n.connection-monitor.disconnected .status-icon {\n color: #ef4444;\n}\n\n.connection-monitor.disconnected .status-text {\n color: #ef4444;\n}\n\n.connection-monitor.disconnected .pulse-ring {\n animation: pulse-red 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Yoxlanma vəziyyəti (Sarı) */\n.connection-monitor.checking {\n background: rgba(251, 191, 36, 0.1);\n border-color: #fbbf24;\n}\n\n.connection-monitor.checking .status-icon {\n color: #fbbf24;\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-monitor.checking .status-text {\n color: #fbbf24;\n}\n\n/* Pulse halqası */\n.pulse-ring {\n position: absolute;\n top: 50%;\n left: 15px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n/* ==================== POPUP STYLES ==================== */\n\n.connection-popup {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: fadeIn 0.3s ease;\n}\n\n.popup-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n}\n\n.popup-content {\n position: relative;\n background: white;\n border-radius: 24px;\n padding: 48px 64px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n text-align: center;\n max-width: 500px;\n width: 90%;\n animation: slideUp 0.4s ease;\n}\n\n.popup-icon-wrapper {\n position: relative;\n width: 120px;\n height: 120px;\n margin: 0 auto 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.popup-icon {\n width: 120px;\n height: 120px;\n transition: all 0.3s ease;\n}\n\n.popup-pulse-ring {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n.popup-title {\n font-size: 32px;\n font-weight: 700;\n margin: 0 0 16px 0;\n color: #1f2937;\n}\n\n.popup-message {\n font-size: 20px;\n font-weight: 500;\n margin: 0;\n color: #6b7280;\n}\n\n/* Popup Connected State */\n.connection-popup.connected .popup-icon {\n color: #10b981;\n}\n\n.connection-popup.connected .popup-pulse-ring {\n animation: pulse-green-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.connected .popup-title {\n color: #10b981;\n}\n\n.connection-popup.connected .popup-message {\n color: #059669;\n}\n\n/* Popup Disconnected State */\n.connection-popup.disconnected .popup-icon {\n color: #ef4444;\n animation: shake-big 0.5s ease;\n}\n\n.connection-popup.disconnected .popup-pulse-ring {\n animation: pulse-red-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.disconnected .popup-title {\n color: #ef4444;\n}\n\n.connection-popup.disconnected .popup-message {\n color: #dc2626;\n}\n\n/* Popup Checking State */\n.connection-popup.checking .popup-icon {\n color: #fbbf24;\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-popup.checking .popup-title {\n color: #fbbf24;\n}\n\n.connection-popup.checking .popup-message {\n color: #f59e0b;\n}\n\n/* Animasiyalar */\n@keyframes pulse-green {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes pulse-green-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 25% { transform: translateX(-4px); }\n 75% { transform: translateX(4px); }\n}\n\n@keyframes shake-big {\n 0%, 100% { transform: translateX(0) rotate(0deg); }\n 25% { transform: translateX(-10px) rotate(-5deg); }\n 75% { transform: translateX(10px) rotate(5deg); }\n}\n\n@keyframes rotate {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(50px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Qaranlıq tema */\n@media (prefers-color-scheme: dark) {\n .connection-monitor {\n background: rgba(31, 41, 55, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);\n }\n \n .popup-content {\n background: #1f2937;\n }\n \n .popup-title {\n color: #f3f4f6;\n }\n \n .popup-message {\n color: #d1d5db;\n }\n}\n\n/* Kiçik ölçü */\n.connection-monitor.size-small {\n padding: 6px 12px;\n font-size: 12px;\n}\n\n.connection-monitor.size-small .status-icon {\n width: 18px;\n height: 18px;\n}\n\n/* Böyük ölçü */\n.connection-monitor.size-large {\n padding: 14px 20px;\n font-size: 16px;\n}\n\n.connection-monitor.size-large .status-icon {\n width: 32px;\n height: 32px;\n}","script":"let checkInterval = null;\nlet isConnected = false;\nlet onConnectHandler = null;\nlet onDisconnectHandler = null;\nlet onReconnectHandler = null;\nlet onErrorHandler = null;\nlet popupTimeout = null;\nlet currentStatus = 'checking';\nlet _langChangedHandler = null;\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n const monitorEl = instance._getDomElement('monitor');\n const statusTextEl = instance._getDomElement('statusText');\n const checkIcon = instance._getDomElement('check');\n const crossIcon = instance._getDomElement('cross');\n const circleIcon = instance._getDomElement('circle');\n \n // Popup elementləri\n const popupEl = instance._getDomElement('connectionPopup');\n const popupTitleEl = instance._getDomElement('popupTitle');\n const popupMessageEl = instance._getDomElement('popupMessage');\n const popupCheckIcon = instance._getDomElement('popupCheck');\n const popupCrossIcon = instance._getDomElement('popupCross');\n \n // Ölçü classı əlavə et\n if (instance.size) {\n monitorEl.classList.add('size-' + instance.size);\n }\n \n // IOB connection hazır olana qədər gözlə\n const waitForConnection = setInterval(() => {\n if (window.IOB && window.IOB.connection) {\n clearInterval(waitForConnection);\n setupMonitoring();\n }\n }, 100);\n \n function setupMonitoring() {\n const connection = window.IOB.connection;\n \n console.log('[ConnectionMonitor] 🚀 Monitoring başladı');\n console.log('[ConnectionMonitor] Connection obyekti:', connection);\n console.log('[ConnectionMonitor] İlkin status:', connection.connected);\n \n // İlk status yoxlaması\n checkConnection();\n \n // Dövri yoxlama\n const interval = instance.checkInterval || 5000;\n checkInterval = setInterval(() => {\n checkConnection();\n }, interval);\n \n // Socket event-lərinə abunə ol\n if (connection._socket) {\n const socket = connection._socket;\n \n onConnectHandler = function() {\n console.log('[ConnectionMonitor] ✅ Socket CONNECT event');\n handleConnectionSuccess();\n };\n \n onReconnectHandler = function() {\n console.log('[ConnectionMonitor] 🔄 Socket RECONNECT event');\n handleConnectionSuccess();\n };\n \n onDisconnectHandler = function(reason) {\n console.log('[ConnectionMonitor] ❌ Socket DISCONNECT event:', reason);\n handleConnectionFailure();\n };\n \n onErrorHandler = function(error) {\n console.error('[ConnectionMonitor] ⚠️ Socket ERROR event:', error);\n handleConnectionFailure();\n };\n \n socket.on('connect', onConnectHandler);\n socket.on('reconnect', onReconnectHandler);\n socket.on('disconnect', onDisconnectHandler);\n socket.on('connect_error', onErrorHandler);\n socket.on('error', onErrorHandler);\n }\n }\n \n function checkConnection() {\n if (!window.IOB || !window.IOB.connection) {\n handleConnectionFailure();\n return;\n }\n \n const connection = window.IOB.connection;\n \n // Connection obyektinin connected property-sini yoxla\n if (connection.connected === true) {\n handleConnectionSuccess();\n } \n // Socket-in connected statusunu yoxla\n else if (connection._socket && connection._socket.connected === true) {\n handleConnectionSuccess();\n }\n // Navigator.onLine yoxla (fallback)\n else if (navigator.onLine === false) {\n handleConnectionFailure();\n }\n else {\n // Əlaqə statusu qeyri-müəyyəndirsə, yoxlanılır statusu göstər\n updateStatus('checking');\n }\n }\n \n function handleConnectionSuccess() {\n const wasDisconnected = !isConnected;\n \n if (wasDisconnected) {\n console.log('[ConnectionMonitor] 💚 Server əlaqəsi BƏRPA OLUNDU');\n isConnected = true;\n \n // Əlaqə bərpa olundu popup-u göstər (3 saniyə)\n showPopup('connected');\n if (popupTimeout) clearTimeout(popupTimeout);\n popupTimeout = setTimeout(() => {\n hidePopup();\n }, 3000);\n }\n \n updateStatus('connected');\n }\n \n function handleConnectionFailure() {\n if (isConnected) {\n console.error('[ConnectionMonitor] 💔 Server əlaqəsi KƏSİLDİ');\n isConnected = false;\n \n // Əlaqə kəsildi popup-u göstər (daimi)\n showPopup('disconnected');\n }\n \n updateStatus('disconnected');\n }\n \n function updateStatus(status) {\n currentStatus = status;\n monitorEl.classList.remove('connected', 'disconnected', 'checking');\n monitorEl.classList.add(status);\n \n checkIcon.style.display = 'none';\n crossIcon.style.display = 'none';\n circleIcon.style.display = 'block';\n \n if (status === 'connected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n checkIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'disconnected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n crossIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'checking') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n }\n \n if (instance.showText === false) {\n statusTextEl.style.display = 'none';\n } else {\n statusTextEl.style.display = 'inline';\n }\n }\n \n function showPopup(status) {\n popupEl.style.display = 'flex';\n popupEl.classList.remove('connected', 'disconnected', 'checking');\n popupEl.classList.add(status);\n \n // İkonları yenilə\n popupCheckIcon.style.display = 'none';\n popupCrossIcon.style.display = 'none';\n \n if (status === 'connected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessageconnected');\n popupCheckIcon.style.display = 'block';\n } else if (status === 'disconnected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagedisconnected');\n popupCrossIcon.style.display = 'block';\n } else if (status === 'checking') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagechecking');\n }\n }\n \n function hidePopup() {\n popupEl.style.display = 'none';\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n }\n\n _langChangedHandler = () => updateStatus(currentStatus);\n window.addEventListener('languageChanged', _langChangedHandler);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n if (checkInterval) {\n clearInterval(checkInterval);\n checkInterval = null;\n }\n \n if (_langChangedHandler) {\n window.removeEventListener('languageChanged', _langChangedHandler);\n _langChangedHandler = null;\n }\n\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n \n // Event listener-ləri təmizlə\n if (window.IOB && window.IOB.connection && window.IOB.connection._socket) {\n const socket = window.IOB.connection._socket;\n \n if (onConnectHandler) socket.off('connect', onConnectHandler);\n if (onReconnectHandler) socket.off('reconnect', onReconnectHandler);\n if (onDisconnectHandler) socket.off('disconnect', onDisconnectHandler);\n if (onErrorHandler) {\n socket.off('connect_error', onErrorHandler);\n socket.off('error', onErrorHandler);\n }\n }\n}","properties":{"checkInterval":{"type":"number","default":"5000"},"showText":{"type":"boolean","default":"false"},"size":{"type":"enum","default":"medium","values":["small","medium","large"]}},"settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
1
+ {"html": "<div id=\"monitor\" class=\"connection-monitor\"><div class=\"status-indicator\">\n <svg class=\"status-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle id=\"circle\" cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"check\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"cross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<span id=\"statusText\" class=\"status-text\"></span>\n</div><div class=\"pulse-ring\"></div></div>\n<!-- Popup Modal -->\n<div id=\"connectionPopup\" class=\"connection-popup\" style=\"display:none;\">\n <div class=\"popup-overlay\"></div>\n <div class=\"popup-content\">\n <div class=\"popup-icon-wrapper\">\n <svg id=\"popupIcon\" class=\"popup-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"9\"></circle>\n <path id=\"popupCheck\" d=\"M9 12l2 2 4-4\" display=\"none\"></path>\n <path id=\"popupCross\" d=\"M9 9l6 6 M15 9l-6 6\" display=\"none\"></path>\n </svg>\n<div class=\"popup-pulse-ring\"></div>\n </div>\n <h2 id=\"popupTitle\" class=\"popup-title\"></h2>\n <p id=\"popupMessage\" class=\"popup-message\"></p>\n </div>\n</div>\n", "style": ":host {\n display: inline-block;\n font-family: system-ui, -apple-system, sans-serif;\n}\n\n* {\n box-sizing: border-box;\n}\n\n.connection-monitor {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 10px 16px;\n border-radius: 10px;\n background: rgba(255, 255, 255, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);\n backdrop-filter: blur(8px);\n transition: all 0.3s ease;\n border: 2px solid transparent;\n}\n\n.status-indicator {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.status-icon {\n width: 24px;\n height: 24px;\n transition: all 0.3s ease;\n}\n\n.status-text {\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\n}\n\n/* Bağlı vəziyyət (Yaşıl) */\n.connection-monitor.connected {\n background: rgba(16, 185, 129, 0.1);\n border-color: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-icon {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .status-text {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-monitor.connected .pulse-ring {\n animation: pulse-green 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Kəsilmiş vəziyyət (Qırmızı) */\n.connection-monitor.disconnected {\n background: rgba(239, 68, 68, 0.1);\n border-color: var(--ui-error,#ef4444);\n animation: shake 0.5s ease;\n}\n\n.connection-monitor.disconnected .status-icon {\n color: var(--ui-error,#ef4444);\n}\n\n.connection-monitor.disconnected .status-text {\n color: var(--ui-error,#ef4444);\n}\n\n.connection-monitor.disconnected .pulse-ring {\n animation: pulse-red 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Yoxlanma vəziyyəti (Sarı) */\n.connection-monitor.checking {\n background: rgba(251, 191, 36, 0.1);\n border-color: var(--ui-warn,#fbbf24);\n}\n\n.connection-monitor.checking .status-icon {\n color: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-monitor.checking .status-text {\n color: var(--ui-warn,#fbbf24);\n}\n\n/* Pulse halqası */\n.pulse-ring {\n position: absolute;\n top: 50%;\n left: 15px;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n/* ==================== POPUP STYLES ==================== */\n\n.connection-popup {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: fadeIn 0.3s ease;\n}\n\n.popup-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n backdrop-filter: blur(8px);\n}\n\n.popup-content {\n position: relative;\n background: white;\n border-radius: 24px;\n padding: 48px 64px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n text-align: center;\n max-width: 500px;\n width: 90%;\n animation: slideUp 0.4s ease;\n}\n\n.popup-icon-wrapper {\n position: relative;\n width: 120px;\n height: 120px;\n margin: 0 auto 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.popup-icon {\n width: 120px;\n height: 120px;\n transition: all 0.3s ease;\n}\n\n.popup-pulse-ring {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 120px;\n height: 120px;\n border-radius: 50%;\n pointer-events: none;\n transform: translate(-50%, -50%);\n}\n\n.popup-title {\n font-size: 32px;\n font-weight: 700;\n margin: 0 0 16px 0;\n color: var(--ui-surface,#1f2937);\n}\n\n.popup-message {\n font-size: 20px;\n font-weight: 500;\n margin: 0;\n color: #6b7280;\n}\n\n/* Popup Connected State */\n.connection-popup.connected .popup-icon {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-popup.connected .popup-pulse-ring {\n animation: pulse-green-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.connected .popup-title {\n color: var(--ui-ok,#10b981);\n}\n\n.connection-popup.connected .popup-message {\n color: #059669;\n}\n\n/* Popup Disconnected State */\n.connection-popup.disconnected .popup-icon {\n color: var(--ui-error,#ef4444);\n animation: shake-big 0.5s ease;\n}\n\n.connection-popup.disconnected .popup-pulse-ring {\n animation: pulse-red-big 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n.connection-popup.disconnected .popup-title {\n color: var(--ui-error,#ef4444);\n}\n\n.connection-popup.disconnected .popup-message {\n color: #dc2626;\n}\n\n/* Popup Checking State */\n.connection-popup.checking .popup-icon {\n color: var(--ui-warn,#fbbf24);\n animation: rotate 1.5s linear infinite;\n}\n\n.connection-popup.checking .popup-title {\n color: var(--ui-warn,#fbbf24);\n}\n\n.connection-popup.checking .popup-message {\n color: #f59e0b;\n}\n\n/* Animasiyalar */\n@keyframes pulse-green {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 12px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes pulse-green-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(16, 185, 129, 0);\n }\n}\n\n@keyframes pulse-red-big {\n 0%, 100% {\n box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);\n }\n 50% {\n box-shadow: 0 0 0 30px rgba(239, 68, 68, 0);\n }\n}\n\n@keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 25% { transform: translateX(-4px); }\n 75% { transform: translateX(4px); }\n}\n\n@keyframes shake-big {\n 0%, 100% { transform: translateX(0) rotate(0deg); }\n 25% { transform: translateX(-10px) rotate(-5deg); }\n 75% { transform: translateX(10px) rotate(5deg); }\n}\n\n@keyframes rotate {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(50px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Qaranlıq tema */\n@media (prefers-color-scheme: dark) {\n .connection-monitor {\n background: rgba(31, 41, 55, 0.95);\n box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);\n }\n \n .popup-content {\n background: var(--ui-surface,#1f2937);\n }\n \n .popup-title {\n color: #f3f4f6;\n }\n \n .popup-message {\n color: #d1d5db;\n }\n}\n\n/* Kiçik ölçü */\n.connection-monitor.size-small {\n padding: 6px 12px;\n font-size: 12px;\n}\n\n.connection-monitor.size-small .status-icon {\n width: 18px;\n height: 18px;\n}\n\n/* Böyük ölçü */\n.connection-monitor.size-large {\n padding: 14px 20px;\n font-size: 16px;\n}\n\n.connection-monitor.size-large .status-icon {\n width: 32px;\n height: 32px;\n}", "script": "let checkInterval = null;\nlet isConnected = false;\nlet onConnectHandler = null;\nlet onDisconnectHandler = null;\nlet onReconnectHandler = null;\nlet onErrorHandler = null;\nlet popupTimeout = null;\nlet currentStatus = 'checking';\nlet _langChangedHandler = null;\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n const monitorEl = instance._getDomElement('monitor');\n const statusTextEl = instance._getDomElement('statusText');\n const checkIcon = instance._getDomElement('check');\n const crossIcon = instance._getDomElement('cross');\n const circleIcon = instance._getDomElement('circle');\n \n // Popup elementləri\n const popupEl = instance._getDomElement('connectionPopup');\n const popupTitleEl = instance._getDomElement('popupTitle');\n const popupMessageEl = instance._getDomElement('popupMessage');\n const popupCheckIcon = instance._getDomElement('popupCheck');\n const popupCrossIcon = instance._getDomElement('popupCross');\n \n // Ölçü classı əlavə et\n if (instance.size) {\n monitorEl.classList.add('size-' + instance.size);\n }\n \n // IOB connection hazır olana qədər gözlə\n const waitForConnection = setInterval(() => {\n if (window.IOB && window.IOB.connection) {\n clearInterval(waitForConnection);\n setupMonitoring();\n }\n }, 100);\n \n function setupMonitoring() {\n const connection = window.IOB.connection;\n \n console.log('[ConnectionMonitor] 🚀 Monitoring başladı');\n console.log('[ConnectionMonitor] Connection obyekti:', connection);\n console.log('[ConnectionMonitor] İlkin status:', connection.connected);\n \n // İlk status yoxlaması\n checkConnection();\n \n // Dövri yoxlama\n const interval = instance.checkInterval || 5000;\n checkInterval = setInterval(() => {\n checkConnection();\n }, interval);\n \n // Socket event-lərinə abunə ol\n if (connection._socket) {\n const socket = connection._socket;\n \n onConnectHandler = function() {\n console.log('[ConnectionMonitor] ✅ Socket CONNECT event');\n handleConnectionSuccess();\n };\n \n onReconnectHandler = function() {\n console.log('[ConnectionMonitor] 🔄 Socket RECONNECT event');\n handleConnectionSuccess();\n };\n \n onDisconnectHandler = function(reason) {\n console.log('[ConnectionMonitor] ❌ Socket DISCONNECT event:', reason);\n handleConnectionFailure();\n };\n \n onErrorHandler = function(error) {\n console.error('[ConnectionMonitor] ⚠️ Socket ERROR event:', error);\n handleConnectionFailure();\n };\n \n socket.on('connect', onConnectHandler);\n socket.on('reconnect', onReconnectHandler);\n socket.on('disconnect', onDisconnectHandler);\n socket.on('connect_error', onErrorHandler);\n socket.on('error', onErrorHandler);\n }\n }\n \n function checkConnection() {\n if (!window.IOB || !window.IOB.connection) {\n handleConnectionFailure();\n return;\n }\n \n const connection = window.IOB.connection;\n \n // Connection obyektinin connected property-sini yoxla\n if (connection.connected === true) {\n handleConnectionSuccess();\n } \n // Socket-in connected statusunu yoxla\n else if (connection._socket && connection._socket.connected === true) {\n handleConnectionSuccess();\n }\n // Navigator.onLine yoxla (fallback)\n else if (navigator.onLine === false) {\n handleConnectionFailure();\n }\n else {\n // Əlaqə statusu qeyri-müəyyəndirsə, yoxlanılır statusu göstər\n updateStatus('checking');\n }\n }\n \n function handleConnectionSuccess() {\n const wasDisconnected = !isConnected;\n \n if (wasDisconnected) {\n console.log('[ConnectionMonitor] 💚 Server əlaqəsi BƏRPA OLUNDU');\n isConnected = true;\n \n // Əlaqə bərpa olundu popup-u göstər (3 saniyə)\n showPopup('connected');\n if (popupTimeout) clearTimeout(popupTimeout);\n popupTimeout = setTimeout(() => {\n hidePopup();\n }, 3000);\n }\n \n updateStatus('connected');\n }\n \n function handleConnectionFailure() {\n if (isConnected) {\n console.error('[ConnectionMonitor] 💔 Server əlaqəsi KƏSİLDİ');\n isConnected = false;\n \n // Əlaqə kəsildi popup-u göstər (daimi)\n showPopup('disconnected');\n }\n \n updateStatus('disconnected');\n }\n \n function updateStatus(status) {\n currentStatus = status;\n monitorEl.classList.remove('connected', 'disconnected', 'checking');\n monitorEl.classList.add(status);\n \n checkIcon.style.display = 'none';\n crossIcon.style.display = 'none';\n circleIcon.style.display = 'block';\n \n if (status === 'connected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n checkIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'disconnected') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n crossIcon.style.display = 'block';\n circleIcon.style.display = 'none';\n } else if (status === 'checking') {\n statusTextEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n }\n \n if (instance.showText === false) {\n statusTextEl.style.display = 'none';\n } else {\n statusTextEl.style.display = 'inline';\n }\n }\n \n function showPopup(status) {\n popupEl.style.display = 'flex';\n popupEl.classList.remove('connected', 'disconnected', 'checking');\n popupEl.classList.add(status);\n \n // İkonları yenilə\n popupCheckIcon.style.display = 'none';\n popupCrossIcon.style.display = 'none';\n \n if (status === 'connected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.connectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessageconnected');\n popupCheckIcon.style.display = 'block';\n } else if (status === 'disconnected') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.disconnectedtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagedisconnected');\n popupCrossIcon.style.display = 'block';\n } else if (status === 'checking') {\n popupTitleEl.textContent = t('WebuiConnectionMonitor.checkingtext');\n popupMessageEl.textContent = t('WebuiConnectionMonitor.popupmessagechecking');\n }\n }\n \n function hidePopup() {\n popupEl.style.display = 'none';\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n }\n\n _langChangedHandler = () => updateStatus(currentStatus);\n window.addEventListener('languageChanged', _langChangedHandler);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n if (checkInterval) {\n clearInterval(checkInterval);\n checkInterval = null;\n }\n \n if (_langChangedHandler) {\n window.removeEventListener('languageChanged', _langChangedHandler);\n _langChangedHandler = null;\n }\n\n if (popupTimeout) {\n clearTimeout(popupTimeout);\n popupTimeout = null;\n }\n \n // Event listener-ləri təmizlə\n if (window.IOB && window.IOB.connection && window.IOB.connection._socket) {\n const socket = window.IOB.connection._socket;\n \n if (onConnectHandler) socket.off('connect', onConnectHandler);\n if (onReconnectHandler) socket.off('reconnect', onReconnectHandler);\n if (onDisconnectHandler) socket.off('disconnect', onDisconnectHandler);\n if (onErrorHandler) {\n socket.off('connect_error', onErrorHandler);\n socket.off('error', onErrorHandler);\n }\n }\n}", "properties": {"checkInterval": {"type": "number", "default": "5000"}, "showText": {"type": "boolean", "default": "false"}, "size": {"type": "enum", "default": "medium", "values": ["small", "medium", "large"]}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
@@ -1 +1 @@
1
- {"html": "<style></style>\n<div class=\"level-bar\" bind-css:width=\"??control_width\" bind-css:height=\"??control_height\" style=\"left:47px;top:10.9844px;width:348px;height:21px;\">\n <div class=\"level-fill\" id=\"levelFill\" draggable=\"false\" @change=\"dfd\" bind-css:background-color=\"??color\" style=\"width:0px;pointer-events:auto;\"></div>\n <div class=\"level-pointer\" id=\"levelPointer\" draggable=\"false\" style=\"pointer-events:auto;\"></div>\n <div class=\"level-needle\" id=\"levelNeedle\" draggable=\"false\" style=\"pointer-events:auto;\"></div>\n <div class=\"level-text\" id=\"levelText\" draggable=\"false\" style=\"pointer-events:auto;\">0%</div>\n <div class=\"level-value\" id=\"minValue\" draggable=\"false\" style=\"pointer-events:auto;\">Min: [[this.min]]</div>\n <div class=\"level-value\" id=\"maxValue\" draggable=\"false\" style=\"pointer-events:auto;right:-45px;\">Max: [[this.max]]</div>\n</div>\n", "style": ":host {\n background-color: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\n\n/* Basic styling for the level bar */\n\n.level-bar {\n width: 300px;\n height: 20px;\n border: 1px solid #000;\n position: relative;\n margin-bottom: 20px;\n}\n\n.level-fill {\n background-color: green;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n.level-pointer {\n width: 2px;\n height: 20px;\n background-color: #FF0000;\n position: absolute;\n top: 0;\n z-index: 1;\n}\n\n.level-needle {\n position: absolute;\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-bottom: 10px solid #FF0000;\n top: -10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2;\n}\n\n.level-text {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: #000;\n}\n\n.level-value {\n position: absolute;\n color: #000;\n font-size: 12px;\n top: 100%;\n text-align: center;\n width: 50px;\n transform: translateX(-50%);\n}\n\n.mm-marking {\n position: absolute;\n background-color: #000;\n width: 1px;\n height: 5px;\n top: 0;\n}\n\n.slider-container {\n margin-top: 20px;\n}", "script": "/**\n* @param {BaseScreenViewerAndControl} instance\n*/\n\nexport function init(instance) {\n //instance._bindingsRefresh();\n let lvvalue = instance.level;\n updateLevel(lvvalue, instance)\n instance._assignEvent('level-changed', (ev) => updateLevel(ev.detail.newValue, instance));\n instance._assignEvent('marking_min-changed', (ev) => drawMMMarkings(ev.detail.newValue, instance.marking_max, instance.marking_step,instance));\n instance._assignEvent('marking_max-changed', (ev) => drawMMMarkings(instance.marking_min, ev.detail.newValue, instance.marking_step,instance));\n instance._assignEvent('marking_step-changed', (ev) => drawMMMarkings(instance.marking_min, instance.marking_max, ev.detail.newValue,instance));\n drawMMMarkings(0, 100, 5,instance);\n /*setInterval(()=>{\n let vcv = instance._bindings;\n \n },2000);*/\n}\n\n\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n \n //instance._bindingsRefresh();\n //updateLevel(instance.level, instance);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n}\n\n// Function to update the level bar based on slider value\n function updateLevel(value, instance) {\n var levelFill = instance._getDomElement('levelFill');\n var levelText = instance._getDomElement('levelText');\n var levelPointer = instance._getDomElement('levelPointer');\n var levelNeedle = instance._getDomElement('levelNeedle');\n var minText = instance._getDomElement('minValue');\n var maxText = instance._getDomElement('maxValue');\n var minimum = instance.min;\n var maximum = instance.max;\n \n \n levelText.innerText = value + '%';\n\n // Update position of pointer and needle\n var levelBarWidth = instance.shadowRoot.querySelector('.level-bar').offsetWidth;\n //var pointerPosition = (value / 100) * levelBarWidth;\n var pointerPosition=((value-minimum)/(maximum-minimum))*levelBarWidth;\n if(instance.level >= maximum)\n {\n levelFill.style.width = levelBarWidth + 'px';\n levelPointer.style.left = levelBarWidth + 'px';\n levelNeedle.style.left = levelBarWidth + 'px';\n }\n if(instance.level <= minimum)\n {\n levelFill.style.width = 0 + 'px';\n levelPointer.style.left = 0 + 'px';\n levelNeedle.style.left = 0 + 'px';\n }\n else{\n levelFill.style.width = pointerPosition + 'px';\n levelPointer.style.left = pointerPosition + 'px';\n levelNeedle.style.left = pointerPosition + 'px';\n }\n \n\n \n\n // Update position of min and max values\n minText.style.left = '0';\n maxText.style.left = '100%';\n }\n\n // Function to draw millimeter markings on the level bar\n function drawMMMarkings(min, max, step,instance) {\n // Get the level bar container\n var levelBar = instance.shadowRoot.querySelector('.level-bar');\n\n // Clear previous markings\n levelBar.querySelectorAll('.mm-marking').forEach(mark => mark.remove());\n\n // Calculate total width of the level bar\n var levelBarWidth = levelBar.offsetWidth;\n\n // Calculate the number of markings\n var numberOfMarks = (max - min) / step;\n\n // Calculate the distance between each marking\n var spacing = levelBarWidth / numberOfMarks;\n\n // Create and position the markings\n for (var i = 0; i <= numberOfMarks; i++) {\n var mark = document.createElement('div');\n mark.classList.add('mm-marking');\n mark.style.left = i * spacing + 'px';\n levelBar.appendChild(mark);\n }\n }\n\n \n\n/**\n* dfd - 'change' event of #slider (<input>)\n* @param {Event} event\n* @param {Element} eventRaisingElement\n* @param {ShadowRoot} shadowRoot\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function dfd(event, eventRaisingElement, shadowRoot, instance) {\n //console.log(event);\n}\n", "properties": {"max": {"type": "number", "default": "20"}, "min": {"type": "number", "default": "4"}, "level": {"type": "number"}, "control_width": {"type": "string", "default": "255px"}, "control_height": {"type": "string", "default": "26px"}, "marking_min": {"type": "number"}, "marking_max": {"type": "number", "default": "100"}, "marking_step": {"type": "number", "default": "5"}, "color": {"type": "color", "default": "red"}}, "settings": {}}
1
+ {"html": "<style></style>\n<div class=\"level-bar\" bind-css:width=\"??control_width\" bind-css:height=\"??control_height\" style=\"left:47px;top:10.9844px;width:348px;height:21px;\">\n <div class=\"level-fill\" id=\"levelFill\" draggable=\"false\" @change=\"dfd\" bind-css:background-color=\"??color\" style=\"width:0px;pointer-events:auto;\"></div>\n <div class=\"level-pointer\" id=\"levelPointer\" draggable=\"false\" style=\"pointer-events:auto;\"></div>\n <div class=\"level-needle\" id=\"levelNeedle\" draggable=\"false\" style=\"pointer-events:auto;\"></div>\n <div class=\"level-text\" id=\"levelText\" draggable=\"false\" style=\"pointer-events:auto;\">0%</div>\n <div class=\"level-value\" id=\"minValue\" draggable=\"false\" style=\"pointer-events:auto;\">Min: [[this.min]]</div>\n <div class=\"level-value\" id=\"maxValue\" draggable=\"false\" style=\"pointer-events:auto;right:-45px;\">Max: [[this.max]]</div>\n</div>\n", "style": ":host {\n background-color: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\n\n/* Basic styling for the level bar */\n\n.level-bar {\n width: 300px;\n height: 20px;\n border: 1px solid #000;\n position: relative;\n margin-bottom: 20px;\n}\n\n.level-fill {\n background-color: green;\n height: 100%;\n position: absolute;\n top: 0;\n left: 0;\n}\n\n.level-pointer {\n width: 2px;\n height: 20px;\n background-color: var(--ui-error,#FF0000);\n position: absolute;\n top: 0;\n z-index: 1;\n}\n\n.level-needle {\n position: absolute;\n width: 0;\n height: 0;\n border-left: 5px solid transparent;\n border-right: 5px solid transparent;\n border-bottom: 10px solid var(--ui-error,#FF0000);\n top: -10px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 2;\n}\n\n.level-text {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: #000;\n}\n\n.level-value {\n position: absolute;\n color: #000;\n font-size: 12px;\n top: 100%;\n text-align: center;\n width: 50px;\n transform: translateX(-50%);\n}\n\n.mm-marking {\n position: absolute;\n background-color: #000;\n width: 1px;\n height: 5px;\n top: 0;\n}\n\n.slider-container {\n margin-top: 20px;\n}", "script": "/**\n* @param {BaseScreenViewerAndControl} instance\n*/\n\nexport function init(instance) {\n //instance._bindingsRefresh();\n let lvvalue = instance.level;\n updateLevel(lvvalue, instance)\n instance._assignEvent('level-changed', (ev) => updateLevel(ev.detail.newValue, instance));\n instance._assignEvent('marking_min-changed', (ev) => drawMMMarkings(ev.detail.newValue, instance.marking_max, instance.marking_step,instance));\n instance._assignEvent('marking_max-changed', (ev) => drawMMMarkings(instance.marking_min, ev.detail.newValue, instance.marking_step,instance));\n instance._assignEvent('marking_step-changed', (ev) => drawMMMarkings(instance.marking_min, instance.marking_max, ev.detail.newValue,instance));\n drawMMMarkings(0, 100, 5,instance);\n /*setInterval(()=>{\n let vcv = instance._bindings;\n \n },2000);*/\n}\n\n\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function connectedCallback(instance) {\n \n //instance._bindingsRefresh();\n //updateLevel(instance.level, instance);\n}\n\n/**\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function disconnectedCallback(instance) {\n}\n\n// Function to update the level bar based on slider value\n function updateLevel(value, instance) {\n var levelFill = instance._getDomElement('levelFill');\n var levelText = instance._getDomElement('levelText');\n var levelPointer = instance._getDomElement('levelPointer');\n var levelNeedle = instance._getDomElement('levelNeedle');\n var minText = instance._getDomElement('minValue');\n var maxText = instance._getDomElement('maxValue');\n var minimum = instance.min;\n var maximum = instance.max;\n \n \n levelText.innerText = value + '%';\n\n // Update position of pointer and needle\n var levelBarWidth = instance.shadowRoot.querySelector('.level-bar').offsetWidth;\n //var pointerPosition = (value / 100) * levelBarWidth;\n var pointerPosition=((value-minimum)/(maximum-minimum))*levelBarWidth;\n if(instance.level >= maximum)\n {\n levelFill.style.width = levelBarWidth + 'px';\n levelPointer.style.left = levelBarWidth + 'px';\n levelNeedle.style.left = levelBarWidth + 'px';\n }\n if(instance.level <= minimum)\n {\n levelFill.style.width = 0 + 'px';\n levelPointer.style.left = 0 + 'px';\n levelNeedle.style.left = 0 + 'px';\n }\n else{\n levelFill.style.width = pointerPosition + 'px';\n levelPointer.style.left = pointerPosition + 'px';\n levelNeedle.style.left = pointerPosition + 'px';\n }\n \n\n \n\n // Update position of min and max values\n minText.style.left = '0';\n maxText.style.left = '100%';\n }\n\n // Function to draw millimeter markings on the level bar\n function drawMMMarkings(min, max, step,instance) {\n // Get the level bar container\n var levelBar = instance.shadowRoot.querySelector('.level-bar');\n\n // Clear previous markings\n levelBar.querySelectorAll('.mm-marking').forEach(mark => mark.remove());\n\n // Calculate total width of the level bar\n var levelBarWidth = levelBar.offsetWidth;\n\n // Calculate the number of markings\n var numberOfMarks = (max - min) / step;\n\n // Calculate the distance between each marking\n var spacing = levelBarWidth / numberOfMarks;\n\n // Create and position the markings\n for (var i = 0; i <= numberOfMarks; i++) {\n var mark = document.createElement('div');\n mark.classList.add('mm-marking');\n mark.style.left = i * spacing + 'px';\n levelBar.appendChild(mark);\n }\n }\n\n \n\n/**\n* dfd - 'change' event of #slider (<input>)\n* @param {Event} event\n* @param {Element} eventRaisingElement\n* @param {ShadowRoot} shadowRoot\n* @param {BaseScreenViewerAndControl} instance\n*/\nexport function dfd(event, eventRaisingElement, shadowRoot, instance) {\n //console.log(event);\n}\n", "properties": {"max": {"type": "number", "default": "20"}, "min": {"type": "number", "default": "4"}, "level": {"type": "number"}, "control_width": {"type": "string", "default": "255px"}, "control_height": {"type": "string", "default": "26px"}, "marking_min": {"type": "number"}, "marking_max": {"type": "number", "default": "100"}, "marking_step": {"type": "number", "default": "5"}, "color": {"type": "color", "default": "red"}}, "settings": {}}
@@ -1 +1 @@
1
- {"html":"<button class=\"md-btn\">\n <img src=\"/icons-material-svg/navigation/ic_menu_48px.svg\" alt=\"3D\" class=\"md-btn-img\">\n Start\n</button>\n","style":":host {\n display: flex;\n flex-direction: column;\n gap: 5px;\n background: transparent;\n --md-primary: #2962ff; /* Material Blue A700 */\n --md-on-primary: #ffffff; /* Text rəngi */\n --md-radius: 4px;\n --md-padding: 0.6rem 1rem;\n}\n\n* {\n box-sizing: border-box;\n}\n:root {\n --md-primary: #2962ff; /* Material Blue A700 */\n --md-on-primary: #ffffff; /* Text rəngi */\n --md-radius: 4px;\n --md-padding: 0.6rem 1rem;\n}\n\n.md-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n font: 500 14px/1.2 \"Roboto\", system-ui, sans-serif;\n color: var(--md-on-primary);\n background-color: var(--md-primary);\n border: none;\n border-radius: var(--md-radius);\n padding: var(--md-padding);\n cursor: pointer;\n transition: box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease;\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n}\n\n.md-btn-img {\n width: 20px;\n height: 20px;\n transition: transform 0.3s ease;\n}\n\n.md-btn:hover {\n background-color: #1e40ff; /* Hover rəngi */\n box-shadow: 0 4px 8px rgba(0,0,0,0.25);\n transform: translateY(-1px);\n}\n\n.md-btn:hover .md-btn-img {\n transform: scale(1.1);\n}\n\n.md-btn:active {\n transform: translateY(0);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n}\n\n.md-btn:focus-visible {\n outline: 2px solid #90caf9; /* Focus rəngi */\n outline-offset: 2px;\n}","settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":""}}
1
+ {"html": "<button class=\"md-btn\">\n <img src=\"/icons-material-svg/navigation/ic_menu_48px.svg\" alt=\"3D\" class=\"md-btn-img\">\n Start\n</button>\n", "style": ":host {\n display: flex;\n flex-direction: column;\n gap: 5px;\n background: transparent;\n --md-primary: var(--accent,#2962ff); /* Material Blue A700 */\n --md-on-primary: #ffffff; /* Text rəngi */\n --md-radius: 4px;\n --md-padding: 0.6rem 1rem;\n}\n\n* {\n box-sizing: border-box;\n}\n:root {\n --md-primary: var(--accent,#2962ff); /* Material Blue A700 */\n --md-on-primary: #ffffff; /* Text rəngi */\n --md-radius: 4px;\n --md-padding: 0.6rem 1rem;\n}\n\n.md-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n font: 500 14px/1.2 \"Roboto\", system-ui, sans-serif;\n color: var(--md-on-primary);\n background-color: var(--md-primary);\n border: none;\n border-radius: var(--md-radius);\n padding: var(--md-padding);\n cursor: pointer;\n transition: box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease;\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n}\n\n.md-btn-img {\n width: 20px;\n height: 20px;\n transition: transform 0.3s ease;\n}\n\n.md-btn:hover {\n background-color: #1e40ff; /* Hover rəngi */\n box-shadow: 0 4px 8px rgba(0,0,0,0.25);\n transform: translateY(-1px);\n}\n\n.md-btn:hover .md-btn-img {\n transform: scale(1.1);\n}\n\n.md-btn:active {\n transform: translateY(0);\n box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n}\n\n.md-btn:focus-visible {\n outline: 2px solid #90caf9; /* Focus rəngi */\n outline-offset: 2px;\n}", "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
@@ -1 +1 @@
1
- {"html": "<!-- Hamburger Menu Button -->\n<div id=\"menu-toggle\" class=\"menu-toggle\">\n <iron-icon icon=\"menu\"></iron-icon>\n</div>\n<!-- Sidebar Overlay (mobil üçün) -->\n<div id=\"sidebar-overlay\" class=\"sidebar-overlay\"></div>\n<!-- Modern Sidebar -->\n<div id=\"modern-sidebar\" class=\"modern-sidebar\">\n <!-- Header -->\n <div class=\"sidebar-header\">\n <div class=\"logo-section\">\n <iron-icon icon=\"dashboard\" class=\"logo-icon\"></iron-icon>\n <h2 class=\"sidebar-title\" id=\"sidebar-title\">Naviqasiya</h2>\n </div>\n <!-- Theme Switcher -->\n <div class=\"theme-switcher\" id=\"theme-switcher\" title=\"Toggle Theme\">\n <iron-icon id=\"theme-icon\" icon=\"wb-sunny\"></iron-icon>\n </div>\n </div>\n <!-- Navigation Menu -->\n <div id=\"menu-container\" class=\"menu-container\"></div>\n <!-- Footer -->\n <div class=\"sidebar-footer\">\n <div class=\"user-info\" id=\"user-info\">\n <iron-icon icon=\"account-circle\" class=\"user-icon\"></iron-icon>\n <div class=\"user-details\">\n <span id=\"username-display\" class=\"username\" bind-content:text=\"local_username\" style=\"left:NaNpx;top:NaNpx;\">Guest</span>\n <span class=\"user-status\">Online</span>\n </div>\n </div>\n <div class=\"version-info\" id=\"version-info\">\n <iron-icon icon=\"info-outline\"></iron-icon>\n <span>KMN WebUI v1.0</span>\n </div>\n </div>\n</div>\n<!-- Main Screen Viewer -->\n<iobroker-webui-screen-viewer id=\"main-screen-viewer\" screen-name=\"st486_Techno\" stretch=\"none\" class=\"main-content\" use-style-from-screen-for-viewer></iobroker-webui-screen-viewer>\n", "style": ":host {\n display: block;\n position: static !important;\n width: 100%;\n height: 100%;\n background: transparent;\n\n}\n\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* CSS Variables for Theming */\n:host {\n /* Light Theme (default) */\n --sidebar-bg-primary: linear-gradient(180deg, #1e3c72 0%, #2a5298 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.1);\n --sidebar-border: rgba(255, 255, 255, 0.15);\n --sidebar-text: rgba(255, 255, 255, 0.9);\n --sidebar-text-secondary: rgba(255, 255, 255, 0.7);\n --menu-item-bg: rgba(255, 255, 255, 0.08);\n --menu-item-hover: rgba(255, 255, 255, 0.15);\n --menu-item-active: rgba(33, 150, 243, 0.25);\n --menu-item-border: transparent;\n --menu-item-border-hover: rgba(255, 255, 255, 0.2);\n --accent-color: #2196F3;\n --shadow-color: rgba(0, 0, 0, 0.3);\n --overlay-bg: rgba(0, 0, 0, 0.5);\n --scrollbar-thumb: rgba(255, 255, 255, 0.3);\n --scrollbar-thumb-hover: rgba(255, 255, 255, 0.5);\n --toggle-bg: rgba(33, 150, 243, 0.9);\n --toggle-hover: rgba(33, 150, 243, 1);\n}\n\n/* Dark Theme */\n:host(.theme-dark) {\n --sidebar-bg-primary: linear-gradient(180deg, #0a0e27 0%, #1a1f3a 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.05);\n --sidebar-border: rgba(255, 255, 255, 0.1);\n --sidebar-text: rgba(255, 255, 255, 0.95);\n --sidebar-text-secondary: rgba(255, 255, 255, 0.6);\n --menu-item-bg: rgba(255, 255, 255, 0.05);\n --menu-item-hover: rgba(255, 255, 255, 0.1);\n --menu-item-active: rgba(76, 175, 80, 0.2);\n --accent-color: #4CAF50;\n --shadow-color: rgba(0, 0, 0, 0.6);\n --toggle-bg: rgba(76, 175, 80, 0.9);\n --toggle-hover: rgba(76, 175, 80, 1);\n}\n\n/* Light Theme Alternative */\n:host(.theme-light) {\n --sidebar-bg-primary: linear-gradient(180deg, #f5f7fa 0%, #e8ecf1 100%);\n --sidebar-bg-secondary: rgba(0, 0, 0, 0.03);\n --sidebar-border: rgba(0, 0, 0, 0.08);\n --sidebar-text: rgba(0, 0, 0, 0.87);\n --sidebar-text-secondary: rgba(0, 0, 0, 0.6);\n --menu-item-bg: rgba(0, 0, 0, 0.04);\n --menu-item-hover: rgba(0, 0, 0, 0.08);\n --menu-item-active: rgba(33, 150, 243, 0.15);\n --accent-color: #1976D2;\n --shadow-color: rgba(0, 0, 0, 0.15);\n --scrollbar-thumb: rgba(0, 0, 0, 0.2);\n --scrollbar-thumb-hover: rgba(0, 0, 0, 0.4);\n --toggle-bg: rgba(33, 150, 243, 0.9);\n --toggle-hover: rgba(33, 150, 243, 1);\n}\n\n/* Purple Theme */\n:host(.theme-purple) {\n --sidebar-bg-primary: linear-gradient(180deg, #4a148c 0%, #6a1b9a 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --sidebar-border: rgba(255, 255, 255, 0.12);\n --accent-color: #AB47BC;\n --menu-item-active: rgba(171, 71, 188, 0.25);\n --toggle-bg: rgba(171, 71, 188, 0.9);\n --toggle-hover: rgba(171, 71, 188, 1);\n}\n\n/* Green Theme */\n:host(.theme-green) {\n --sidebar-bg-primary: linear-gradient(180deg, #1b5e20 0%, #2e7d32 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --accent-color: #4CAF50;\n --menu-item-active: rgba(76, 175, 80, 0.25);\n --toggle-bg: rgba(76, 175, 80, 0.9);\n --toggle-hover: rgba(76, 175, 80, 1);\n}\n\n/* Orange Theme */\n:host(.theme-orange) {\n --sidebar-bg-primary: linear-gradient(180deg, #e65100 0%, #f57c00 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --accent-color: #FF9800;\n --menu-item-active: rgba(255, 152, 0, 0.25);\n --toggle-bg: rgba(255, 152, 0, 0.9);\n --toggle-hover: rgba(255, 152, 0, 1);\n}\n\n/* Hamburger Menu Toggle Button */\n.menu-toggle {\n position: fixed;\n left: 20px;\n top: 20px;\n z-index: 10001;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--toggle-bg);\n border-radius: 12px;\n cursor: pointer;\n box-shadow: 0 4px 12px var(--shadow-color);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.menu-toggle:hover {\n background: var(--toggle-hover);\n transform: scale(1.05);\n box-shadow: 0 6px 16px var(--shadow-color);\n}\n\n.menu-toggle:active {\n transform: scale(0.95);\n}\n\n.menu-toggle iron-icon {\n color: white;\n width: 28px;\n height: 28px;\n}\n\n/* Sidebar Overlay */\n.sidebar-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: var(--overlay-bg);\n z-index: 9998;\n opacity: 0;\n visibility: hidden;\n transition: all 0.3s ease;\n}\n\n.sidebar-overlay.active {\n opacity: 1;\n visibility: visible;\n}\n\n/* Modern Sidebar */\n.modern-sidebar {\n position: fixed;\n left: 0;\n top: 0;\n width: 300px;\n height: 100vh;\n background: var(--sidebar-bg-primary);\n box-shadow: 4px 0 24px var(--shadow-color);\n z-index: 9999;\n display: flex;\n flex-direction: column;\n transform: translateX(-100%);\n transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.modern-sidebar.open {\n transform: translateX(0);\n}\n\n/* Sidebar Header */\n.sidebar-header {\n padding: 24px 20px;\n background: var(--sidebar-bg-secondary);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid var(--sidebar-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: 80px;\n}\n\n.logo-section {\n display: flex;\n align-items: center;\n flex: 1;\n}\n\n.logo-icon {\n width: 32px;\n height: 32px;\n color: var(--sidebar-text);\n margin-right: 12px;\n}\n\n.sidebar-title {\n color: var(--sidebar-text);\n font-size: 22px;\n font-weight: 600;\n letter-spacing: 0.5px;\n}\n\n/* Theme Switcher */\n.theme-switcher {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--menu-item-bg);\n border-radius: 10px;\n cursor: pointer;\n transition: all 0.3s ease;\n}\n\n.theme-switcher:hover {\n background: var(--menu-item-hover);\n transform: rotate(45deg);\n}\n\n.theme-switcher iron-icon {\n width: 22px;\n height: 22px;\n color: var(--sidebar-text);\n}\n\n/* Menu Container */\n.menu-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 20px 16px;\n scrollbar-width: thin;\n scrollbar-color: var(--scrollbar-thumb) transparent;\n}\n\n.menu-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.menu-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.menu-container::-webkit-scrollbar-thumb {\n background: var(--scrollbar-thumb);\n border-radius: 3px;\n}\n\n.menu-container::-webkit-scrollbar-thumb:hover {\n background: var(--scrollbar-thumb-hover);\n}\n\n/* Menu Items */\n.menu-item {\n margin-bottom: 10px;\n background: var(--menu-item-bg);\n border-radius: 14px;\n padding: 16px 18px;\n color: var(--sidebar-text);\n font-size: 15px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n border: 1px solid var(--menu-item-border);\n position: relative;\n overflow: hidden;\n user-select: none;\n}\n\n.menu-item::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 4px;\n height: 100%;\n background: var(--accent-color);\n transform: scaleY(0);\n transition: transform 0.3s ease;\n}\n\n.menu-item:hover {\n background: var(--menu-item-hover);\n border-color: var(--menu-item-border-hover);\n transform: translateX(6px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.menu-item:hover::before {\n transform: scaleY(1);\n}\n\n.menu-item:active {\n transform: translateX(3px) scale(0.98);\n}\n\n.menu-item.active {\n background: var(--menu-item-active);\n border-color: var(--accent-color);\n color: var(--sidebar-text);\n font-weight: 600;\n}\n\n.menu-item.active::before {\n transform: scaleY(1);\n}\n\n.menu-item iron-icon {\n width: 24px;\n height: 24px;\n margin-right: 14px;\n opacity: 0.95;\n flex-shrink: 0;\n}\n\n.menu-item-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Sidebar Footer */\n.sidebar-footer {\n padding: 16px 20px;\n background: rgba(0, 0, 0, 0.2);\n border-top: 1px solid var(--sidebar-border);\n}\n\n.user-info {\n display: flex;\n align-items: center;\n padding: 12px 14px;\n background: var(--sidebar-bg-secondary);\n border-radius: 12px;\n margin-bottom: 12px;\n transition: background 0.3s ease;\n}\n\n.user-info:hover {\n background: var(--menu-item-hover);\n}\n\n.user-icon {\n width: 36px;\n height: 36px;\n color: var(--sidebar-text);\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.user-details {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.username {\n color: var(--sidebar-text);\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.user-status {\n color: var(--sidebar-text-secondary);\n font-size: 12px;\n margin-top: 2px;\n}\n\n.version-info {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 8px;\n color: var(--sidebar-text-secondary);\n font-size: 12px;\n gap: 6px;\n}\n\n.version-info iron-icon {\n width: 16px;\n height: 16px;\n}\n\n/* Main Content */\n.main-content {\n width: 100%;\n height: 100%;\n position: absolute;\n left: 0;\n top: 0;\n transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .modern-sidebar {\n width: 280px;\n }\n \n .sidebar-title {\n font-size: 20px;\n }\n \n .menu-item {\n padding: 14px 16px;\n font-size: 14px;\n }\n}\n\n/* Animations */\n@keyframes slideIn {\n from {\n transform: translateX(-20px);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n.menu-item {\n animation: slideIn 0.3s ease forwards;\n}\n\n/* Stagger animation for menu items */\n.menu-item:nth-child(1) { animation-delay: 0.05s; }\n.menu-item:nth-child(2) { animation-delay: 0.1s; }\n.menu-item:nth-child(3) { animation-delay: 0.15s; }\n.menu-item:nth-child(4) { animation-delay: 0.2s; }\n.menu-item:nth-child(5) { animation-delay: 0.25s; }\n.menu-item:nth-child(6) { animation-delay: 0.3s; }\n.menu-item:nth-child(7) { animation-delay: 0.35s; }\n.menu-item:nth-child(8) { animation-delay: 0.4s; }\n\n@media print {\n /* Sidebar və menu button gizlət */\n .menu-toggle,\n .sidebar-overlay,\n .modern-sidebar {\n display: none !important;\n }\n \n /* Main content tam genişlik */\n .main-content {\n width: 100% !important;\n height: auto !important;\n position: static !important;\n }\n \n /* Shadow-ları sil */\n * {\n box-shadow: none !important;\n text-shadow: none !important;\n }\n \n \n}", "script": "export async function connectedCallback(instance) {\n // IOB hazır olana qədər gözlə\n \n await window.IOB.waitForReady();\n \n // Custom elementlər register olana qədər gözlə\n try {\n await customElements.whenDefined('iron-icon');\n } catch (err) {\n console.warn('Custom elements wait error:', err);\n }\n\n const menuToggle = instance._getDomElement('menu-toggle');\n const sidebar = instance._getDomElement('modern-sidebar');\n const overlay = instance._getDomElement('sidebar-overlay');\n const menuContainer = instance._getDomElement('menu-container');\n const mainScreenViewer = instance._getDomElement('main-screen-viewer');\n const themeSwitcher = instance._getDomElement('theme-switcher');\n const themeIcon = instance._getDomElement('theme-icon');\n // Load saved theme\n let currentTheme = localStorage.getItem('webui-sidebar-theme') || 'default';\n applyTheme(currentTheme);\n \n // Theme switcher\n const themes = ['default', 'dark', 'light', 'purple', 'green', 'orange'];\n let themeIndex = themes.indexOf(currentTheme);\n \n themeSwitcher.addEventListener('click', () => {\n themeIndex = (themeIndex + 1) % themes.length;\n currentTheme = themes[themeIndex];\n applyTheme(currentTheme);\n localStorage.setItem('webui-sidebar-theme', currentTheme);\n });\n \n function applyTheme(theme) {\n // Remove all theme classes\n instance.classList.remove('theme-default', 'theme-dark', 'theme-light', 'theme-purple', 'theme-green', 'theme-orange');\n \n // Add new theme class\n if (theme !== 'default') {\n instance.classList.add('theme-' + theme);\n }\n \n // Update icon\n const themeIcons = {\n 'default': 'wb-sunny',\n 'dark': 'brightness-2',\n 'light': 'wb-sunny',\n 'purple': 'palette',\n 'green': 'eco',\n 'orange': 'local-fire-department'\n };\n themeIcon.setAttribute('icon', themeIcons[theme] || 'wb-sunny');\n }\n \n // Toggle sidebar\n function toggleSidebar(open) {\n if (open) {\n sidebar.classList.add('open');\n overlay.classList.add('active');\n } else {\n sidebar.classList.remove('open');\n overlay.classList.remove('active');\n }\n }\n \n menuToggle.addEventListener('click', () => {\n toggleSidebar(!sidebar.classList.contains('open'));\n });\n \n overlay.addEventListener('click', () => {\n toggleSidebar(false);\n });\n \n // Load screens\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\n \n // Set default screen if not set\n if (!mainScreenViewer.screenName && instance.defaultScreen) {\n mainScreenViewer.screenName = instance.defaultScreen;\n window.dispatchEvent(new CustomEvent('webui-screen-change', {\n detail: { screenName: instance.defaultScreen }\n }));\n }\n \n for (const screenName of screenNames) {\n try {\n const screenData = await window.IOB.getScreen(screenName);\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\n const sidebarOrder = screenData.properties?.sidebarOrder?.default ?? 999;\n \n if (showInSidebar) {\n const menuItem = document.createElement('div');\n menuItem.className = 'menu-item';\n menuItem.dataset.screenName = screenName;\n menuItem.dataset.order = sidebarOrder;\n \n const icon = document.createElement('iron-icon');\n icon.setAttribute('icon', sidebarIcon);\n \n const label = document.createElement('span');\n label.className = 'menu-item-label';\n label.textContent = sidebarLabel;\n \n menuItem.appendChild(icon);\n menuItem.appendChild(label);\n \n menuItem.addEventListener('click', () => {\n // Remove active from all\n const allItems = menuContainer.querySelectorAll('.menu-item');\n allItems.forEach(item => item.classList.remove('active'));\n \n // Add active to clicked\n menuItem.classList.add('active');\n \n // Change screen\n mainScreenViewer.screenName = screenName;\n // ✅ Global event at — bütün dünya eşidə bilsin\n window.dispatchEvent(new CustomEvent('webui-screen-change', {\n detail: { screenName }\n }));\n\n // Close sidebar on mobile\n if (window.innerWidth < 768) {\n toggleSidebar(false);\n }\n });\n menuContainer.appendChild(menuItem);\n \n // Set first screen as active if no screen is set\n if (!mainScreenViewer.screenName) {\n mainScreenViewer.screenName = screenName;\n menuItem.classList.add('active');\n } else if (mainScreenViewer.screenName === screenName) {\n menuItem.classList.add('active');\n }\n }\n } catch (err) {\n console.error(`Screen ${screenName} loading error:`, err);\n }\n }\n \n // Sort menu items by order\n const items = Array.from(menuContainer.querySelectorAll('.menu-item'));\n items.sort((a, b) => {\n const orderA = parseInt(a.dataset.order) || 999;\n const orderB = parseInt(b.dataset.order) || 999;\n return orderA - orderB;\n });\n items.forEach(item => menuContainer.appendChild(item));\n \n // Load user info\n try {\n const currentUser = await window.IOB.getCurrentUser();\n const usernameDisplay = instance._getDomElement('username-display');\n if (usernameDisplay && currentUser?.name) {\n usernameDisplay.textContent = currentUser.name;\n }\n } catch (err) {\n console.warn('User info error:', err);\n }\n \n // Set version from config if available\n const versionInfo = instance._getDomElement('version-info');\n if (versionInfo && instance.appVersion) {\n versionInfo.querySelector('span').textContent = instance.appVersion;\n }\n}\n\n\nexport async function init(instance) {\n}", "properties": {"sidebarTitle": {"type": "string", "default": "Navigation"}, "defaultScreen": {"type": "string"}, "appVersion": {"type": "string", "default": "KMN WebUI v1.0"}, "theme": {"type": "string", "default": "default"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "width": "100%", "height": "100%", "useGlobalStyle": "false", "bindToSize": "false"}}
1
+ {"html": "<!-- Hamburger Menu Button -->\n<div id=\"menu-toggle\" class=\"menu-toggle\">\n <iron-icon icon=\"menu\"></iron-icon>\n</div>\n<!-- Sidebar Overlay (mobil üçün) -->\n<div id=\"sidebar-overlay\" class=\"sidebar-overlay\"></div>\n<!-- Modern Sidebar -->\n<div id=\"modern-sidebar\" class=\"modern-sidebar\">\n <!-- Header -->\n <div class=\"sidebar-header\">\n <div class=\"logo-section\">\n <iron-icon icon=\"dashboard\" class=\"logo-icon\"></iron-icon>\n <h2 class=\"sidebar-title\" id=\"sidebar-title\">Naviqasiya</h2>\n </div>\n <!-- Theme Switcher -->\n <div class=\"theme-switcher\" id=\"theme-switcher\" title=\"Toggle Theme\">\n <iron-icon id=\"theme-icon\" icon=\"wb-sunny\"></iron-icon>\n </div>\n </div>\n <!-- Navigation Menu -->\n <div id=\"menu-container\" class=\"menu-container\"></div>\n <!-- Footer -->\n <div class=\"sidebar-footer\">\n <div class=\"user-info\" id=\"user-info\">\n <iron-icon icon=\"account-circle\" class=\"user-icon\"></iron-icon>\n <div class=\"user-details\">\n <span id=\"username-display\" class=\"username\" bind-content:text=\"local_username\" style=\"left:NaNpx;top:NaNpx;\">Guest</span>\n <span class=\"user-status\">Online</span>\n </div>\n </div>\n <div class=\"version-info\" id=\"version-info\">\n <iron-icon icon=\"info-outline\"></iron-icon>\n <span>KMN WebUI v1.0</span>\n </div>\n </div>\n</div>\n<!-- Main Screen Viewer -->\n<iobroker-webui-screen-viewer id=\"main-screen-viewer\" screen-name=\"st486_Techno\" stretch=\"none\" class=\"main-content\" use-style-from-screen-for-viewer></iobroker-webui-screen-viewer>\n", "style": ":host {\n display: block;\n position: static !important;\n width: 100%;\n height: 100%;\n background: transparent;\n\n}\n\n* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\n/* CSS Variables for Theming */\n:host {\n /* Light Theme (default) */\n --sidebar-bg-primary: linear-gradient(180deg, #1e3c72 0%, #2a5298 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.1);\n --sidebar-border: rgba(255, 255, 255, 0.15);\n --sidebar-text: rgba(255, 255, 255, 0.9);\n --sidebar-text-secondary: rgba(255, 255, 255, 0.7);\n --menu-item-bg: rgba(255, 255, 255, 0.08);\n --menu-item-hover: rgba(255, 255, 255, 0.15);\n --menu-item-active: rgba(33, 150, 243, 0.25);\n --menu-item-border: transparent;\n --menu-item-border-hover: rgba(255, 255, 255, 0.2);\n --accent-color: var(--accent,#2196F3);\n --shadow-color: rgba(0, 0, 0, 0.3);\n --overlay-bg: rgba(0, 0, 0, 0.5);\n --scrollbar-thumb: rgba(255, 255, 255, 0.3);\n --scrollbar-thumb-hover: rgba(255, 255, 255, 0.5);\n --toggle-bg: rgba(33, 150, 243, 0.9);\n --toggle-hover: rgba(33, 150, 243, 1);\n}\n\n/* Dark Theme */\n:host(.theme-dark) {\n --sidebar-bg-primary: linear-gradient(180deg, var(--ui-surface,#0a0e27) 0%, var(--ui-surface,#1a1f3a) 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.05);\n --sidebar-border: rgba(255, 255, 255, 0.1);\n --sidebar-text: rgba(255, 255, 255, 0.95);\n --sidebar-text-secondary: rgba(255, 255, 255, 0.6);\n --menu-item-bg: rgba(255, 255, 255, 0.05);\n --menu-item-hover: rgba(255, 255, 255, 0.1);\n --menu-item-active: rgba(76, 175, 80, 0.2);\n --accent-color: var(--ui-ok,#4CAF50);\n --shadow-color: rgba(0, 0, 0, 0.6);\n --toggle-bg: rgba(76, 175, 80, 0.9);\n --toggle-hover: rgba(76, 175, 80, 1);\n}\n\n/* Light Theme Alternative */\n:host(.theme-light) {\n --sidebar-bg-primary: linear-gradient(180deg, #f5f7fa 0%, #e8ecf1 100%);\n --sidebar-bg-secondary: rgba(0, 0, 0, 0.03);\n --sidebar-border: rgba(0, 0, 0, 0.08);\n --sidebar-text: rgba(0, 0, 0, 0.87);\n --sidebar-text-secondary: rgba(0, 0, 0, 0.6);\n --menu-item-bg: rgba(0, 0, 0, 0.04);\n --menu-item-hover: rgba(0, 0, 0, 0.08);\n --menu-item-active: rgba(33, 150, 243, 0.15);\n --accent-color: var(--accent,#1976D2);\n --shadow-color: rgba(0, 0, 0, 0.15);\n --scrollbar-thumb: rgba(0, 0, 0, 0.2);\n --scrollbar-thumb-hover: rgba(0, 0, 0, 0.4);\n --toggle-bg: rgba(33, 150, 243, 0.9);\n --toggle-hover: rgba(33, 150, 243, 1);\n}\n\n/* Purple Theme */\n:host(.theme-purple) {\n --sidebar-bg-primary: linear-gradient(180deg, #4a148c 0%, #6a1b9a 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --sidebar-border: rgba(255, 255, 255, 0.12);\n --accent-color: #AB47BC;\n --menu-item-active: rgba(171, 71, 188, 0.25);\n --toggle-bg: rgba(171, 71, 188, 0.9);\n --toggle-hover: rgba(171, 71, 188, 1);\n}\n\n/* Green Theme */\n:host(.theme-green) {\n --sidebar-bg-primary: linear-gradient(180deg, var(--ui-ok,#1b5e20) 0%, var(--ui-ok,#2e7d32) 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --accent-color: var(--ui-ok,#4CAF50);\n --menu-item-active: rgba(76, 175, 80, 0.25);\n --toggle-bg: rgba(76, 175, 80, 0.9);\n --toggle-hover: rgba(76, 175, 80, 1);\n}\n\n/* Orange Theme */\n:host(.theme-orange) {\n --sidebar-bg-primary: linear-gradient(180deg, #e65100 0%, #f57c00 100%);\n --sidebar-bg-secondary: rgba(255, 255, 255, 0.08);\n --accent-color: var(--ui-warn,#FF9800);\n --menu-item-active: rgba(255, 152, 0, 0.25);\n --toggle-bg: rgba(255, 152, 0, 0.9);\n --toggle-hover: rgba(255, 152, 0, 1);\n}\n\n/* Hamburger Menu Toggle Button */\n.menu-toggle {\n position: fixed;\n left: 20px;\n top: 20px;\n z-index: 10001;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--toggle-bg);\n border-radius: 12px;\n cursor: pointer;\n box-shadow: 0 4px 12px var(--shadow-color);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.menu-toggle:hover {\n background: var(--toggle-hover);\n transform: scale(1.05);\n box-shadow: 0 6px 16px var(--shadow-color);\n}\n\n.menu-toggle:active {\n transform: scale(0.95);\n}\n\n.menu-toggle iron-icon {\n color: white;\n width: 28px;\n height: 28px;\n}\n\n/* Sidebar Overlay */\n.sidebar-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: var(--overlay-bg);\n z-index: 9998;\n opacity: 0;\n visibility: hidden;\n transition: all 0.3s ease;\n}\n\n.sidebar-overlay.active {\n opacity: 1;\n visibility: visible;\n}\n\n/* Modern Sidebar */\n.modern-sidebar {\n position: fixed;\n left: 0;\n top: 0;\n width: 300px;\n height: 100vh;\n background: var(--sidebar-bg-primary);\n box-shadow: 4px 0 24px var(--shadow-color);\n z-index: 9999;\n display: flex;\n flex-direction: column;\n transform: translateX(-100%);\n transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.modern-sidebar.open {\n transform: translateX(0);\n}\n\n/* Sidebar Header */\n.sidebar-header {\n padding: 24px 20px;\n background: var(--sidebar-bg-secondary);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid var(--sidebar-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n min-height: 80px;\n}\n\n.logo-section {\n display: flex;\n align-items: center;\n flex: 1;\n}\n\n.logo-icon {\n width: 32px;\n height: 32px;\n color: var(--sidebar-text);\n margin-right: 12px;\n}\n\n.sidebar-title {\n color: var(--sidebar-text);\n font-size: 22px;\n font-weight: 600;\n letter-spacing: 0.5px;\n}\n\n/* Theme Switcher */\n.theme-switcher {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--menu-item-bg);\n border-radius: 10px;\n cursor: pointer;\n transition: all 0.3s ease;\n}\n\n.theme-switcher:hover {\n background: var(--menu-item-hover);\n transform: rotate(45deg);\n}\n\n.theme-switcher iron-icon {\n width: 22px;\n height: 22px;\n color: var(--sidebar-text);\n}\n\n/* Menu Container */\n.menu-container {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 20px 16px;\n scrollbar-width: thin;\n scrollbar-color: var(--scrollbar-thumb) transparent;\n}\n\n.menu-container::-webkit-scrollbar {\n width: 6px;\n}\n\n.menu-container::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.menu-container::-webkit-scrollbar-thumb {\n background: var(--scrollbar-thumb);\n border-radius: 3px;\n}\n\n.menu-container::-webkit-scrollbar-thumb:hover {\n background: var(--scrollbar-thumb-hover);\n}\n\n/* Menu Items */\n.menu-item {\n margin-bottom: 10px;\n background: var(--menu-item-bg);\n border-radius: 14px;\n padding: 16px 18px;\n color: var(--sidebar-text);\n font-size: 15px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n display: flex;\n align-items: center;\n border: 1px solid var(--menu-item-border);\n position: relative;\n overflow: hidden;\n user-select: none;\n}\n\n.menu-item::before {\n content: '';\n position: absolute;\n left: 0;\n top: 0;\n width: 4px;\n height: 100%;\n background: var(--accent-color);\n transform: scaleY(0);\n transition: transform 0.3s ease;\n}\n\n.menu-item:hover {\n background: var(--menu-item-hover);\n border-color: var(--menu-item-border-hover);\n transform: translateX(6px);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n.menu-item:hover::before {\n transform: scaleY(1);\n}\n\n.menu-item:active {\n transform: translateX(3px) scale(0.98);\n}\n\n.menu-item.active {\n background: var(--menu-item-active);\n border-color: var(--accent-color);\n color: var(--sidebar-text);\n font-weight: 600;\n}\n\n.menu-item.active::before {\n transform: scaleY(1);\n}\n\n.menu-item iron-icon {\n width: 24px;\n height: 24px;\n margin-right: 14px;\n opacity: 0.95;\n flex-shrink: 0;\n}\n\n.menu-item-label {\n flex: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Sidebar Footer */\n.sidebar-footer {\n padding: 16px 20px;\n background: rgba(0, 0, 0, 0.2);\n border-top: 1px solid var(--sidebar-border);\n}\n\n.user-info {\n display: flex;\n align-items: center;\n padding: 12px 14px;\n background: var(--sidebar-bg-secondary);\n border-radius: 12px;\n margin-bottom: 12px;\n transition: background 0.3s ease;\n}\n\n.user-info:hover {\n background: var(--menu-item-hover);\n}\n\n.user-icon {\n width: 36px;\n height: 36px;\n color: var(--sidebar-text);\n margin-right: 12px;\n flex-shrink: 0;\n}\n\n.user-details {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.username {\n color: var(--sidebar-text);\n font-size: 14px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.user-status {\n color: var(--sidebar-text-secondary);\n font-size: 12px;\n margin-top: 2px;\n}\n\n.version-info {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 8px;\n color: var(--sidebar-text-secondary);\n font-size: 12px;\n gap: 6px;\n}\n\n.version-info iron-icon {\n width: 16px;\n height: 16px;\n}\n\n/* Main Content */\n.main-content {\n width: 100%;\n height: 100%;\n position: absolute;\n left: 0;\n top: 0;\n transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n .modern-sidebar {\n width: 280px;\n }\n \n .sidebar-title {\n font-size: 20px;\n }\n \n .menu-item {\n padding: 14px 16px;\n font-size: 14px;\n }\n}\n\n/* Animations */\n@keyframes slideIn {\n from {\n transform: translateX(-20px);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n.menu-item {\n animation: slideIn 0.3s ease forwards;\n}\n\n/* Stagger animation for menu items */\n.menu-item:nth-child(1) { animation-delay: 0.05s; }\n.menu-item:nth-child(2) { animation-delay: 0.1s; }\n.menu-item:nth-child(3) { animation-delay: 0.15s; }\n.menu-item:nth-child(4) { animation-delay: 0.2s; }\n.menu-item:nth-child(5) { animation-delay: 0.25s; }\n.menu-item:nth-child(6) { animation-delay: 0.3s; }\n.menu-item:nth-child(7) { animation-delay: 0.35s; }\n.menu-item:nth-child(8) { animation-delay: 0.4s; }\n\n@media print {\n /* Sidebar və menu button gizlət */\n .menu-toggle,\n .sidebar-overlay,\n .modern-sidebar {\n display: none !important;\n }\n \n /* Main content tam genişlik */\n .main-content {\n width: 100% !important;\n height: auto !important;\n position: static !important;\n }\n \n /* Shadow-ları sil */\n * {\n box-shadow: none !important;\n text-shadow: none !important;\n }\n \n \n}", "script": "export async function connectedCallback(instance) {\n // IOB hazır olana qədər gözlə\n \n await window.IOB.waitForReady();\n \n // Custom elementlər register olana qədər gözlə\n try {\n await customElements.whenDefined('iron-icon');\n } catch (err) {\n console.warn('Custom elements wait error:', err);\n }\n\n const menuToggle = instance._getDomElement('menu-toggle');\n const sidebar = instance._getDomElement('modern-sidebar');\n const overlay = instance._getDomElement('sidebar-overlay');\n const menuContainer = instance._getDomElement('menu-container');\n const mainScreenViewer = instance._getDomElement('main-screen-viewer');\n const themeSwitcher = instance._getDomElement('theme-switcher');\n const themeIcon = instance._getDomElement('theme-icon');\n // Load saved theme\n let currentTheme = localStorage.getItem('webui-sidebar-theme') || 'default';\n applyTheme(currentTheme);\n \n // Theme switcher\n const themes = ['default', 'dark', 'light', 'purple', 'green', 'orange'];\n let themeIndex = themes.indexOf(currentTheme);\n \n themeSwitcher.addEventListener('click', () => {\n themeIndex = (themeIndex + 1) % themes.length;\n currentTheme = themes[themeIndex];\n applyTheme(currentTheme);\n localStorage.setItem('webui-sidebar-theme', currentTheme);\n });\n \n function applyTheme(theme) {\n // Remove all theme classes\n instance.classList.remove('theme-default', 'theme-dark', 'theme-light', 'theme-purple', 'theme-green', 'theme-orange');\n \n // Add new theme class\n if (theme !== 'default') {\n instance.classList.add('theme-' + theme);\n }\n \n // Update icon\n const themeIcons = {\n 'default': 'wb-sunny',\n 'dark': 'brightness-2',\n 'light': 'wb-sunny',\n 'purple': 'palette',\n 'green': 'eco',\n 'orange': 'local-fire-department'\n };\n themeIcon.setAttribute('icon', themeIcons[theme] || 'wb-sunny');\n }\n \n // Toggle sidebar\n function toggleSidebar(open) {\n if (open) {\n sidebar.classList.add('open');\n overlay.classList.add('active');\n } else {\n sidebar.classList.remove('open');\n overlay.classList.remove('active');\n }\n }\n \n menuToggle.addEventListener('click', () => {\n toggleSidebar(!sidebar.classList.contains('open'));\n });\n \n overlay.addEventListener('click', () => {\n toggleSidebar(false);\n });\n \n // Load screens\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\n \n // Set default screen if not set\n if (!mainScreenViewer.screenName && instance.defaultScreen) {\n mainScreenViewer.screenName = instance.defaultScreen;\n window.dispatchEvent(new CustomEvent('webui-screen-change', {\n detail: { screenName: instance.defaultScreen }\n }));\n }\n \n for (const screenName of screenNames) {\n try {\n const screenData = await window.IOB.getScreen(screenName);\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\n const sidebarOrder = screenData.properties?.sidebarOrder?.default ?? 999;\n \n if (showInSidebar) {\n const menuItem = document.createElement('div');\n menuItem.className = 'menu-item';\n menuItem.dataset.screenName = screenName;\n menuItem.dataset.order = sidebarOrder;\n \n const icon = document.createElement('iron-icon');\n icon.setAttribute('icon', sidebarIcon);\n \n const label = document.createElement('span');\n label.className = 'menu-item-label';\n label.textContent = sidebarLabel;\n \n menuItem.appendChild(icon);\n menuItem.appendChild(label);\n \n menuItem.addEventListener('click', () => {\n // Remove active from all\n const allItems = menuContainer.querySelectorAll('.menu-item');\n allItems.forEach(item => item.classList.remove('active'));\n \n // Add active to clicked\n menuItem.classList.add('active');\n \n // Change screen\n mainScreenViewer.screenName = screenName;\n // ✅ Global event at — bütün dünya eşidə bilsin\n window.dispatchEvent(new CustomEvent('webui-screen-change', {\n detail: { screenName }\n }));\n\n // Close sidebar on mobile\n if (window.innerWidth < 768) {\n toggleSidebar(false);\n }\n });\n menuContainer.appendChild(menuItem);\n \n // Set first screen as active if no screen is set\n if (!mainScreenViewer.screenName) {\n mainScreenViewer.screenName = screenName;\n menuItem.classList.add('active');\n } else if (mainScreenViewer.screenName === screenName) {\n menuItem.classList.add('active');\n }\n }\n } catch (err) {\n console.error(`Screen ${screenName} loading error:`, err);\n }\n }\n \n // Sort menu items by order\n const items = Array.from(menuContainer.querySelectorAll('.menu-item'));\n items.sort((a, b) => {\n const orderA = parseInt(a.dataset.order) || 999;\n const orderB = parseInt(b.dataset.order) || 999;\n return orderA - orderB;\n });\n items.forEach(item => menuContainer.appendChild(item));\n \n // Load user info\n try {\n const currentUser = await window.IOB.getCurrentUser();\n const usernameDisplay = instance._getDomElement('username-display');\n if (usernameDisplay && currentUser?.name) {\n usernameDisplay.textContent = currentUser.name;\n }\n } catch (err) {\n console.warn('User info error:', err);\n }\n \n // Set version from config if available\n const versionInfo = instance._getDomElement('version-info');\n if (versionInfo && instance.appVersion) {\n versionInfo.querySelector('span').textContent = instance.appVersion;\n }\n}\n\n\nexport async function init(instance) {\n}", "properties": {"sidebarTitle": {"type": "string", "default": "Navigation"}, "defaultScreen": {"type": "string"}, "appVersion": {"type": "string", "default": "KMN WebUI v1.0"}, "theme": {"type": "string", "default": "default"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "width": "100%", "height": "100%", "useGlobalStyle": "false", "bindToSize": "false"}}
@@ -1 +1 @@
1
- {"html": "<side-drawer id=\"sidebar1\" style=\"position:absolute;left:-323px;top:1px;width:310px;height:185px;filter:blur(50px);\">\n <side-menu id=\"sidemenu1\" style=\"width:1131px;left:0px;top:0px;\"></side-menu>\n</side-drawer>\n<!-- Panel wrapper -->\n<iobroker-webui-screen-viewer id=\"mymainscreenviewer\" screen-name=\"st486_Techno\" stretch=\"none\" use-style-from-screen-for-viewer style=\"width:1920px;height:100%;position:absolute;left:0px;top:0px;\"></iobroker-webui-screen-viewer>\n<div id=\"screen-panel\" class=\"panel visible\" bind-css:display='{\"signal\":\"webui.0.hideshow\",\"converter\":{\"true\":\"block\",\"false\":\"none\"}}' style=\"height:692px;left:566px;top:50px;position:absolute;\">\n <h2>Screen Settings</h2>\n <div id=\"screen-container\"></div>\n <button id=\"set-btn\">Set</button>\n</div>\n<webui-sidebar-menuitems @click=\"openSideBar\" class=\"dispnone\" style=\"position:absolute;left:0px;top:1px;\"></webui-sidebar-menuitems>\n", "style": ":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\nside-drawer{\n/*background-color: rgba(37, 131, 155, 0.3);*/\n\n}\n\n.panel input, .panel checkbox {\n pointer-events: auto;\n}\n\n.panel {\n border: 1px solid #ccc;\n border-radius: 8px;\n padding: 1rem;\n margin: 1rem 0;\n background: #f9f9f9;\n max-width: 400px;\n display: none;\n\n /* Əlavə hissə */\n max-height: 900px; /* panelin maksimum hündürlüyü */\n overflow-y: auto; /* vertical scrollbar çıxsın */\n}\n.panel.visible {\n display: block;\n}\n.screen-group {\n border: 1px solid #ddd;\n border-radius: 6px;\n padding: 0.5rem;\n margin-bottom: 0.8rem;\n background: #fff;\n z-index: 2;\n}\n.screen-group label {\n display: block;\n margin: 0.3rem 0;\n}\n#set-btn {\n margin-top: 1rem;\n padding: 0.6rem 1.2rem;\n background: #2962ff;\n color: #fff;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n}\n#set-btn:hover {\n background: #1e40ff;\n}\n.panel::-webkit-scrollbar {\n width: 8px;\n}\n.panel::-webkit-scrollbar-thumb {\n background: #2962ff;\n border-radius: 4px;\n}\n.panel::-webkit-scrollbar-track {\n background: #e0e0e0;\n}\n@media print {\n .dispnone{\n display: none;\n }\n}", "script": "let protocol = window.location.protocol;\r\nlet ip = window.location.hostname;\r\nlet port = window.location.port;\r\n\r\n\r\n/*let BaseCustomControl;\r\nlet webuiCustomControlSymbol;\r\nlet path =(`${protocol}//${ip}:${port}/webui/dist/frontend/runtime/CustomControls.js`);\r\nconst module = await import(path) \r\n .then(module => { \r\n BaseCustomControl = module.BaseCustomControl;\r\n webuiCustomControlSymbol=module.webuiCustomControlSymbol;\r\n }) \r\n .catch(err => { \r\n console.error('Error loading module:', err); \r\n }); */\r\n\r\nexport async function init(instance) {\r\n /*const container = instance._getDomElement('sidemenu1');\r\n \r\n // Bütün screenləri tap\r\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\r\n \r\n for (const screenName of screenNames) {\r\n try {\r\n // Screen datasını oxu (ScreenViewer-in istifadə etdiyi metod)\r\n const screenData = await window.IOB.getScreen(screenName);\r\n \r\n // Properties-dən oxu\r\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\r\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\r\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\r\n if (showInSidebar) {\r\n // Menu item yarat\r\n const menuItem = document.createElement('side-menu-item');\r\n menuItem.setAttribute('label', sidebarLabel);\r\n \r\n const icon = document.createElement('iron-icon');\r\n icon.setAttribute('icon', sidebarIcon);\r\n icon.setAttribute('slot', 'icon');\r\n \r\n menuItem.appendChild(icon);\r\n menuItem.addEventListener('click', (event) => {\r\n event.preventDefault();\r\n changeview(event, instance);\r\n });\r\n \r\n container.appendChild(menuItem);\r\n }\r\n } catch (err) {\r\n console.error(`Screen ${screenName} oxunarkən səhv:`, err);\r\n }\r\n }*/\r\n}\r\n\r\nfunction hidepanel(instance, ev) {\r\n \r\n}\r\n\r\n\r\nasync function createJsonState() {\r\n const stateId = 'webui.0.myJsonData';\r\n // Əvvəlcə obyektin olub‑olmadığını yoxla\r\n window.IOB.connection._socket.emit('getObject', stateId, (err, obj) => {\r\n if (err) {\r\n console.error('Səhv:', err);\r\n return;\r\n }\r\n\r\n if (!obj) {\r\n // Yoxdursa, yeni state yarat\r\n window.IOB.connection._socket.emit('setObject', stateId, {\r\n type: 'state',\r\n common: {\r\n name: 'JSON Data',\r\n type: 'string',\r\n role: 'json',\r\n read: true,\r\n write: true\r\n },\r\n native: {}\r\n }, (err, res) => {\r\n if (err) {\r\n console.error('State yaratmaqda səhv:', err);\r\n } else {\r\n console.log('Yeni state yaradıldı:', res);\r\n }\r\n });\r\n } else {\r\n console.log('State artıq mövcuddur, yenidən yaratmadıq.');\r\n }\r\n });\r\n}\r\n\r\n\r\n\r\n\r\nexport function changeview(event, instance) {\r\n const mymainscreenviewer = instance._getDomElement('mymainscreenviewer');\r\n let buttonscreennametext = event.currentTarget.label;\r\n mymainscreenviewer.screenName = buttonscreennametext;\r\n}\r\n\r\n\r\n\r\nexport function openSideBar(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n let sidebar1 = instance._getDomElement('sidebar1');\r\n sidebar1.open = true;\r\n\r\n}\r\n\r\n\r\nexport async function connectedCallback(instance) { \r\n \r\n // IOB hazır olana qədər gözlə\r\n await window.IOB.waitForReady();\r\n \r\n // Custom elementlər register olana qədər gözlə\r\n try {\r\n await customElements.whenDefined('iron-icon');\r\n await customElements.whenDefined('side-menu-item');\r\n } catch (err) {\r\n console.warn('Custom elements wait error:', err);\r\n }\r\n\r\n const container = instance._getDomElement('sidemenu1');\r\n \r\n // Bütün screenləri tap\r\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\r\n \r\n for (const screenName of screenNames) {\r\n try {\r\n // Screen datasını oxu (ScreenViewer-in istifadə etdiyi metod)\r\n const screenData = await window.IOB.getScreen(screenName);\r\n \r\n // Properties-dən oxu\r\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\r\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\r\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\r\n \r\n console.log(`Screen: ${screenName}, Show: ${showInSidebar}, Icon: ${sidebarIcon}`);\r\n \r\n if (showInSidebar) {\r\n // Menu item yarat\r\n const menuItem = document.createElement('side-menu-item');\r\n menuItem.setAttribute('label', sidebarLabel);\r\n \r\n const icon = document.createElement('iron-icon');\r\n icon.setAttribute('icon', sidebarIcon);\r\n icon.setAttribute('slot', 'icon');\r\n \r\n menuItem.appendChild(icon);\r\n menuItem.addEventListener('click', (event) => {\r\n event.preventDefault();\r\n changeview(event, instance);\r\n });\r\n \r\n container.appendChild(menuItem);\r\n console.log(`✅ Added to sidebar: ${sidebarLabel} with icon: ${sidebarIcon}`);\r\n }\r\n } catch (err) {\r\n console.error(`Screen ${screenName} oxunarkən səhv:`, err);\r\n }\r\n }\r\n}", "properties": {"hide_Screen_Settings": {"type": "boolean"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "width": "1920px", "height": "1080px", "useGlobalStyle": "true", "bindToSize": "true"}}
1
+ {"html": "<side-drawer id=\"sidebar1\" style=\"position:absolute;left:-323px;top:1px;width:310px;height:185px;filter:blur(50px);\">\n <side-menu id=\"sidemenu1\" style=\"width:1131px;left:0px;top:0px;\"></side-menu>\n</side-drawer>\n<!-- Panel wrapper -->\n<iobroker-webui-screen-viewer id=\"mymainscreenviewer\" screen-name=\"st486_Techno\" stretch=\"none\" use-style-from-screen-for-viewer style=\"width:1920px;height:100%;position:absolute;left:0px;top:0px;\"></iobroker-webui-screen-viewer>\n<div id=\"screen-panel\" class=\"panel visible\" bind-css:display='{\"signal\":\"webui.0.hideshow\",\"converter\":{\"true\":\"block\",\"false\":\"none\"}}' style=\"height:692px;left:566px;top:50px;position:absolute;\">\n <h2>Screen Settings</h2>\n <div id=\"screen-container\"></div>\n <button id=\"set-btn\">Set</button>\n</div>\n<webui-sidebar-menuitems @click=\"openSideBar\" class=\"dispnone\" style=\"position:absolute;left:0px;top:1px;\"></webui-sidebar-menuitems>\n", "style": ":host {\n background: transparent;\n}\n\n* {\n box-sizing: border-box;\n}\nside-drawer{\n/*background-color: rgba(37, 131, 155, 0.3);*/\n\n}\n\n.panel input, .panel checkbox {\n pointer-events: auto;\n}\n\n.panel {\n border: 1px solid var(--ui-border,#ccc);\n border-radius: 8px;\n padding: 1rem;\n margin: 1rem 0;\n background: #f9f9f9;\n max-width: 400px;\n display: none;\n\n /* Əlavə hissə */\n max-height: 900px; /* panelin maksimum hündürlüyü */\n overflow-y: auto; /* vertical scrollbar çıxsın */\n}\n.panel.visible {\n display: block;\n}\n.screen-group {\n border: 1px solid var(--ui-border,#ddd);\n border-radius: 6px;\n padding: 0.5rem;\n margin-bottom: 0.8rem;\n background: #fff;\n z-index: 2;\n}\n.screen-group label {\n display: block;\n margin: 0.3rem 0;\n}\n#set-btn {\n margin-top: 1rem;\n padding: 0.6rem 1.2rem;\n background: var(--accent,#2962ff);\n color: #fff;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n}\n#set-btn:hover {\n background: #1e40ff;\n}\n.panel::-webkit-scrollbar {\n width: 8px;\n}\n.panel::-webkit-scrollbar-thumb {\n background: var(--accent,#2962ff);\n border-radius: 4px;\n}\n.panel::-webkit-scrollbar-track {\n background: var(--ui-text,#e0e0e0);\n}\n@media print {\n .dispnone{\n display: none;\n }\n}", "script": "let protocol = window.location.protocol;\r\nlet ip = window.location.hostname;\r\nlet port = window.location.port;\r\n\r\n\r\n/*let BaseCustomControl;\r\nlet webuiCustomControlSymbol;\r\nlet path =(`${protocol}//${ip}:${port}/webui/dist/frontend/runtime/CustomControls.js`);\r\nconst module = await import(path) \r\n .then(module => { \r\n BaseCustomControl = module.BaseCustomControl;\r\n webuiCustomControlSymbol=module.webuiCustomControlSymbol;\r\n }) \r\n .catch(err => { \r\n console.error('Error loading module:', err); \r\n }); */\r\n\r\nexport async function init(instance) {\r\n /*const container = instance._getDomElement('sidemenu1');\r\n \r\n // Bütün screenləri tap\r\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\r\n \r\n for (const screenName of screenNames) {\r\n try {\r\n // Screen datasını oxu (ScreenViewer-in istifadə etdiyi metod)\r\n const screenData = await window.IOB.getScreen(screenName);\r\n \r\n // Properties-dən oxu\r\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\r\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\r\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\r\n if (showInSidebar) {\r\n // Menu item yarat\r\n const menuItem = document.createElement('side-menu-item');\r\n menuItem.setAttribute('label', sidebarLabel);\r\n \r\n const icon = document.createElement('iron-icon');\r\n icon.setAttribute('icon', sidebarIcon);\r\n icon.setAttribute('slot', 'icon');\r\n \r\n menuItem.appendChild(icon);\r\n menuItem.addEventListener('click', (event) => {\r\n event.preventDefault();\r\n changeview(event, instance);\r\n });\r\n \r\n container.appendChild(menuItem);\r\n }\r\n } catch (err) {\r\n console.error(`Screen ${screenName} oxunarkən səhv:`, err);\r\n }\r\n }*/\r\n}\r\n\r\nfunction hidepanel(instance, ev) {\r\n \r\n}\r\n\r\n\r\nasync function createJsonState() {\r\n const stateId = 'webui.0.myJsonData';\r\n // Əvvəlcə obyektin olub‑olmadığını yoxla\r\n window.IOB.connection._socket.emit('getObject', stateId, (err, obj) => {\r\n if (err) {\r\n console.error('Səhv:', err);\r\n return;\r\n }\r\n\r\n if (!obj) {\r\n // Yoxdursa, yeni state yarat\r\n window.IOB.connection._socket.emit('setObject', stateId, {\r\n type: 'state',\r\n common: {\r\n name: 'JSON Data',\r\n type: 'string',\r\n role: 'json',\r\n read: true,\r\n write: true\r\n },\r\n native: {}\r\n }, (err, res) => {\r\n if (err) {\r\n console.error('State yaratmaqda səhv:', err);\r\n } else {\r\n console.log('Yeni state yaradıldı:', res);\r\n }\r\n });\r\n } else {\r\n console.log('State artıq mövcuddur, yenidən yaratmadıq.');\r\n }\r\n });\r\n}\r\n\r\n\r\n\r\n\r\nexport function changeview(event, instance) {\r\n const mymainscreenviewer = instance._getDomElement('mymainscreenviewer');\r\n let buttonscreennametext = event.currentTarget.label;\r\n mymainscreenviewer.screenName = buttonscreennametext;\r\n}\r\n\r\n\r\n\r\nexport function openSideBar(event, eventRaisingElement, shadowRoot, instance, parameters) {\r\n let sidebar1 = instance._getDomElement('sidebar1');\r\n sidebar1.open = true;\r\n\r\n}\r\n\r\n\r\nexport async function connectedCallback(instance) { \r\n \r\n // IOB hazır olana qədər gözlə\r\n await window.IOB.waitForReady();\r\n \r\n // Custom elementlər register olana qədər gözlə\r\n try {\r\n await customElements.whenDefined('iron-icon');\r\n await customElements.whenDefined('side-menu-item');\r\n } catch (err) {\r\n console.warn('Custom elements wait error:', err);\r\n }\r\n\r\n const container = instance._getDomElement('sidemenu1');\r\n \r\n // Bütün screenləri tap\r\n const screenNames = await window.IOB.getObjectNames('screen', \"\");\r\n \r\n for (const screenName of screenNames) {\r\n try {\r\n // Screen datasını oxu (ScreenViewer-in istifadə etdiyi metod)\r\n const screenData = await window.IOB.getScreen(screenName);\r\n \r\n // Properties-dən oxu\r\n const showInSidebar = screenData.properties?.showInSidebar?.default ?? false;\r\n const sidebarIcon = screenData.properties?.sidebarIcon?.default ?? 'label';\r\n const sidebarLabel = screenData.properties?.sidebarLabel?.default || screenName;\r\n \r\n console.log(`Screen: ${screenName}, Show: ${showInSidebar}, Icon: ${sidebarIcon}`);\r\n \r\n if (showInSidebar) {\r\n // Menu item yarat\r\n const menuItem = document.createElement('side-menu-item');\r\n menuItem.setAttribute('label', sidebarLabel);\r\n \r\n const icon = document.createElement('iron-icon');\r\n icon.setAttribute('icon', sidebarIcon);\r\n icon.setAttribute('slot', 'icon');\r\n \r\n menuItem.appendChild(icon);\r\n menuItem.addEventListener('click', (event) => {\r\n event.preventDefault();\r\n changeview(event, instance);\r\n });\r\n \r\n container.appendChild(menuItem);\r\n console.log(`✅ Added to sidebar: ${sidebarLabel} with icon: ${sidebarIcon}`);\r\n }\r\n } catch (err) {\r\n console.error(`Screen ${screenName} oxunarkən səhv:`, err);\r\n }\r\n }\r\n}", "properties": {"hide_Screen_Settings": {"type": "boolean"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "width": "1920px", "height": "1080px", "useGlobalStyle": "true", "bindToSize": "true"}}
@@ -1 +1 @@
1
- {"html": "<div class=\"traffic-light\">\n <div class=\"light red active\" id=\"red\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;red&#39;?&#39;red&#39;:&#39;other&#39;;\",\"converter\":{\"red\":\"light red active\",\"other\":\"light red\"}}'></div>\n <div class=\"light yellow\" id=\"yellow\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;yellow&#39;?&#39;yellow&#39;:&#39;other&#39;;\",\"converter\":{\"yellow\":\"light yellow active\",\"other\":\"light yellow\"}}'></div>\n <div class=\"light green\" id=\"green\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;green&#39;?&#39;green&#39;:&#39;other&#39;;\",\"converter\":{\"green\":\"light green active\",\"other\":\"light green\"}}'></div>\n</div>\n", "style": ":host {\n background: transparent;\n}\n\n\n.traffic-light {\n width: 50px;\n padding: 10px;\n background-color: #333;\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.light {\n width: 50px;\n height: 50px;\n border-radius: 50%;\n background-color: #555;\n transition: background-color 0.3s;\n}\n\n.red.active {\n background-color: #ff0000;\n box-shadow: 0 0 20px #ff0000;\n}\n\n.yellow.active {\n background-color: #ffff00;\n box-shadow: 0 0 20px #ffff00;\n}\n\n.green.active {\n background-color: #00ff00;\n box-shadow: 0 0 20px #00ff00;\n}", "script": "// ════════════════════════════════════════════════════════════════════════════\n// Custom Control — Lifecycle Script\n// Each exported function is called by the runtime with (instance, shadowRoot)\n// instance = the web component element (shadow host)\n// shadowRoot = instance.shadowRoot\n// ════════════════════════════════════════════════════════════════════════════\n\n// ── Import (needed only if you use iobrokerHandler directly) ─────────────────\n// import { iobrokerHandler } from '../common/IobrokerHandler.js';\n\n// ── 1. DOM ───────────────────────────────────────────────────────────────────\n// instance._getDomElement('myId')\n// getElementById inside shadow root — returns the element or null\n// NOTE: pass the bare ID, NOT '#myId'\n//\n// instance._getDomElements('.myClass')\n// querySelectorAll inside shadow root — returns NodeList\n//\n// shadowRoot.querySelector('tag')\n// raw shadow root query (any CSS selector)\n\n// ── 2. Events ────────────────────────────────────────────────────────────────\n// instance._assignEvent('click', handler)\n// Adds a listener on the host element itself.\n// → Auto-removed when the control is disconnected from DOM.\n// Returns { remove() } for early removal.\n//\n// el.addEventListener('click', handler) ← on a child element (manual cleanup)\n\n// ── 3. Properties (defined in Properties tab) ────────────────────────────────\n// instance.myProp read / write a declared property\n// instance['myProp'] same — useful for dynamic names\n\n// ── 4. Relative OID path ─────────────────────────────────────────────────────\n// instance._getRelativeSignalsPath()\n// Returns the OID prefix set by the parent screen/viewer.\n// Relative signals start with '.' → prepend this prefix yourself, or let\n// iobrokerHandler.getNormalizedSignalName() do it.\n\n// ── 5. ioBroker state (iobrokerHandler) ──────────────────────────────────────\n// Must import at top: import { iobrokerHandler } from '../common/IobrokerHandler.js';\n//\n// Read a state once:\n// const state = await iobrokerHandler.getState('hm-rpc.0.ABC.STATE');\n// console.log(state.val, state.ts, state.ack);\n//\n// Write a state:\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', true);\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', { val: 42, ack: false });\n//\n// Subscribe to state changes (remember to unsubscribe on disconnect!):\n// const handler = (id, state) => { console.log(id, state.val); };\n// await iobrokerHandler.subscribeState('hm-rpc.0.ABC.STATE', handler);\n// // in disconnectedCallback:\n// iobrokerHandler.unsubscribeState('hm-rpc.0.ABC.STATE', handler);\n//\n// Get an ioBroker object (type/role/unit/min/max/…):\n// const obj = await iobrokerHandler.getObject('hm-rpc.0.ABC.STATE');\n//\n// Current user and groups:\n// const user = await iobrokerHandler.getCurrentUser(); // { id, groups }\n//\n// Historic data (requires History adapter):\n// const { values } = await iobrokerHandler.getHistoricData(\n// 'hm-rpc.0.ABC.STATE',\n// { start: Date.now() - 3600000, end: Date.now(), count: 100, aggregate: 'none' }\n// );\n//\n// Language:\n// console.log(iobrokerHandler.language); // 'en' | 'de' | 'ru' | …\n// iobrokerHandler.languageChanged.on(lang => {}); // subscribe to language switch\n//\n// Low-level socket connection (for advanced use):\n// iobrokerHandler.connection.subscribeState(id, handler);\n// iobrokerHandler.connection.getStates(pattern); // wildcard query\n\n// ── 6. Navigation & Dialogs (window.RUNTIME) ────────────────────────────────\n// Open a screen (replaces current view):\n// RUNTIME.openScreen({ screen: 'myScreen', relativeSignalsPath: 'hm-rpc.0.device', noHistory: false });\n//\n// Open a screen in a dialog:\n// RUNTIME.openDialog({ screen: 'myScreen', title: 'Details', width: '600px', height: '400px',\n// moveable: true, closeable: true });\n//\n// Run any script command object:\n// await RUNTIME.runSimpleScriptCommand({ type: 'SetSignalValue',\n// signal: 'hm-rpc.0.ABC.STATE', target: 'signal', value: true });\n//\n// Find a parent screen/control:\n// const parentScreen = RUNTIME.getParentScreen(instance); // 1 level up\n// const grandParent = RUNTIME.getParentScreen(instance, 2); // 2 levels up\n\n// ── 7. Bindings (applyBinding) ───────────────────────────────────────────────\n// The framework applies all [[signal]] bindings automatically via applyAllBindings.\n// For dynamic / programmatic bindings you can call it manually:\n//\n// const cleanup = window.appShell.bindingsHelper.applyAllBindings(\n// shadowRoot, // root element to scan\n// instance._getRelativeSignalsPath(), // relative OID prefix\n// instance // property source for ?prop bindings\n// );\n// // cleanup is an array of unsubscribe functions — call them on disconnect:\n// // cleanup.forEach(fn => fn());\n//\n// Single-element binding (low-level — normally not needed):\n// window.appShell.bindingsHelper.applyBinding(\n// element, // target DOM element\n// binding, // binding descriptor (parsed from bind-prop:* attribute)\n// relPath, // relative signal path\n// instance, // root for ?property lookups\n// null // optional specialValueHandler\n// );\n\n// ════════════════════════════════════════════════════════════════════════════\n\nexport function init(instance, shadowRoot) {\n // Called ONCE when the script module is first loaded (before connectedCallback).\n // Use for one-time setup that does not depend on the element being in the DOM.\n //instance._assignEvent('color-changed', colorchange);\n trafficLight(instance);\n}\n\n/*let time = {\n red: 5000, // 5 saniyə qırmızı\n green: 4000, // 4 saniyə yaşıl\n yellow: 2000 // 2 saniyə sarı\n};*/\n\nfunction trafficLight(instance) {\n _updateLights('red',instance);\n console.log(\"🔴 Qırmızı işıq yandı\");\n setTimeout(() => {\n _updateLights('green',instance);\n console.log(\"🟢 Yaşıl işıq yandı\");\n setTimeout(() => {\n _updateLights('yellow',instance);\n console.log(\"🟡 Sarı işıq yandı\");\n // Dövrü təkrar başlatmaq üçün yenidən çağırırıq\n setTimeout(trafficLight, instance.time_yellow);\n }, instance.time_green);\n }, instance.time_red);\n}\n\n// Başlat\n\n\n\nexport function connectedCallback(instance, shadowRoot) {\n // Called each time the control is inserted into the DOM.\n // Start subscriptions, set up event listeners, render initial state.\n //\n // Example:\n // const label = instance._getDomElement('statusLabel');\n // instance._assignEvent('click', async () => {\n // await iobrokerHandler.setState('hm-rpc.0.device.SWITCH', true);\n // });\n}\n\nexport function disconnectedCallback(instance, shadowRoot) {\n // Called each time the control is removed from the DOM.\n // _assignEvent listeners are removed automatically.\n // Manually unsubscribe from iobrokerHandler.subscribeState calls here.\n}\n\n\n\n function colorchange(e) {\n \n _updateLights(e.detail.newValue,e.currentTarget);\n \n }\n\n function _updateLights(color,instance) {\n // Remove active class from all\n instance.shadowRoot.querySelectorAll('.light').forEach(light => light.classList.remove('active'));\n // Add active class to current\n instance.shadowRoot.querySelector(`#${color}`)?.classList.add('active');\n }\n\n\n", "properties": {"color": {"type": "string", "default": "yellow"}, "time_red": {"type": "number", "default": "5000"}, "time_yellow": {"type": "number", "default": "4000"}, "time_green": {"type": "number", "default": "2000"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
1
+ {"html": "<div class=\"traffic-light\">\n <div class=\"light red active\" id=\"red\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;red&#39;?&#39;red&#39;:&#39;other&#39;;\",\"converter\":{\"red\":\"light red active\",\"other\":\"light red\"}}'></div>\n <div class=\"light yellow\" id=\"yellow\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;yellow&#39;?&#39;yellow&#39;:&#39;other&#39;;\",\"converter\":{\"yellow\":\"light yellow active\",\"other\":\"light yellow\"}}'></div>\n <div class=\"light green\" id=\"green\" bind-attr:class='{\"signal\":\"??color\",\"expression\":\"return __0==&#39;green&#39;?&#39;green&#39;:&#39;other&#39;;\",\"converter\":{\"green\":\"light green active\",\"other\":\"light green\"}}'></div>\n</div>\n", "style": ":host {\n background: transparent;\n}\n\n\n.traffic-light {\n width: 50px;\n padding: 10px;\n background-color: #333;\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.light {\n width: 50px;\n height: 50px;\n border-radius: 50%;\n background-color: #555;\n transition: background-color 0.3s;\n}\n\n.red.active {\n background-color: var(--ui-error,#ff0000);\n box-shadow: 0 0 20px var(--ui-error,#ff0000);\n}\n\n.yellow.active {\n background-color: var(--ui-warn,#ffff00);\n box-shadow: 0 0 20px var(--ui-warn,#ffff00);\n}\n\n.green.active {\n background-color: var(--ui-ok,#00ff00);\n box-shadow: 0 0 20px var(--ui-ok,#00ff00);\n}", "script": "// ════════════════════════════════════════════════════════════════════════════\n// Custom Control — Lifecycle Script\n// Each exported function is called by the runtime with (instance, shadowRoot)\n// instance = the web component element (shadow host)\n// shadowRoot = instance.shadowRoot\n// ════════════════════════════════════════════════════════════════════════════\n\n// ── Import (needed only if you use iobrokerHandler directly) ─────────────────\n// import { iobrokerHandler } from '../common/IobrokerHandler.js';\n\n// ── 1. DOM ───────────────────────────────────────────────────────────────────\n// instance._getDomElement('myId')\n// getElementById inside shadow root — returns the element or null\n// NOTE: pass the bare ID, NOT '#myId'\n//\n// instance._getDomElements('.myClass')\n// querySelectorAll inside shadow root — returns NodeList\n//\n// shadowRoot.querySelector('tag')\n// raw shadow root query (any CSS selector)\n\n// ── 2. Events ────────────────────────────────────────────────────────────────\n// instance._assignEvent('click', handler)\n// Adds a listener on the host element itself.\n// → Auto-removed when the control is disconnected from DOM.\n// Returns { remove() } for early removal.\n//\n// el.addEventListener('click', handler) ← on a child element (manual cleanup)\n\n// ── 3. Properties (defined in Properties tab) ────────────────────────────────\n// instance.myProp read / write a declared property\n// instance['myProp'] same — useful for dynamic names\n\n// ── 4. Relative OID path ─────────────────────────────────────────────────────\n// instance._getRelativeSignalsPath()\n// Returns the OID prefix set by the parent screen/viewer.\n// Relative signals start with '.' → prepend this prefix yourself, or let\n// iobrokerHandler.getNormalizedSignalName() do it.\n\n// ── 5. ioBroker state (iobrokerHandler) ──────────────────────────────────────\n// Must import at top: import { iobrokerHandler } from '../common/IobrokerHandler.js';\n//\n// Read a state once:\n// const state = await iobrokerHandler.getState('hm-rpc.0.ABC.STATE');\n// console.log(state.val, state.ts, state.ack);\n//\n// Write a state:\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', true);\n// await iobrokerHandler.setState('hm-rpc.0.ABC.STATE', { val: 42, ack: false });\n//\n// Subscribe to state changes (remember to unsubscribe on disconnect!):\n// const handler = (id, state) => { console.log(id, state.val); };\n// await iobrokerHandler.subscribeState('hm-rpc.0.ABC.STATE', handler);\n// // in disconnectedCallback:\n// iobrokerHandler.unsubscribeState('hm-rpc.0.ABC.STATE', handler);\n//\n// Get an ioBroker object (type/role/unit/min/max/…):\n// const obj = await iobrokerHandler.getObject('hm-rpc.0.ABC.STATE');\n//\n// Current user and groups:\n// const user = await iobrokerHandler.getCurrentUser(); // { id, groups }\n//\n// Historic data (requires History adapter):\n// const { values } = await iobrokerHandler.getHistoricData(\n// 'hm-rpc.0.ABC.STATE',\n// { start: Date.now() - 3600000, end: Date.now(), count: 100, aggregate: 'none' }\n// );\n//\n// Language:\n// console.log(iobrokerHandler.language); // 'en' | 'de' | 'ru' | …\n// iobrokerHandler.languageChanged.on(lang => {}); // subscribe to language switch\n//\n// Low-level socket connection (for advanced use):\n// iobrokerHandler.connection.subscribeState(id, handler);\n// iobrokerHandler.connection.getStates(pattern); // wildcard query\n\n// ── 6. Navigation & Dialogs (window.RUNTIME) ────────────────────────────────\n// Open a screen (replaces current view):\n// RUNTIME.openScreen({ screen: 'myScreen', relativeSignalsPath: 'hm-rpc.0.device', noHistory: false });\n//\n// Open a screen in a dialog:\n// RUNTIME.openDialog({ screen: 'myScreen', title: 'Details', width: '600px', height: '400px',\n// moveable: true, closeable: true });\n//\n// Run any script command object:\n// await RUNTIME.runSimpleScriptCommand({ type: 'SetSignalValue',\n// signal: 'hm-rpc.0.ABC.STATE', target: 'signal', value: true });\n//\n// Find a parent screen/control:\n// const parentScreen = RUNTIME.getParentScreen(instance); // 1 level up\n// const grandParent = RUNTIME.getParentScreen(instance, 2); // 2 levels up\n\n// ── 7. Bindings (applyBinding) ───────────────────────────────────────────────\n// The framework applies all [[signal]] bindings automatically via applyAllBindings.\n// For dynamic / programmatic bindings you can call it manually:\n//\n// const cleanup = window.appShell.bindingsHelper.applyAllBindings(\n// shadowRoot, // root element to scan\n// instance._getRelativeSignalsPath(), // relative OID prefix\n// instance // property source for ?prop bindings\n// );\n// // cleanup is an array of unsubscribe functions — call them on disconnect:\n// // cleanup.forEach(fn => fn());\n//\n// Single-element binding (low-level — normally not needed):\n// window.appShell.bindingsHelper.applyBinding(\n// element, // target DOM element\n// binding, // binding descriptor (parsed from bind-prop:* attribute)\n// relPath, // relative signal path\n// instance, // root for ?property lookups\n// null // optional specialValueHandler\n// );\n\n// ════════════════════════════════════════════════════════════════════════════\n\nexport function init(instance, shadowRoot) {\n // Called ONCE when the script module is first loaded (before connectedCallback).\n // Use for one-time setup that does not depend on the element being in the DOM.\n //instance._assignEvent('color-changed', colorchange);\n trafficLight(instance);\n}\n\n/*let time = {\n red: 5000, // 5 saniyə qırmızı\n green: 4000, // 4 saniyə yaşıl\n yellow: 2000 // 2 saniyə sarı\n};*/\n\nfunction trafficLight(instance) {\n _updateLights('red',instance);\n console.log(\"🔴 Qırmızı işıq yandı\");\n setTimeout(() => {\n _updateLights('green',instance);\n console.log(\"🟢 Yaşıl işıq yandı\");\n setTimeout(() => {\n _updateLights('yellow',instance);\n console.log(\"🟡 Sarı işıq yandı\");\n // Dövrü təkrar başlatmaq üçün yenidən çağırırıq\n setTimeout(trafficLight, instance.time_yellow);\n }, instance.time_green);\n }, instance.time_red);\n}\n\n// Başlat\n\n\n\nexport function connectedCallback(instance, shadowRoot) {\n // Called each time the control is inserted into the DOM.\n // Start subscriptions, set up event listeners, render initial state.\n //\n // Example:\n // const label = instance._getDomElement('statusLabel');\n // instance._assignEvent('click', async () => {\n // await iobrokerHandler.setState('hm-rpc.0.device.SWITCH', true);\n // });\n}\n\nexport function disconnectedCallback(instance, shadowRoot) {\n // Called each time the control is removed from the DOM.\n // _assignEvent listeners are removed automatically.\n // Manually unsubscribe from iobrokerHandler.subscribeState calls here.\n}\n\n\n\n function colorchange(e) {\n \n _updateLights(e.detail.newValue,e.currentTarget);\n \n }\n\n function _updateLights(color,instance) {\n // Remove active class from all\n instance.shadowRoot.querySelectorAll('.light').forEach(light => light.classList.remove('active'));\n // Add active class to current\n instance.shadowRoot.querySelector(`#${color}`)?.classList.add('active');\n }\n\n\n", "properties": {"color": {"type": "string", "default": "yellow"}, "time_red": {"type": "number", "default": "5000"}, "time_yellow": {"type": "number", "default": "4000"}, "time_green": {"type": "number", "default": "2000"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
@@ -1 +1 @@
1
- {"html":"<label class=\"switch-button\" for=\"switch\">\n <div class=\"switch-outer\">\n <input id=\"switch\" type=\"checkbox\">\n <div class=\"button\">\n <span class=\"button-toggle\"></span>\n <span class=\"button-indicator\"></span>\n </div>\n </div>\n</label>\n","style":":host {\n background: transparent;\n height: 100%;\n width: 100%;\n}\n\n* {\n box-sizing: border-box;\n}\n .switch-button {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n margin: auto;\n height: 55px;\n }\n\n .switch-button .switch-outer {\n height: 100%;\n background: #252532;\n width: 115px;\n border-radius: 165px;\n -webkit-box-shadow: inset 0px 5px 10px 0px #16151c, 0px 3px 6px -2px #403f4e;\n box-shadow: inset 0px 5px 10px 0px #16151c, 0px 3px 6px -2px #403f4e;\n border: 1px solid #32303e;\n padding: 6px;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n\n .switch-button .switch-outer input[type=\"checkbox\"] {\n opacity: 0;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n position: absolute;\n }\n\n .switch-button .switch-outer .button-toggle {\n height: 42px;\n width: 42px;\n background: -webkit-gradient(\n linear,\n left top,\n left bottom,\n from(#3b3a4e),\n to(#272733)\n );\n background: -o-linear-gradient(#3b3a4e, #272733);\n background: linear-gradient(#3b3a4e, #272733);\n border-radius: 100%;\n -webkit-box-shadow: inset 0px 5px 4px 0px #424151, 0px 4px 15px 0px #0f0e17;\n box-shadow: inset 0px 5px 4px 0px #424151, 0px 4px 15px 0px #0f0e17;\n position: relative;\n z-index: 2;\n -webkit-transition: left 0.3s ease-in;\n -o-transition: left 0.3s ease-in;\n transition: left 0.3s ease-in;\n left: 0;\n }\n\n .switch-button\n .switch-outer\n input[type=\"checkbox\"]:checked\n + .button\n .button-toggle {\n left: 58%;\n }\n\n .switch-button\n .switch-outer\n input[type=\"checkbox\"]:checked\n + .button\n .button-indicator {\n -webkit-animation: indicator 1s forwards;\n animation: indicator 1s forwards;\n }\n\n .switch-button .switch-outer .button {\n width: 100%;\n height: 100%;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n position: relative;\n -webkit-box-pack: justify;\n justify-content: space-between;\n }\n\n .switch-button .switch-outer .button-indicator {\n height: 25px;\n width: 25px;\n top: 50%;\n -webkit-transform: translateY(-50%);\n transform: translateY(-50%);\n border-radius: 50%;\n border: 3px solid #ef565f;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n right: 10px;\n position: relative;\n }\n\n @-webkit-keyframes indicator {\n 30% {\n opacity: 0;\n }\n\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n border: 3px solid #60d480;\n left: -68%;\n }\n }\n\n @keyframes indicator {\n 30% {\n opacity: 0;\n }\n\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n border: 3px solid #60d480;\n left: -68%;\n }\n }","settings":{"visibilityEnabled":"false","visibilityGroups":"","visibilityAction":"hide","visibilityRedirectScreen":"","width":"","height":""}}
1
+ {"html": "<label class=\"switch-button\" for=\"switch\">\n <div class=\"switch-outer\">\n <input id=\"switch\" type=\"checkbox\">\n <div class=\"button\">\n <span class=\"button-toggle\"></span>\n <span class=\"button-indicator\"></span>\n </div>\n </div>\n</label>\n", "style": ":host {\n background: transparent;\n height: 100%;\n width: 100%;\n}\n\n* {\n box-sizing: border-box;\n}\n .switch-button {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n margin: auto;\n height: 55px;\n }\n\n .switch-button .switch-outer {\n height: 100%;\n background: #252532;\n width: 115px;\n border-radius: 165px;\n -webkit-box-shadow: inset 0px 5px 10px 0px #16151c, 0px 3px 6px -2px #403f4e;\n box-shadow: inset 0px 5px 10px 0px #16151c, 0px 3px 6px -2px #403f4e;\n border: 1px solid #32303e;\n padding: 6px;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n\n .switch-button .switch-outer input[type=\"checkbox\"] {\n opacity: 0;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n position: absolute;\n }\n\n .switch-button .switch-outer .button-toggle {\n height: 42px;\n width: 42px;\n background: -webkit-gradient(\n linear,\n left top,\n left bottom,\n from(var(--ui-surface-2,#3b3a4e)),\n to(var(--ui-surface,#272733))\n );\n background: -o-linear-gradient(var(--ui-surface-2,#3b3a4e), var(--ui-surface,#272733));\n background: linear-gradient(var(--ui-surface-2,#3b3a4e), var(--ui-surface,#272733));\n border-radius: 100%;\n -webkit-box-shadow: inset 0px 5px 4px 0px #424151, 0px 4px 15px 0px #0f0e17;\n box-shadow: inset 0px 5px 4px 0px #424151, 0px 4px 15px 0px #0f0e17;\n position: relative;\n z-index: 2;\n -webkit-transition: left 0.3s ease-in;\n -o-transition: left 0.3s ease-in;\n transition: left 0.3s ease-in;\n left: 0;\n }\n\n .switch-button\n .switch-outer\n input[type=\"checkbox\"]:checked\n + .button\n .button-toggle {\n left: 58%;\n }\n\n .switch-button\n .switch-outer\n input[type=\"checkbox\"]:checked\n + .button\n .button-indicator {\n -webkit-animation: indicator 1s forwards;\n animation: indicator 1s forwards;\n }\n\n .switch-button .switch-outer .button {\n width: 100%;\n height: 100%;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n position: relative;\n -webkit-box-pack: justify;\n justify-content: space-between;\n }\n\n .switch-button .switch-outer .button-indicator {\n height: 25px;\n width: 25px;\n top: 50%;\n -webkit-transform: translateY(-50%);\n transform: translateY(-50%);\n border-radius: 50%;\n border: 3px solid #ef565f;\n -webkit-box-sizing: border-box;\n box-sizing: border-box;\n right: 10px;\n position: relative;\n }\n\n @-webkit-keyframes indicator {\n 30% {\n opacity: 0;\n }\n\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n border: 3px solid #60d480;\n left: -68%;\n }\n }\n\n @keyframes indicator {\n 30% {\n opacity: 0;\n }\n\n 0% {\n opacity: 1;\n }\n\n 100% {\n opacity: 1;\n border: 3px solid #60d480;\n left: -68%;\n }\n }", "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": "", "width": "", "height": ""}}