@iobroker/gui-components 10.4.1 → 10.4.3

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/README.md CHANGED
@@ -36,7 +36,7 @@ npm create vite@latest src -- --template react-ts
36
36
  "dependencies": {
37
37
  "@emotion/react": "^11.14.0",
38
38
  "@emotion/styled": "^11.14.1",
39
- "@iobroker/gui-components": "^10.4.1",
39
+ "@iobroker/gui-components": "^10.4.3",
40
40
  "@mui/icons-material": "^9.0.1",
41
41
  "@mui/material": "^9.0.1",
42
42
  "react": "^19.2.5",
@@ -877,6 +877,13 @@ You can find the migration instructions:
877
877
  -->
878
878
 
879
879
  ## Changelog
880
+ ### 10.4.3 (2026-11-05)
881
+ - (@GermanBluefox) Fixed: `LoaderCommunity` uses the background of the theme (modernDark/modernLight) or neutral #121212 in dark
882
+
883
+ ### 10.4.2 (2026-11-05)
884
+ - (@GermanBluefox) Added: `ObjectBrowser` prop `objectsNotEditable`: objects read-only, state values still editable
885
+ - (@GermanBluefox) Added: `ObjectBrowser` checks the rights of each object: no edit or delete where the user may not
886
+
880
887
  ### 10.4.1 (2026-11-05)
881
888
  - (@GermanBluefox) Changed: `UploadImage`: the upload symbol is animated (it bobs while a file is dragged over the drop area and moves up while the file is read), the drop area pulses while a file is dragged over it, also over an existing image, and a new image fades in
882
889
  - (@GermanBluefox) Added: `UploadImage` got the props `uploading` and `uploadError`: while the parent uploads the image (e.g. writes it into a file) an upload arrow is shown in the corner of the image, followed by a check mark or a cross
@@ -10,6 +10,7 @@
10
10
  * Only SVG and CSS, no JavaScript timer. The previous loader is `LoaderCommunityLegacy`.
11
11
  */
12
12
  import React, { useEffect, useRef, useState } from 'react';
13
+ import { MODERN_DARK, MODERN_LIGHT } from '../../ThemeModern';
13
14
  /** The ring of the mark (img/logo.svg), used as mask for the light running through it */
14
15
  const RING_PATH = 'M386,91v68.6c68.2,25.1,117,90.5,117,167c0,98.1-80.2,177.9-178.7,177.9s-178.7-79.8-178.7-177.9c0-76.5,48.8-141.9,117-167V91C159.2,118.5,83.1,213.5,83.1,326.5c0,134.6,107.9,243.7,241.1,243.7s241.1-109.1,241.1-243.7C565.3,213.5,489.2,118.5,386,91z';
15
16
  const CAP_PATH = 'M324.2,148.6c12.4,0,24.5,1.3,36.3,3.7V85.6c-11.8-1.8-23.9-2.7-36.3-2.7s-24.4,0.9-36.3,2.7v66.7C299.7,149.9,311.8,148.6,324.2,148.6z';
@@ -35,7 +36,7 @@ const loaderStyles = `
35
36
  position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden;
36
37
  background-color: var(--iob-ld-bg); background-size: cover; background-position: center;
37
38
  }
38
- .iob-ld-dark { --iob-ld-bg: #080B1C; --iob-ld-orbit: rgba(126, 195, 243, 0.16); --iob-ld-node: #1D90CA; --iob-ld-packet: #7EC3F3; --iob-ld-label: #1D90CA; --iob-ld-flash: #E8F6FF; }
39
+ .iob-ld-dark { --iob-ld-bg: #121212; --iob-ld-orbit: rgba(126, 195, 243, 0.16); --iob-ld-node: #1D90CA; --iob-ld-packet: #7EC3F3; --iob-ld-label: #1D90CA; --iob-ld-flash: #E8F6FF; }
39
40
  .iob-ld-blue { --iob-ld-bg: #031D38; --iob-ld-orbit: rgba(126, 195, 243, 0.22); --iob-ld-node: #7EC3F3; --iob-ld-packet: #E8F6FF; --iob-ld-label: #7EC3F3; --iob-ld-flash: #E8F6FF; }
40
41
  .iob-ld-light { --iob-ld-bg: #FFFFFF; --iob-ld-orbit: rgba(0, 88, 148, 0.14); --iob-ld-node: #1D90CA; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }
41
42
  .iob-ld-colored { --iob-ld-bg: #E6EEF7; --iob-ld-orbit: rgba(0, 88, 148, 0.16); --iob-ld-node: #005894; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }
@@ -167,6 +168,13 @@ export function LoaderCommunity(props) {
167
168
  ? window.loadingBackgroundColor
168
169
  : undefined;
169
170
  const style = { backgroundImage, backgroundColor };
171
+ // The modern themes have an own page background: the loader must not bring one more color into play
172
+ if (themeName === 'modernDark') {
173
+ style['--iob-ld-bg'] = MODERN_DARK.background;
174
+ }
175
+ else if (themeName === 'modernLight') {
176
+ style['--iob-ld-bg'] = MODERN_LIGHT.background;
177
+ }
170
178
  if (props.size) {
171
179
  style['--iob-ld-mark'] = `min(${props.size}px, 30vmin)`;
172
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Community.js","sourceRoot":"./src/","sources":["Components/Loaders/Community.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright 2018-2026 Denis Haev (bluefox) <dogafox@gmail.com>\n *\n * MIT License\n *\n * Loader of the community edition: the ioBroker mark is a power button, so the loader switches ioBroker on.\n * 1. Switch on: the top of the \"i\" lights up, the ring draws itself from there to both sides, the stem grows.\n * 2. Wait: light runs through the ring, devices on an orbit send data packets into it.\n * 3. Open (only with `done`): the devices gather in the ring, the ring flashes and a circle opens from the centre.\n * Only SVG and CSS, no JavaScript timer. The previous loader is `LoaderCommunityLegacy`.\n */\nimport React, { useEffect, useRef, useState } from 'react';\nimport type { ThemeName, ThemeType } from '../../types';\n\ndeclare global {\n interface Window {\n loadingBackgroundImage: undefined | string;\n loadingBackgroundColor: undefined | string;\n loadingHideLogo: undefined | 'true';\n }\n}\n\n/** The ring of the mark (img/logo.svg), used as mask for the light running through it */\nconst RING_PATH =\n 'M386,91v68.6c68.2,25.1,117,90.5,117,167c0,98.1-80.2,177.9-178.7,177.9s-178.7-79.8-178.7-177.9c0-76.5,48.8-141.9,117-167V91C159.2,118.5,83.1,213.5,83.1,326.5c0,134.6,107.9,243.7,241.1,243.7s241.1-109.1,241.1-243.7C565.3,213.5,489.2,118.5,386,91z';\nconst CAP_PATH =\n 'M324.2,148.6c12.4,0,24.5,1.3,36.3,3.7V85.6c-11.8-1.8-23.9-2.7-36.3-2.7s-24.4,0.9-36.3,2.7v66.7C299.7,149.9,311.8,148.6,324.2,148.6z';\nconst STEM_PATH =\n 'M324.2,175.3c-12.5,0-24.6,1.5-36.2,4.4v291c11.6,2.9,23.8,4.4,36.2,4.4c12.5,0,24.6-1.5,36.3-4.4v-291C348.9,176.9,336.7,175.3,324.2,175.3z';\nconst RING_MASK = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 648.5 652.8'%3E%3Cpath d='${RING_PATH}'/%3E%3C/svg%3E\") center / 100% 100% no-repeat`;\n\n/** Devices on the orbit: angle in degrees, radius of the dot, delay of its packet in seconds */\nconst NODES: [number, number, number][] = [\n [14, 10, 0],\n [66, 7, 1.9],\n [117, 12, 0.7],\n [163, 8, 3.1],\n [208, 10, 1.3],\n [259, 7, 2.5],\n [312, 9, 3.7],\n];\n\n/** Duration of the closing animation in ms (reduced motion: crossfade) */\nconst OPEN_DURATION = 1150;\nconst OPEN_DURATION_REDUCED = 350;\n\nconst loaderStyles = `\n@property --iob-ld-hole { syntax: '<length>'; inherits: false; initial-value: 0px; }\n.iob-ld {\n --iob-ld-mark: min(210px, 30vmin);\n position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden;\n background-color: var(--iob-ld-bg); background-size: cover; background-position: center;\n}\n.iob-ld-dark { --iob-ld-bg: #080B1C; --iob-ld-orbit: rgba(126, 195, 243, 0.16); --iob-ld-node: #1D90CA; --iob-ld-packet: #7EC3F3; --iob-ld-label: #1D90CA; --iob-ld-flash: #E8F6FF; }\n.iob-ld-blue { --iob-ld-bg: #031D38; --iob-ld-orbit: rgba(126, 195, 243, 0.22); --iob-ld-node: #7EC3F3; --iob-ld-packet: #E8F6FF; --iob-ld-label: #7EC3F3; --iob-ld-flash: #E8F6FF; }\n.iob-ld-light { --iob-ld-bg: #FFFFFF; --iob-ld-orbit: rgba(0, 88, 148, 0.14); --iob-ld-node: #1D90CA; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }\n.iob-ld-colored { --iob-ld-bg: #E6EEF7; --iob-ld-orbit: rgba(0, 88, 148, 0.16); --iob-ld-node: #005894; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }\n\n.iob-ld-orbit {\n position: absolute; left: 50%; top: 50%; width: calc(var(--iob-ld-mark) * 1.8505); height: calc(var(--iob-ld-mark) * 1.8505);\n transform: translate(-50%, -50%); overflow: visible; opacity: 0;\n animation: iob-ld-fade-in 0.9s 0.95s forwards;\n}\n.iob-ld-orbit-line { fill: none; stroke: var(--iob-ld-orbit); stroke-width: 2; stroke-dasharray: 2 12; }\n.iob-ld-gather, .iob-ld-nodes { transform-box: view-box; transform-origin: 0 0; }\n.iob-ld-nodes { animation: iob-ld-spin 90s linear infinite; }\n.iob-ld-dot { fill: var(--iob-ld-node); transform-box: fill-box; transform-origin: center; animation: iob-ld-send 4.2s ease-out infinite; }\n.iob-ld-packet { fill: var(--iob-ld-packet); opacity: 0; animation: iob-ld-travel 4.2s infinite; }\n.iob-ld-splash { fill: none; stroke: var(--iob-ld-packet); stroke-width: 3; opacity: 0; transform-box: fill-box; transform-origin: center; animation: iob-ld-splash 4.2s infinite; }\n\n.iob-ld-mark { position: absolute; left: 50%; top: 50%; width: var(--iob-ld-mark); aspect-ratio: 648.5 / 652.8; transform: translate(-50%, -50%); }\n.iob-ld-mark svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: visible; }\n.iob-ld-draw { fill: none; stroke: #FFFFFF; stroke-width: 90; stroke-dasharray: 1 1; stroke-dashoffset: 1; animation: iob-ld-draw 0.62s cubic-bezier(0.6, 0, 0.2, 1) 0.3s forwards; }\n.iob-ld-cap { transform-box: fill-box; transform-origin: center; animation: iob-ld-pop 0.42s cubic-bezier(0.3, 1.7, 0.5, 1) both; }\n.iob-ld-stem { transform-box: fill-box; transform-origin: 50% 100%; animation: iob-ld-grow 0.42s cubic-bezier(0.2, 0.8, 0.2, 1) 0.8s both; }\n.iob-ld-flash { fill: var(--iob-ld-flash); opacity: 0; }\n.iob-ld-current { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; animation: iob-ld-fade-in 0.5s 1.15s forwards; -webkit-mask: ${RING_MASK}; mask: ${RING_MASK}; }\n.iob-ld-current > div {\n position: absolute; top: 0; left: 0; width: 100%; height: 100%;\n background: conic-gradient(from 0deg, transparent 0turn 0.68turn, rgba(126, 195, 243, 0.55) 0.95turn, rgba(232, 246, 255, 0.95) 1turn);\n animation: iob-ld-spin 1.9s linear infinite;\n}\n.iob-ld-status {\n position: absolute; left: 0; right: 0; top: calc(50% + var(--iob-ld-mark) * 0.86);\n text-align: center; padding: 0 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n font: 500 13px/1.5 Roboto, Arial, sans-serif; letter-spacing: 0.08em; text-transform: uppercase; color: var(--iob-ld-label);\n opacity: 0; animation: iob-ld-fade-in 0.5s 1s forwards;\n}\n.iob-ld-status::before { content: '// '; }\n\n.iob-ld-done {\n -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent var(--iob-ld-hole), #000 calc(var(--iob-ld-hole) + 1px));\n mask-image: radial-gradient(circle at 50% 50%, transparent var(--iob-ld-hole), #000 calc(var(--iob-ld-hole) + 1px));\n animation: iob-ld-open 0.75s 0.3s cubic-bezier(0.6, 0, 0.4, 1) forwards;\n}\n.iob-ld-done .iob-ld-gather { animation: iob-ld-gather 0.45s cubic-bezier(0.5, 0, 0.9, 0.4) forwards; }\n.iob-ld-done .iob-ld-flash { animation: iob-ld-flash 0.5s 0.12s ease-out; }\n.iob-ld-done .iob-ld-mark { animation: iob-ld-mark-out 0.6s 0.35s cubic-bezier(0.5, 0, 0.75, 0) forwards; }\n.iob-ld-done .iob-ld-status { animation: none; opacity: 0; transition: opacity 0.2s; }\n\n@keyframes iob-ld-fade-in { to { opacity: 1; } }\n@keyframes iob-ld-fade-out { to { opacity: 0; } }\n@keyframes iob-ld-spin { to { transform: rotate(360deg); } }\n@keyframes iob-ld-draw { to { stroke-dashoffset: 0; } }\n@keyframes iob-ld-pop { from { transform: scale(0); opacity: 0; } to { transform: scale(1); opacity: 1; } }\n@keyframes iob-ld-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }\n@keyframes iob-ld-send { 0% { transform: scale(1.5); } 12%, 100% { transform: scale(1); } }\n@keyframes iob-ld-travel {\n 0% { transform: translateY(0); opacity: 0; animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.6); }\n 6% { opacity: 1; }\n 36% { transform: translateY(176px); opacity: 1; }\n 38%, 100% { transform: translateY(176px); opacity: 0; }\n}\n@keyframes iob-ld-splash {\n 0%, 35% { transform: scale(0.2); opacity: 0; }\n 38% { transform: scale(0.5); opacity: 0.9; }\n 58%, 100% { transform: scale(2); opacity: 0; }\n}\n@keyframes iob-ld-gather { to { transform: scale(0.5); opacity: 0; } }\n@keyframes iob-ld-flash { 0% { opacity: 0; } 35% { opacity: 0.85; } 100% { opacity: 0; } }\n@keyframes iob-ld-mark-out { to { transform: translate(-50%, -50%) scale(1.4); opacity: 0; } }\n@keyframes iob-ld-open { to { --iob-ld-hole: 120vmax; } }\n\n@media (prefers-reduced-motion: reduce) {\n .iob-ld-orbit, .iob-ld-current { display: none; }\n .iob-ld-draw { animation: none; stroke-dashoffset: 0; }\n .iob-ld-cap, .iob-ld-stem { animation: none; }\n .iob-ld-mark { opacity: 0; animation: iob-ld-fade-in 0.3s forwards; }\n .iob-ld-status { animation: iob-ld-fade-in 0.3s forwards; }\n .iob-ld-done { -webkit-mask-image: none; mask-image: none; animation: iob-ld-fade-out 0.3s forwards; }\n .iob-ld-done .iob-ld-mark { animation: none; opacity: 1; }\n}\n`;\n\ninterface LoaderProps {\n /** Width of the mark in pixels (default 210); on small screens it shrinks to 30% of the shorter side. */\n size?: number;\n /** The chosen theme type. */\n themeType?: ThemeType;\n /** Theme name */\n themeName?: ThemeName;\n /** @deprecated Theme name. use themeName instead */\n theme?: ThemeName;\n /** Background color */\n backgroundColor?: string;\n /** Background image URL */\n backgroundImage?: string;\n /** Optional line below the mark, e.g. the current loading step. Shown in capitals with a leading \"//\". */\n status?: string;\n /** Play the closing animation: the loader opens and shows what is rendered below it. */\n done?: boolean;\n /** Called when the closing animation is over, so the loader can be removed. */\n onFinished?: () => void;\n}\n\nlet maskCounter = 0;\n\nexport function LoaderCommunity(props: LoaderProps): React.JSX.Element {\n // Every instance needs its own mask id, there may be more than one loader on a page\n const [maskId] = useState(() => `iob-ld-draw-${++maskCounter}`);\n\n useEffect(() => {\n if (!window.document.getElementById('loader-iobroker-community')) {\n const style = window.document.createElement('style');\n style.setAttribute('id', 'loader-iobroker-community');\n style.innerHTML = loaderStyles;\n window.document.head.appendChild(style);\n }\n }, []);\n\n // Kept in a ref, so a new callback on every render of the parent does not restart the timer\n const onFinished = useRef(props.onFinished);\n useEffect(() => {\n onFinished.current = props.onFinished;\n }, [props.onFinished]);\n\n const { done } = props;\n useEffect(() => {\n if (!done) {\n return undefined;\n }\n const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n const timer = setTimeout(() => onFinished.current?.(), reduced ? OPEN_DURATION_REDUCED : OPEN_DURATION);\n return () => clearTimeout(timer);\n }, [done]);\n\n // \"blue\" and \"colored\" have own looks; \"modernDark\" is dark; other names (PT, DX...) follow themeType\n const themeName = props.themeName || props.theme;\n let theme: 'dark' | 'light' | 'blue' | 'colored';\n if (themeName === 'blue' || themeName === 'colored') {\n theme = themeName;\n } else if (themeName === 'dark' || themeName === 'modernDark') {\n theme = 'dark';\n } else if (themeName === 'light' || themeName === 'modernLight') {\n theme = 'light';\n } else {\n theme = props.themeType === 'dark' ? 'dark' : 'light';\n }\n\n const backgroundImage =\n props.backgroundImage && props.backgroundImage !== '@@loginBackgroundImage@@'\n ? props.backgroundImage\n : window.loadingBackgroundImage && window.loadingBackgroundImage !== '@@loginBackgroundImage@@'\n ? `url(${window.loadingBackgroundImage})`\n : undefined;\n const backgroundColor =\n props.backgroundColor && props.backgroundColor !== '@@loginBackgroundColor@@'\n ? props.backgroundColor\n : window.loadingBackgroundColor && window.loadingBackgroundColor !== '@@loginBackgroundColor@@'\n ? window.loadingBackgroundColor\n : undefined;\n\n const style: React.CSSProperties & Record<string, string | undefined> = { backgroundImage, backgroundColor };\n if (props.size) {\n style['--iob-ld-mark'] = `min(${props.size}px, 30vmin)`;\n }\n\n return (\n <div\n className={`iob-ld iob-ld-${theme}${done ? ' iob-ld-done' : ''}`}\n style={style}\n aria-busy={!done}\n >\n <svg\n className=\"iob-ld-orbit\"\n viewBox=\"-600 -600 1200 1200\"\n aria-hidden=\"true\"\n >\n <circle\n className=\"iob-ld-orbit-line\"\n r=\"430\"\n />\n <g className=\"iob-ld-gather\">\n <g className=\"iob-ld-nodes\">\n {NODES.map(([angle, radius, delay]) => {\n const animationDelay = `${delay}s`;\n return (\n <g\n key={angle}\n transform={`rotate(${angle})`}\n >\n <circle\n className=\"iob-ld-splash\"\n cy=\"-252\"\n r=\"16\"\n style={{ animationDelay }}\n />\n <circle\n className=\"iob-ld-packet\"\n cy=\"-430\"\n r=\"7\"\n style={{ animationDelay }}\n />\n <circle\n className=\"iob-ld-dot\"\n cy=\"-430\"\n r={radius}\n style={{ animationDelay }}\n />\n </g>\n );\n })}\n </g>\n </g>\n </svg>\n {window.loadingHideLogo === 'true' ? null : (\n <div\n className=\"iob-ld-mark\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 648.5 652.8\">\n <defs>\n <mask\n id={maskId}\n maskUnits=\"userSpaceOnUse\"\n x=\"0\"\n y=\"0\"\n width=\"648.5\"\n height=\"652.8\"\n >\n <path\n className=\"iob-ld-draw\"\n pathLength={1}\n d=\"M324.2,116.5A210,210 0 0 1 324.2,536.5\"\n />\n <path\n className=\"iob-ld-draw\"\n pathLength={1}\n d=\"M324.2,116.5A210,210 0 0 0 324.2,536.5\"\n />\n </mask>\n </defs>\n <path\n mask={`url(#${maskId})`}\n fill=\"#164477\"\n d={RING_PATH}\n />\n <path\n className=\"iob-ld-cap\"\n fill=\"#3399CC\"\n d={CAP_PATH}\n />\n <path\n className=\"iob-ld-stem\"\n fill=\"#3399CC\"\n d={STEM_PATH}\n />\n <path\n className=\"iob-ld-flash\"\n d={RING_PATH}\n />\n </svg>\n <div className=\"iob-ld-current\">\n <div />\n </div>\n </div>\n )}\n {props.status ? (\n <div\n className=\"iob-ld-status\"\n role=\"status\"\n >\n {props.status}\n </div>\n ) : null}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"Community.js","sourceRoot":"./src/","sources":["Components/Loaders/Community.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright 2018-2026 Denis Haev (bluefox) <dogafox@gmail.com>\n *\n * MIT License\n *\n * Loader of the community edition: the ioBroker mark is a power button, so the loader switches ioBroker on.\n * 1. Switch on: the top of the \"i\" lights up, the ring draws itself from there to both sides, the stem grows.\n * 2. Wait: light runs through the ring, devices on an orbit send data packets into it.\n * 3. Open (only with `done`): the devices gather in the ring, the ring flashes and a circle opens from the centre.\n * Only SVG and CSS, no JavaScript timer. The previous loader is `LoaderCommunityLegacy`.\n */\nimport React, { useEffect, useRef, useState } from 'react';\nimport type { ThemeName, ThemeType } from '../../types';\nimport { MODERN_DARK, MODERN_LIGHT } from '../../ThemeModern';\n\ndeclare global {\n interface Window {\n loadingBackgroundImage: undefined | string;\n loadingBackgroundColor: undefined | string;\n loadingHideLogo: undefined | 'true';\n }\n}\n\n/** The ring of the mark (img/logo.svg), used as mask for the light running through it */\nconst RING_PATH =\n 'M386,91v68.6c68.2,25.1,117,90.5,117,167c0,98.1-80.2,177.9-178.7,177.9s-178.7-79.8-178.7-177.9c0-76.5,48.8-141.9,117-167V91C159.2,118.5,83.1,213.5,83.1,326.5c0,134.6,107.9,243.7,241.1,243.7s241.1-109.1,241.1-243.7C565.3,213.5,489.2,118.5,386,91z';\nconst CAP_PATH =\n 'M324.2,148.6c12.4,0,24.5,1.3,36.3,3.7V85.6c-11.8-1.8-23.9-2.7-36.3-2.7s-24.4,0.9-36.3,2.7v66.7C299.7,149.9,311.8,148.6,324.2,148.6z';\nconst STEM_PATH =\n 'M324.2,175.3c-12.5,0-24.6,1.5-36.2,4.4v291c11.6,2.9,23.8,4.4,36.2,4.4c12.5,0,24.6-1.5,36.3-4.4v-291C348.9,176.9,336.7,175.3,324.2,175.3z';\nconst RING_MASK = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 648.5 652.8'%3E%3Cpath d='${RING_PATH}'/%3E%3C/svg%3E\") center / 100% 100% no-repeat`;\n\n/** Devices on the orbit: angle in degrees, radius of the dot, delay of its packet in seconds */\nconst NODES: [number, number, number][] = [\n [14, 10, 0],\n [66, 7, 1.9],\n [117, 12, 0.7],\n [163, 8, 3.1],\n [208, 10, 1.3],\n [259, 7, 2.5],\n [312, 9, 3.7],\n];\n\n/** Duration of the closing animation in ms (reduced motion: crossfade) */\nconst OPEN_DURATION = 1150;\nconst OPEN_DURATION_REDUCED = 350;\n\nconst loaderStyles = `\n@property --iob-ld-hole { syntax: '<length>'; inherits: false; initial-value: 0px; }\n.iob-ld {\n --iob-ld-mark: min(210px, 30vmin);\n position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden;\n background-color: var(--iob-ld-bg); background-size: cover; background-position: center;\n}\n.iob-ld-dark { --iob-ld-bg: #121212; --iob-ld-orbit: rgba(126, 195, 243, 0.16); --iob-ld-node: #1D90CA; --iob-ld-packet: #7EC3F3; --iob-ld-label: #1D90CA; --iob-ld-flash: #E8F6FF; }\n.iob-ld-blue { --iob-ld-bg: #031D38; --iob-ld-orbit: rgba(126, 195, 243, 0.22); --iob-ld-node: #7EC3F3; --iob-ld-packet: #E8F6FF; --iob-ld-label: #7EC3F3; --iob-ld-flash: #E8F6FF; }\n.iob-ld-light { --iob-ld-bg: #FFFFFF; --iob-ld-orbit: rgba(0, 88, 148, 0.14); --iob-ld-node: #1D90CA; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }\n.iob-ld-colored { --iob-ld-bg: #E6EEF7; --iob-ld-orbit: rgba(0, 88, 148, 0.16); --iob-ld-node: #005894; --iob-ld-packet: #1D90CA; --iob-ld-label: #005894; --iob-ld-flash: #FFFFFF; }\n\n.iob-ld-orbit {\n position: absolute; left: 50%; top: 50%; width: calc(var(--iob-ld-mark) * 1.8505); height: calc(var(--iob-ld-mark) * 1.8505);\n transform: translate(-50%, -50%); overflow: visible; opacity: 0;\n animation: iob-ld-fade-in 0.9s 0.95s forwards;\n}\n.iob-ld-orbit-line { fill: none; stroke: var(--iob-ld-orbit); stroke-width: 2; stroke-dasharray: 2 12; }\n.iob-ld-gather, .iob-ld-nodes { transform-box: view-box; transform-origin: 0 0; }\n.iob-ld-nodes { animation: iob-ld-spin 90s linear infinite; }\n.iob-ld-dot { fill: var(--iob-ld-node); transform-box: fill-box; transform-origin: center; animation: iob-ld-send 4.2s ease-out infinite; }\n.iob-ld-packet { fill: var(--iob-ld-packet); opacity: 0; animation: iob-ld-travel 4.2s infinite; }\n.iob-ld-splash { fill: none; stroke: var(--iob-ld-packet); stroke-width: 3; opacity: 0; transform-box: fill-box; transform-origin: center; animation: iob-ld-splash 4.2s infinite; }\n\n.iob-ld-mark { position: absolute; left: 50%; top: 50%; width: var(--iob-ld-mark); aspect-ratio: 648.5 / 652.8; transform: translate(-50%, -50%); }\n.iob-ld-mark svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: visible; }\n.iob-ld-draw { fill: none; stroke: #FFFFFF; stroke-width: 90; stroke-dasharray: 1 1; stroke-dashoffset: 1; animation: iob-ld-draw 0.62s cubic-bezier(0.6, 0, 0.2, 1) 0.3s forwards; }\n.iob-ld-cap { transform-box: fill-box; transform-origin: center; animation: iob-ld-pop 0.42s cubic-bezier(0.3, 1.7, 0.5, 1) both; }\n.iob-ld-stem { transform-box: fill-box; transform-origin: 50% 100%; animation: iob-ld-grow 0.42s cubic-bezier(0.2, 0.8, 0.2, 1) 0.8s both; }\n.iob-ld-flash { fill: var(--iob-ld-flash); opacity: 0; }\n.iob-ld-current { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; animation: iob-ld-fade-in 0.5s 1.15s forwards; -webkit-mask: ${RING_MASK}; mask: ${RING_MASK}; }\n.iob-ld-current > div {\n position: absolute; top: 0; left: 0; width: 100%; height: 100%;\n background: conic-gradient(from 0deg, transparent 0turn 0.68turn, rgba(126, 195, 243, 0.55) 0.95turn, rgba(232, 246, 255, 0.95) 1turn);\n animation: iob-ld-spin 1.9s linear infinite;\n}\n.iob-ld-status {\n position: absolute; left: 0; right: 0; top: calc(50% + var(--iob-ld-mark) * 0.86);\n text-align: center; padding: 0 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n font: 500 13px/1.5 Roboto, Arial, sans-serif; letter-spacing: 0.08em; text-transform: uppercase; color: var(--iob-ld-label);\n opacity: 0; animation: iob-ld-fade-in 0.5s 1s forwards;\n}\n.iob-ld-status::before { content: '// '; }\n\n.iob-ld-done {\n -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent var(--iob-ld-hole), #000 calc(var(--iob-ld-hole) + 1px));\n mask-image: radial-gradient(circle at 50% 50%, transparent var(--iob-ld-hole), #000 calc(var(--iob-ld-hole) + 1px));\n animation: iob-ld-open 0.75s 0.3s cubic-bezier(0.6, 0, 0.4, 1) forwards;\n}\n.iob-ld-done .iob-ld-gather { animation: iob-ld-gather 0.45s cubic-bezier(0.5, 0, 0.9, 0.4) forwards; }\n.iob-ld-done .iob-ld-flash { animation: iob-ld-flash 0.5s 0.12s ease-out; }\n.iob-ld-done .iob-ld-mark { animation: iob-ld-mark-out 0.6s 0.35s cubic-bezier(0.5, 0, 0.75, 0) forwards; }\n.iob-ld-done .iob-ld-status { animation: none; opacity: 0; transition: opacity 0.2s; }\n\n@keyframes iob-ld-fade-in { to { opacity: 1; } }\n@keyframes iob-ld-fade-out { to { opacity: 0; } }\n@keyframes iob-ld-spin { to { transform: rotate(360deg); } }\n@keyframes iob-ld-draw { to { stroke-dashoffset: 0; } }\n@keyframes iob-ld-pop { from { transform: scale(0); opacity: 0; } to { transform: scale(1); opacity: 1; } }\n@keyframes iob-ld-grow { from { transform: scaleY(0); } to { transform: scaleY(1); } }\n@keyframes iob-ld-send { 0% { transform: scale(1.5); } 12%, 100% { transform: scale(1); } }\n@keyframes iob-ld-travel {\n 0% { transform: translateY(0); opacity: 0; animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.6); }\n 6% { opacity: 1; }\n 36% { transform: translateY(176px); opacity: 1; }\n 38%, 100% { transform: translateY(176px); opacity: 0; }\n}\n@keyframes iob-ld-splash {\n 0%, 35% { transform: scale(0.2); opacity: 0; }\n 38% { transform: scale(0.5); opacity: 0.9; }\n 58%, 100% { transform: scale(2); opacity: 0; }\n}\n@keyframes iob-ld-gather { to { transform: scale(0.5); opacity: 0; } }\n@keyframes iob-ld-flash { 0% { opacity: 0; } 35% { opacity: 0.85; } 100% { opacity: 0; } }\n@keyframes iob-ld-mark-out { to { transform: translate(-50%, -50%) scale(1.4); opacity: 0; } }\n@keyframes iob-ld-open { to { --iob-ld-hole: 120vmax; } }\n\n@media (prefers-reduced-motion: reduce) {\n .iob-ld-orbit, .iob-ld-current { display: none; }\n .iob-ld-draw { animation: none; stroke-dashoffset: 0; }\n .iob-ld-cap, .iob-ld-stem { animation: none; }\n .iob-ld-mark { opacity: 0; animation: iob-ld-fade-in 0.3s forwards; }\n .iob-ld-status { animation: iob-ld-fade-in 0.3s forwards; }\n .iob-ld-done { -webkit-mask-image: none; mask-image: none; animation: iob-ld-fade-out 0.3s forwards; }\n .iob-ld-done .iob-ld-mark { animation: none; opacity: 1; }\n}\n`;\n\ninterface LoaderProps {\n /** Width of the mark in pixels (default 210); on small screens it shrinks to 30% of the shorter side. */\n size?: number;\n /** The chosen theme type. */\n themeType?: ThemeType;\n /** Theme name */\n themeName?: ThemeName;\n /** @deprecated Theme name. use themeName instead */\n theme?: ThemeName;\n /** Background color */\n backgroundColor?: string;\n /** Background image URL */\n backgroundImage?: string;\n /** Optional line below the mark, e.g. the current loading step. Shown in capitals with a leading \"//\". */\n status?: string;\n /** Play the closing animation: the loader opens and shows what is rendered below it. */\n done?: boolean;\n /** Called when the closing animation is over, so the loader can be removed. */\n onFinished?: () => void;\n}\n\nlet maskCounter = 0;\n\nexport function LoaderCommunity(props: LoaderProps): React.JSX.Element {\n // Every instance needs its own mask id, there may be more than one loader on a page\n const [maskId] = useState(() => `iob-ld-draw-${++maskCounter}`);\n\n useEffect(() => {\n if (!window.document.getElementById('loader-iobroker-community')) {\n const style = window.document.createElement('style');\n style.setAttribute('id', 'loader-iobroker-community');\n style.innerHTML = loaderStyles;\n window.document.head.appendChild(style);\n }\n }, []);\n\n // Kept in a ref, so a new callback on every render of the parent does not restart the timer\n const onFinished = useRef(props.onFinished);\n useEffect(() => {\n onFinished.current = props.onFinished;\n }, [props.onFinished]);\n\n const { done } = props;\n useEffect(() => {\n if (!done) {\n return undefined;\n }\n const reduced = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;\n const timer = setTimeout(() => onFinished.current?.(), reduced ? OPEN_DURATION_REDUCED : OPEN_DURATION);\n return () => clearTimeout(timer);\n }, [done]);\n\n // \"blue\" and \"colored\" have own looks; \"modernDark\" is dark; other names (PT, DX...) follow themeType\n const themeName = props.themeName || props.theme;\n let theme: 'dark' | 'light' | 'blue' | 'colored';\n if (themeName === 'blue' || themeName === 'colored') {\n theme = themeName;\n } else if (themeName === 'dark' || themeName === 'modernDark') {\n theme = 'dark';\n } else if (themeName === 'light' || themeName === 'modernLight') {\n theme = 'light';\n } else {\n theme = props.themeType === 'dark' ? 'dark' : 'light';\n }\n\n const backgroundImage =\n props.backgroundImage && props.backgroundImage !== '@@loginBackgroundImage@@'\n ? props.backgroundImage\n : window.loadingBackgroundImage && window.loadingBackgroundImage !== '@@loginBackgroundImage@@'\n ? `url(${window.loadingBackgroundImage})`\n : undefined;\n const backgroundColor =\n props.backgroundColor && props.backgroundColor !== '@@loginBackgroundColor@@'\n ? props.backgroundColor\n : window.loadingBackgroundColor && window.loadingBackgroundColor !== '@@loginBackgroundColor@@'\n ? window.loadingBackgroundColor\n : undefined;\n\n const style: React.CSSProperties & Record<string, string | undefined> = { backgroundImage, backgroundColor };\n // The modern themes have an own page background: the loader must not bring one more color into play\n if (themeName === 'modernDark') {\n style['--iob-ld-bg'] = MODERN_DARK.background;\n } else if (themeName === 'modernLight') {\n style['--iob-ld-bg'] = MODERN_LIGHT.background;\n }\n if (props.size) {\n style['--iob-ld-mark'] = `min(${props.size}px, 30vmin)`;\n }\n\n return (\n <div\n className={`iob-ld iob-ld-${theme}${done ? ' iob-ld-done' : ''}`}\n style={style}\n aria-busy={!done}\n >\n <svg\n className=\"iob-ld-orbit\"\n viewBox=\"-600 -600 1200 1200\"\n aria-hidden=\"true\"\n >\n <circle\n className=\"iob-ld-orbit-line\"\n r=\"430\"\n />\n <g className=\"iob-ld-gather\">\n <g className=\"iob-ld-nodes\">\n {NODES.map(([angle, radius, delay]) => {\n const animationDelay = `${delay}s`;\n return (\n <g\n key={angle}\n transform={`rotate(${angle})`}\n >\n <circle\n className=\"iob-ld-splash\"\n cy=\"-252\"\n r=\"16\"\n style={{ animationDelay }}\n />\n <circle\n className=\"iob-ld-packet\"\n cy=\"-430\"\n r=\"7\"\n style={{ animationDelay }}\n />\n <circle\n className=\"iob-ld-dot\"\n cy=\"-430\"\n r={radius}\n style={{ animationDelay }}\n />\n </g>\n );\n })}\n </g>\n </g>\n </svg>\n {window.loadingHideLogo === 'true' ? null : (\n <div\n className=\"iob-ld-mark\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 648.5 652.8\">\n <defs>\n <mask\n id={maskId}\n maskUnits=\"userSpaceOnUse\"\n x=\"0\"\n y=\"0\"\n width=\"648.5\"\n height=\"652.8\"\n >\n <path\n className=\"iob-ld-draw\"\n pathLength={1}\n d=\"M324.2,116.5A210,210 0 0 1 324.2,536.5\"\n />\n <path\n className=\"iob-ld-draw\"\n pathLength={1}\n d=\"M324.2,116.5A210,210 0 0 0 324.2,536.5\"\n />\n </mask>\n </defs>\n <path\n mask={`url(#${maskId})`}\n fill=\"#164477\"\n d={RING_PATH}\n />\n <path\n className=\"iob-ld-cap\"\n fill=\"#3399CC\"\n d={CAP_PATH}\n />\n <path\n className=\"iob-ld-stem\"\n fill=\"#3399CC\"\n d={STEM_PATH}\n />\n <path\n className=\"iob-ld-flash\"\n d={RING_PATH}\n />\n </svg>\n <div className=\"iob-ld-current\">\n <div />\n </div>\n </div>\n )}\n {props.status ? (\n <div\n className=\"iob-ld-status\"\n role=\"status\"\n >\n {props.status}\n </div>\n ) : null}\n </div>\n );\n}\n"]}
@@ -248,6 +248,17 @@ export declare class ObjectBrowserClass extends Component<ObjectBrowserProps, Ob
248
248
  * @param id ID of the object
249
249
  */
250
250
  isDeleteAllowed(id: string): boolean;
251
+ /**
252
+ * May the user change this object (`object`) or the value of this state (`state`)? Asks the
253
+ * connection, which knows the rights of the group of the user and checks the rights of the object
254
+ * itself (`obj.acl`) as js-controller does. Without known rights - or with a connection that
255
+ * cannot tell - the answer is yes and the server decides.
256
+ *
257
+ * @param obj the object
258
+ * @param part `object` for the object itself, `state` for the value of a state
259
+ * @param operation `delete` to delete the object
260
+ */
261
+ mayChange(obj: ioBroker.Object | null | undefined, part?: 'object' | 'state', operation?: 'write' | 'delete'): boolean;
251
262
  /**
252
263
  * Show the deletion dialog for a given object
253
264
  */
@@ -845,6 +845,22 @@ export class ObjectBrowserClass extends Component {
845
845
  isDeleteAllowed(id) {
846
846
  return this.state.filter.expertMode || USER_OWNED_NAMESPACES.some(prefix => id.startsWith(prefix));
847
847
  }
848
+ /**
849
+ * May the user change this object (`object`) or the value of this state (`state`)? Asks the
850
+ * connection, which knows the rights of the group of the user and checks the rights of the object
851
+ * itself (`obj.acl`) as js-controller does. Without known rights - or with a connection that
852
+ * cannot tell - the answer is yes and the server decides.
853
+ *
854
+ * @param obj the object
855
+ * @param part `object` for the object itself, `state` for the value of a state
856
+ * @param operation `delete` to delete the object
857
+ */
858
+ mayChange(obj, part = 'object', operation = 'write') {
859
+ // `hasObjectPermission` came with @iobroker/socket-client 5.6.0. Described here, so that this
860
+ // compiles and runs with an older socket client as well - which does not know the method
861
+ const socket = this.props.socket;
862
+ return typeof socket?.hasObjectPermission !== 'function' || socket.hasObjectPermission(obj, operation, part);
863
+ }
848
864
  /**
849
865
  * Show the deletion dialog for a given object
850
866
  */