@iobroker/gui-components 10.3.14 → 10.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +12 -1
  2. package/build/Components/AnimatedIcons/IconBell.d.ts +21 -0
  3. package/build/Components/AnimatedIcons/IconBell.js +50 -0
  4. package/build/Components/AnimatedIcons/IconBell.js.map +1 -0
  5. package/build/Components/AnimatedIcons/IconConnectionState.d.ts +19 -0
  6. package/build/Components/AnimatedIcons/IconConnectionState.js +25 -0
  7. package/build/Components/AnimatedIcons/IconConnectionState.js.map +1 -0
  8. package/build/Components/AnimatedIcons/IconExpertMode.d.ts +22 -0
  9. package/build/Components/AnimatedIcons/IconExpertMode.js +47 -0
  10. package/build/Components/AnimatedIcons/IconExpertMode.js.map +1 -0
  11. package/build/Components/AnimatedIcons/IconFolder.d.ts +22 -0
  12. package/build/Components/AnimatedIcons/IconFolder.js +29 -0
  13. package/build/Components/AnimatedIcons/IconFolder.js.map +1 -0
  14. package/build/Components/AnimatedIcons/IconMenuArrow.d.ts +23 -0
  15. package/build/Components/AnimatedIcons/IconMenuArrow.js +26 -0
  16. package/build/Components/AnimatedIcons/IconMenuArrow.js.map +1 -0
  17. package/build/Components/AnimatedIcons/IconPlayPause.d.ts +23 -0
  18. package/build/Components/AnimatedIcons/IconPlayPause.js +25 -0
  19. package/build/Components/AnimatedIcons/IconPlayPause.js.map +1 -0
  20. package/build/Components/AnimatedIcons/IconRefresh.d.ts +23 -0
  21. package/build/Components/AnimatedIcons/IconRefresh.js +86 -0
  22. package/build/Components/AnimatedIcons/IconRefresh.js.map +1 -0
  23. package/build/Components/AnimatedIcons/IconResult.d.ts +21 -0
  24. package/build/Components/AnimatedIcons/IconResult.js +36 -0
  25. package/build/Components/AnimatedIcons/IconResult.js.map +1 -0
  26. package/build/Components/AnimatedIcons/IconStatusLed.d.ts +25 -0
  27. package/build/Components/AnimatedIcons/IconStatusLed.js +58 -0
  28. package/build/Components/AnimatedIcons/IconStatusLed.js.map +1 -0
  29. package/build/Components/AnimatedIcons/IconTrash.d.ts +20 -0
  30. package/build/Components/AnimatedIcons/IconTrash.js +23 -0
  31. package/build/Components/AnimatedIcons/IconTrash.js.map +1 -0
  32. package/build/Components/AnimatedIcons/common.d.ts +31 -0
  33. package/build/Components/AnimatedIcons/common.js +164 -0
  34. package/build/Components/AnimatedIcons/common.js.map +1 -0
  35. package/build/Components/FileBrowser.js +4 -5
  36. package/build/Components/FileBrowser.js.map +1 -1
  37. package/build/Components/Loaders/Community.d.ts +13 -2
  38. package/build/Components/Loaders/Community.js +176 -243
  39. package/build/Components/Loaders/Community.js.map +1 -1
  40. package/build/Components/Loaders/CommunityLegacy.d.ts +32 -0
  41. package/build/Components/Loaders/CommunityLegacy.js +266 -0
  42. package/build/Components/Loaders/CommunityLegacy.js.map +1 -0
  43. package/build/Components/ObjectBrowser/ObjectBrowserClass.d.ts +18 -0
  44. package/build/Components/ObjectBrowser/ObjectBrowserClass.js +131 -3
  45. package/build/Components/ObjectBrowser/ObjectBrowserClass.js.map +1 -1
  46. package/build/Components/ObjectBrowser/dialogs.js +2 -2
  47. package/build/Components/ObjectBrowser/dialogs.js.map +1 -1
  48. package/build/Components/ObjectBrowser/renderLeaf.js +48 -8
  49. package/build/Components/ObjectBrowser/renderLeaf.js.map +1 -1
  50. package/build/Components/ObjectBrowser/rowAnimation.d.ts +9 -0
  51. package/build/Components/ObjectBrowser/rowAnimation.js +122 -0
  52. package/build/Components/ObjectBrowser/rowAnimation.js.map +1 -0
  53. package/build/Components/ObjectBrowser/toolbar.js +5 -4
  54. package/build/Components/ObjectBrowser/toolbar.js.map +1 -1
  55. package/build/Components/ObjectBrowser/utils.d.ts +2 -0
  56. package/build/Components/ObjectBrowser/utils.js +2 -0
  57. package/build/Components/ObjectBrowser/utils.js.map +1 -1
  58. package/build/Components/SaveCloseButtons.d.ts +12 -1
  59. package/build/Components/SaveCloseButtons.js +38 -2
  60. package/build/Components/SaveCloseButtons.js.map +1 -1
  61. package/build/index.d.ts +13 -0
  62. package/build/index.js +12 -0
  63. package/build/index.js.map +1 -1
  64. package/package.json +1 -1
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Copyright 2018-2024 Denis Haev (bluefox) <dogafox@gmail.com>
3
+ *
4
+ * MIT License
5
+ *
6
+ * Previous loader of the community edition, replaced by `LoaderCommunity` in 2026-11.
7
+ */
8
+ import React, { useEffect } from 'react';
9
+ const loaderStyles = `
10
+ /**
11
+ * Copyright 2018-2024 Denis Haev (bluefox) <dogafox@gmail.com>
12
+ *
13
+ * MIT License
14
+ *
15
+ **/
16
+
17
+ .logo-background-light, .logo-background-colored {
18
+ background: white;
19
+ }
20
+ .logo-background-dark, .logo-background-blue {
21
+ background: black;
22
+ }
23
+ .logo-div {
24
+ position: absolute;
25
+ top: 50%;
26
+ left: 50%;
27
+ -ms-transform: translateX(-50%) translateY(-50%);
28
+ -webkit-transform: translate(-50%,-50%);
29
+ transform: translate(-50%,-50%);
30
+ overflow: hidden;
31
+ border-radius: 50%;
32
+ z-index: 2;
33
+ }
34
+ .logo-border {
35
+ /*border-color: #164477;*/
36
+ border-top-color: #3399CC;
37
+ border-left-color: #164477;
38
+ border-bottom-color: #164477;
39
+ border-right-color: #164477;
40
+ border-radius: 50%;
41
+ border-style: solid;
42
+ box-sizing: border-box;
43
+ width: 100%;
44
+ height: 100%;
45
+ position: absolute;
46
+ }
47
+ .logo-top {
48
+ position: absolute;
49
+ width: 4.5%;
50
+ height: 16%;
51
+ top: 0;
52
+ z-index: 2;
53
+ }
54
+ .logo-i {
55
+ position: absolute;
56
+ width: 14.5%;
57
+ height: 60%;
58
+ top: 20%;
59
+ left: 42%;
60
+ background: #3399CC;
61
+ }
62
+ .logo-i-top {
63
+ position: absolute;
64
+ width: 14.5%;
65
+ height: 4%;
66
+ left: 42%;
67
+ background: #3399CC;
68
+ border-radius: 100%;
69
+ }
70
+ .logo-back {
71
+ width: 100%;
72
+ height: 100%;
73
+ z-index: 0;
74
+ overflow: hidden;
75
+ }
76
+ @keyframes logo-grow {
77
+ 0% {
78
+ width: 230px;
79
+ height: 230px;
80
+ transform: translate(-50%,-50%) scale(1);
81
+ opacity: 1
82
+ }
83
+ 99% {
84
+ width: 230px;
85
+ height: 230px;
86
+ transform: translate(-50%,-50%) scale(10);
87
+ opacity: 0;
88
+ }
89
+ 100% {
90
+ width: 0;
91
+ height: 0;
92
+ opacity: 0;
93
+ }
94
+ }
95
+ @keyframes logo-spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }
96
+ @keyframes logo-color-inside-light {
97
+ 0% {
98
+ background: #FEFEFE;
99
+ }
100
+ 100% {
101
+ background: #3399CC;
102
+ }
103
+ }
104
+ @keyframes logo-color-inside-dark {
105
+ 0% {
106
+ background: #030303;
107
+ }
108
+ 100% {
109
+ background: #3399CC;
110
+ }
111
+ }
112
+ @keyframes logo-color-inside-colored {
113
+ 0% {
114
+ background: #FEFEFE;
115
+ }
116
+ 100% {
117
+ background: #3399CC;
118
+ }
119
+ }
120
+ @keyframes logo-color-inside-blue {
121
+ 0% {
122
+ background: #030303;
123
+ }
124
+ 100% {
125
+ background: #3399CC;
126
+ }
127
+ }
128
+
129
+ @keyframes logo-color-outside-light {
130
+ 0% {
131
+ border-color: #FEFEFE;
132
+ }
133
+ 100% {
134
+ border-top-color: #3399CC;
135
+ border-left-color: #164477;
136
+ border-bottom-color: #164477;
137
+ border-right-color: #164477;
138
+ }
139
+ }
140
+ @keyframes logo-color-outside-dark {
141
+ 0% {
142
+ border-color: #040404;
143
+ }
144
+ 100% {
145
+ border-top-color: #3399CC;
146
+ border-left-color: #164477;
147
+ border-bottom-color: #164477;
148
+ border-right-color: #164477;
149
+ }
150
+ }
151
+ @keyframes logo-color-outside-colored {
152
+ 0% {
153
+ border-color: #FEFEFE;
154
+ }
155
+ 100% {
156
+ border-top-color: #3399CC;
157
+ border-left-color: #164477;
158
+ border-bottom-color: #164477;
159
+ border-right-color: #164477;
160
+ }
161
+ }
162
+ @keyframes logo-color-outside-blue {
163
+ 0% {
164
+ border-color: #040404;
165
+ }
166
+ 100% {
167
+ border-top-color: #3399CC;
168
+ border-left-color: #164477;
169
+ border-bottom-color: #164477;
170
+ border-right-color: #164477;
171
+ }
172
+ }
173
+
174
+ .logo-animate-wait {
175
+ animation: logo-color-outside 1.5s, logo-spin 1.5s linear infinite;
176
+ }
177
+
178
+ .logo-animate-grow-light {
179
+ background: #DDD;
180
+ }
181
+ .logo-animate-grow-dark {
182
+ background: #1d1d1d;
183
+ }
184
+ .logo-animate-grow-colored {
185
+ background: #DDD;
186
+ }
187
+ .logo-animate-grow-blue {
188
+ background: #1d1d1d;
189
+ }
190
+
191
+ .logo-animate-grow {
192
+ display: inline-block;
193
+ text-align: center;
194
+ z-index: 1;
195
+ top: 50%;
196
+ left: 50%;
197
+ -ms-transform: translateX(-50%) translateY(-50%);
198
+ -webkit-transform: translate(-50%,-50%);
199
+ transform: translate(-50%,-50%);
200
+ width: 245px;
201
+ height: 245px;
202
+ border-radius: 50%;
203
+ position: absolute;
204
+ animation: logo-grow 1s 1 ease forwards;
205
+ }
206
+
207
+ .logo-animate-color-inside-light {
208
+ animation: logo-color-inside-light 2.5s;
209
+ }
210
+ .logo-animate-color-inside-dark {
211
+ animation: logo-color-inside-dark 2.5s;
212
+ }
213
+ .logo-animate-color-inside-colored {
214
+ animation: logo-color-inside-colored 2.5s;
215
+ }
216
+ .logo-animate-color-inside-blue {
217
+ animation: logo-color-inside-blue 2.5s;
218
+ }
219
+
220
+ .logo-animate-color-outside-light {
221
+ animation: logo-color-outside-light 1.5s;
222
+ }
223
+ .logo-animate-color-outside-dark {
224
+ animation: logo-color-outside-dark 1.5s;
225
+ }
226
+ .logo-animate-color-outside-colored {
227
+ animation: logo-color-outside-colored 1.5s;
228
+ }
229
+ .logo-animate-color-outside-blue {
230
+ animation: logo-color-outside-blue 1.5s;
231
+ }
232
+ `;
233
+ export function LoaderCommunityLegacy(props) {
234
+ useEffect(() => {
235
+ if (!window.document.getElementById('loader-iobroker-component')) {
236
+ const style = window.document.createElement('style');
237
+ style.setAttribute('id', 'loader-iobroker-component');
238
+ style.innerHTML = loaderStyles;
239
+ window.document.head.appendChild(style);
240
+ }
241
+ }, []);
242
+ const size = props.size || 234;
243
+ const theme = props.themeName || props.theme || props.themeType || 'light';
244
+ return (React.createElement("div", { className: `logo-back logo-background-${theme}`, style: {
245
+ backgroundImage: props.backgroundImage && props.backgroundImage !== '@@loginBackgroundImage@@'
246
+ ? props.backgroundImage
247
+ : window.loadingBackgroundImage && window.loadingBackgroundImage !== '@@loginBackgroundImage@@'
248
+ ? `url(${window.loadingBackgroundImage})`
249
+ : undefined,
250
+ backgroundColor: props.backgroundColor && props.backgroundColor !== '@@loginBackgroundColor@@'
251
+ ? props.backgroundColor
252
+ : window.loadingBackgroundColor && window.loadingBackgroundColor !== '@@loginBackgroundColor@@'
253
+ ? window.loadingBackgroundColor
254
+ : undefined,
255
+ backgroundSize: 'cover',
256
+ } }, window.loadingHideLogo === 'true' ? null : (React.createElement(React.Fragment, null,
257
+ React.createElement("div", { className: "logo-div", style: { width: size, height: size } },
258
+ React.createElement("div", { className: `logo-top logo-background-${theme}`, style: { left: '37%' } }),
259
+ React.createElement("div", { className: `logo-top logo-background-${theme}`, style: { left: '57%' } }),
260
+ React.createElement("div", { className: `logo-border logo-background-${theme} logo-animate-wait`, style: { borderWidth: size * 0.132 } }),
261
+ React.createElement("div", { className: `logo-i logo-animate-color-inside-${theme}` }),
262
+ React.createElement("div", { className: `logo-i-top logo-animate-color-inside-${theme}`, style: { top: '18%' } }),
263
+ React.createElement("div", { className: `logo-i-top logo-animate-color-inside-${theme}`, style: { bottom: '18%' } })),
264
+ React.createElement("div", { className: `logo-animate-grow logo-animate-grow-${theme}`, style: { width: size + 11, height: size + 11 } })))));
265
+ }
266
+ //# sourceMappingURL=CommunityLegacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommunityLegacy.js","sourceRoot":"./src/","sources":["Components/Loaders/CommunityLegacy.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright 2018-2024 Denis Haev (bluefox) <dogafox@gmail.com>\n *\n * MIT License\n *\n * Previous loader of the community edition, replaced by `LoaderCommunity` in 2026-11.\n */\nimport React, { useEffect } 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\nconst loaderStyles = `\n/**\n * Copyright 2018-2024 Denis Haev (bluefox) <dogafox@gmail.com>\n *\n * MIT License\n *\n **/\n\n.logo-background-light, .logo-background-colored {\n background: white;\n}\n.logo-background-dark, .logo-background-blue {\n background: black;\n}\n.logo-div {\n position: absolute;\n top: 50%;\n left: 50%;\n -ms-transform: translateX(-50%) translateY(-50%);\n -webkit-transform: translate(-50%,-50%);\n transform: translate(-50%,-50%);\n overflow: hidden;\n border-radius: 50%;\n z-index: 2;\n}\n.logo-border {\n /*border-color: #164477;*/\n border-top-color: #3399CC;\n border-left-color: #164477;\n border-bottom-color: #164477;\n border-right-color: #164477;\n border-radius: 50%;\n border-style: solid;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n position: absolute;\n}\n.logo-top {\n position: absolute;\n width: 4.5%;\n height: 16%;\n top: 0;\n z-index: 2;\n}\n.logo-i {\n position: absolute;\n width: 14.5%;\n height: 60%;\n top: 20%;\n left: 42%;\n background: #3399CC;\n}\n.logo-i-top {\n position: absolute;\n width: 14.5%;\n height: 4%;\n left: 42%;\n background: #3399CC;\n border-radius: 100%;\n}\n.logo-back {\n width: 100%;\n height: 100%;\n z-index: 0;\n overflow: hidden;\n}\n@keyframes logo-grow {\n 0% {\n width: 230px;\n height: 230px;\n transform: translate(-50%,-50%) scale(1);\n opacity: 1\n }\n 99% {\n width: 230px;\n height: 230px;\n transform: translate(-50%,-50%) scale(10);\n opacity: 0;\n }\n 100% {\n width: 0;\n height: 0;\n opacity: 0;\n }\n}\n@keyframes logo-spin { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }\n@keyframes logo-color-inside-light {\n 0% {\n background: #FEFEFE;\n }\n 100% {\n background: #3399CC;\n }\n}\n@keyframes logo-color-inside-dark {\n 0% {\n background: #030303;\n }\n 100% {\n background: #3399CC;\n }\n}\n@keyframes logo-color-inside-colored {\n 0% {\n background: #FEFEFE;\n }\n 100% {\n background: #3399CC;\n }\n}\n@keyframes logo-color-inside-blue {\n 0% {\n background: #030303;\n }\n 100% {\n background: #3399CC;\n }\n}\n\n@keyframes logo-color-outside-light {\n 0% {\n border-color: #FEFEFE;\n }\n 100% {\n border-top-color: #3399CC;\n border-left-color: #164477;\n border-bottom-color: #164477;\n border-right-color: #164477;\n }\n}\n@keyframes logo-color-outside-dark {\n 0% {\n border-color: #040404;\n }\n 100% {\n border-top-color: #3399CC;\n border-left-color: #164477;\n border-bottom-color: #164477;\n border-right-color: #164477;\n }\n}\n@keyframes logo-color-outside-colored {\n 0% {\n border-color: #FEFEFE;\n }\n 100% {\n border-top-color: #3399CC;\n border-left-color: #164477;\n border-bottom-color: #164477;\n border-right-color: #164477;\n }\n}\n@keyframes logo-color-outside-blue {\n 0% {\n border-color: #040404;\n }\n 100% {\n border-top-color: #3399CC;\n border-left-color: #164477;\n border-bottom-color: #164477;\n border-right-color: #164477;\n }\n}\n\n.logo-animate-wait {\n animation: logo-color-outside 1.5s, logo-spin 1.5s linear infinite;\n}\n\n.logo-animate-grow-light {\n background: #DDD;\n}\n.logo-animate-grow-dark {\n background: #1d1d1d;\n}\n.logo-animate-grow-colored {\n background: #DDD;\n}\n.logo-animate-grow-blue {\n background: #1d1d1d;\n}\n\n.logo-animate-grow {\n display: inline-block;\n text-align: center;\n z-index: 1;\n top: 50%;\n left: 50%;\n -ms-transform: translateX(-50%) translateY(-50%);\n -webkit-transform: translate(-50%,-50%);\n transform: translate(-50%,-50%);\n width: 245px;\n height: 245px;\n border-radius: 50%;\n position: absolute;\n animation: logo-grow 1s 1 ease forwards;\n}\n\n.logo-animate-color-inside-light {\n animation: logo-color-inside-light 2.5s;\n}\n.logo-animate-color-inside-dark {\n animation: logo-color-inside-dark 2.5s;\n}\n.logo-animate-color-inside-colored {\n animation: logo-color-inside-colored 2.5s;\n}\n.logo-animate-color-inside-blue {\n animation: logo-color-inside-blue 2.5s;\n}\n\n.logo-animate-color-outside-light {\n animation: logo-color-outside-light 1.5s;\n}\n.logo-animate-color-outside-dark {\n animation: logo-color-outside-dark 1.5s;\n}\n.logo-animate-color-outside-colored {\n animation: logo-color-outside-colored 1.5s;\n}\n.logo-animate-color-outside-blue {\n animation: logo-color-outside-blue 1.5s;\n}\n`;\n\ninterface LoaderProps {\n /** The size in pixels of this loader. */\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}\n\nexport function LoaderCommunityLegacy(props: LoaderProps): React.JSX.Element {\n useEffect(() => {\n if (!window.document.getElementById('loader-iobroker-component')) {\n const style = window.document.createElement('style');\n style.setAttribute('id', 'loader-iobroker-component');\n style.innerHTML = loaderStyles;\n window.document.head.appendChild(style);\n }\n }, []);\n\n const size = props.size || 234;\n const theme = props.themeName || props.theme || props.themeType || 'light';\n return (\n <div\n className={`logo-back logo-background-${theme}`}\n style={{\n backgroundImage:\n props.backgroundImage && props.backgroundImage !== '@@loginBackgroundImage@@'\n ? props.backgroundImage\n : window.loadingBackgroundImage && window.loadingBackgroundImage !== '@@loginBackgroundImage@@'\n ? `url(${window.loadingBackgroundImage})`\n : undefined,\n backgroundColor:\n props.backgroundColor && props.backgroundColor !== '@@loginBackgroundColor@@'\n ? props.backgroundColor\n : window.loadingBackgroundColor && window.loadingBackgroundColor !== '@@loginBackgroundColor@@'\n ? window.loadingBackgroundColor\n : undefined,\n backgroundSize: 'cover',\n }}\n >\n {window.loadingHideLogo === 'true' ? null : (\n <>\n <div\n className=\"logo-div\"\n style={{ width: size, height: size }}\n >\n <div\n className={`logo-top logo-background-${theme}`}\n style={{ left: '37%' }}\n />\n <div\n className={`logo-top logo-background-${theme}`}\n style={{ left: '57%' }}\n />\n <div\n className={`logo-border logo-background-${theme} logo-animate-wait`}\n style={{ borderWidth: size * 0.132 }}\n />\n <div className={`logo-i logo-animate-color-inside-${theme}`} />\n <div\n className={`logo-i-top logo-animate-color-inside-${theme}`}\n style={{ top: '18%' }}\n />\n <div\n className={`logo-i-top logo-animate-color-inside-${theme}`}\n style={{ bottom: '18%' }}\n />\n </div>\n <div\n className={`logo-animate-grow logo-animate-grow-${theme}`}\n style={{ width: size + 11, height: size + 11 }}\n />\n </>\n )}\n </div>\n );\n}\n"]}
@@ -97,6 +97,20 @@ export declare class ObjectBrowserClass extends Component<ObjectBrowserProps, Ob
97
97
  private windowLast;
