iobroker.mywebui 1.89.0 → 1.91.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.
- package/default-controls/controls/Language/LanguageSelector2.control +1 -1
- package/default-controls/controls/WebuiConnectionMonitor1.control +1 -1
- package/default-controls/controls/analog_points/scada_point_panel.control +1 -1
- package/default-controls/controls/gauges/setpoint-gauge.control +1 -1
- package/default-controls/controls/input_controls/input_with_button.control +1 -1
- package/default-controls/controls/logout.control +1 -1
- package/default-controls/controls/navigation/app-bar.control +1 -1
- package/default-controls/controls/navigation/sidebar.control +1 -1
- package/default-controls/controls/sidebar/modern-sidebar.control +1 -1
- package/default-controls/controls/sidebar/sidebarcontrol.control +1 -1
- package/default-controls/controls/theme/theme-selector.control +1 -1
- package/default-controls/controls/theme/theme-toggle.control +1 -1
- package/default-controls/controls/user-badge.control +1 -1
- package/dist/backend/3d-editor-integration.js +1 -1
- package/dist/backend/ImportmapCreator.js +1 -1
- package/dist/backend/UploadHelper.js +1 -1
- package/dist/backend/main.js +1 -1
- package/io-package.json +1 -1
- package/package.json +1 -1
- package/scripts/_build_logout.py +1 -1
- package/scripts/_build_themetoggle.py +12 -5
- package/scripts/_build_userbadge.py +1 -1
- package/www/3d-editor/js/Animation.js +1 -1
- package/www/3d-editor/js/AnimationResizer.js +1 -1
- package/www/3d-editor/js/Command.js +1 -1
- package/www/3d-editor/js/Config.js +1 -1
- package/www/3d-editor/js/Editor.js +1 -1
- package/www/3d-editor/js/EditorControls.js +1 -1
- package/www/3d-editor/js/GLTFImportDialog.js +1 -1
- package/www/3d-editor/js/History.js +1 -1
- package/www/3d-editor/js/Loader.js +1 -1
- package/www/3d-editor/js/LoaderUtils.js +1 -1
- package/www/3d-editor/js/Menubar.Add.js +1 -1
- package/www/3d-editor/js/Menubar.Edit.js +1 -1
- package/www/3d-editor/js/Menubar.File.js +1 -1
- package/www/3d-editor/js/Menubar.Help.js +1 -1
- package/www/3d-editor/js/Menubar.Render.js +1 -1
- package/www/3d-editor/js/Menubar.Status.js +1 -1
- package/www/3d-editor/js/Menubar.View.js +1 -1
- package/www/3d-editor/js/Menubar.js +1 -1
- package/www/3d-editor/js/Player.js +1 -1
- package/www/3d-editor/js/Resizer.js +1 -1
- package/www/3d-editor/js/Script.js +1 -1
- package/www/3d-editor/js/Selector.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.BoxGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.BufferGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.CapsuleGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.CircleGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.CylinderGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.DodecahedronGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.ExtrudeGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.IcosahedronGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.LatheGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.Modifiers.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.OctahedronGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.PlaneGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.RingGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.ShapeGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.SphereGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.TetrahedronGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.TextGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.TorusGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.TorusKnotGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.TubeGeometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Geometry.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.BooleanProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.ColorProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.ConstantProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.MapProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.NumberProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.Program.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.RangeValueProperty.js +1 -1
- package/www/3d-editor/js/Sidebar.Material.js +1 -1
- package/www/3d-editor/js/Sidebar.Object.js +1 -1
- package/www/3d-editor/js/Sidebar.Project.App.js +1 -1
- package/www/3d-editor/js/Sidebar.Project.Materials.js +1 -1
- package/www/3d-editor/js/Sidebar.Project.Renderer.js +1 -1
- package/www/3d-editor/js/Sidebar.Project.Resources.js +1 -1
- package/www/3d-editor/js/Sidebar.Project.js +1 -1
- package/www/3d-editor/js/Sidebar.Properties.js +1 -1
- package/www/3d-editor/js/Sidebar.Scene.js +1 -1
- package/www/3d-editor/js/Sidebar.Script.js +1 -1
- package/www/3d-editor/js/Sidebar.Settings.History.js +1 -1
- package/www/3d-editor/js/Sidebar.Settings.Shortcuts.js +1 -1
- package/www/3d-editor/js/Sidebar.Settings.js +1 -1
- package/www/3d-editor/js/Sidebar.js +1 -1
- package/www/3d-editor/js/Storage.js +1 -1
- package/www/3d-editor/js/Strings.js +1 -1
- package/www/3d-editor/js/TextureParametersDialog.js +1 -1
- package/www/3d-editor/js/Toolbar.js +1 -1
- package/www/3d-editor/js/Viewport.Controls.js +1 -1
- package/www/3d-editor/js/Viewport.Info.js +1 -1
- package/www/3d-editor/js/Viewport.Pathtracer.js +1 -1
- package/www/3d-editor/js/Viewport.ViewHelper.js +1 -1
- package/www/3d-editor/js/Viewport.XR.js +1 -1
- package/www/3d-editor/js/Viewport.js +1 -1
- package/www/3d-editor/js/commands/AddObjectCommand.js +1 -1
- package/www/3d-editor/js/commands/AddScriptCommand.js +1 -1
- package/www/3d-editor/js/commands/Commands.js +1 -1
- package/www/3d-editor/js/commands/MoveObjectCommand.js +1 -1
- package/www/3d-editor/js/commands/MultiCmdsCommand.js +1 -1
- package/www/3d-editor/js/commands/RemoveObjectCommand.js +1 -1
- package/www/3d-editor/js/commands/RemoveScriptCommand.js +1 -1
- package/www/3d-editor/js/commands/SetColorCommand.js +1 -1
- package/www/3d-editor/js/commands/SetGeometryCommand.js +1 -1
- package/www/3d-editor/js/commands/SetGeometryValueCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialColorCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialMapCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialRangeCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialValueCommand.js +1 -1
- package/www/3d-editor/js/commands/SetMaterialVectorCommand.js +1 -1
- package/www/3d-editor/js/commands/SetPositionCommand.js +1 -1
- package/www/3d-editor/js/commands/SetRotationCommand.js +1 -1
- package/www/3d-editor/js/commands/SetScaleCommand.js +1 -1
- package/www/3d-editor/js/commands/SetSceneCommand.js +1 -1
- package/www/3d-editor/js/commands/SetScriptValueCommand.js +1 -1
- package/www/3d-editor/js/commands/SetShadowValueCommand.js +1 -1
- package/www/3d-editor/js/commands/SetTextureParametersCommand.js +1 -1
- package/www/3d-editor/js/commands/SetUuidCommand.js +1 -1
- package/www/3d-editor/js/commands/SetValueCommand.js +1 -1
- package/www/dist/frontend/bundle/chunk-3C6XC7RW.js +1 -1
- package/www/dist/frontend/bundle/chunk-3WGNLIWJ.js +2 -2
- package/www/dist/frontend/bundle/chunk-3YK3Y2SJ.js +1 -1
- package/www/dist/frontend/bundle/chunk-43HEBO5G.js +3 -3
- package/www/dist/frontend/bundle/chunk-5MUGMRXU.js +1 -1
- package/www/dist/frontend/bundle/chunk-5UBRCTRX.js +2 -2
- package/www/dist/frontend/bundle/chunk-65A3PNMG.js +1 -1
- package/www/dist/frontend/bundle/chunk-7CWDQGAG.js +2 -2
- package/www/dist/frontend/bundle/chunk-7PT7IGUJ.js +3 -3
- package/www/dist/frontend/bundle/chunk-AI5PDLDE.js +2 -2
- package/www/dist/frontend/bundle/chunk-AWKVUUKN.js +3 -3
- package/www/dist/frontend/bundle/chunk-B6NXWGSB.js +1 -1
- package/www/dist/frontend/bundle/chunk-BHSHNRXW.js +1 -1
- package/www/dist/frontend/bundle/chunk-BVONDEOL.js +3 -3
- package/www/dist/frontend/bundle/chunk-C7OGVQZC.js +1 -0
- package/www/dist/frontend/bundle/chunk-CHDKROX5.js +3 -3
- package/www/dist/frontend/bundle/chunk-DNCD2F6X.js +3 -3
- package/www/dist/frontend/bundle/chunk-FHF6DL5U.js +1 -1
- package/www/dist/frontend/bundle/chunk-FO56TEZH.js +3 -3
- package/www/dist/frontend/bundle/chunk-FSRRPQMX.js +3 -3
- package/www/dist/frontend/bundle/chunk-GLQNGCFL.js +1 -1
- package/www/dist/frontend/bundle/chunk-JBDSDK2C.js +2 -2
- package/www/dist/frontend/bundle/chunk-JSC66ZLQ.js +1 -1
- package/www/dist/frontend/bundle/chunk-K6IV7LKW.js +1 -1
- package/www/dist/frontend/bundle/chunk-KY73TL6O.js +2 -2
- package/www/dist/frontend/bundle/chunk-M6VVTDVX.js +3 -3
- package/www/dist/frontend/bundle/chunk-MECWXZRN.js +1 -1
- package/www/dist/frontend/bundle/chunk-N2YIZTB2.js +1 -1
- package/www/dist/frontend/bundle/chunk-NQ7L7KNT.js +2 -2
- package/www/dist/frontend/bundle/chunk-P7EOR36H.js +1 -1
- package/www/dist/frontend/bundle/chunk-PJYK24KZ.js +1 -1
- package/www/dist/frontend/bundle/chunk-PYLAW6UA.js +3 -3
- package/www/dist/frontend/bundle/chunk-QJJGP23I.js +1 -1
- package/www/dist/frontend/bundle/chunk-QV4IBWXA.js +1 -1
- package/www/dist/frontend/bundle/chunk-R6IQDPAP.js +3 -3
- package/www/dist/frontend/bundle/chunk-RTTBZOQU.js +3 -3
- package/www/dist/frontend/bundle/chunk-S4TLNW5D.js +1 -1
- package/www/dist/frontend/bundle/chunk-SE6N4ZEN.js +1 -1
- package/www/dist/frontend/bundle/chunk-SQHXO2IT.js +3 -3
- package/www/dist/frontend/bundle/chunk-T4AIQBBH.js +2 -2
- package/www/dist/frontend/bundle/chunk-TWQQHCU7.js +21 -0
- package/www/dist/frontend/bundle/chunk-U5Q332OH.js +1 -1
- package/www/dist/frontend/bundle/chunk-UIP324PP.js +1 -1
- package/www/dist/frontend/bundle/chunk-V27IRYSL.js +1 -1
- package/www/dist/frontend/bundle/chunk-V4C5FGJL.js +3 -3
- package/www/dist/frontend/bundle/chunk-WED7CGWA.js +3 -3
- package/www/dist/frontend/bundle/chunk-WQ5H5LTG.js +2 -2
- package/www/dist/frontend/bundle/chunk-WU54NCEU.js +1 -1
- package/www/dist/frontend/bundle/chunk-XFO5FUCN.js +1 -1
- package/www/dist/frontend/bundle/chunk-XWAQ2BWM.js +3 -3
- package/www/dist/frontend/bundle/chunk-YTQCHNRQ.js +2 -2
- package/www/dist/frontend/bundle/chunk-YUSK4FN3.js +3 -3
- package/www/dist/frontend/bundle/chunk-Z34BSG5O.js +3 -3
- package/www/dist/frontend/bundle/common/IobrokerHandler.js +1 -1
- package/www/dist/frontend/bundle/runtime/CustomControls.js +1 -1
- package/www/dist/frontend/bundle/runtime/ScreenViewer.js +1 -1
- package/www/dist/frontend/bundle/runtime/controls.js +4 -4
- package/www/dist/frontend/bundle/runtime/init.js +3 -3
- package/www/dist/frontend/common/Common.globals.js +1 -1
- package/www/dist/frontend/common/IobrokerHandler.js +1 -1
- package/www/dist/frontend/common/Runtime.js +1 -1
- package/www/dist/frontend/config/CommandHandling.js +1 -1
- package/www/dist/frontend/config/ConfigureWebcomponentDesigner.js +1 -1
- package/www/dist/frontend/config/DockHelper.js +1 -1
- package/www/dist/frontend/config/IobrokerWebui3DDriveEngine.js +1 -1
- package/www/dist/frontend/config/IobrokerWebui3DScreenEditor.js +3 -3
- package/www/dist/frontend/config/IobrokerWebui3DScreenPropertiesPanel.js +3 -3
- package/www/dist/frontend/config/IobrokerWebui3DScreenViewer.js +2 -2
- package/www/dist/frontend/config/IobrokerWebuiAppShell.js +2 -2
- package/www/dist/frontend/config/IobrokerWebuiBindingsEditor.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiConfirmationWrapper.js +2 -2
- package/www/dist/frontend/config/IobrokerWebuiControlPropertiesEditor.js +3 -3
- package/www/dist/frontend/config/IobrokerWebuiDynamicPropsEditor.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiEventAssignment.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiIconsView.js +3 -3
- package/www/dist/frontend/config/IobrokerWebuiMonacoEditor.js +3 -3
- package/www/dist/frontend/config/IobrokerWebuiPropertyGrid.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiScreenEditor.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiScreensView.js +3 -3
- package/www/dist/frontend/config/IobrokerWebuiSignalPropertyEditor.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiSolutionExplorer.js +3 -3
- package/www/dist/frontend/config/IobrokerWebuiTranslationEditor.js +1 -1
- package/www/dist/frontend/config/IobrokerWebuiWidgetGallery.js +3 -3
- package/www/dist/frontend/helper/DialogHelper.js +3 -3
- package/www/dist/frontend/helper/EsprimaHelper.js +1 -1
- package/www/dist/frontend/helper/Helper.js +1 -1
- package/www/dist/frontend/helper/LayoutHelper.js +1 -1
- package/www/dist/frontend/helper/XmlHelper.js +1 -1
- package/www/dist/frontend/helper/ZipHelper.js +1 -1
- package/www/dist/frontend/runtime/AnimationService.js +1 -1
- package/www/dist/frontend/runtime/CustomControls.js +1 -1
- package/www/dist/frontend/runtime/DynamicElementProperties.js +1 -1
- package/www/dist/frontend/runtime/HabPanelLikeMenu.js +3 -3
- package/www/dist/frontend/runtime/NavigationShell.js +1 -1
- package/www/dist/frontend/runtime/RuntimeTheme.js +1 -1
- package/www/dist/frontend/runtime/ScreenViewer.js +3 -3
- package/www/dist/frontend/runtime/TranslateableText.js +2 -2
- package/www/dist/frontend/runtime/VisibilityService.js +1 -1
- package/www/dist/frontend/runtime/controls.js +1 -1
- package/www/dist/frontend/runtime/init.js +1 -1
- package/www/dist/frontend/scripting/IobrokerWebuiScriptSystem.js +1 -1
- package/www/dist/frontend/scripting/blockly/OpenScreen.js +1 -1
- package/www/dist/frontend/scripting/blockly/webuiBlocklyToolbox.js +1 -1
- package/www/dist/frontend/services/DynamicPropertiesHelper.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiBindableLocalObjectsService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiBindableObjectDragDropService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiBindableObjectsForPropertiesService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiBindableObjectsService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiConfigButtonProvider.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiCopyPasteService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiCustomControlEventsService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiCustomElementContextMenu.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiDemoProviderService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiDynamicPropertiesService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiEventsService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiExternalDragDropService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiLitPropertiesService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiPropertiesService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiPropertyGroupsService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiRefactorService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiScreenContextMenu.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiSpecialPropertiesService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiSpectrumEditorService.js +1 -1
- package/www/dist/frontend/services/IobrokerWebuiVisibilityPropertiesService.js +1 -1
- package/www/dist/frontend/services/LayoutComponentsPropertiesService.js +1 -1
- package/www/dist/frontend/widgets/OrthoFlowLineTool.js +1 -1
- package/www/dist/frontend/widgets/customElementsObserver.js +1 -1
- package/www/dist/frontend/widgets/importWidgetFiles.js +1 -1
- package/www/dist/frontend/widgets/importWidgetFilesRuntime.js +1 -1
- package/www/dist/frontend/widgets/layout-components.js +1 -1
- package/www/dist/frontend/widgets/layout-placement-services.js +1 -1
- package/www/dist/frontend/widgets/testElement.js +1 -1
|
@@ -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: 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
|
+
{"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);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);\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: 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
|
+
{"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 -webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);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": "<div id=\"panel-container\" style=\"width:100%;height:100%;border:2px solid var(--ui-border,rgb(58, 90, 90));border-radius:4px;padding:8px;display:flex;flex-direction:column;font:"Times New Roman", serif;background:var(--ui-surface,rgb(90, 122, 122));\">\n <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;flex-shrink:0;\">\n <div id=\"point-name\" style=\"color:var(--ui-text,#fff);text-align:center;flex:1;font:bold 14px "Times New Roman", serif;\">Point</div>\n <svg id=\"trend-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" title=\"Show Trend\" style=\"cursor:pointer;fill:none;stroke:#fff;stroke-width:2;margin-left:4px;flex-shrink:0;\">\n <polyline points=\"3 17 9 11 13 15 21 7\"></polyline>\n <polyline points=\"14 7 21 7 21 14\"></polyline>\n </svg>\n </div>\n <div style=\"display:grid;grid-template-columns:auto 1fr auto;gap:4px 8px;align-items:center;flex:1;\">\n <div id=\"label1\" style=\"color:var(--ui-text,#e0e0e0);font:italic 12px "Times New Roman", serif;\"></div>\n <div id=\"value1\" bind-content:text=\"??value1_value\" style=\"color:var(--ui-text,#fff);text-align:center;font:bold 16px "Times New Roman", serif;\">NaN</div>\n <div id=\"unit1\" style=\"color:var(--ui-text,#e0e0e0);font:italic 11px "Times New Roman", serif;\"></div>\n <div id=\"label2\" style=\"color:var(--ui-text,#e0e0e0);display:none;font:italic 12px "Times New Roman", serif;\"></div>\n <div id=\"value2\" style=\"color:var(--ui-text,#fff);text-align:center;display:none;font:bold 16px "Times New Roman", serif;\"></div>\n <div id=\"unit2\" style=\"color:var(--ui-text,#e0e0e0);display:none;font:italic 11px "Times New Roman", serif;\"></div>\n </div>\n <div id=\"action-button\" style=\"margin-top:8px;text-align:center;display:none;flex-shrink:0;\">\n<button id=\"action-btn\" style=\"border:1px solid var(--ui-border,rgb(58, 90, 90));color:var(--ui-text,#fff);border-radius:3px;cursor:pointer;background:rgb(74, 106, 106);font:11px "Times New Roman", serif;padding:4px 12px;\">Button</button>\n </div>\n</div>\n<dialog id=\"trend-dialog\" bind-css:width=\"??grafana_dialogwidth\" bind-css:height=\"??grafana_dialogheight\" style=\"overflow:hidden;border:2px solid var(--ui-border,rgb(58, 90, 90));border-radius:8px;padding:0px;background:var(--ui-input,rgb(255, 255, 255));\">\n <div id=\"dialog-header\" style=\"color:var(--ui-text,#fff);padding:12px;display:flex;justify-content:space-between;align-items:center;background:var(--ui-surface,rgb(90, 122, 122));\">\n <h3 id=\"dialog-title\" style=\"margin:0px;font:16px "Times New Roman", serif;\">Trend</h3>\n <button id=\"close-dialog\" style=\"border:initial none initial;color:var(--ui-text,#fff);cursor:pointer;padding:0px;width:30px;height:30px;background:transparent;font:24px "Times New Roman", serif;\">×</button>\n </div>\n <div id=\"iframecontainerdiv1\" style=\"padding:0px;width:100%;height:91%;\">\n<iframe id=\"grafana-iframe\" frameborder=\"0\" style=\"width:100%;height:100%;border:initial none initial;\"></iframe>\n </div>\n</dialog>\n", "style": "#trend-dialog{\r\n width: 600px;\r\n height: 600px;\r\n}\r\n", "script": "let protocol = window.location.protocol;\nlet ip = window.location.hostname;\nlet port = window.location.port;\nlet baseaddress = protocol+\"//\"+ip+\":\"+port;\nfunction formatValue(value, options) {\n if (value === null || value === undefined || value === '' || isNaN(value)) {\n return 'NaN';\n }\n\n let numValue = Number(value);\n\n // Apply scaling\n if (options.scale && options.scale !== 1) {\n numValue = numValue * Number(options.scale);\n }\n\n // Apply offset\n if (options.offset && options.offset !== 0) {\n numValue = numValue + Number(options.offset);\n }\n\n // Apply min/max limits\n if (options.minLimit !== undefined && options.minLimit !== null && options.minLimit !== '') {\n const min = Number(options.minLimit);\n if (numValue < min) numValue = min;\n }\n if (options.maxLimit !== undefined && options.maxLimit !== null && options.maxLimit !== '') {\n const max = Number(options.maxLimit);\n if (numValue > max) numValue = max;\n }\n\n // Apply rounding\n let formattedValue;\n const decimals = options.decimals !== undefined ? Number(options.decimals) : 2;\n\n switch (options.roundMode) {\n case 'floor':\n formattedValue = Math.floor(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'ceil':\n formattedValue = Math.ceil(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'round':\n default:\n formattedValue = Math.round(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n }\n\n // Format to fixed decimals\n let result = formattedValue.toFixed(decimals);\n\n // Add thousand separator if enabled\n if (options.thousandSep) {\n const parts = result.split('.');\n parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, options.thousandSep);\n result = parts.join('.');\n }\n\n // Add prefix/suffix\n if (options.prefix) result = options.prefix + result;\n if (options.suffix) result = result + options.suffix;\n\n return result;\n}\nexport async function init(instance) {\n instance._bindingsRefresh();\n\n applyTheming(instance);\n updatePanel(instance);\n\n instance._assignEvent('point-name-changed', () => updatePanel(instance));\n instance._assignEvent('value1_label-changed', () => updatePanel(instance));\n instance._assignEvent('value1_value-changed', () => updatePanel(instance));\n instance._assignEvent('value1_unit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_label-changed', () => updatePanel(instance));\n instance._assignEvent('value2_value-changed', () => updatePanel(instance));\n instance._assignEvent('value2_unit-changed', () => updatePanel(instance));\n instance._assignEvent('theme-changed', () => applyTheming(instance));\n instance._assignEvent('grafana_url-changed', () => { });\n\n instance._assignEvent('show-button-changed', () => updatePanel(instance));\n instance._assignEvent('button-text-changed', () => updatePanel(instance));\n\n // SCADA functions event listeners\n instance._assignEvent('value1_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value1_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value1_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value1_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value1_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_thousandsep-changed', () => updatePanel(instance));\n instance._assignEvent('value2_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value2_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value2_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value2_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value2_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_thousandsep-changed', () => updatePanel(instance));\n\n const trendIcon = instance._getDomElement('trend-icon');\n const dialog = instance._getDomElement('trend-dialog');\n const closeBtn = instance._getDomElement('close-dialog');\n const iframe = instance._getDomElement('grafana-iframe');\n const dialogTitle = instance._getDomElement('dialog-title');\n\n /*if (!instance.grafanaUrl || instance.grafanaUrl.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n // URL boş deyilsə, parametrləri əlavə et\n const params = new URLSearchParams({\n \"var-selected_state\": instance.grafana_selected_state_name1,\n \"var-selected_state2\": instance.grafana_selected_state_name2,\n \"var-tag_name\": instance.pointName,\n kiosk: \"\" // kiosk parametri əlavə olunur\n });\n\n\n\n const finalUrl = `${instance.grafanaUrl}?${params.toString()}`;\n // instance.grafanaUrl = finalUrl;\n }*/\n\n\n\n\n\n if (trendIcon && dialog) {\n trendIcon.addEventListener('click', async() => {\n if (!instance.grafana_url || instance.grafana_url.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n // URL obyektini qururuq\n const url = new URL(instance.grafana_url, baseaddress); // baza hostu əlavə et\n\n // Mövcud parametrləri götürürük\n const params = url.searchParams;\n\n // İstədiyin parametrlərə dəyər ver\n params.set(\"var-selected_state\", instance.grafana_selected_state_name1);\n params.set(\"var-selected_state2\", instance.grafana_selected_state_name2);\n params.set(\"var-tag_name\", instance.pointName);\n\n\n // Yenilənmiş URL\n const finalUrl = `${url.href}&kiosk`;\n //console.log(finalUrl);\n\n //const grafanaUrl = instance.grafanaUrl || 'about:blank';\n // ioBroker-dən cari useri al\n iframe.src = finalUrl;\n dialogTitle.textContent = (instance.pointName || 'Trend') + ' - Trend';\n dialog.showModal();\n \n\n /*const pointName = instance.pointName || 'Trend';\n iframe.src = finalUrl;\n dialogTitle.textContent = pointName + ' - Trend';\n dialog.showModal();*/\n }\n });\n }\n\n if (closeBtn && dialog) {\n closeBtn.addEventListener('click', () => {\n dialog.close();\n iframe.src = 'about:blank';\n });\n }\n\n if (dialog) {\n dialog.addEventListener('click', (e) => {\n const rect = dialog.getBoundingClientRect();\n if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {\n dialog.close();\n iframe.src = 'about:blank';\n }\n });\n }\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.addEventListener('click', () => {\n const event = new CustomEvent('scada-point-button-clicked', {\n detail: { pointName: instance.pointName },\n bubbles: true\n });\n instance.dispatchEvent(event);\n });\n }\n}\n\nfunction updatePanel(instance) {\n const nameEl = instance._getDomElement('point-name');\n if (nameEl) nameEl.textContent = instance.pointName || 'Point';\n\n const label1El = instance._getDomElement('label1');\n const value1El = instance._getDomElement('value1');\n const unit1El = instance._getDomElement('unit1');\n if (label1El) label1El.textContent = instance.value1_label || '';\n\n // Format value1 with SCADA functions\n if (value1El) {\n const value1Options = {\n decimals: instance.value1_decimals,\n roundMode: instance.value1_roundmode,\n scale: instance.value1_scale,\n offset: instance.value1_offset,\n minLimit: instance.value1_minlimit,\n maxLimit: instance.value1_maxlimit,\n prefix: instance.value1_prefix,\n suffix: instance.value1_suffix,\n thousandSep: instance.value1_thousandsep\n };\n value1El.textContent = formatValue(instance.value1_value, value1Options);\n }\n\n if (unit1El) unit1El.textContent = instance.value1_unit || '';\n\n const label2El = instance._getDomElement('label2');\n const value2El = instance._getDomElement('value2');\n const unit2El = instance._getDomElement('unit2');\n const hasRow2 = instance.value2_label || instance.value2_value || instance.value2_unit;\n\n if (label2El) {\n label2El.textContent = instance.value2_label || '';\n label2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (value2El) {\n const value2Options = {\n decimals: instance.value2_decimals,\n roundMode: instance.value2_roundmode,\n scale: instance.value2_scale,\n offset: instance.value2_offset,\n minLimit: instance.value2_minlimit,\n maxLimit: instance.value2_maxlimit,\n prefix: instance.value2_prefix,\n suffix: instance.value2_suffix,\n thousandSep: instance.value2_thousandsep\n };\n value2El.textContent = formatValue(instance.value2_value, value2Options);\n value2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (unit2El) {\n unit2El.textContent = instance.value2_unit || '';\n unit2El.style.display = hasRow2 ? 'block' : 'none';\n }\n\n const buttonEl = instance._getDomElement('action-button');\n const buttonTextEl = instance._getDomElement('action-btn');\n if (buttonEl && buttonTextEl) {\n const showButton = instance.showButton === true || instance.showButton === 'true';\n buttonEl.style.display = showButton ? 'block' : 'none';\n buttonTextEl.textContent = instance.buttonText || 'Button';\n }\n}\n\nfunction applyTheming(instance) {\n const theme = instance.theme || 'default';\n const container = instance._getDomElement('panel-container');\n if (!container) return;\n\n const themes = {\n 'default': { bg: 'var(--ui-surface,#5a7a7a)', border: 'var(--ui-border,#3a5a5a)', text: 'var(--ui-text,#fff)', label: 'var(--ui-dim,#e0e0e0)', btnBg: 'var(--ui-surface-2,#4a6a6a)' },\n 'dark': { bg: '#2d3436', border: '#000', text: '#fff', label: '#dfe6e9', btnBg: '#636e72' },\n 'light': { bg: '#dfe6e9', border: '#b2bec3', text: '#2d3436', label: '#636e72', btnBg: '#b2bec3' },\n 'blue': { bg: '#0984e3', border: '#0652DD', text: '#fff', label: '#dfe6e9', btnBg: '#74b9ff' },\n 'green': { bg: '#00b894', border: '#00a383', text: '#fff', label: '#d4f1e8', btnBg: '#55efc4' },\n 'red': { bg: '#d63031', border: '#b71c1c', text: '#fff', label: '#ffcccc', btnBg: '#ff7675' },\n 'purple': { bg: '#6c5ce7', border: '#5f3dc4', text: '#fff', label: '#e5dbff', btnBg: '#a29bfe' }\n };\n\n const colors = themes[theme] || themes['default'];\n container.style.background = colors.bg;\n container.style.borderColor = colors.border;\n\n const nameEl = instance._getDomElement('point-name');\n const value1El = instance._getDomElement('value1');\n const value2El = instance._getDomElement('value2');\n if (nameEl) nameEl.style.color = colors.text;\n if (value1El) value1El.style.color = colors.text;\n if (value2El) value2El.style.color = colors.text;\n\n const label1El = instance._getDomElement('label1');\n const label2El = instance._getDomElement('label2');\n const unit1El = instance._getDomElement('unit1');\n const unit2El = instance._getDomElement('unit2');\n if (label1El) label1El.style.color = colors.label;\n if (label2El) label2El.style.color = colors.label;\n if (unit1El) unit1El.style.color = colors.label;\n if (unit2El) unit2El.style.color = colors.label;\n\n const trendIcon = instance._getDomElement('trend-icon');\n if (trendIcon) trendIcon.style.stroke = colors.text;\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.style.background = colors.btnBg;\n actionBtn.style.borderColor = colors.border;\n actionBtn.style.color = colors.text;\n }\n\n const dialog = instance._getDomElement('trend-dialog');\n if (dialog) {\n dialog.style.borderColor = colors.border;\n const header = instance._getDomElement('dialog-header');\n if (header) {\n header.style.background = colors.bg;\n header.style.color = colors.text;\n }\n }\n}\n\nexport function connectedCallback(instance)\n{\n \n}\n\nfunction getResolvedSignalId() {\n // bind-prop:value1 atributundan ham pattern-i oxu\n const raw = element.getAttribute('bind-prop:value1');\n if (!raw) return null;\n\n // {??propName} → screen[propName] ilə replace et\n return raw.replace(/\\{(\\?\\?([^}]+))\\}/g, (_, __, propName) => {\n return screen?.[propName] ?? '';\n });\n }", "properties": {"pointName": {"type": "string", "default": "DPT 010"}, "value1_value": {"type": "number"}, "value1_label": {"type": "string"}, "value1_unit": {"type": "string", "default": "kPa"}, "value2_label": {"type": "string"}, "value2_value": {"type": "number"}, "theme": {"type": "enum", "default": "default", "values": ["default", "dark", "light", "blue", "green", "red", "purple"]}, "value2_unit": {"type": "string"}, "grafana_url": {"type": "string"}, "grafana_selected_state_name1": {"type": "string"}, "grafana_selected_state_name2": {"type": "string"}, "showButton": {"type": "boolean"}, "buttonText": {"type": "string", "default": "Button"}, "grafana_dialogheight": {"type": "string"}, "grafana_dialogwidth": {"type": "string"}, "value1_decimals": {"type": "number", "default": "2"}, "value1_roundmode": {"type": "enum", "default": "round", "values": ["round", "floor", "ceil"]}, "value1_scale": {"type": "number", "default": "1"}, "value1_offset": {"type": "number"}, "value1_maxlimit": {"type": "number"}, "value1_minlimit": {"type": "number"}, "value1_prefix": {"type": "string"}, "value1_suffix": {"type": "string"}, "value1_thousandsep": {"type": "string"}, "value2_decimals": {"type": "number", "default": "2"}, "value2_roundmode": {"type": "enum", "default": "round", "values": ["round", "floor", "ceil"]}, "value2_scale": {"type": "number", "default": "1"}, "value2_offset": {"type": "number"}, "value2_minlimit": {"type": "number"}, "value2_maxlimit": {"type": "number"}, "value2_prefix": {"type": "string"}, "value2_suffix": {"type": "string"}, "value2_thousandsep": {"type": "string"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
|
|
1
|
+
{"html": "<div id=\"panel-container\" style=\"width:100%;height:100%;border:2px solid var(--ui-border,rgb(58, 90, 90));border-radius:4px;padding:8px;display:flex;flex-direction:column;font:"Times New Roman", serif;-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);background:var(--ui-surface,rgb(90, 122, 122));\">\n <div style=\"display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;flex-shrink:0;\">\n <div id=\"point-name\" style=\"color:var(--ui-text,#fff);text-align:center;flex:1;font:bold 14px "Times New Roman", serif;\">Point</div>\n <svg id=\"trend-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" title=\"Show Trend\" style=\"cursor:pointer;fill:none;stroke:#fff;stroke-width:2;margin-left:4px;flex-shrink:0;\">\n <polyline points=\"3 17 9 11 13 15 21 7\"></polyline>\n <polyline points=\"14 7 21 7 21 14\"></polyline>\n </svg>\n </div>\n <div style=\"display:grid;grid-template-columns:auto 1fr auto;gap:4px 8px;align-items:center;flex:1;\">\n <div id=\"label1\" style=\"color:var(--ui-text,#e0e0e0);font:italic 12px "Times New Roman", serif;\"></div>\n <div id=\"value1\" bind-content:text=\"??value1_value\" style=\"color:var(--ui-text,#fff);text-align:center;font:bold 16px "Times New Roman", serif;\">NaN</div>\n <div id=\"unit1\" style=\"color:var(--ui-text,#e0e0e0);font:italic 11px "Times New Roman", serif;\"></div>\n <div id=\"label2\" style=\"color:var(--ui-text,#e0e0e0);display:none;font:italic 12px "Times New Roman", serif;\"></div>\n <div id=\"value2\" style=\"color:var(--ui-text,#fff);text-align:center;display:none;font:bold 16px "Times New Roman", serif;\"></div>\n <div id=\"unit2\" style=\"color:var(--ui-text,#e0e0e0);display:none;font:italic 11px "Times New Roman", serif;\"></div>\n </div>\n <div id=\"action-button\" style=\"margin-top:8px;text-align:center;display:none;flex-shrink:0;\">\n<button id=\"action-btn\" style=\"border:1px solid var(--ui-border,rgb(58, 90, 90));color:var(--ui-text,#fff);border-radius:3px;cursor:pointer;background:rgb(74, 106, 106);font:11px "Times New Roman", serif;padding:4px 12px;\">Button</button>\n </div>\n</div>\n<dialog id=\"trend-dialog\" bind-css:width=\"??grafana_dialogwidth\" bind-css:height=\"??grafana_dialogheight\" style=\"overflow:hidden;border:2px solid var(--ui-border,rgb(58, 90, 90));border-radius:8px;padding:0px;background:var(--ui-input,rgb(255, 255, 255));\">\n <div id=\"dialog-header\" style=\"color:var(--ui-text,#fff);padding:12px;display:flex;justify-content:space-between;align-items:center;background:var(--ui-surface,rgb(90, 122, 122));\">\n <h3 id=\"dialog-title\" style=\"margin:0px;font:16px "Times New Roman", serif;\">Trend</h3>\n <button id=\"close-dialog\" style=\"border:initial none initial;color:var(--ui-text,#fff);cursor:pointer;padding:0px;width:30px;height:30px;background:transparent;font:24px "Times New Roman", serif;\">×</button>\n </div>\n <div id=\"iframecontainerdiv1\" style=\"padding:0px;width:100%;height:91%;\">\n<iframe id=\"grafana-iframe\" frameborder=\"0\" style=\"width:100%;height:100%;border:initial none initial;\"></iframe>\n </div>\n</dialog>\n", "style": "#trend-dialog{\r\n width: 600px;\r\n height: 600px;\r\n}\r\n", "script": "let protocol = window.location.protocol;\nlet ip = window.location.hostname;\nlet port = window.location.port;\nlet baseaddress = protocol+\"//\"+ip+\":\"+port;\nfunction formatValue(value, options) {\n if (value === null || value === undefined || value === '' || isNaN(value)) {\n return 'NaN';\n }\n\n let numValue = Number(value);\n\n // Apply scaling\n if (options.scale && options.scale !== 1) {\n numValue = numValue * Number(options.scale);\n }\n\n // Apply offset\n if (options.offset && options.offset !== 0) {\n numValue = numValue + Number(options.offset);\n }\n\n // Apply min/max limits\n if (options.minLimit !== undefined && options.minLimit !== null && options.minLimit !== '') {\n const min = Number(options.minLimit);\n if (numValue < min) numValue = min;\n }\n if (options.maxLimit !== undefined && options.maxLimit !== null && options.maxLimit !== '') {\n const max = Number(options.maxLimit);\n if (numValue > max) numValue = max;\n }\n\n // Apply rounding\n let formattedValue;\n const decimals = options.decimals !== undefined ? Number(options.decimals) : 2;\n\n switch (options.roundMode) {\n case 'floor':\n formattedValue = Math.floor(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'ceil':\n formattedValue = Math.ceil(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n case 'round':\n default:\n formattedValue = Math.round(numValue * Math.pow(10, decimals)) / Math.pow(10, decimals);\n break;\n }\n\n // Format to fixed decimals\n let result = formattedValue.toFixed(decimals);\n\n // Add thousand separator if enabled\n if (options.thousandSep) {\n const parts = result.split('.');\n parts[0] = parts[0].replace(/\\B(?=(\\d{3})+(?!\\d))/g, options.thousandSep);\n result = parts.join('.');\n }\n\n // Add prefix/suffix\n if (options.prefix) result = options.prefix + result;\n if (options.suffix) result = result + options.suffix;\n\n return result;\n}\nexport async function init(instance) {\n instance._bindingsRefresh();\n\n applyTheming(instance);\n updatePanel(instance);\n\n instance._assignEvent('point-name-changed', () => updatePanel(instance));\n instance._assignEvent('value1_label-changed', () => updatePanel(instance));\n instance._assignEvent('value1_value-changed', () => updatePanel(instance));\n instance._assignEvent('value1_unit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_label-changed', () => updatePanel(instance));\n instance._assignEvent('value2_value-changed', () => updatePanel(instance));\n instance._assignEvent('value2_unit-changed', () => updatePanel(instance));\n instance._assignEvent('theme-changed', () => applyTheming(instance));\n instance._assignEvent('grafana_url-changed', () => { });\n\n instance._assignEvent('show-button-changed', () => updatePanel(instance));\n instance._assignEvent('button-text-changed', () => updatePanel(instance));\n\n // SCADA functions event listeners\n instance._assignEvent('value1_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value1_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value1_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value1_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value1_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value1_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value1_thousandsep-changed', () => updatePanel(instance));\n instance._assignEvent('value2_decimals-changed', () => updatePanel(instance));\n instance._assignEvent('value2_roundmode-changed', () => updatePanel(instance));\n instance._assignEvent('value2_scale-changed', () => updatePanel(instance));\n instance._assignEvent('value2_offset-changed', () => updatePanel(instance));\n instance._assignEvent('value2_minlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_maxlimit-changed', () => updatePanel(instance));\n instance._assignEvent('value2_prefix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_suffix-changed', () => updatePanel(instance));\n instance._assignEvent('value2_thousandsep-changed', () => updatePanel(instance));\n\n const trendIcon = instance._getDomElement('trend-icon');\n const dialog = instance._getDomElement('trend-dialog');\n const closeBtn = instance._getDomElement('close-dialog');\n const iframe = instance._getDomElement('grafana-iframe');\n const dialogTitle = instance._getDomElement('dialog-title');\n\n /*if (!instance.grafanaUrl || instance.grafanaUrl.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n // URL boş deyilsə, parametrləri əlavə et\n const params = new URLSearchParams({\n \"var-selected_state\": instance.grafana_selected_state_name1,\n \"var-selected_state2\": instance.grafana_selected_state_name2,\n \"var-tag_name\": instance.pointName,\n kiosk: \"\" // kiosk parametri əlavə olunur\n });\n\n\n\n const finalUrl = `${instance.grafanaUrl}?${params.toString()}`;\n // instance.grafanaUrl = finalUrl;\n }*/\n\n\n\n\n\n if (trendIcon && dialog) {\n trendIcon.addEventListener('click', async() => {\n if (!instance.grafana_url || instance.grafana_url.trim() === \"\") {\n console.log(\"Grafana URL boşdur!\");\n } else {\n // URL obyektini qururuq\n const url = new URL(instance.grafana_url, baseaddress); // baza hostu əlavə et\n\n // Mövcud parametrləri götürürük\n const params = url.searchParams;\n\n // İstədiyin parametrlərə dəyər ver\n params.set(\"var-selected_state\", instance.grafana_selected_state_name1);\n params.set(\"var-selected_state2\", instance.grafana_selected_state_name2);\n params.set(\"var-tag_name\", instance.pointName);\n\n\n // Yenilənmiş URL\n const finalUrl = `${url.href}&kiosk`;\n //console.log(finalUrl);\n\n //const grafanaUrl = instance.grafanaUrl || 'about:blank';\n // ioBroker-dən cari useri al\n iframe.src = finalUrl;\n dialogTitle.textContent = (instance.pointName || 'Trend') + ' - Trend';\n dialog.showModal();\n \n\n /*const pointName = instance.pointName || 'Trend';\n iframe.src = finalUrl;\n dialogTitle.textContent = pointName + ' - Trend';\n dialog.showModal();*/\n }\n });\n }\n\n if (closeBtn && dialog) {\n closeBtn.addEventListener('click', () => {\n dialog.close();\n iframe.src = 'about:blank';\n });\n }\n\n if (dialog) {\n dialog.addEventListener('click', (e) => {\n const rect = dialog.getBoundingClientRect();\n if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {\n dialog.close();\n iframe.src = 'about:blank';\n }\n });\n }\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.addEventListener('click', () => {\n const event = new CustomEvent('scada-point-button-clicked', {\n detail: { pointName: instance.pointName },\n bubbles: true\n });\n instance.dispatchEvent(event);\n });\n }\n}\n\nfunction updatePanel(instance) {\n const nameEl = instance._getDomElement('point-name');\n if (nameEl) nameEl.textContent = instance.pointName || 'Point';\n\n const label1El = instance._getDomElement('label1');\n const value1El = instance._getDomElement('value1');\n const unit1El = instance._getDomElement('unit1');\n if (label1El) label1El.textContent = instance.value1_label || '';\n\n // Format value1 with SCADA functions\n if (value1El) {\n const value1Options = {\n decimals: instance.value1_decimals,\n roundMode: instance.value1_roundmode,\n scale: instance.value1_scale,\n offset: instance.value1_offset,\n minLimit: instance.value1_minlimit,\n maxLimit: instance.value1_maxlimit,\n prefix: instance.value1_prefix,\n suffix: instance.value1_suffix,\n thousandSep: instance.value1_thousandsep\n };\n value1El.textContent = formatValue(instance.value1_value, value1Options);\n }\n\n if (unit1El) unit1El.textContent = instance.value1_unit || '';\n\n const label2El = instance._getDomElement('label2');\n const value2El = instance._getDomElement('value2');\n const unit2El = instance._getDomElement('unit2');\n const hasRow2 = instance.value2_label || instance.value2_value || instance.value2_unit;\n\n if (label2El) {\n label2El.textContent = instance.value2_label || '';\n label2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (value2El) {\n const value2Options = {\n decimals: instance.value2_decimals,\n roundMode: instance.value2_roundmode,\n scale: instance.value2_scale,\n offset: instance.value2_offset,\n minLimit: instance.value2_minlimit,\n maxLimit: instance.value2_maxlimit,\n prefix: instance.value2_prefix,\n suffix: instance.value2_suffix,\n thousandSep: instance.value2_thousandsep\n };\n value2El.textContent = formatValue(instance.value2_value, value2Options);\n value2El.style.display = hasRow2 ? 'block' : 'none';\n }\n if (unit2El) {\n unit2El.textContent = instance.value2_unit || '';\n unit2El.style.display = hasRow2 ? 'block' : 'none';\n }\n\n const buttonEl = instance._getDomElement('action-button');\n const buttonTextEl = instance._getDomElement('action-btn');\n if (buttonEl && buttonTextEl) {\n const showButton = instance.showButton === true || instance.showButton === 'true';\n buttonEl.style.display = showButton ? 'block' : 'none';\n buttonTextEl.textContent = instance.buttonText || 'Button';\n }\n}\n\nfunction applyTheming(instance) {\n const theme = instance.theme || 'default';\n const container = instance._getDomElement('panel-container');\n if (!container) return;\n\n const themes = {\n 'default': { bg: 'var(--ui-surface,#5a7a7a)', border: 'var(--ui-border,#3a5a5a)', text: 'var(--ui-text,#fff)', label: 'var(--ui-dim,#e0e0e0)', btnBg: 'var(--ui-surface-2,#4a6a6a)' },\n 'dark': { bg: '#2d3436', border: '#000', text: '#fff', label: '#dfe6e9', btnBg: '#636e72' },\n 'light': { bg: '#dfe6e9', border: '#b2bec3', text: '#2d3436', label: '#636e72', btnBg: '#b2bec3' },\n 'blue': { bg: '#0984e3', border: '#0652DD', text: '#fff', label: '#dfe6e9', btnBg: '#74b9ff' },\n 'green': { bg: '#00b894', border: '#00a383', text: '#fff', label: '#d4f1e8', btnBg: '#55efc4' },\n 'red': { bg: '#d63031', border: '#b71c1c', text: '#fff', label: '#ffcccc', btnBg: '#ff7675' },\n 'purple': { bg: '#6c5ce7', border: '#5f3dc4', text: '#fff', label: '#e5dbff', btnBg: '#a29bfe' }\n };\n\n const colors = themes[theme] || themes['default'];\n container.style.background = colors.bg;\n container.style.borderColor = colors.border;\n\n const nameEl = instance._getDomElement('point-name');\n const value1El = instance._getDomElement('value1');\n const value2El = instance._getDomElement('value2');\n if (nameEl) nameEl.style.color = colors.text;\n if (value1El) value1El.style.color = colors.text;\n if (value2El) value2El.style.color = colors.text;\n\n const label1El = instance._getDomElement('label1');\n const label2El = instance._getDomElement('label2');\n const unit1El = instance._getDomElement('unit1');\n const unit2El = instance._getDomElement('unit2');\n if (label1El) label1El.style.color = colors.label;\n if (label2El) label2El.style.color = colors.label;\n if (unit1El) unit1El.style.color = colors.label;\n if (unit2El) unit2El.style.color = colors.label;\n\n const trendIcon = instance._getDomElement('trend-icon');\n if (trendIcon) trendIcon.style.stroke = colors.text;\n\n const actionBtn = instance._getDomElement('action-btn');\n if (actionBtn) {\n actionBtn.style.background = colors.btnBg;\n actionBtn.style.borderColor = colors.border;\n actionBtn.style.color = colors.text;\n }\n\n const dialog = instance._getDomElement('trend-dialog');\n if (dialog) {\n dialog.style.borderColor = colors.border;\n const header = instance._getDomElement('dialog-header');\n if (header) {\n header.style.background = colors.bg;\n header.style.color = colors.text;\n }\n }\n}\n\nexport function connectedCallback(instance)\n{\n \n}\n\nfunction getResolvedSignalId() {\n // bind-prop:value1 atributundan ham pattern-i oxu\n const raw = element.getAttribute('bind-prop:value1');\n if (!raw) return null;\n\n // {??propName} → screen[propName] ilə replace et\n return raw.replace(/\\{(\\?\\?([^}]+))\\}/g, (_, __, propName) => {\n return screen?.[propName] ?? '';\n });\n }", "properties": {"pointName": {"type": "string", "default": "DPT 010"}, "value1_value": {"type": "number"}, "value1_label": {"type": "string"}, "value1_unit": {"type": "string", "default": "kPa"}, "value2_label": {"type": "string"}, "value2_value": {"type": "number"}, "theme": {"type": "enum", "default": "default", "values": ["default", "dark", "light", "blue", "green", "red", "purple"]}, "value2_unit": {"type": "string"}, "grafana_url": {"type": "string"}, "grafana_selected_state_name1": {"type": "string"}, "grafana_selected_state_name2": {"type": "string"}, "showButton": {"type": "boolean"}, "buttonText": {"type": "string", "default": "Button"}, "grafana_dialogheight": {"type": "string"}, "grafana_dialogwidth": {"type": "string"}, "value1_decimals": {"type": "number", "default": "2"}, "value1_roundmode": {"type": "enum", "default": "round", "values": ["round", "floor", "ceil"]}, "value1_scale": {"type": "number", "default": "1"}, "value1_offset": {"type": "number"}, "value1_maxlimit": {"type": "number"}, "value1_minlimit": {"type": "number"}, "value1_prefix": {"type": "string"}, "value1_suffix": {"type": "string"}, "value1_thousandsep": {"type": "string"}, "value2_decimals": {"type": "number", "default": "2"}, "value2_roundmode": {"type": "enum", "default": "round", "values": ["round", "floor", "ceil"]}, "value2_scale": {"type": "number", "default": "1"}, "value2_offset": {"type": "number"}, "value2_minlimit": {"type": "number"}, "value2_maxlimit": {"type": "number"}, "value2_prefix": {"type": "string"}, "value2_suffix": {"type": "string"}, "value2_thousandsep": {"type": "string"}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"properties": {"value": {"type": "number", "default": 26}, "setpoint": {"type": "number", "default": 62}, "min": {"type": "number", "default": 0}, "max": {"type": "number", "default": 100}, "bandmin": {"type": "number", "default": 15}, "bandmax": {"type": "number", "default": 80}, "title": {"type": "string", "default": "Outdoor"}, "decimals": {"type": "number", "default": 0}, "bandcolor": {"type": "color", "default": "#cfe2f3"}, "endcolor": {"type": "color", "default": "#5b6470"}, "valuecolor": {"type": "color", "default": "#2b57b3"}}, "settings": {"width": "360px", "height": "120px"}, "html": "<div id=\"wrap\" class=\"sg-wrap\"><div class=\"sg-title\" bind-content:text=\"??title\"></div><div class=\"sg-track\"><div class=\"sg-vptr\" bind-css:left=\"??value;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div><div class=\"sg-end sg-end-l\" bind-css:background-color=\"??endcolor\" bind-css:width=\"??bandmin;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div><div class=\"sg-band\" bind-css:background-color=\"??bandcolor\" bind-css:left=\"??bandmin;??min;??max;((__0-__1)/(__2-__1))*100+'%'\" bind-css:width=\"??bandmin;??bandmax;??min;??max;((__1-__0)/(__3-__2))*100+'%'\"></div><div class=\"sg-end sg-end-r\" bind-css:background-color=\"??endcolor\" bind-css:left=\"??bandmax;??min;??max;((__0-__1)/(__2-__1))*100+'%'\" bind-css:width=\"??bandmax;??min;??max;((__2-__0)/(__2-__1))*100+'%'\"></div><div class=\"sg-diamond\" bind-css:left=\"??setpoint;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div></div><div class=\"sg-foot\"><span class=\"sg-tick\" bind-content:text=\"??min\"></span><span class=\"sg-value\" bind-css:color=\"??valuecolor\" bind-content:text=\"??value;??decimals;Number(__0).toFixed(Number(__1))\"></span><span class=\"sg-tick\" bind-content:text=\"??max\"></span></div></div>", "style": ":host{display:block;width:100%;color:var(--ui-text,#e2e6ec);font-family:'Segoe UI',Roboto,sans-serif;box-sizing:border-box;}\n.sg-wrap{padding:20px 16px 8px;box-sizing:border-box;}\n.sg-title{text-align:center;font-size:18px;font-weight:500;margin-bottom:16px;}\n.sg-track{position:relative;height:16px;border:1px solid var(--ui-border,#3b4250);border-radius:3px;overflow:visible;background:var(--ui-input,#2a2f3a);}\n.sg-end{position:absolute;top:0;bottom:0;}\n.sg-end-l{left:0;border-radius:2px 0 0 2px;}\n.sg-band{position:absolute;top:0;bottom:0;}\n.sg-vptr{position:absolute;top:-13px;transform:translateX(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-top:11px solid var(--ui-text,#e2e6ec);}\n.sg-diamond{position:absolute;top:50%;width:11px;height:11px;transform:translate(-50%,-50%) rotate(45deg);background:var(--ui-input,#2a2f3a);border:2px solid var(--ui-dim,#9aa3b0);}\n.sg-foot{display:flex;align-items:center;justify-content:space-between;margin-top:6px;}\n.sg-tick{font-size:13px;opacity:.75;}\n.sg-value{font-size:24px;font-weight:700;}", "script": "export function init(instance){ try{ instance._bindingsRefresh && instance._bindingsRefresh(); }catch(e){} }\nexport function connectedCallback(instance){}\n"}
|
|
1
|
+
{"properties": {"value": {"type": "number", "default": 26}, "setpoint": {"type": "number", "default": 62}, "min": {"type": "number", "default": 0}, "max": {"type": "number", "default": 100}, "bandmin": {"type": "number", "default": 15}, "bandmax": {"type": "number", "default": 80}, "title": {"type": "string", "default": "Outdoor"}, "decimals": {"type": "number", "default": 0}, "bandcolor": {"type": "color", "default": "#cfe2f3"}, "endcolor": {"type": "color", "default": "#5b6470"}, "valuecolor": {"type": "color", "default": "#2b57b3"}}, "settings": {"width": "360px", "height": "120px"}, "html": "<div id=\"wrap\" class=\"sg-wrap\"><div class=\"sg-title\" bind-content:text=\"??title\"></div><div class=\"sg-track\"><div class=\"sg-vptr\" bind-css:left=\"??value;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div><div class=\"sg-end sg-end-l\" bind-css:background-color=\"??endcolor\" bind-css:width=\"??bandmin;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div><div class=\"sg-band\" bind-css:background-color=\"??bandcolor\" bind-css:left=\"??bandmin;??min;??max;((__0-__1)/(__2-__1))*100+'%'\" bind-css:width=\"??bandmin;??bandmax;??min;??max;((__1-__0)/(__3-__2))*100+'%'\"></div><div class=\"sg-end sg-end-r\" bind-css:background-color=\"??endcolor\" bind-css:left=\"??bandmax;??min;??max;((__0-__1)/(__2-__1))*100+'%'\" bind-css:width=\"??bandmax;??min;??max;((__2-__0)/(__2-__1))*100+'%'\"></div><div class=\"sg-diamond\" bind-css:left=\"??setpoint;??min;??max;((__0-__1)/(__2-__1))*100+'%'\"></div></div><div class=\"sg-foot\"><span class=\"sg-tick\" bind-content:text=\"??min\"></span><span class=\"sg-value\" bind-css:color=\"??valuecolor\" bind-content:text=\"??value;??decimals;Number(__0).toFixed(Number(__1))\"></span><span class=\"sg-tick\" bind-content:text=\"??max\"></span></div></div>", "style": ":host{display:block;width:100%;color:var(--ui-text,#e2e6ec);font-family:'Segoe UI',Roboto,sans-serif;box-sizing:border-box;}\n.sg-wrap{padding:20px 16px 8px;box-sizing:border-box;}\n.sg-title{text-align:center;font-size:18px;font-weight:500;margin-bottom:16px;}\n.sg-track{position:relative;height:16px;border:1px solid var(--ui-border,#3b4250);border-radius:3px;overflow:visible;-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);background:var(--ui-input,#2a2f3a);}\n.sg-end{position:absolute;top:0;bottom:0;}\n.sg-end-l{left:0;border-radius:2px 0 0 2px;}\n.sg-band{position:absolute;top:0;bottom:0;}\n.sg-vptr{position:absolute;top:-13px;transform:translateX(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-top:11px solid var(--ui-text,#e2e6ec);}\n.sg-diamond{position:absolute;top:50%;width:11px;height:11px;transform:translate(-50%,-50%) rotate(45deg);background:var(--ui-input,#2a2f3a);border:2px solid var(--ui-dim,#9aa3b0);}\n.sg-foot{display:flex;align-items:center;justify-content:space-between;margin-top:6px;}\n.sg-tick{font-size:13px;opacity:.75;}\n.sg-value{font-size:24px;font-weight:700;}", "script": "export function init(instance){ try{ instance._bindingsRefresh && instance._bindingsRefresh(); }catch(e){} }\nexport function connectedCallback(instance){}\n"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"html": "<div id=\"main-container\" style=\"display:flex;gap:8px;align-items:stretch;width:100%;height:100%;font:"Times New Roman", serif;\">\n <!-- Input Container -->\n <div id=\"input-container\" style=\"position:relative;flex:1;display:flex;align-items:center;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;background:rgb(240, 240, 240);padding:4px 8px;\">\n <input id=\"text-input\" type=\"text\" placeholder style=\"flex:1;border:initial none initial;outline:none;padding:4px;color:var(--ui-text,#333);background:transparent;font:14px "Times New Roman", serif;\">\n <!-- Numberpad Icon (hidden by default) --> <svg id=\"numberpad-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" title=\"Numberpad\" style=\"cursor:pointer;fill:#666;margin-left:4px;display:none;\">\n <rect x=\"3\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"17\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"3\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"17\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"3\" y=\"17\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"17\" width=\"11\" height=\"4\" rx=\"1\"></rect>\n </svg>\n </div>\n <!-- Button -->\n <button id=\"submit-btn\" style=\"color:var(--ui-text,#fff);border:initial none initial;border-radius:4px;cursor:pointer;white-space:nowrap;min-width:80px;background:rgb(108, 117, 125);font:14px "Times New Roman", serif;padding:8px 16px;\">\n<t-t>input_with_button.volume</t-t>\n </button>\n</div>\n<!-- Numberpad Dialog -->\n<dialog id=\"numberpad-dialog\" style=\"border:2px solid var(--ui-surface,rgb(90, 122, 122));border-radius:8px;padding:0px;background:var(--ui-input,rgb(255, 255, 255));\">\n <div id=\"dialog-header\" style=\"color:var(--ui-text,#fff);padding:12px;display:flex;justify-content:space-between;align-items:center;background:var(--ui-surface,rgb(90, 122, 122));\">\n <h3 id=\"dialog-title\" style=\"margin:0px;font:16px "Times New Roman", serif;\">Numberpad</h3>\n <button id=\"close-numberpad\" style=\"border:initial none initial;color:var(--ui-text,#fff);cursor:pointer;padding:0px;width:30px;height:30px;background:transparent;font:24px "Times New Roman", serif;\">×</button>\n </div>\n <div id=\"numberpad-body\" style=\"padding:16px;\">\n <!-- Display --><input id=\"numberpad-display\" type=\"text\" readonly style=\"width:100%;padding:2px;text-align:right;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;margin-bottom:12px;background:rgb(249, 249, 249);font:20px "Times New Roman", serif;\"><!-- Numberpad Grid -->\n<div style=\"display:grid;grid-template-columns:repeat(3, 60px);gap:8px;\">\n <button class=\"num-btn\" data-value=\"7\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">7</button>\n <button class=\"num-btn\" data-value=\"8\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">8</button>\n <button class=\"num-btn\" data-value=\"9\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">9</button>\n <button class=\"num-btn\" data-value=\"4\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">4</button>\n <button class=\"num-btn\" data-value=\"5\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">5</button>\n <button class=\"num-btn\" data-value=\"6\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">6</button>\n <button class=\"num-btn\" data-value=\"1\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">1</button>\n <button class=\"num-btn\" data-value=\"2\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">2</button>\n <button class=\"num-btn\" data-value=\"3\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">3</button>\n <button class=\"num-btn\" data-value=\"0\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;grid-column:span 2;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">0</button>\n <button class=\"num-btn\" data-value=\".\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">.</button>\n <button id=\"num-clear\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;grid-column:span 2;background:rgb(255, 107, 107);font:14px "Times New Roman", serif;\">\n<t-t>input_with_button.clear</t-t>\n </button>\n <button id=\"num-backspace\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;background:rgb(255, 165, 0);font:14px "Times New Roman", serif;\">←</button>\n </div>\n <!-- OK Button --><button id=\"num-ok\" style=\"width:100%;padding:12px;margin-top:12px;border:initial none initial;border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;background:var(--ui-surface,rgb(90, 122, 122));font:16px "Times New Roman", serif;\">OK</button>\n </div>\n</dialog>\n", "script": "export function init(instance) {\n //instance._bindingsRefresh();\n\n applyTheming(instance);\n updateControl(instance);\n\n instance._assignEvent('input-type-changed', () => updateControl(instance));\n instance._assignEvent('button-text-changed', () => updateControl(instance));\n instance._assignEvent('placeholder-changed', () => updateControl(instance));\n instance._assignEvent('theme-changed', () => applyTheming(instance));\n instance._assignEvent('state-id-changed', () => { });\n\n const input = instance._getDomElement('text-input');\n const submitBtn = instance._getDomElement('submit-btn');\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n const numberpadDialog = instance._getDomElement('numberpad-dialog');\n const numberpadDisplay = instance._getDomElement('numberpad-display');\n const closeNumberpad = instance._getDomElement('close-numberpad');\n const numOk = instance._getDomElement('num-ok');\n const numClear = instance._getDomElement('num-clear');\n const numBackspace = instance._getDomElement('num-backspace');\n\n window.addEventListener('languageChanged', () => {\n if (input) {\n input.placeholder = t('input_with_button.addvalue');\n }\n });\n\n // Subscribe to state changes\n let stateSubscription = null;\n function subscribeToState() {\n // Unsubscribe from previous state\n if (stateSubscription) {\n window.IOB.unsubscribeState(stateSubscription);\n stateSubscription = null;\n }\n\n // Subscribe to new state\n const stateId = instance.stateId;\n if (stateId) {\n stateSubscription = window.IOB.subscribeState(stateId, (id, state) => {\n const input = instance._getDomElement('text-input');\n if (input && state && state.val !== undefined) {\n input.value = state.val;\n }\n });\n\n // Load initial value\n window.IOB.getState(stateId).then(state => {\n const input = instance._getDomElement('text-input');\n if (input && state && state.val !== undefined) {\n input.value = state.val;\n }\n });\n }\n }\n\n subscribeToState();\n\n // Submit function\n async function submitValue() {\n const value = input.value;\n const stateId = instance.stateId;\n\n if (!stateId) {\n console.error('State ID not configured');\n return;\n }\n\n try {\n let finalValue = value;\n\n // Convert based on input type\n if (instance.inputType === 'number') {\n finalValue = parseFloat(value);\n if (isNaN(finalValue)) {\n console.error('Invalid number:', value);\n return;\n }\n } else if (instance.inputType === 'integer') {\n finalValue = parseInt(value);\n if (isNaN(finalValue)) {\n console.error('Invalid integer:', value);\n return;\n }\n }\n\n await window.IOB.setState(stateId, finalValue);\n console.log('Value written to state:', stateId, finalValue);\n\n // Optional: Clear input after submit\n if (instance.clearAfterSubmit === true) {\n input.value = '';\n }\n } catch (err) {\n console.error('Error writing to state:', err);\n }\n }\n\n // Enter key handler\n if (input) {\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n submitValue();\n }\n });\n }\n\n // Submit button handler\n if (submitBtn) {\n submitBtn.addEventListener('click', () => {\n submitValue();\n });\n }\n\n // Numberpad icon handler\n if (numberpadIcon && numberpadDialog) {\n numberpadIcon.addEventListener('click', () => {\n numberpadDisplay.value = input.value || '';\n numberpadDialog.showModal();\n });\n }\n // Numberpad buttons\n const numBtns = instance.shadowRoot.querySelectorAll('.num-btn');\n numBtns.forEach(btn => {\n btn.addEventListener('click', () => {\n numberpadDisplay.value += btn.dataset.value;\n });\n });\n\n // Numberpad clear\n if (numClear) {\n numClear.addEventListener('click', () => {\n numberpadDisplay.value = '';\n });\n }\n\n // Numberpad backspace\n if (numBackspace) {\n numBackspace.addEventListener('click', () => {\n numberpadDisplay.value = numberpadDisplay.value.slice(0, -1);\n });\n }\n\n // Numberpad OK\n if (numOk) {\n numOk.addEventListener('click', () => {\n input.value = numberpadDisplay.value;\n numberpadDialog.close();\n });\n }\n\n // Close numberpad\n if (closeNumberpad) {\n closeNumberpad.addEventListener('click', () => {\n numberpadDialog.close();\n });\n }\n\n // Close dialog on outside click\n if (numberpadDialog) {\n numberpadDialog.addEventListener('click', (e) => {\n const rect = numberpadDialog.getBoundingClientRect();\n if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {\n numberpadDialog.close();\n }\n });\n }\n}\n\n\nfunction updateControl(instance) {\n const input = instance._getDomElement('text-input');\n const submitBtn = instance._getDomElement('submit-btn');\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n\n // Update input type\n if (input) {\n const inputType = instance.inputType || 'string';\n\n if (inputType === 'password') {\n input.type = 'password';\n } else if (inputType === 'number' || inputType === 'integer') {\n input.type = 'text';\n } else {\n input.type = 'text';\n }\n\n //input.placeholder = instance.placeholder || 'Dəyər daxil edin';\n }\n\n // Show/hide numberpad icon\n if (numberpadIcon) {\n const showNumberpad = instance.inputType === 'number' || instance.inputType === 'integer';\n numberpadIcon.style.display = showNumberpad ? 'block' : 'none';\n }\n\n}\n\nfunction applyTheming(instance) {\n const theme = instance.theme || 'default';\n\n const themes = {\n 'default': { inputBg: 'var(--ui-input,#f0f0f0)', inputBorder: 'var(--ui-border,#ccc)', inputText: 'var(--ui-text,#333)', btnBg: 'var(--ui-surface-2,#6c757d)', btnText: 'var(--ui-text,#fff)', headerBg: 'var(--ui-surface,#5a7a7a)' },\n 'dark': { inputBg: '#2d3436', inputBorder: '#000', inputText: '#fff', btnBg: '#636e72', btnText: '#fff', headerBg: '#2d3436' },\n 'light': { inputBg: '#fff', inputBorder: '#dfe6e9', inputText: '#2d3436', btnBg: '#b2bec3', btnText: '#2d3436', headerBg: '#dfe6e9' },\n 'blue': { inputBg: '#dfe6e9', inputBorder: '#74b9ff', inputText: '#0984e3', btnBg: '#0984e3', btnText: '#fff', headerBg: '#0984e3' },\n 'green': { inputBg: '#d4f1e8', inputBorder: '#55efc4', inputText: '#00b894', btnBg: '#00b894', btnText: '#fff', headerBg: '#00b894' },\n 'red': { inputBg: '#ffcccc', inputBorder: '#ff7675', inputText: '#d63031', btnBg: '#d63031', btnText: '#fff', headerBg: '#d63031' },\n 'purple': { inputBg: '#e5dbff', inputBorder: '#a29bfe', inputText: '#6c5ce7', btnBg: '#6c5ce7', btnText: '#fff', headerBg: '#6c5ce7' }\n };\n\n const colors = themes[theme] || themes['default'];\n\n // Input container\n const inputContainer = instance._getDomElement('input-container');\n if (inputContainer) {\n inputContainer.style.background = colors.inputBg;\n inputContainer.style.borderColor = colors.inputBorder;\n }\n\n // Input text\n const input = instance._getDomElement('text-input');\n if (input) {\n input.style.color = colors.inputText;\n }\n\n // Numberpad icon\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n if (numberpadIcon) {\n numberpadIcon.style.fill = colors.inputText;\n }\n\n // Submit button\n const submitBtn = instance._getDomElement('submit-btn');\n if (submitBtn) {\n submitBtn.style.background = colors.btnBg;\n submitBtn.style.color = colors.btnText;\n }\n\n // Dialog header\n const dialogHeader = instance._getDomElement('dialog-header');\n if (dialogHeader) {\n dialogHeader.style.background = colors.headerBg;\n }\n\n // Dialog\n const dialog = instance._getDomElement('numberpad-dialog');\n if (dialog) {\n dialog.style.borderColor = colors.headerBg;\n }\n\n // OK button in numberpad\n const numOk = instance._getDomElement('num-ok');\n if (numOk) {\n numOk.style.background = colors.headerBg;\n }\n}", "properties": {"inputType": {"type": "enum", "default": "string", "values": ["string", "number", "integer", "password"]}, "placeholder": {"type": "string", "default": "Dəyər daxil edin"}, "stateId": {"type": "signal"}, "clearAfterSubmit": {"type": "boolean"}, "theme": {"type": "enum", "default": "default", "values": ["default", "dark", "light", "blue", "green", "red", "purple"]}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
|
|
1
|
+
{"html": "<div id=\"main-container\" style=\"display:flex;gap:8px;align-items:stretch;width:100%;height:100%;font:"Times New Roman", serif;\">\n <!-- Input Container -->\n <div id=\"input-container\" style=\"position:relative;flex:1;display:flex;align-items:center;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;background:rgb(240, 240, 240);padding:4px 8px;\">\n <input id=\"text-input\" type=\"text\" placeholder style=\"flex:1;border:initial none initial;outline:none;padding:4px;color:var(--ui-text,#333);background:transparent;font:14px "Times New Roman", serif;\">\n <!-- Numberpad Icon (hidden by default) --> <svg id=\"numberpad-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" title=\"Numberpad\" style=\"cursor:pointer;fill:#666;margin-left:4px;display:none;\">\n <rect x=\"3\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"17\" y=\"3\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"3\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"17\" y=\"10\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"3\" y=\"17\" width=\"4\" height=\"4\" rx=\"1\"></rect>\n <rect x=\"10\" y=\"17\" width=\"11\" height=\"4\" rx=\"1\"></rect>\n </svg>\n </div>\n <!-- Button -->\n <button id=\"submit-btn\" style=\"color:var(--ui-text,#fff);border:initial none initial;border-radius:4px;cursor:pointer;white-space:nowrap;min-width:80px;background:rgb(108, 117, 125);font:14px "Times New Roman", serif;padding:8px 16px;\">\n<t-t>input_with_button.volume</t-t>\n </button>\n</div>\n<!-- Numberpad Dialog -->\n<dialog id=\"numberpad-dialog\" style=\"border:2px solid var(--ui-surface,rgb(90, 122, 122));border-radius:8px;padding:0px;-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);background:var(--ui-input,rgb(255, 255, 255));\">\n <div id=\"dialog-header\" style=\"color:var(--ui-text,#fff);padding:12px;display:flex;justify-content:space-between;align-items:center;background:var(--ui-surface,rgb(90, 122, 122));\">\n <h3 id=\"dialog-title\" style=\"margin:0px;font:16px "Times New Roman", serif;\">Numberpad</h3>\n <button id=\"close-numberpad\" style=\"border:initial none initial;color:var(--ui-text,#fff);cursor:pointer;padding:0px;width:30px;height:30px;background:transparent;font:24px "Times New Roman", serif;\">×</button>\n </div>\n <div id=\"numberpad-body\" style=\"padding:16px;\">\n <!-- Display --><input id=\"numberpad-display\" type=\"text\" readonly style=\"width:100%;padding:2px;text-align:right;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;margin-bottom:12px;background:rgb(249, 249, 249);font:20px "Times New Roman", serif;\"><!-- Numberpad Grid -->\n<div style=\"display:grid;grid-template-columns:repeat(3, 60px);gap:8px;\">\n <button class=\"num-btn\" data-value=\"7\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">7</button>\n <button class=\"num-btn\" data-value=\"8\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">8</button>\n <button class=\"num-btn\" data-value=\"9\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">9</button>\n <button class=\"num-btn\" data-value=\"4\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">4</button>\n <button class=\"num-btn\" data-value=\"5\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">5</button>\n <button class=\"num-btn\" data-value=\"6\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">6</button>\n <button class=\"num-btn\" data-value=\"1\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">1</button>\n <button class=\"num-btn\" data-value=\"2\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">2</button>\n <button class=\"num-btn\" data-value=\"3\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">3</button>\n <button class=\"num-btn\" data-value=\"0\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;grid-column:span 2;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">0</button>\n <button class=\"num-btn\" data-value=\".\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;cursor:pointer;background:var(--ui-input,rgb(255, 255, 255));font:18px "Times New Roman", serif;\">.</button>\n <button id=\"num-clear\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;grid-column:span 2;background:rgb(255, 107, 107);font:14px "Times New Roman", serif;\">\n<t-t>input_with_button.clear</t-t>\n </button>\n <button id=\"num-backspace\" style=\"padding:16px;border:1px solid var(--ui-border,rgb(204, 204, 204));border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;background:rgb(255, 165, 0);font:14px "Times New Roman", serif;\">←</button>\n </div>\n <!-- OK Button --><button id=\"num-ok\" style=\"width:100%;padding:12px;margin-top:12px;border:initial none initial;border-radius:4px;color:var(--ui-text,#fff);cursor:pointer;background:var(--ui-surface,rgb(90, 122, 122));font:16px "Times New Roman", serif;\">OK</button>\n </div>\n</dialog>\n", "script": "export function init(instance) {\n //instance._bindingsRefresh();\n\n applyTheming(instance);\n updateControl(instance);\n\n instance._assignEvent('input-type-changed', () => updateControl(instance));\n instance._assignEvent('button-text-changed', () => updateControl(instance));\n instance._assignEvent('placeholder-changed', () => updateControl(instance));\n instance._assignEvent('theme-changed', () => applyTheming(instance));\n instance._assignEvent('state-id-changed', () => { });\n\n const input = instance._getDomElement('text-input');\n const submitBtn = instance._getDomElement('submit-btn');\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n const numberpadDialog = instance._getDomElement('numberpad-dialog');\n const numberpadDisplay = instance._getDomElement('numberpad-display');\n const closeNumberpad = instance._getDomElement('close-numberpad');\n const numOk = instance._getDomElement('num-ok');\n const numClear = instance._getDomElement('num-clear');\n const numBackspace = instance._getDomElement('num-backspace');\n\n window.addEventListener('languageChanged', () => {\n if (input) {\n input.placeholder = t('input_with_button.addvalue');\n }\n });\n\n // Subscribe to state changes\n let stateSubscription = null;\n function subscribeToState() {\n // Unsubscribe from previous state\n if (stateSubscription) {\n window.IOB.unsubscribeState(stateSubscription);\n stateSubscription = null;\n }\n\n // Subscribe to new state\n const stateId = instance.stateId;\n if (stateId) {\n stateSubscription = window.IOB.subscribeState(stateId, (id, state) => {\n const input = instance._getDomElement('text-input');\n if (input && state && state.val !== undefined) {\n input.value = state.val;\n }\n });\n\n // Load initial value\n window.IOB.getState(stateId).then(state => {\n const input = instance._getDomElement('text-input');\n if (input && state && state.val !== undefined) {\n input.value = state.val;\n }\n });\n }\n }\n\n subscribeToState();\n\n // Submit function\n async function submitValue() {\n const value = input.value;\n const stateId = instance.stateId;\n\n if (!stateId) {\n console.error('State ID not configured');\n return;\n }\n\n try {\n let finalValue = value;\n\n // Convert based on input type\n if (instance.inputType === 'number') {\n finalValue = parseFloat(value);\n if (isNaN(finalValue)) {\n console.error('Invalid number:', value);\n return;\n }\n } else if (instance.inputType === 'integer') {\n finalValue = parseInt(value);\n if (isNaN(finalValue)) {\n console.error('Invalid integer:', value);\n return;\n }\n }\n\n await window.IOB.setState(stateId, finalValue);\n console.log('Value written to state:', stateId, finalValue);\n\n // Optional: Clear input after submit\n if (instance.clearAfterSubmit === true) {\n input.value = '';\n }\n } catch (err) {\n console.error('Error writing to state:', err);\n }\n }\n\n // Enter key handler\n if (input) {\n input.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') {\n submitValue();\n }\n });\n }\n\n // Submit button handler\n if (submitBtn) {\n submitBtn.addEventListener('click', () => {\n submitValue();\n });\n }\n\n // Numberpad icon handler\n if (numberpadIcon && numberpadDialog) {\n numberpadIcon.addEventListener('click', () => {\n numberpadDisplay.value = input.value || '';\n numberpadDialog.showModal();\n });\n }\n // Numberpad buttons\n const numBtns = instance.shadowRoot.querySelectorAll('.num-btn');\n numBtns.forEach(btn => {\n btn.addEventListener('click', () => {\n numberpadDisplay.value += btn.dataset.value;\n });\n });\n\n // Numberpad clear\n if (numClear) {\n numClear.addEventListener('click', () => {\n numberpadDisplay.value = '';\n });\n }\n\n // Numberpad backspace\n if (numBackspace) {\n numBackspace.addEventListener('click', () => {\n numberpadDisplay.value = numberpadDisplay.value.slice(0, -1);\n });\n }\n\n // Numberpad OK\n if (numOk) {\n numOk.addEventListener('click', () => {\n input.value = numberpadDisplay.value;\n numberpadDialog.close();\n });\n }\n\n // Close numberpad\n if (closeNumberpad) {\n closeNumberpad.addEventListener('click', () => {\n numberpadDialog.close();\n });\n }\n\n // Close dialog on outside click\n if (numberpadDialog) {\n numberpadDialog.addEventListener('click', (e) => {\n const rect = numberpadDialog.getBoundingClientRect();\n if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {\n numberpadDialog.close();\n }\n });\n }\n}\n\n\nfunction updateControl(instance) {\n const input = instance._getDomElement('text-input');\n const submitBtn = instance._getDomElement('submit-btn');\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n\n // Update input type\n if (input) {\n const inputType = instance.inputType || 'string';\n\n if (inputType === 'password') {\n input.type = 'password';\n } else if (inputType === 'number' || inputType === 'integer') {\n input.type = 'text';\n } else {\n input.type = 'text';\n }\n\n //input.placeholder = instance.placeholder || 'Dəyər daxil edin';\n }\n\n // Show/hide numberpad icon\n if (numberpadIcon) {\n const showNumberpad = instance.inputType === 'number' || instance.inputType === 'integer';\n numberpadIcon.style.display = showNumberpad ? 'block' : 'none';\n }\n\n}\n\nfunction applyTheming(instance) {\n const theme = instance.theme || 'default';\n\n const themes = {\n 'default': { inputBg: 'var(--ui-input,#f0f0f0)', inputBorder: 'var(--ui-border,#ccc)', inputText: 'var(--ui-text,#333)', btnBg: 'var(--ui-surface-2,#6c757d)', btnText: 'var(--ui-text,#fff)', headerBg: 'var(--ui-surface,#5a7a7a)' },\n 'dark': { inputBg: '#2d3436', inputBorder: '#000', inputText: '#fff', btnBg: '#636e72', btnText: '#fff', headerBg: '#2d3436' },\n 'light': { inputBg: '#fff', inputBorder: '#dfe6e9', inputText: '#2d3436', btnBg: '#b2bec3', btnText: '#2d3436', headerBg: '#dfe6e9' },\n 'blue': { inputBg: '#dfe6e9', inputBorder: '#74b9ff', inputText: '#0984e3', btnBg: '#0984e3', btnText: '#fff', headerBg: '#0984e3' },\n 'green': { inputBg: '#d4f1e8', inputBorder: '#55efc4', inputText: '#00b894', btnBg: '#00b894', btnText: '#fff', headerBg: '#00b894' },\n 'red': { inputBg: '#ffcccc', inputBorder: '#ff7675', inputText: '#d63031', btnBg: '#d63031', btnText: '#fff', headerBg: '#d63031' },\n 'purple': { inputBg: '#e5dbff', inputBorder: '#a29bfe', inputText: '#6c5ce7', btnBg: '#6c5ce7', btnText: '#fff', headerBg: '#6c5ce7' }\n };\n\n const colors = themes[theme] || themes['default'];\n\n // Input container\n const inputContainer = instance._getDomElement('input-container');\n if (inputContainer) {\n inputContainer.style.background = colors.inputBg;\n inputContainer.style.borderColor = colors.inputBorder;\n }\n\n // Input text\n const input = instance._getDomElement('text-input');\n if (input) {\n input.style.color = colors.inputText;\n }\n\n // Numberpad icon\n const numberpadIcon = instance._getDomElement('numberpad-icon');\n if (numberpadIcon) {\n numberpadIcon.style.fill = colors.inputText;\n }\n\n // Submit button\n const submitBtn = instance._getDomElement('submit-btn');\n if (submitBtn) {\n submitBtn.style.background = colors.btnBg;\n submitBtn.style.color = colors.btnText;\n }\n\n // Dialog header\n const dialogHeader = instance._getDomElement('dialog-header');\n if (dialogHeader) {\n dialogHeader.style.background = colors.headerBg;\n }\n\n // Dialog\n const dialog = instance._getDomElement('numberpad-dialog');\n if (dialog) {\n dialog.style.borderColor = colors.headerBg;\n }\n\n // OK button in numberpad\n const numOk = instance._getDomElement('num-ok');\n if (numOk) {\n numOk.style.background = colors.headerBg;\n }\n}", "properties": {"inputType": {"type": "enum", "default": "string", "values": ["string", "number", "integer", "password"]}, "placeholder": {"type": "string", "default": "Dəyər daxil edin"}, "stateId": {"type": "signal"}, "clearAfterSubmit": {"type": "boolean"}, "theme": {"type": "enum", "default": "default", "values": ["default", "dark", "light", "blue", "green", "red", "purple"]}}, "settings": {"visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"properties": {"showlabel": {"type": "enum", "default": "true", "values": ["true", "false"]}}, "settings": {"width": "120px", "height": "40px", "visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}, "html": "<button id=\"logoutBtn\" class=\"lo\" @click=\"Logout\" data-visibility-action=\"hide\" type=\"button\" title=\"Logout\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z\"/></svg><span class=\"lo-lbl\" bind-css:display=\"??showlabel;(__0===false||__0==='false')?'none':''\"><t-t>logout.button</t-t></span></button>", "style": ":host{display:inline-block;height:100%;font-family:'Segoe UI',system-ui,Roboto,sans-serif;}.lo{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;height:100%;width:100%;padding:6px 16px;margin:0;box-sizing:border-box;cursor:pointer;border-radius:999px;background:color-mix(in srgb,var(--ui-text,#fff) 7%,transparent);border:1px solid var(--ui-border,#3b4250);color:var(--ui-text,#e2e6ec);font-size:13px;font-weight:600;letter-spacing:.3px;white-space:nowrap;}.lo svg{width:18px;height:18px;fill:currentColor;flex:none;display:block;}.lo:hover{background:color-mix(in srgb,var(--ui-error,#ef4d5e) 18%,transparent);border-color:color-mix(in srgb,var(--ui-error,#ef4d5e) 60%,transparent);color:var(--ui-error,#ef4d5e);}.lo:active{background:color-mix(in srgb,var(--ui-error,#ef4d5e) 30%,transparent);}.lo:focus-visible{outline:2px solid var(--accent,#2680eb);outline-offset:2px;}", "script": "export function init(instance) {\n try{ instance._bindingsRefresh && instance._bindingsRefresh(); }catch(e){}\n}\nexport function Logout(event, eventRaisingElement, shadowRoot, instance, parameters) {\n window.location.href = '/logout';\n}"}
|
|
1
|
+
{"properties": {"showlabel": {"type": "enum", "default": "true", "values": ["true", "false"]}}, "settings": {"width": "120px", "height": "40px", "visibilityEnabled": "false", "visibilityGroups": "", "visibilityAction": "hide", "visibilityRedirectScreen": ""}, "html": "<button id=\"logoutBtn\" class=\"lo\" @click=\"Logout\" data-visibility-action=\"hide\" type=\"button\" title=\"Logout\"><svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z\"/></svg><span class=\"lo-lbl\" bind-css:display=\"??showlabel;(__0===false||__0==='false')?'none':''\"><t-t>logout.button</t-t></span></button>", "style": ":host{display:inline-block;height:100%;font-family:'Segoe UI',system-ui,Roboto,sans-serif;}.lo{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;height:100%;width:100%;padding:6px 16px;margin:0;box-sizing:border-box;cursor:pointer;border-radius:999px;background:color-mix(in srgb,var(--ui-text,#fff) 7%,transparent);border:1px solid var(--ui-border,#3b4250);color:var(--ui-text,#e2e6ec);font-size:13px;font-weight:600;letter-spacing:.3px;white-space:nowrap;-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);}.lo svg{width:18px;height:18px;fill:currentColor;flex:none;display:block;}.lo:hover{background:color-mix(in srgb,var(--ui-error,#ef4d5e) 18%,transparent);border-color:color-mix(in srgb,var(--ui-error,#ef4d5e) 60%,transparent);color:var(--ui-error,#ef4d5e);}.lo:active{background:color-mix(in srgb,var(--ui-error,#ef4d5e) 30%,transparent);}.lo:focus-visible{outline:2px solid var(--accent,#2680eb);outline-offset:2px;}", "script": "export function init(instance) {\n try{ instance._bindingsRefresh && instance._bindingsRefresh(); }catch(e){}\n}\nexport function Logout(event, eventRaisingElement, shadowRoot, instance, parameters) {\n window.location.href = '/logout';\n}"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"properties": {"caption": {"type": "string", "default": ""}, "icon": {"type": "string", "default": ""}, "bgColor": {"type": "color", "default": ""}}, "settings": {"width": "100%", "height": "48px"}, "html": "<div id=\"bar\" class=\"bar\"><div id=\"slotLeft\" class=\"bar-slot\"></div><img id=\"barIcon\" class=\"bar-icon\" alt=\"\" style=\"display:none;\"/><span id=\"barCap\" class=\"bar-cap\"></span><div id=\"slotCenter\" class=\"bar-slot bar-grow\" style=\"justify-content:center;\"></div><div id=\"slotRight\" class=\"bar-slot\"></div></div>", "style": ":host{display:block;width:100%;height:100%;}\n.bar{display:flex;align-items:center;gap:12px;height:100%;padding:0 16px 0 60px;color:var(--bar-fg,#fff);box-sizing:border-box;background:var(--bar-bg,linear-gradient(90deg,#1e3c72 0%,#2a5298 100%));box-shadow:0 2px 12px rgba(0,0,0,.25);}\n.bar-icon{height:30px;width:auto;filter:drop-shadow(0 1px 2px rgba(0,0,0,.3));}\n.bar-cap{font-size:17px;font-weight:600;letter-spacing:.3px;}\n.bar-slot{display:flex;align-items:center;gap:8px;}\n.bar-grow{flex:1;min-width:0;}\n:host([data-theme=\"dark\"]){--bar-bg:linear-gradient(90deg,#0a0e27 0%,#1a1f3a 100%);}\n:host([data-theme=\"light\"]){--bar-bg:linear-gradient(90deg,#ffffff 0%,#eceff1 100%);}\n:host([data-theme=\"purple\"]){--bar-bg:linear-gradient(90deg,#4a1d6e 0%,#7b2d8e 100%);}\n:host([data-theme=\"green\"]){--bar-bg:linear-gradient(90deg,#1b5e20 0%,#2e7d32 100%);}\n:host([data-theme=\"orange\"]){--bar-bg:linear-gradient(90deg,#bf360c 0%,#e64a19 100%);}\n:host([data-theme=\"teal\"]){--bar-bg:linear-gradient(90deg,#004d40 0%,#00695c 100%);}\n:host([data-theme=\"auto\"]){--bar-bg:var(--ui-surface,#252b38);--bar-fg:var(--ui-text,#e2e6ec);}", "script": "export function init(instance){\n instance._bindingsRefresh();\n const resolveImg = (img) => { if(!img) return ''; if(/^(https?:|data:|blob:|\\/)/.test(img)) return img; try{ return (window.IOB&&window.IOB.imagePrefix?window.IOB.imagePrefix:'')+img; }catch(e){ return img; } };\n const cfg = (window.IOB && window.IOB.config && window.IOB.config.appBar) || {};\n const navCfg = (window.IOB && window.IOB.config && window.IOB.config.navigation) || {};\n instance.setAttribute('data-theme', navCfg.theme || 'default');\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'"')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const upd = () => {\n const bar=instance._getDomElement('bar'), ic=instance._getDomElement('barIcon'), cap=instance._getDomElement('barCap');\n const bg = instance.bgColor || cfg.bgColor;\n if(bg) instance.style.setProperty('--bar-bg', bg);\n const icon = instance.icon || cfg.icon;\n if(ic){ if(icon){ ic.src=resolveImg(icon); ic.style.display=''; } else ic.style.display='none'; }\n if(cap) cap.textContent = instance.caption || cfg.caption || '';\n };\n instance._assignEvent('caption-changed', upd);\n instance._assignEvent('icon-changed', upd);\n instance._assignEvent('bg-color-changed', upd);\n upd();\n wireSlot(instance._getDomElement('slotLeft'), cfg, 'left');\n wireSlot(instance._getDomElement('slotCenter'), cfg, 'center');\n wireSlot(instance._getDomElement('slotRight'), cfg, 'right');\n}"}
|
|
1
|
+
{"properties": {"caption": {"type": "string", "default": ""}, "icon": {"type": "string", "default": ""}, "bgColor": {"type": "color", "default": ""}}, "settings": {"width": "100%", "height": "48px"}, "html": "<div id=\"bar\" class=\"bar\"><div id=\"slotLeft\" class=\"bar-slot\"></div><img id=\"barIcon\" class=\"bar-icon\" alt=\"\" style=\"display:none;\"/><span id=\"barCap\" class=\"bar-cap\"></span><div id=\"slotCenter\" class=\"bar-slot bar-grow\" style=\"justify-content:center;\"></div><div id=\"slotRight\" class=\"bar-slot\"></div></div>", "style": ":host{display:block;width:100%;height:100%;}\n.bar{display:flex;align-items:center;gap:12px;height:100%;padding:0 16px 0 60px;color:var(--bar-fg,#fff);box-sizing:border-box;background:var(--bar-bg,linear-gradient(90deg,#1e3c72 0%,#2a5298 100%));box-shadow:0 2px 12px rgba(0,0,0,.25);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);}\n.bar-icon{height:30px;width:auto;filter:drop-shadow(0 1px 2px rgba(0,0,0,.3));}\n.bar-cap{font-size:17px;font-weight:600;letter-spacing:.3px;}\n.bar-slot{display:flex;align-items:center;gap:8px;}\n.bar-grow{flex:1;min-width:0;}\n:host([data-theme=\"dark\"]){--bar-bg:linear-gradient(90deg,#0a0e27 0%,#1a1f3a 100%);}\n:host([data-theme=\"light\"]){--bar-bg:linear-gradient(90deg,#ffffff 0%,#eceff1 100%);}\n:host([data-theme=\"purple\"]){--bar-bg:linear-gradient(90deg,#4a1d6e 0%,#7b2d8e 100%);}\n:host([data-theme=\"green\"]){--bar-bg:linear-gradient(90deg,#1b5e20 0%,#2e7d32 100%);}\n:host([data-theme=\"orange\"]){--bar-bg:linear-gradient(90deg,#bf360c 0%,#e64a19 100%);}\n:host([data-theme=\"teal\"]){--bar-bg:linear-gradient(90deg,#004d40 0%,#00695c 100%);}\n:host([data-theme=\"auto\"]){--bar-bg:var(--ui-surface,#252b38);--bar-fg:var(--ui-text,#e2e6ec);}", "script": "export function init(instance){\n instance._bindingsRefresh();\n const resolveImg = (img) => { if(!img) return ''; if(/^(https?:|data:|blob:|\\/)/.test(img)) return img; try{ return (window.IOB&&window.IOB.imagePrefix?window.IOB.imagePrefix:'')+img; }catch(e){ return img; } };\n const cfg = (window.IOB && window.IOB.config && window.IOB.config.appBar) || {};\n const navCfg = (window.IOB && window.IOB.config && window.IOB.config.navigation) || {};\n instance.setAttribute('data-theme', navCfg.theme || 'default');\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'"')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const upd = () => {\n const bar=instance._getDomElement('bar'), ic=instance._getDomElement('barIcon'), cap=instance._getDomElement('barCap');\n const bg = instance.bgColor || cfg.bgColor;\n if(bg) instance.style.setProperty('--bar-bg', bg);\n const icon = instance.icon || cfg.icon;\n if(ic){ if(icon){ ic.src=resolveImg(icon); ic.style.display=''; } else ic.style.display='none'; }\n if(cap) cap.textContent = instance.caption || cfg.caption || '';\n };\n instance._assignEvent('caption-changed', upd);\n instance._assignEvent('icon-changed', upd);\n instance._assignEvent('bg-color-changed', upd);\n upd();\n wireSlot(instance._getDomElement('slotLeft'), cfg, 'left');\n wireSlot(instance._getDomElement('slotCenter'), cfg, 'center');\n wireSlot(instance._getDomElement('slotRight'), cfg, 'right');\n}"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"properties": {}, "settings": {"width": "220px", "height": "100%"}, "html": "<div id=\"sb\" class=\"sb\"><div id=\"sbHdr\" class=\"sb-hdr\"></div><div id=\"slotTop\" class=\"sb-slot\"></div><div id=\"sbItems\" class=\"sb-items\"></div><div id=\"slotBottom\" class=\"sb-slot\"></div></div>", "style": ":host{display:block;width:100%;height:100%;box-sizing:border-box;}\n.sb{display:flex;flex-direction:column;height:100%;width:100%;background:var(--sb-bg,linear-gradient(180deg,#1e3c72 0%,#2a5298 100%));color:var(--sb-fg,rgba(255,255,255,.9));box-shadow:2px 0 18px rgba(0,0,0,.28);box-sizing:border-box;}\n.sb-hdr{padding:18px 16px 14px;font-weight:700;font-size:16px;letter-spacing:.5px;color:var(--sb-hdr-fg,#fff);border-bottom:1px solid rgba(255,255,255,.12);white-space:nowrap;overflow:hidden;}\n.sb-items{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0;}\n.sb-items::-webkit-scrollbar{width:6px;}\n.sb-items::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3);border-radius:3px;}\n.sb-slot{display:block;}\n:host([data-theme=\"dark\"]){--sb-bg:linear-gradient(180deg,#0a0e27 0%,#1a1f3a 100%);--sb-fg:rgba(255,255,255,.88);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.04);--mi-hover:rgba(255,255,255,.1);--mi-active:linear-gradient(135deg,rgba(74,158,255,.35),rgba(74,158,255,.14));--mi-active-fg:#fff;--mi-active-icon:#4a9eff;}\n:host([data-theme=\"light\"]){--sb-bg:linear-gradient(180deg,#f5f7fa 0%,#e4e8f0 100%);--sb-fg:#37474f;--sb-hdr-fg:#1a237e;--mi-bg:rgba(0,0,0,.04);--mi-hover:rgba(33,150,243,.1);--mi-active:linear-gradient(135deg,rgba(33,150,243,.22),rgba(33,150,243,.1));--mi-active-fg:#1565c0;--mi-active-icon:#1976d2;}\n:host([data-theme=\"purple\"]){--sb-bg:linear-gradient(180deg,#4a1d6e 0%,#7b2d8e 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(224,64,251,.35),rgba(224,64,251,.15));--mi-active-fg:#fff;--mi-active-icon:#8e24aa;}\n:host([data-theme=\"green\"]){--sb-bg:linear-gradient(180deg,#1b5e20 0%,#2e7d32 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(105,240,174,.3),rgba(105,240,174,.12));--mi-active-fg:#fff;--mi-active-icon:#2e7d32;}\n:host([data-theme=\"orange\"]){--sb-bg:linear-gradient(180deg,#bf360c 0%,#e64a19 100%);--sb-fg:rgba(255,255,255,.92);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.08);--mi-hover:rgba(255,255,255,.18);--mi-active:linear-gradient(135deg,rgba(255,171,64,.32),rgba(255,171,64,.14));--mi-active-fg:#fff;--mi-active-icon:#e64a19;}\n:host([data-theme=\"teal\"]){--sb-bg:linear-gradient(180deg,#004d40 0%,#00695c 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(29,233,182,.3),rgba(29,233,182,.12));--mi-active-fg:#fff;--mi-active-icon:#00897b;}\n:host([data-theme=\"auto\"]){--sb-bg:var(--ui-surface,#252b38);--sb-fg:var(--ui-text,#e2e6ec);--sb-hdr-fg:var(--ui-text,#fff);--mi-bg:color-mix(in srgb,var(--ui-text,#fff) 6%,transparent);--mi-fg:var(--ui-text,#e2e6ec);--mi-hover:color-mix(in srgb,var(--ui-text,#fff) 13%,transparent);--mi-active:color-mix(in srgb,var(--accent,#2680eb) 26%,transparent);--mi-active-border:color-mix(in srgb,var(--accent,#2680eb) 55%,transparent);--mi-active-fg:var(--ui-text,#fff);--mi-active-icon:var(--accent,#2680eb);}", "script": "export function init(instance){\n instance._bindingsRefresh();\n const IOB = window.IOB;\n const cfg = (IOB && IOB.config && IOB.config.navigation) || {};\n instance.setAttribute('data-theme', cfg.theme || 'default');\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'"')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n const hdr=instance._getDomElement('sbHdr'), items=instance._getDomElement('sbItems');\n if(cfg.bgColor) instance.style.setProperty('--sb-bg', cfg.bgColor);\n if(cfg.textColor){ instance.style.setProperty('--sb-fg', cfg.textColor); instance.style.setProperty('--mi-fg', cfg.textColor); }\n if(cfg.menuHeaderTextColor) instance.style.setProperty('--sb-hdr-fg', cfg.menuHeaderTextColor);\n if(cfg.bgColorSelected){ instance.style.setProperty('--mi-active', cfg.bgColorSelected); instance.style.setProperty('--mi-active-icon', cfg.bgColorSelected); }\n if(cfg.textColorSelected) instance.style.setProperty('--mi-active-fg', cfg.textColorSelected);\n if(hdr){ hdr.textContent=cfg.menuHeaderText||''; hdr.style.display=cfg.menuHeaderText?'':'none'; }\n let state='expanded'; const ICONS_W=64; let burger=null; let hasItems=false;\n const navWidth = cfg.menuWidth ? (/^\\d+$/.test(String(cfg.menuWidth))?cfg.menuWidth+'px':String(cfg.menuWidth)) : '220px';\n const setActive=(name)=>{ if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>{ mi.selected=(mi.screenName===name); }); };\n const navTo=(name)=>{ if(IOB&&IOB.changeView) IOB.changeView.emit(name); setActive(name); if(cfg.hideAfterSelection) setState('hidden'); };\n async function loadEntries(){\n if(!IOB||!IOB.getAllNames) return [];\n const names=await IOB.getAllNames('screen').catch(()=>[]);\n const list=[];\n for(const n of names){ const o=await IOB.getWebuiObject('screen',n).catch(()=>null); const nv=o&&o.settings&&o.settings.navigation; if(nv&&nv.show) list.push({name:n,title:nv.title||n,order:parseFloat(nv.order)||0,image:nv.image||''}); }\n list.sort((a,b)=>a.order-b.order); return list;\n }\n async function render(){\n const list=await loadEntries(); hasItems=list.length>0;\n if(items){ items.innerHTML='';\n for(const e of list){ const mi=document.createElement('webui-navigation-menu-item'); mi.screenName=e.name; mi.title=e.title; mi.image=e.image; mi.addEventListener('menuitemclick',()=>navTo(e.name)); items.appendChild(mi); }\n }\n if(!isDesigner){ if(hasItems && !cfg.hideMenu) applyRuntimeLayout(); else { const v=document.getElementById('viewer'); if(v){ v.style.left='0'; v.style.width='100%'; } instance.style.display='none'; } }\n }\n function applyRuntimeLayout(){\n const ab=document.getElementById('__nav-appbar-host'); const top=ab?ab.offsetHeight:0;\n instance.style.position='fixed'; instance.style.left='0'; instance.style.bottom='0'; instance.style.top=top+'px';\n instance.style.zIndex='99999'; instance.style.overflow='hidden'; instance.style.transition='width .2s ease,transform .2s ease';\n if(!burger){ burger=document.createElement('div'); burger.id='__nav-burger'; burger.textContent='☰'; const inBar=top>0; burger.style.cssText='position:fixed;top:'+(inBar?0:8)+'px;left:'+(inBar?0:8)+'px;z-index:100002;width:'+(inBar?top:42)+'px;height:'+(inBar?top:42)+'px;display:flex;align-items:center;justify-content:center;font-size:22px;cursor:pointer;'+(inBar?'':'border-radius:8px;')+'background:'+(cfg.showButtonBg||(inBar?'transparent':'rgba(0,0,0,.45)'))+';color:'+(cfg.showButtonColor||'#fff')+';transition:background .15s;'; burger.onclick=cycle; document.body.appendChild(burger); }\n setState(state);\n }\n function setState(s){\n state=s; const v=document.getElementById('viewer'); const top=parseInt(instance.style.top)||0; let left='0px';\n const compact=(state==='icons');\n if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>mi.compact=compact);\n if(state==='expanded'){ instance.style.width=navWidth; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display=cfg.menuHeaderText?'':'none'; left=navWidth; }\n else if(state==='icons'){ instance.style.width=ICONS_W+'px'; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display='none'; left=ICONS_W+'px'; }\n else { instance.style.transform='translateX(-110%)'; left='0px'; }\n if(v){ v.style.left=left; v.style.width='calc(100% - '+left+')'; v.style.top=top+'px'; v.style.height='calc(100% - '+top+'px)'; }\n }\n function cycle(){ const o=['expanded','icons','hidden']; let n=o[(o.indexOf(state)+1)%o.length]; if(n==='hidden'&&cfg.doNotHideCompletely)n='icons'; setState(n); }\n wireSlot(instance._getDomElement('slotTop'), cfg, 'top');\n wireSlot(instance._getDomElement('slotBottom'), cfg, 'bottom');\n render();\n if(IOB&&IOB.changeView) IOB.changeView.on(v=>setActive(v));\n const sv=document.getElementById('viewer'); if(sv&&sv.screenName) setActive(sv.screenName);\n}"}
|
|
1
|
+
{"properties": {}, "settings": {"width": "220px", "height": "100%"}, "html": "<div id=\"sb\" class=\"sb\"><div id=\"sbHdr\" class=\"sb-hdr\"></div><div id=\"slotTop\" class=\"sb-slot\"></div><div id=\"sbItems\" class=\"sb-items\"></div><div id=\"slotBottom\" class=\"sb-slot\"></div></div>", "style": ":host{display:block;width:100%;height:100%;box-sizing:border-box;}\n.sb{display:flex;flex-direction:column;height:100%;width:100%;background:var(--sb-bg,linear-gradient(180deg,#1e3c72 0%,#2a5298 100%));color:var(--sb-fg,rgba(255,255,255,.9));box-shadow:2px 0 18px rgba(0,0,0,.28);-webkit-backdrop-filter:var(--ui-backdrop,none);backdrop-filter:var(--ui-backdrop,none);box-sizing:border-box;}\n.sb-hdr{padding:18px 16px 14px;font-weight:700;font-size:16px;letter-spacing:.5px;color:var(--sb-hdr-fg,#fff);border-bottom:1px solid rgba(255,255,255,.12);white-space:nowrap;overflow:hidden;}\n.sb-items{flex:1;overflow-y:auto;overflow-x:hidden;padding:8px 0;}\n.sb-items::-webkit-scrollbar{width:6px;}\n.sb-items::-webkit-scrollbar-thumb{background:rgba(255,255,255,.3);border-radius:3px;}\n.sb-slot{display:block;}\n:host([data-theme=\"dark\"]){--sb-bg:linear-gradient(180deg,#0a0e27 0%,#1a1f3a 100%);--sb-fg:rgba(255,255,255,.88);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.04);--mi-hover:rgba(255,255,255,.1);--mi-active:linear-gradient(135deg,rgba(74,158,255,.35),rgba(74,158,255,.14));--mi-active-fg:#fff;--mi-active-icon:#4a9eff;}\n:host([data-theme=\"light\"]){--sb-bg:linear-gradient(180deg,#f5f7fa 0%,#e4e8f0 100%);--sb-fg:#37474f;--sb-hdr-fg:#1a237e;--mi-bg:rgba(0,0,0,.04);--mi-hover:rgba(33,150,243,.1);--mi-active:linear-gradient(135deg,rgba(33,150,243,.22),rgba(33,150,243,.1));--mi-active-fg:#1565c0;--mi-active-icon:#1976d2;}\n:host([data-theme=\"purple\"]){--sb-bg:linear-gradient(180deg,#4a1d6e 0%,#7b2d8e 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(224,64,251,.35),rgba(224,64,251,.15));--mi-active-fg:#fff;--mi-active-icon:#8e24aa;}\n:host([data-theme=\"green\"]){--sb-bg:linear-gradient(180deg,#1b5e20 0%,#2e7d32 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(105,240,174,.3),rgba(105,240,174,.12));--mi-active-fg:#fff;--mi-active-icon:#2e7d32;}\n:host([data-theme=\"orange\"]){--sb-bg:linear-gradient(180deg,#bf360c 0%,#e64a19 100%);--sb-fg:rgba(255,255,255,.92);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.08);--mi-hover:rgba(255,255,255,.18);--mi-active:linear-gradient(135deg,rgba(255,171,64,.32),rgba(255,171,64,.14));--mi-active-fg:#fff;--mi-active-icon:#e64a19;}\n:host([data-theme=\"teal\"]){--sb-bg:linear-gradient(180deg,#004d40 0%,#00695c 100%);--sb-fg:rgba(255,255,255,.9);--sb-hdr-fg:#fff;--mi-bg:rgba(255,255,255,.07);--mi-hover:rgba(255,255,255,.16);--mi-active:linear-gradient(135deg,rgba(29,233,182,.3),rgba(29,233,182,.12));--mi-active-fg:#fff;--mi-active-icon:#00897b;}\n:host([data-theme=\"auto\"]){--sb-bg:var(--ui-surface,#252b38);--sb-fg:var(--ui-text,#e2e6ec);--sb-hdr-fg:var(--ui-text,#fff);--mi-bg:color-mix(in srgb,var(--ui-text,#fff) 6%,transparent);--mi-fg:var(--ui-text,#e2e6ec);--mi-hover:color-mix(in srgb,var(--ui-text,#fff) 13%,transparent);--mi-active:color-mix(in srgb,var(--accent,#2680eb) 26%,transparent);--mi-active-border:color-mix(in srgb,var(--accent,#2680eb) 55%,transparent);--mi-active-fg:var(--ui-text,#fff);--mi-active-icon:var(--accent,#2680eb);}", "script": "export function init(instance){\n instance._bindingsRefresh();\n const IOB = window.IOB;\n const cfg = (IOB && IOB.config && IOB.config.navigation) || {};\n instance.setAttribute('data-theme', cfg.theme || 'default');\n function htmlFromDef(def){\n let attrs='';\n if(def.defaultAttributes) for(const a in def.defaultAttributes){ const v=def.defaultAttributes[a]; attrs+=' '+a+'=\"'+String(typeof v==='object'?JSON.stringify(v):v).replace(/\"/g,'"')+'\"'; }\n let style='';\n if(def.defaultStyles) for(const k in def.defaultStyles) style+=k+':'+def.defaultStyles[k]+';';\n if(def.defaultWidth) style+='width:'+def.defaultWidth+';';\n if(def.defaultHeight) style+='height:'+def.defaultHeight+';';\n return '<'+def.tag+attrs+(style?' style=\"'+style+'\"':'')+'>'+(def.defaultContent||'')+'</'+def.tag+'>';\n }\n function wireSlot(zone, cfgRoot, key){\n if(!zone) return;\n zone.innerHTML = (cfgRoot && cfgRoot.slots && cfgRoot.slots[key]) || '';\n if(!isDesigner) return;\n if(!zone.innerHTML.trim()){ zone.dataset.empty='1'; zone.style.minHeight='30px'; zone.style.outline='1px dashed rgba(255,255,255,.28)'; zone.style.borderRadius='8px'; zone.style.margin='4px 8px'; }\n (function(){\n let chip=null,target=null,hideT=null;\n const saveFromDom=()=>{ if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=Array.from(zone.children).map(c=>c.outerHTML).join(''); if(window.IOB&&window.IOB.saveConfig) window.IOB.saveConfig(); };\n const hideChip=()=>{ if(chip) chip.style.display='none'; target=null; };\n const ensureChip=()=>{ if(chip) return chip; chip=document.createElement('div'); chip.className='nav-slot-delchip'; chip.textContent='✕'; chip.title='Delete'; chip.style.cssText='position:fixed;z-index:2147483647;width:18px;height:18px;line-height:18px;text-align:center;font:700 12px/18px sans-serif;background:#e74c3c;color:#fff;border-radius:50%;cursor:pointer;box-shadow:0 1px 5px rgba(0,0,0,.45);user-select:none;display:none;'; chip.addEventListener('mouseenter',()=>{ if(hideT){clearTimeout(hideT);hideT=null;} }); chip.addEventListener('mouseleave',hideChip); chip.addEventListener('click',e=>{ e.preventDefault(); e.stopPropagation(); if(target&&target.parentNode===zone){ target.remove(); saveFromDom(); } hideChip(); }); document.body.appendChild(chip); return chip; };\n const showChip=(el)=>{ if(hideT){clearTimeout(hideT);hideT=null;} target=el; const c=ensureChip(); const r=el.getBoundingClientRect(); c.style.left=(r.right-9)+'px'; c.style.top=(r.top-9)+'px'; c.style.display='block'; };\n zone.addEventListener('mouseover', e=>{ let el=e.target; while(el && el!==zone && el.parentNode!==zone) el=el.parentNode; if(el && el.parentNode===zone && el.nodeType===1) showChip(el); });\n zone.addEventListener('mouseleave', ()=>{ hideT=setTimeout(()=>{ if(chip && chip.style.display!=='none' && !chip.matches(':hover')) hideChip(); }, 160); });\n })();\n zone.addEventListener('dragover', e=>{ if(e.dataTransfer && e.dataTransfer.types && e.dataTransfer.types.indexOf('text/json/elementdefintion')>=0){ e.preventDefault(); zone.style.background='rgba(255,255,255,.18)'; } });\n zone.addEventListener('dragleave', ()=>{ zone.style.background=''; });\n zone.addEventListener('drop', e=>{ e.preventDefault(); e.stopPropagation(); zone.style.background='';\n try{ const td=e.dataTransfer.getData('text/json/elementdefintion'); if(!td) return; const def=JSON.parse(td); const html=htmlFromDef(def);\n if(!cfgRoot.slots) cfgRoot.slots={}; cfgRoot.slots[key]=(cfgRoot.slots[key]||'')+html;\n if(window.IOB && window.IOB.saveConfig) window.IOB.saveConfig();\n }catch(err){ console.warn('[nav] slot drop failed', err); }\n });\n }\n const isDesigner = !!document.querySelector('iobroker-webui-app-shell');\n if(typeof isDesigner!=='undefined' && isDesigner){ try{ document.querySelectorAll('.nav-slot-delchip').forEach(n=>n.remove()); }catch(e){} }\n const hdr=instance._getDomElement('sbHdr'), items=instance._getDomElement('sbItems');\n if(cfg.bgColor) instance.style.setProperty('--sb-bg', cfg.bgColor);\n if(cfg.textColor){ instance.style.setProperty('--sb-fg', cfg.textColor); instance.style.setProperty('--mi-fg', cfg.textColor); }\n if(cfg.menuHeaderTextColor) instance.style.setProperty('--sb-hdr-fg', cfg.menuHeaderTextColor);\n if(cfg.bgColorSelected){ instance.style.setProperty('--mi-active', cfg.bgColorSelected); instance.style.setProperty('--mi-active-icon', cfg.bgColorSelected); }\n if(cfg.textColorSelected) instance.style.setProperty('--mi-active-fg', cfg.textColorSelected);\n if(hdr){ hdr.textContent=cfg.menuHeaderText||''; hdr.style.display=cfg.menuHeaderText?'':'none'; }\n let state='expanded'; const ICONS_W=64; let burger=null; let hasItems=false;\n const navWidth = cfg.menuWidth ? (/^\\d+$/.test(String(cfg.menuWidth))?cfg.menuWidth+'px':String(cfg.menuWidth)) : '220px';\n const setActive=(name)=>{ if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>{ mi.selected=(mi.screenName===name); }); };\n const navTo=(name)=>{ if(IOB&&IOB.changeView) IOB.changeView.emit(name); setActive(name); if(cfg.hideAfterSelection) setState('hidden'); };\n async function loadEntries(){\n if(!IOB||!IOB.getAllNames) return [];\n const names=await IOB.getAllNames('screen').catch(()=>[]);\n const list=[];\n for(const n of names){ const o=await IOB.getWebuiObject('screen',n).catch(()=>null); const nv=o&&o.settings&&o.settings.navigation; if(nv&&nv.show) list.push({name:n,title:nv.title||n,order:parseFloat(nv.order)||0,image:nv.image||''}); }\n list.sort((a,b)=>a.order-b.order); return list;\n }\n async function render(){\n const list=await loadEntries(); hasItems=list.length>0;\n if(items){ items.innerHTML='';\n for(const e of list){ const mi=document.createElement('webui-navigation-menu-item'); mi.screenName=e.name; mi.title=e.title; mi.image=e.image; mi.addEventListener('menuitemclick',()=>navTo(e.name)); items.appendChild(mi); }\n }\n if(!isDesigner){ if(hasItems && !cfg.hideMenu) applyRuntimeLayout(); else { const v=document.getElementById('viewer'); if(v){ v.style.left='0'; v.style.width='100%'; } instance.style.display='none'; } }\n }\n function applyRuntimeLayout(){\n const ab=document.getElementById('__nav-appbar-host'); const top=ab?ab.offsetHeight:0;\n instance.style.position='fixed'; instance.style.left='0'; instance.style.bottom='0'; instance.style.top=top+'px';\n instance.style.zIndex='99999'; instance.style.overflow='hidden'; instance.style.transition='width .2s ease,transform .2s ease';\n if(!burger){ burger=document.createElement('div'); burger.id='__nav-burger'; burger.textContent='☰'; const inBar=top>0; burger.style.cssText='position:fixed;top:'+(inBar?0:8)+'px;left:'+(inBar?0:8)+'px;z-index:100002;width:'+(inBar?top:42)+'px;height:'+(inBar?top:42)+'px;display:flex;align-items:center;justify-content:center;font-size:22px;cursor:pointer;'+(inBar?'':'border-radius:8px;')+'background:'+(cfg.showButtonBg||(inBar?'transparent':'rgba(0,0,0,.45)'))+';color:'+(cfg.showButtonColor||'#fff')+';transition:background .15s;'; burger.onclick=cycle; document.body.appendChild(burger); }\n setState(state);\n }\n function setState(s){\n state=s; const v=document.getElementById('viewer'); const top=parseInt(instance.style.top)||0; let left='0px';\n const compact=(state==='icons');\n if(items) items.querySelectorAll('webui-navigation-menu-item').forEach(mi=>mi.compact=compact);\n if(state==='expanded'){ instance.style.width=navWidth; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display=cfg.menuHeaderText?'':'none'; left=navWidth; }\n else if(state==='icons'){ instance.style.width=ICONS_W+'px'; instance.style.transform='translateX(0)'; if(hdr)hdr.style.display='none'; left=ICONS_W+'px'; }\n else { instance.style.transform='translateX(-110%)'; left='0px'; }\n if(v){ v.style.left=left; v.style.width='calc(100% - '+left+')'; v.style.top=top+'px'; v.style.height='calc(100% - '+top+'px)'; }\n }\n function cycle(){ const o=['expanded','icons','hidden']; let n=o[(o.indexOf(state)+1)%o.length]; if(n==='hidden'&&cfg.doNotHideCompletely)n='icons'; setState(n); }\n wireSlot(instance._getDomElement('slotTop'), cfg, 'top');\n wireSlot(instance._getDomElement('slotBottom'), cfg, 'bottom');\n render();\n if(IOB&&IOB.changeView) IOB.changeView.on(v=>setActive(v));\n const sv=document.getElementById('viewer'); if(sv&&sv.screenName) setActive(sv.screenName);\n}"}
|