@iobroker/gui-components 10.0.11 → 10.0.12

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.0.11",
39
+ "@iobroker/gui-components": "^10.0.12",
40
40
  "@mui/icons-material": "^9.0.1",
41
41
  "@mui/material": "^9.0.1",
42
42
  "react": "^19.2.5",
@@ -856,6 +856,10 @@ You can find the migration instructions:
856
856
  -->
857
857
 
858
858
  ## Changelog
859
+ ### 10.0.12 (2026-08-02)
860
+
861
+ - (@GermanBluefox) Replaced the spark line
862
+
859
863
  ### 10.0.11 (2026-07-31)
860
864
 
861
865
  - (@GermanBluefox) Added support of styled scrollbars
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright 2020-2026, Denis Haev <dogafox@gmail.com>
3
+ *
4
+ * MIT License
5
+ *
6
+ * The small 24 hours chart that is shown in the value tooltip of the object browser, if the state
7
+ * is recorded by a history adapter. It draws itself into an SVG, so no charting library is needed.
8
+ */
9
+ import { type JSX } from 'react';
10
+ import type { Connection } from '../../Connection';
11
+ import type { Translate } from '../../types';
12
+ /** One point of the drawn line */
13
+ interface ChartPoint {
14
+ ts: number;
15
+ val: number;
16
+ }
17
+ interface HistoryChartProps {
18
+ socket: Connection;
19
+ /** ID of the state, the history of which is shown */
20
+ id: string;
21
+ /** Instance of the history adapter, e.g. `history.0` */
22
+ instance: string;
23
+ t: Translate;
24
+ /** Booleans are drawn as steps and are labeled with `false`/`true` instead of `0`/`1` */
25
+ isBoolean?: boolean;
26
+ /** Write the decimal point as a comma */
27
+ isFloatComma?: boolean;
28
+ /** Current value of the state, so the line reaches the right edge even if the last record is older */
29
+ current?: ChartPoint | null;
30
+ /** Shown period in hours */
31
+ hours?: number;
32
+ /**
33
+ * Color of the line. The tooltip has a dark background in the light theme too, so one color fits
34
+ * both themes.
35
+ */
36
+ color?: string;
37
+ }
38
+ /**
39
+ * The last 24 hours of a state as a sparkline: area, line, the min/max of the period and the value
40
+ * at the right edge. Reads the history itself as soon as it is mounted.
41
+ */
42
+ export declare function HistoryChart(props: HistoryChartProps): JSX.Element;
43
+ export {};
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Copyright 2020-2026, Denis Haev <dogafox@gmail.com>
3
+ *
4
+ * MIT License
5
+ *
6
+ * The small 24 hours chart that is shown in the value tooltip of the object browser, if the state
7
+ * is recorded by a history adapter. It draws itself into an SVG, so no charting library is needed.
8
+ */
9
+ import React, { useEffect, useState } from 'react';
10
+ const WIDTH = 224;
11
+ const HEIGHT = 68;
12
+ /** Left border of the plot. Left of it are the min/max labels */
13
+ const PLOT_LEFT = 38;
14
+ const PLOT_RIGHT = 217;
15
+ const PLOT_TOP = 5;
16
+ const PLOT_BOTTOM = 54;
17
+ /** The values are mapped into this band, so the line does not touch the border of the plot */
18
+ const VALUE_TOP = 10;
19
+ const VALUE_BOTTOM = 50;
20
+ const FONT_SIZE = 9;
21
+ /** Number of the aggregated intervals that are requested from the history: one point every 15 minutes */
22
+ const INTERVALS = 96;
23
+ const DEFAULT_HOURS = 24;
24
+ /** Blue that stays readable on the dark tooltip background */
25
+ const DEFAULT_COLOR = '#4a93eb';
26
+ /** Every chart needs its own gradient, so the IDs must not collide */
27
+ let gradientCounter = 0;
28
+ /**
29
+ * Format a value for the min/max label: short, but without pretending a precision that is not there.
30
+ */
31
+ function formatValue(value, isFloatComma) {
32
+ const abs = Math.abs(value);
33
+ let text;
34
+ if (abs >= 100_000) {
35
+ // the label may not be wider than the space left of the plot
36
+ text = `${Math.round(value / 1000)}k`;
37
+ }
38
+ else if (Number.isInteger(value)) {
39
+ text = value.toString();
40
+ }
41
+ else if (abs >= 10) {
42
+ text = value.toFixed(1);
43
+ }
44
+ else if (abs >= 1) {
45
+ text = value.toFixed(2);
46
+ }
47
+ else {
48
+ text = value.toFixed(3);
49
+ }
50
+ if (text.includes('.')) {
51
+ text = text.replace(/0+$/, '').replace(/\.$/, '');
52
+ }
53
+ return isFloatComma ? text.replace('.', ',') : text;
54
+ }
55
+ /**
56
+ * Take the numbers out of the history result: booleans become 0/1, nulls and strings are ignored.
57
+ */
58
+ function readPoints(values) {
59
+ const points = [];
60
+ for (const item of values || []) {
61
+ if (!item || typeof item.ts !== 'number' || item.val === null || item.val === undefined) {
62
+ continue;
63
+ }
64
+ const val = typeof item.val === 'boolean' ? (item.val ? 1 : 0) : Number(item.val);
65
+ if (Number.isFinite(val)) {
66
+ points.push({ ts: item.ts, val });
67
+ }
68
+ }
69
+ // The min/max aggregation delivers two points per interval, and not always in the right order
70
+ points.sort((a, b) => a.ts - b.ts);
71
+ return points;
72
+ }
73
+ /**
74
+ * Build the SVG path of the line. Booleans (and everything else that is drawn as steps) keep their
75
+ * value till the next point instead of ramping to it.
76
+ */
77
+ function buildLine(points, x, y, steps) {
78
+ let path = '';
79
+ points.forEach((point, i) => {
80
+ const px = x(point.ts).toFixed(2);
81
+ const py = y(point.val).toFixed(2);
82
+ if (!i) {
83
+ path = `M${px} ${py}`;
84
+ }
85
+ else if (steps) {
86
+ path += `H${px}V${py}`;
87
+ }
88
+ else {
89
+ path += `L${px} ${py}`;
90
+ }
91
+ });
92
+ return path;
93
+ }
94
+ /**
95
+ * The last 24 hours of a state as a sparkline: area, line, the min/max of the period and the value
96
+ * at the right edge. Reads the history itself as soon as it is mounted.
97
+ */
98
+ export function HistoryChart(props) {
99
+ const { socket, id, instance, t, isBoolean, isFloatComma, current, hours = DEFAULT_HOURS } = props;
100
+ const color = props.color || DEFAULT_COLOR;
101
+ const [gradientId] = useState(() => `iob-history-chart-${++gradientCounter}`);
102
+ const [data, setData] = useState(null);
103
+ const [failed, setFailed] = useState(false);
104
+ useEffect(() => {
105
+ let mounted = true;
106
+ const to = Date.now();
107
+ const from = to - hours * 3_600_000;
108
+ socket
109
+ .getHistory(id, {
110
+ instance,
111
+ start: from,
112
+ end: to,
113
+ step: Math.round((to - from) / INTERVALS),
114
+ from: false,
115
+ ack: false,
116
+ q: false,
117
+ addId: false,
118
+ aggregate: 'minmax',
119
+ })
120
+ .then(values => {
121
+ if (mounted) {
122
+ setData({ points: readPoints(values), from, to });
123
+ }
124
+ })
125
+ .catch(e => {
126
+ console.warn(`Cannot read history of ${id}: ${e}`);
127
+ if (mounted) {
128
+ setFailed(true);
129
+ }
130
+ });
131
+ return () => {
132
+ mounted = false;
133
+ };
134
+ }, [socket, id, instance, hours]);
135
+ let content;
136
+ if (failed || (data && !data.points.length)) {
137
+ content = (React.createElement("text", { x: WIDTH / 2, y: (PLOT_TOP + PLOT_BOTTOM) / 2 + 3, textAnchor: "middle", fill: "currentColor", fillOpacity: 0.5, fontSize: 11 }, t('ra_No data')));
138
+ }
139
+ else if (!data) {
140
+ content = (React.createElement("line", { x1: PLOT_LEFT + 6, x2: PLOT_RIGHT - 6, y1: (PLOT_TOP + PLOT_BOTTOM) / 2, y2: (PLOT_TOP + PLOT_BOTTOM) / 2, stroke: "currentColor", strokeOpacity: 0.25, strokeWidth: 1.5, strokeDasharray: "3 4", strokeLinecap: "round" },
141
+ React.createElement("animate", { attributeName: "stroke-opacity", values: "0.3;0.1;0.3", dur: "1.4s", repeatCount: "indefinite" })));
142
+ }
143
+ else {
144
+ const points = data.points.slice();
145
+ // Let the line reach "now": a state that was not written since an hour still has its value
146
+ if (current && Number.isFinite(current.val) && (!points.length || current.ts >= points[points.length - 1].ts)) {
147
+ points.push({ ts: Math.min(current.ts, data.to), val: current.val });
148
+ }
149
+ let min = points[0].val;
150
+ let max = points[0].val;
151
+ for (const point of points) {
152
+ if (point.val < min) {
153
+ min = point.val;
154
+ }
155
+ if (point.val > max) {
156
+ max = point.val;
157
+ }
158
+ }
159
+ // The drawn range is a bit larger than the values, so the line has some air above and below
160
+ let low;
161
+ let high;
162
+ if (isBoolean) {
163
+ low = 0;
164
+ high = 1;
165
+ }
166
+ else if (min === max) {
167
+ // a constant value is drawn in the middle
168
+ low = min - 1;
169
+ high = max + 1;
170
+ }
171
+ else {
172
+ const air = (max - min) * 0.1;
173
+ low = min - air;
174
+ high = max + air;
175
+ }
176
+ const scaleX = (PLOT_RIGHT - PLOT_LEFT) / (data.to - data.from || 1);
177
+ const x = (ts) => Math.min(PLOT_RIGHT, Math.max(PLOT_LEFT, PLOT_LEFT + (ts - data.from) * scaleX));
178
+ const y = (val) => VALUE_BOTTOM - ((val - low) / (high - low)) * (VALUE_BOTTOM - VALUE_TOP);
179
+ const steps = !!isBoolean;
180
+ const line = buildLine(points, x, y, steps);
181
+ const lastX = x(points[points.length - 1].ts);
182
+ const lastY = y(points[points.length - 1].val);
183
+ // the area is closed against the bottom of the plot, and not against the lowest value
184
+ const area = `${line}V${PLOT_BOTTOM}H${x(points[0].ts).toFixed(2)}Z`;
185
+ const zeroY = low < 0 && high > 0 ? y(0) : null;
186
+ content = (React.createElement(React.Fragment, null,
187
+ React.createElement("path", { d: area, fill: `url(#${gradientId})` }),
188
+ zeroY !== null ? (React.createElement("line", { x1: PLOT_LEFT, x2: PLOT_RIGHT, y1: zeroY, y2: zeroY, stroke: "currentColor", strokeOpacity: 0.25, strokeWidth: 1, strokeDasharray: "2 3" })) : null,
189
+ React.createElement("path", { d: line, fill: "none", stroke: color, strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" }),
190
+ React.createElement("circle", { cx: lastX, cy: lastY, r: 5, fill: color, fillOpacity: 0.25 }),
191
+ React.createElement("circle", { cx: lastX, cy: lastY, r: 2.4, fill: color }),
192
+ React.createElement("text", { x: PLOT_LEFT - 5, y: y(isBoolean ? 1 : max) + 3, textAnchor: "end", fill: "currentColor", fillOpacity: 0.55, fontSize: FONT_SIZE }, isBoolean ? 'true' : formatValue(max, isFloatComma)),
193
+ isBoolean || min !== max ? (React.createElement("text", { x: PLOT_LEFT - 5, y: y(isBoolean ? 0 : min) + 3, textAnchor: "end", fill: "currentColor", fillOpacity: 0.55, fontSize: FONT_SIZE }, isBoolean ? 'false' : formatValue(min, isFloatComma))) : null));
194
+ }
195
+ return (React.createElement("svg", { viewBox: `0 0 ${WIDTH} ${HEIGHT}`, style: { width: '100%', height: 'auto', display: 'block', marginTop: 4 } },
196
+ React.createElement("defs", null,
197
+ React.createElement("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1" },
198
+ React.createElement("stop", { offset: "0%", stopColor: color, stopOpacity: 0.35 }),
199
+ React.createElement("stop", { offset: "100%", stopColor: color, stopOpacity: 0 }))),
200
+ React.createElement("rect", { x: PLOT_LEFT, y: PLOT_TOP, width: PLOT_RIGHT - PLOT_LEFT, height: PLOT_BOTTOM - PLOT_TOP, rx: 4, fill: "currentColor", fillOpacity: 0.07 }),
201
+ content,
202
+ React.createElement("text", { x: PLOT_LEFT, y: HEIGHT - 3, fill: "currentColor", fillOpacity: 0.45, fontSize: FONT_SIZE }, `-${hours} h`),
203
+ React.createElement("text", { x: PLOT_RIGHT, y: HEIGHT - 3, textAnchor: "end", fill: "currentColor", fillOpacity: 0.45, fontSize: FONT_SIZE }, t('ra_now'))));
204
+ }
205
+ //# sourceMappingURL=HistoryChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HistoryChart.js","sourceRoot":"./src/","sources":["Components/ObjectBrowser/HistoryChart.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright 2020-2026, Denis Haev <dogafox@gmail.com>\n *\n * MIT License\n *\n * The small 24 hours chart that is shown in the value tooltip of the object browser, if the state\n * is recorded by a history adapter. It draws itself into an SVG, so no charting library is needed.\n */\nimport React, { type JSX, useEffect, useState } from 'react';\n\nimport type { Connection } from '../../Connection';\nimport type { Translate } from '../../types';\n\n/** One point of the drawn line */\ninterface ChartPoint {\n ts: number;\n val: number;\n}\n\ninterface HistoryChartProps {\n socket: Connection;\n /** ID of the state, the history of which is shown */\n id: string;\n /** Instance of the history adapter, e.g. `history.0` */\n instance: string;\n t: Translate;\n /** Booleans are drawn as steps and are labeled with `false`/`true` instead of `0`/`1` */\n isBoolean?: boolean;\n /** Write the decimal point as a comma */\n isFloatComma?: boolean;\n /** Current value of the state, so the line reaches the right edge even if the last record is older */\n current?: ChartPoint | null;\n /** Shown period in hours */\n hours?: number;\n /**\n * Color of the line. The tooltip has a dark background in the light theme too, so one color fits\n * both themes.\n */\n color?: string;\n}\n\nconst WIDTH = 224;\nconst HEIGHT = 68;\n/** Left border of the plot. Left of it are the min/max labels */\nconst PLOT_LEFT = 38;\nconst PLOT_RIGHT = 217;\nconst PLOT_TOP = 5;\nconst PLOT_BOTTOM = 54;\n/** The values are mapped into this band, so the line does not touch the border of the plot */\nconst VALUE_TOP = 10;\nconst VALUE_BOTTOM = 50;\nconst FONT_SIZE = 9;\n/** Number of the aggregated intervals that are requested from the history: one point every 15 minutes */\nconst INTERVALS = 96;\nconst DEFAULT_HOURS = 24;\n/** Blue that stays readable on the dark tooltip background */\nconst DEFAULT_COLOR = '#4a93eb';\n\n/** Every chart needs its own gradient, so the IDs must not collide */\nlet gradientCounter = 0;\n\n/**\n * Format a value for the min/max label: short, but without pretending a precision that is not there.\n */\nfunction formatValue(value: number, isFloatComma?: boolean): string {\n const abs = Math.abs(value);\n let text: string;\n if (abs >= 100_000) {\n // the label may not be wider than the space left of the plot\n text = `${Math.round(value / 1000)}k`;\n } else if (Number.isInteger(value)) {\n text = value.toString();\n } else if (abs >= 10) {\n text = value.toFixed(1);\n } else if (abs >= 1) {\n text = value.toFixed(2);\n } else {\n text = value.toFixed(3);\n }\n if (text.includes('.')) {\n text = text.replace(/0+$/, '').replace(/\\.$/, '');\n }\n return isFloatComma ? text.replace('.', ',') : text;\n}\n\n/**\n * Take the numbers out of the history result: booleans become 0/1, nulls and strings are ignored.\n */\nfunction readPoints(values: ioBroker.GetHistoryResult): ChartPoint[] {\n const points: ChartPoint[] = [];\n\n for (const item of values || []) {\n if (!item || typeof item.ts !== 'number' || item.val === null || item.val === undefined) {\n continue;\n }\n const val = typeof item.val === 'boolean' ? (item.val ? 1 : 0) : Number(item.val);\n if (Number.isFinite(val)) {\n points.push({ ts: item.ts, val });\n }\n }\n\n // The min/max aggregation delivers two points per interval, and not always in the right order\n points.sort((a, b) => a.ts - b.ts);\n\n return points;\n}\n\n/**\n * Build the SVG path of the line. Booleans (and everything else that is drawn as steps) keep their\n * value till the next point instead of ramping to it.\n */\nfunction buildLine(\n points: ChartPoint[],\n x: (ts: number) => number,\n y: (val: number) => number,\n steps: boolean,\n): string {\n let path = '';\n\n points.forEach((point, i) => {\n const px = x(point.ts).toFixed(2);\n const py = y(point.val).toFixed(2);\n if (!i) {\n path = `M${px} ${py}`;\n } else if (steps) {\n path += `H${px}V${py}`;\n } else {\n path += `L${px} ${py}`;\n }\n });\n\n return path;\n}\n\n/**\n * The last 24 hours of a state as a sparkline: area, line, the min/max of the period and the value\n * at the right edge. Reads the history itself as soon as it is mounted.\n */\nexport function HistoryChart(props: HistoryChartProps): JSX.Element {\n const { socket, id, instance, t, isBoolean, isFloatComma, current, hours = DEFAULT_HOURS } = props;\n const color = props.color || DEFAULT_COLOR;\n const [gradientId] = useState(() => `iob-history-chart-${++gradientCounter}`);\n const [data, setData] = useState<{ points: ChartPoint[]; from: number; to: number } | null>(null);\n const [failed, setFailed] = useState(false);\n\n useEffect(() => {\n let mounted = true;\n const to = Date.now();\n const from = to - hours * 3_600_000;\n\n socket\n .getHistory(id, {\n instance,\n start: from,\n end: to,\n step: Math.round((to - from) / INTERVALS),\n from: false,\n ack: false,\n q: false,\n addId: false,\n aggregate: 'minmax',\n })\n .then(values => {\n if (mounted) {\n setData({ points: readPoints(values), from, to });\n }\n })\n .catch(e => {\n console.warn(`Cannot read history of ${id}: ${e}`);\n if (mounted) {\n setFailed(true);\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [socket, id, instance, hours]);\n\n let content: JSX.Element;\n\n if (failed || (data && !data.points.length)) {\n content = (\n <text\n x={WIDTH / 2}\n y={(PLOT_TOP + PLOT_BOTTOM) / 2 + 3}\n textAnchor=\"middle\"\n fill=\"currentColor\"\n fillOpacity={0.5}\n fontSize={11}\n >\n {t('ra_No data')}\n </text>\n );\n } else if (!data) {\n content = (\n <line\n x1={PLOT_LEFT + 6}\n x2={PLOT_RIGHT - 6}\n y1={(PLOT_TOP + PLOT_BOTTOM) / 2}\n y2={(PLOT_TOP + PLOT_BOTTOM) / 2}\n stroke=\"currentColor\"\n strokeOpacity={0.25}\n strokeWidth={1.5}\n strokeDasharray=\"3 4\"\n strokeLinecap=\"round\"\n >\n <animate\n attributeName=\"stroke-opacity\"\n values=\"0.3;0.1;0.3\"\n dur=\"1.4s\"\n repeatCount=\"indefinite\"\n />\n </line>\n );\n } else {\n const points = data.points.slice();\n // Let the line reach \"now\": a state that was not written since an hour still has its value\n if (current && Number.isFinite(current.val) && (!points.length || current.ts >= points[points.length - 1].ts)) {\n points.push({ ts: Math.min(current.ts, data.to), val: current.val });\n }\n\n let min = points[0].val;\n let max = points[0].val;\n for (const point of points) {\n if (point.val < min) {\n min = point.val;\n }\n if (point.val > max) {\n max = point.val;\n }\n }\n\n // The drawn range is a bit larger than the values, so the line has some air above and below\n let low: number;\n let high: number;\n if (isBoolean) {\n low = 0;\n high = 1;\n } else if (min === max) {\n // a constant value is drawn in the middle\n low = min - 1;\n high = max + 1;\n } else {\n const air = (max - min) * 0.1;\n low = min - air;\n high = max + air;\n }\n\n const scaleX = (PLOT_RIGHT - PLOT_LEFT) / (data.to - data.from || 1);\n const x = (ts: number): number =>\n Math.min(PLOT_RIGHT, Math.max(PLOT_LEFT, PLOT_LEFT + (ts - data.from) * scaleX));\n const y = (val: number): number => VALUE_BOTTOM - ((val - low) / (high - low)) * (VALUE_BOTTOM - VALUE_TOP);\n\n const steps = !!isBoolean;\n const line = buildLine(points, x, y, steps);\n const lastX = x(points[points.length - 1].ts);\n const lastY = y(points[points.length - 1].val);\n // the area is closed against the bottom of the plot, and not against the lowest value\n const area = `${line}V${PLOT_BOTTOM}H${x(points[0].ts).toFixed(2)}Z`;\n const zeroY = low < 0 && high > 0 ? y(0) : null;\n\n content = (\n <>\n <path\n d={area}\n fill={`url(#${gradientId})`}\n />\n {zeroY !== null ? (\n <line\n x1={PLOT_LEFT}\n x2={PLOT_RIGHT}\n y1={zeroY}\n y2={zeroY}\n stroke=\"currentColor\"\n strokeOpacity={0.25}\n strokeWidth={1}\n strokeDasharray=\"2 3\"\n />\n ) : null}\n <path\n d={line}\n fill=\"none\"\n stroke={color}\n strokeWidth={1.6}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <circle\n cx={lastX}\n cy={lastY}\n r={5}\n fill={color}\n fillOpacity={0.25}\n />\n <circle\n cx={lastX}\n cy={lastY}\n r={2.4}\n fill={color}\n />\n <text\n x={PLOT_LEFT - 5}\n y={y(isBoolean ? 1 : max) + 3}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fillOpacity={0.55}\n fontSize={FONT_SIZE}\n >\n {isBoolean ? 'true' : formatValue(max, isFloatComma)}\n </text>\n {isBoolean || min !== max ? (\n <text\n x={PLOT_LEFT - 5}\n y={y(isBoolean ? 0 : min) + 3}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fillOpacity={0.55}\n fontSize={FONT_SIZE}\n >\n {isBoolean ? 'false' : formatValue(min, isFloatComma)}\n </text>\n ) : null}\n </>\n );\n }\n\n return (\n <svg\n viewBox={`0 0 ${WIDTH} ${HEIGHT}`}\n style={{ width: '100%', height: 'auto', display: 'block', marginTop: 4 }}\n >\n <defs>\n <linearGradient\n id={gradientId}\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\"\n >\n <stop\n offset=\"0%\"\n stopColor={color}\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor={color}\n stopOpacity={0}\n />\n </linearGradient>\n </defs>\n <rect\n x={PLOT_LEFT}\n y={PLOT_TOP}\n width={PLOT_RIGHT - PLOT_LEFT}\n height={PLOT_BOTTOM - PLOT_TOP}\n rx={4}\n fill=\"currentColor\"\n fillOpacity={0.07}\n />\n {content}\n <text\n x={PLOT_LEFT}\n y={HEIGHT - 3}\n fill=\"currentColor\"\n fillOpacity={0.45}\n fontSize={FONT_SIZE}\n >\n {`-${hours} h`}\n </text>\n <text\n x={PLOT_RIGHT}\n y={HEIGHT - 3}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fillOpacity={0.45}\n fontSize={FONT_SIZE}\n >\n {t('ra_now')}\n </text>\n </svg>\n );\n}\n"]}
@@ -13,13 +13,6 @@ import { getSelectIdIconFromObjects } from './utils';
13
13
  import type { Width } from '../../types';
14
14
  import type { ObjectBrowserProps, AdapterColumn, ObjectBrowserFilter, ObjectBrowserPossibleColumns, ObjectBrowserState, TreeInfo, TreeItem, CustomAdminColumnStored, ObjectEvent } from './types';
15
15
  export { getSelectIdIconFromObjects, type ObjectBrowserFilter };
16
- declare global {
17
- interface Window {
18
- sparkline: {
19
- sparkline: (el: HTMLDivElement, data: number[]) => JSX.Element;
20
- };
21
- }
22
- }
23
16
  declare module '@mui/material/Button' {
24
17
  interface ButtonPropsColorOverrides {
25
18
  grey: true;
@@ -255,7 +248,6 @@ export declare class ObjectBrowserClass extends Component<ObjectBrowserProps, Ob
255
248
  onCopy(e: React.MouseEvent, text: string | undefined): void;
256
249
  renderTooltipAccessControl(acl: ioBroker.StateACL): null | JSX.Element;
257
250
  renderColumnButtons(id: string, item: TreeItem): (JSX.Element | null)[] | JSX.Element | null;
258
- readHistory(id: string): void;
259
251
  getTooltipInfo(id: string, cb?: () => void): void;
260
252
  private _syncEnum;
261
253
  syncEnum(id: string, enumName: 'func' | 'room', newArray: string[]): Promise<void>;
@@ -17,7 +17,8 @@ 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 { applyFilter, binarySearch, buildTree, findNode, formatValue, generateFile, getName, colVar, widthFromContainer, growVar, getSelectIdIconFromObjects, setCustomValue, prepareSparkData, } from './utils';
20
+ import { applyFilter, binarySearch, buildTree, findNode, formatValue, generateFile, getName, colVar, widthFromContainer, growVar, getSelectIdIconFromObjects, setCustomValue, } from './utils';
21
+ import { HistoryChart } from './HistoryChart';
21
22
  import { styles } from './styles';
22
23
  import * as dialogs from './dialogs';
23
24
  import * as toolbar from './toolbar';
@@ -1602,57 +1603,6 @@ export class ObjectBrowserClass extends Component {
1602
1603
  renderColumnButtons(id, item) {
1603
1604
  return leaf.renderColumnButtons(this, id, item);
1604
1605
  }
1605
- readHistory(id) {
1606
- /* interface GetHistoryOptions {
1607
- instance?: string;
1608
- start?: number;
1609
- end?: number;
1610
- step?: number;
1611
- count?: number;
1612
- from?: boolean;
1613
- ack?: boolean;
1614
- q?: boolean;
1615
- addID?: boolean;
1616
- limit?: number;
1617
- ignoreNull?: boolean;
1618
- sessionId?: any;
1619
- aggregate?: 'minmax' | 'min' | 'max' | 'average' | 'total' | 'count' | 'none';
1620
- } */
1621
- if (window.sparkline &&
1622
- this.defaultHistory &&
1623
- this.objects[id]?.common?.custom &&
1624
- this.objects[id].common.custom[this.defaultHistory]) {
1625
- const now = new Date();
1626
- now.setHours(now.getHours() - 24);
1627
- now.setMinutes(0);
1628
- now.setSeconds(0);
1629
- now.setMilliseconds(0);
1630
- const nowMs = now.getTime();
1631
- this.props.socket
1632
- .getHistory(id, {
1633
- instance: this.defaultHistory,
1634
- start: nowMs,
1635
- end: Date.now(),
1636
- step: 3600000,
1637
- from: false,
1638
- ack: false,
1639
- q: false,
1640
- addId: false,
1641
- aggregate: 'minmax',
1642
- })
1643
- .then(values => {
1644
- const sparks = window.document.getElementsByClassName('sparkline');
1645
- for (let s = 0; s < sparks.length; s++) {
1646
- if (sparks[s].dataset.id === id) {
1647
- const v = prepareSparkData(values, nowMs);
1648
- window.sparkline.sparkline(sparks[s], v);
1649
- break;
1650
- }
1651
- }
1652
- })
1653
- .catch(e => console.warn(`Cannot read history: ${e}`));
1654
- }
1655
- }
1656
1606
  getTooltipInfo(id, cb) {
1657
1607
  const obj = this.objects[id];
1658
1608
  const state = this.states[id];
@@ -1697,7 +1647,13 @@ export class ObjectBrowserClass extends Component {
1697
1647
  valFullRx.unshift(React.createElement("div", { key: "ctrl", style: { textDecoration: 'underline', fontWeight: 'bold' } }, this.texts.ctrlForLink));
1698
1648
  }
1699
1649
  else if (this.defaultHistory && obj?.common?.custom?.[this.defaultHistory]) {
1700
- valFullRx.push(React.createElement("svg", { key: "sparkline", className: "sparkline", "data-id": id, style: { fill: '#3d85de' }, width: "200", height: "30", strokeWidth: "3" }));
1650
+ const isNumber = typeof state?.val === 'number';
1651
+ const isBoolean = obj.common.type === 'boolean' || typeof state?.val === 'boolean';
1652
+ valFullRx.push(React.createElement(HistoryChart, { key: "chart", socket: this.props.socket, id: id, instance: this.defaultHistory, t: this.props.t, isBoolean: isBoolean, isFloatComma: this.props.isFloatComma === undefined
1653
+ ? (this.systemConfig?.common.isFloatComma ?? true)
1654
+ : this.props.isFloatComma, current: isNumber || isBoolean
1655
+ ? { ts: state.ts, val: typeof state.val === 'boolean' ? +state.val : state.val }
1656
+ : null }));
1701
1657
  }
1702
1658
  this.setState({ tooltipInfo: { el: valFullRx, id } }, () => cb && cb());
1703
1659
  }