98
98
  /** id -> how often the state of this row has changed. Part of the memo key of the row */
99
99
  readonly rowStateVersion: Record<string, number>;
100
+ /**
101
+ * id -> last change of a number (arrow up or down) or of a boolean (the value flips), see `renderColumnValue`.
102
+ * `shown` is the time the row showed it first, so a row that is rendered again continues the animation.
103
+ */
104
+ readonly valueTrends: Record<string, {
105
+ dir: 'up' | 'down' | 'toggle';
106
+ ts: number;
107
+ shown?: number;
108
+ }>;
109
+ /** The running animations of an unfolded or folding node (see `rowAnimation.ts`) */
110
+ private rowAnimations;
111
+ /** The node whose children slide under its row at the moment; they are removed when that ends */
112
+ private foldingId;
113
+ private foldTimer;
100
114
  private stateVersionCounter;
101
115
  private selectFirst;
102
116
  /** Last navigation that was applied from `navigateTo` or reported via `onNavigateTo` (loop guard). */
@@ -349,6 +363,10 @@ export declare class ObjectBrowserClass extends Component<ObjectBrowserProps, Ob
349
363
  */
350
364
  getToolbar(): JSX.Element;
351
365
  toggleExpanded(id: string): void;
366
+ /** The fold animation ended (or is cut short): remove the children of the node now */
367
+ private applyFold;
368
+ /** Ends a running fold or unfold animation at once */
369
+ private finishRowAnimations;
352
370
  onCopy(e: React.MouseEvent, text: string | undefined): void;
353
371
  renderTooltipAccessControl(acl: ioBroker.StateACL): null | JSX.Element;
354
372
  renderColumnButtons(id: string, item: TreeItem): (JSX.Element | null)[] | JSX.Element | null;
@@ -17,13 +17,14 @@ import { Utils } from '../Utils'; // @iobroker/gui-components/Components/Utils
17
17
  import { TabContainer } from '../TabContainer';
18
18
  import { TabContent } from '../TabContent';
19
19
  import { TabHeader } from '../TabHeader';
20
- import { ROW_HEIGHT, applyFilter, buildTree, findNode, formatValue, generateFile, getName, colVar, widthFromContainer, growVar, getSelectIdIconFromObjects, setCustomValue, } from './utils';
20
+ import { ROW_HEIGHT, TREND_DURATION, applyFilter, buildTree, findNode, formatValue, generateFile, getName, colVar, widthFromContainer, growVar, getSelectIdIconFromObjects, setCustomValue, } from './utils';
21
21
  import { HistoryChart } from './HistoryChart';
22
22
  import { styles } from './styles';
23
23
  import * as dialogs from './dialogs';
24
24
  import * as toolbar from './toolbar';
25
25
  import * as leaf from './renderLeaf';
26
26
  import * as contextMenu from './contextMenu';
27
+ import * as rowAnimation from './rowAnimation';
27
28
  import { DEFAULT_DATE_FORMAT, DEFAULT_FILTER, ITEM_LEVEL, MIN_COLUMN_WIDTHS, SCREEN_WIDTHS, } from './constants';
28
29
  export { getSelectIdIconFromObjects };
29
30
  let objectsAlreadyLoaded = false;
@@ -123,6 +124,16 @@ export class ObjectBrowserClass extends Component {
123
124
  windowLast = 0;
124
125
  /** id -> how often the state of this row has changed. Part of the memo key of the row */
125
126
  rowStateVersion = {};
127
+ /**
128
+ * id -> last change of a number (arrow up or down) or of a boolean (the value flips), see `renderColumnValue`.
129
+ * `shown` is the time the row showed it first, so a row that is rendered again continues the animation.
130
+ */
131
+ valueTrends = {};
132
+ /** The running animations of an unfolded or folding node (see `rowAnimation.ts`) */
133
+ rowAnimations = [];
134
+ /** The node whose children slide under its row at the moment; they are removed when that ends */
135
+ foldingId = null;
136
+ foldTimer = null;
126
137
  stateVersionCounter = 0;
127
138
  selectFirst;
128
139
  /** Last navigation that was applied from `navigateTo` or reported via `onNavigateTo` (loop guard). */
@@ -764,6 +775,14 @@ export class ObjectBrowserClass extends Component {
764
775
  window.cancelAnimationFrame(this.scrollFrame);
765
776
  this.scrollFrame = null;
766
777
  }
778
+ // a fold animation must not update the state after the component was destroyed
779
+ this.foldingId = null;
780
+ if (this.foldTimer) {
781
+ clearTimeout(this.foldTimer);
782
+ this.foldTimer = null;
783
+ }
784
+ this.rowAnimations.forEach(animation => animation.cancel());
785
+ this.rowAnimations = [];
767
786
  // the timers must not fire after the component was destroyed
768
787
  if (this.unsubscribeTimer) {
769
788
  clearTimeout(this.unsubscribeTimer);
@@ -1059,6 +1078,16 @@ export class ObjectBrowserClass extends Component {
1059
1078
  item.data.state = undefined;
1060
1079
  }
1061
1080
  }
1081
+ const oldVal = oldState?.val;
1082
+ const newVal = state?.val;
1083
+ if (typeof oldVal === 'number' && typeof newVal === 'number' && oldVal !== newVal) {
1084
+ if (!Number.isNaN(oldVal) && !Number.isNaN(newVal)) {
1085
+ this.valueTrends[id] = { dir: newVal > oldVal ? 'up' : 'down', ts: Date.now() };
1086
+ }
1087
+ }
1088
+ else if (typeof oldVal === 'boolean' && typeof newVal === 'boolean' && oldVal !== newVal) {
1089
+ this.valueTrends[id] = { dir: 'toggle', ts: Date.now() };
1090
+ }
1062
1091
  if (state) {
1063
1092
  this.states[id] = state;
1064
1093
  }
@@ -1775,7 +1804,10 @@ export class ObjectBrowserClass extends Component {
1775
1804
  return toolbar.getToolbar(this);
1776
1805
  }
1777
1806
  toggleExpanded(id) {
1778
- const expanded = JSON.parse(JSON.stringify(this.state.expanded));
1807
+ // a node that is still folding is folded now, this toggle builds on it
1808
+ const folding = this.foldingId;
1809
+ this.finishRowAnimations();
1810
+ const expanded = this.state.expanded.filter(item => item !== folding);
1779
1811
  const pos = expanded.indexOf(id);
1780
1812
  if (pos === -1) {
1781
1813
  expanded.push(id);
@@ -1785,7 +1817,50 @@ export class ObjectBrowserClass extends Component {
1785
1817
  expanded.splice(pos, 1);
1786
1818
  }
1787
1819
  this.localStorage.setItem(`${this.props.dialogName || 'App'}.objectExpanded`, JSON.stringify(expanded));
1788
- this.setState({ expanded });
1820
+ if (pos === -1) {
1821
+ // the children are rendered after this update: let them slide out from under the row
1822
+ this.setState({ expanded }, () => {
1823
+ this.rowAnimations = rowAnimation.unfold(this.tableRef.current, id);
1824
+ });
1825
+ return;
1826
+ }
1827
+ const animations = rowAnimation.fold(this.tableRef.current, id, () => this.applyFold());
1828
+ if (animations) {
1829
+ // the children slide under the row first and are removed when the animation ends
1830
+ this.rowAnimations = animations;
1831
+ this.foldingId = id;
1832
+ // a hidden tab does not run animations: fold anyway
1833
+ this.foldTimer = setTimeout(() => this.applyFold(), 600);
1834
+ }
1835
+ else {
1836
+ this.setState({ expanded });
1837
+ }
1838
+ }
1839
+ /** The fold animation ended (or is cut short): remove the children of the node now */
1840
+ applyFold() {
1841
+ const id = this.foldingId;
1842
+ if (id === null) {
1843
+ return;
1844
+ }
1845
+ this.foldingId = null;
1846
+ if (this.foldTimer) {
1847
+ clearTimeout(this.foldTimer);
1848
+ this.foldTimer = null;
1849
+ }
1850
+ const animations = this.rowAnimations;
1851
+ this.rowAnimations = [];
1852
+ // the animations hold the rows in their end position until the children are really gone
1853
+ this.setState(state => ({ expanded: state.expanded.filter(item => item !== id) }), () => animations.forEach(animation => animation.cancel()));
1854
+ }
1855
+ /** Ends a running fold or unfold animation at once */
1856
+ finishRowAnimations() {
1857
+ if (this.foldingId !== null) {
1858
+ this.applyFold();
1859
+ }
1860
+ else {
1861
+ this.rowAnimations.forEach(animation => animation.cancel());
1862
+ this.rowAnimations = [];
1863
+ }
1789
1864
  }
1790
1865
  onCopy(e, text) {
1791
1866
  e.stopPropagation();
@@ -2832,6 +2907,59 @@ export class ObjectBrowserClass extends Component {
2832
2907
  .newValueBrowser-light {
2833
2908
  animation: newValueAnimation-light 2s ease-in-out;
2834
2909
  }
2910
+ .iob-ob-trend {
2911
+ display: inline-flex;
2912
+ align-self: center;
2913
+ flex-shrink: 0;
2914
+ width: 0.85em;
2915
+ height: 0.85em;
2916
+ margin-left: 4px;
2917
+ opacity: 0;
2918
+ animation: iob-ob-trend-up ${TREND_DURATION}ms ease-out forwards;
2919
+ }
2920
+ .iob-ob-trend svg {
2921
+ width: 100%;
2922
+ height: 100%;
2923
+ fill: currentColor;
2924
+ }
2925
+ .iob-ob-trend-up {
2926
+ color: ${this.props.themeType === 'dark' ? '#66bb6a' : '#2e7d32'};
2927
+ }
2928
+ .iob-ob-trend-down {
2929
+ color: ${this.props.themeType === 'dark' ? '#ef5350' : '#c62828'};
2930
+ animation-name: iob-ob-trend-down;
2931
+ }
2932
+ @keyframes iob-ob-trend-up {
2933
+ 0% { opacity: 0; transform: translateY(45%); }
2934
+ 12% { opacity: 1; transform: none; }
2935
+ 80% { opacity: 1; }
2936
+ 100% { opacity: 0; }
2937
+ }
2938
+ @keyframes iob-ob-trend-down {
2939
+ 0% { opacity: 0; transform: translateY(-45%); }
2940
+ 12% { opacity: 1; transform: none; }
2941
+ 80% { opacity: 1; }
2942
+ 100% { opacity: 0; }
2943
+ }
2944
+ .newValueBrowser-dark.iob-ob-flip {
2945
+ animation: iob-ob-flip 0.32s cubic-bezier(0.3, 1.4, 0.5, 1), newValueAnimation-dark 2s ease-in-out;
2946
+ }
2947
+ .newValueBrowser-light.iob-ob-flip {
2948
+ animation: iob-ob-flip 0.32s cubic-bezier(0.3, 1.4, 0.5, 1), newValueAnimation-light 2s ease-in-out;
2949
+ }
2950
+ @keyframes iob-ob-flip {
2951
+ 0% { transform: perspective(60px) rotateX(90deg); opacity: 0.3; }
2952
+ 100% { transform: none; opacity: 1; }
2953
+ }
2954
+ @media (prefers-reduced-motion: reduce) {
2955
+ .iob-ob-trend { animation-name: iob-ob-trend-fade; }
2956
+ .newValueBrowser-dark.iob-ob-flip { animation: newValueAnimation-dark 2s ease-in-out; }
2957
+ .newValueBrowser-light.iob-ob-flip { animation: newValueAnimation-light 2s ease-in-out; }
2958
+ @keyframes iob-ob-trend-fade {
2959
+ 0%, 80% { opacity: 1; }
2960
+ 100% { opacity: 0; }
2961
+ }
2962
+ }
2835
2963
  .highlight-link .iob-link {
2836
2964
  text-decoration: underline;
2837
2965
  cursor: pointer;