visual fixes

This commit is contained in:
jhartworks
2026-07-07 10:37:26 +02:00
parent 2c055fcb63
commit 7a1fe401f2
5 changed files with 77306 additions and 24 deletions
+77153
View File
File diff suppressed because one or more lines are too long
+23 -3
View File
@@ -59,7 +59,7 @@ const emptyTrendRange = {
presetDays: "1",
from: "",
to: "",
maxRows: 4000,
maxRows: 20000,
};
const defaultSelectionDraft = {
@@ -700,6 +700,7 @@ export default function App() {
const [themeMode, setThemeMode] = useState("system");
const [csvDelimiter, setCsvDelimiter] = useState(";");
const [sidebarPinned, setSidebarPinned] = useState(true);
const [trendLineWidth, setTrendLineWidth] = useState("1.8");
const [sidebarHover, setSidebarHover] = useState(false);
const [trendFocus, setTrendFocus] = useState(false);
const [adminIsp, setAdminIsp] = useState("");
@@ -738,6 +739,8 @@ export default function App() {
const sidebarCollapsed = !sidebarPinned && !sidebarHover;
const currentView = views.find((view) => view.id === activeView) || views[0];
const selectionListClassName = selections.length <= 2 ? "selection-list compact" : "selection-list";
const trendMaxRows = Number(trendRange.maxRows) || 0;
const showTrendLimitWarning = trendMaxRows > 20000;
const roleOptions = roles.length ? roles : [
{ name: "viewer", label: "Viewer" },
{ name: "technician", label: "Techniker" },
@@ -888,6 +891,7 @@ export default function App() {
setThemeMode(pref.theme || "system");
setCsvDelimiter(pref.csvDelimiter || ";");
setSidebarPinned(typeof pref.sidebarPinned === "boolean" ? pref.sidebarPinned : true);
setTrendLineWidth(String(pref.trendLineWidth || 1.8));
setIsps(ispResult.isps || []);
setSelections(selectionResult.selections);
setDashboard(dashboardResult);
@@ -967,8 +971,9 @@ export default function App() {
theme: themeMode,
csvDelimiter,
sidebarPinned,
trendLineWidth: Number(trendLineWidth),
}).catch(() => undefined);
}, [csvDelimiter, preferencesReady, sidebarPinned, themeMode, token]);
}, [csvDelimiter, preferencesReady, sidebarPinned, themeMode, token, trendLineWidth]);
useEffect(() => {
if (!selectedIsp || metadataCache[selectedIsp]) {
@@ -2180,6 +2185,7 @@ export default function App() {
onCsvDelimiterChange={setCsvDelimiter}
allowExports
allowFocusMode
lineWidth={Number(trendLineWidth)}
/>
) : (
<div className="panel empty-state slim-empty">
@@ -2258,7 +2264,18 @@ export default function App() {
</label>
<label>
<span>Max. Zeilen</span>
<input type="number" min="100" max="10000" value={trendRange.maxRows} onChange={(event) => setTrendRange((current) => ({ ...current, maxRows: event.target.value }))} />
<input type="number" min="100" value={trendRange.maxRows} onChange={(event) => setTrendRange((current) => ({ ...current, maxRows: event.target.value }))} />
</label>
<label>
<span>Liniendicke</span>
<select value={trendLineWidth} onChange={(event) => setTrendLineWidth(event.target.value)}>
<option value="1">1.0 px</option>
<option value="1.4">1.4 px</option>
<option value="1.8">1.8 px</option>
<option value="2.2">2.2 px</option>
<option value="2.8">2.8 px</option>
<option value="3.4">3.4 px</option>
</select>
</label>
<label>
<span>Start</span>
@@ -2270,6 +2287,8 @@ export default function App() {
</label>
</div>
{showTrendLimitWarning ? <p className="warning-note">{"Mehr als 20000 Datensätze sind möglich, können aber zu längeren Ladezeiten führen."}</p> : null}
<div className="button-row">
<button className="primary-button compact-button" type="button" onClick={runTrendQuery}>{busy ? "Lädt ..." : "Trend anzeigen"}</button>
</div>
@@ -2328,6 +2347,7 @@ export default function App() {
onCsvDelimiterChange={setCsvDelimiter}
allowExports
allowFocusMode
lineWidth={Number(trendLineWidth)}
/>
) : (
<div className="panel empty-state slim-empty">
+33 -11
View File
@@ -72,9 +72,12 @@ export default function TrendChart({
allowExports = true,
allowFocusMode = true,
compact = false,
lineWidth = 1.8,
}) {
const wrapperRef = useRef(null);
const chartHostRef = useRef(null);
const dragBoxRef = useRef(null);
const domainRef = useRef({ x: [0, 1], y: [0, 1] });
const [selectedKeys, setSelectedKeys] = useState([]);
const [dragBox, setDragBox] = useState(null);
const [zoomDomain, setZoomDomain] = useState(null);
@@ -116,17 +119,23 @@ export default function TrendChart({
const xDomain = zoomDomain?.x || baseXDomain;
const yDomain = zoomDomain?.y || baseYDomain;
const hasFocusSelection = selectedKeys.length > 0 && selectedKeys.length < series.length;
const normalizedLineWidth = Number.isFinite(Number(lineWidth)) ? Number(lineWidth) : 1.8;
useEffect(() => {
domainRef.current = { x: xDomain, y: yDomain };
}, [xDomain, yDomain]);
const mapDragToDomain = (clientX, clientY) => {
const rect = wrapperRef.current?.getBoundingClientRect();
const rect = chartHostRef.current?.getBoundingClientRect() || wrapperRef.current?.getBoundingClientRect();
if (!rect) {
return null;
}
const activeDomain = domainRef.current;
const xRatio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
const yRatio = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
const xValue = xDomain[0] + (xDomain[1] - xDomain[0]) * xRatio;
const yValue = yDomain[1] - (yDomain[1] - yDomain[0]) * yRatio;
const xValue = activeDomain.x[0] + (activeDomain.x[1] - activeDomain.x[0]) * xRatio;
const yValue = activeDomain.y[1] - (activeDomain.y[1] - activeDomain.y[0]) * yRatio;
return {
xValue,
@@ -145,7 +154,9 @@ export default function TrendChart({
if (!mapped) {
return;
}
setDragBox({ start: mapped, end: mapped });
const nextDragBox = { start: mapped, end: mapped };
dragBoxRef.current = nextDragBox;
setDragBox(nextDragBox);
};
const updateDragBox = (clientX, clientY) => {
@@ -153,7 +164,14 @@ export default function TrendChart({
if (!mapped) {
return;
}
setDragBox((current) => current ? { ...current, end: mapped } : current);
setDragBox((current) => {
if (!current) {
return current;
}
const nextDragBox = { ...current, end: mapped };
dragBoxRef.current = nextDragBox;
return nextDragBox;
});
};
const finalizeDragBox = (box) => {
@@ -163,15 +181,18 @@ export default function TrendChart({
const deltaX = Math.abs(box.end.xPixel - box.start.xPixel);
const deltaY = Math.abs(box.end.yPixel - box.start.yPixel);
if (deltaX < 12 || deltaY < 12) {
if (deltaX < 12 && deltaY < 12) {
dragBoxRef.current = null;
setDragBox(null);
return;
}
const currentDomain = domainRef.current;
setZoomDomain({
x: [Math.min(box.start.xValue, box.end.xValue), Math.max(box.start.xValue, box.end.xValue)],
y: [Math.min(box.start.yValue, box.end.yValue), Math.max(box.start.yValue, box.end.yValue)],
x: deltaX < 12 ? currentDomain.x : [Math.min(box.start.xValue, box.end.xValue), Math.max(box.start.xValue, box.end.xValue)],
y: deltaY < 12 ? currentDomain.y : [Math.min(box.start.yValue, box.end.yValue), Math.max(box.start.yValue, box.end.yValue)],
});
dragBoxRef.current = null;
setDragBox(null);
};
@@ -185,7 +206,7 @@ export default function TrendChart({
};
const handleWindowMouseUp = () => {
finalizeDragBox(dragBox);
finalizeDragBox(dragBoxRef.current);
};
window.addEventListener("mousemove", handleWindowMouseMove);
@@ -444,10 +465,11 @@ export default function TrendChart({
<Line
key={entry.key}
yAxisId={entry.kind === "digital" ? "digital" : "analog"}
type={entry.kind === "digital" ? "stepAfter" : "monotone"}
type={entry.kind === "digital" ? "stepAfter" : "linear"}
dataKey={entry.key}
dot={false}
strokeWidth={selectedKeys.includes(entry.key) ? 2.35 : 1.8}
connectNulls
strokeWidth={selectedKeys.includes(entry.key) ? normalizedLineWidth + 0.55 : normalizedLineWidth}
stroke={entry.color}
strokeOpacity={isolated ? 0.1 : 1}
isAnimationActive={false}
+94 -10
View File
@@ -525,27 +525,42 @@ span {
.user-row,
.stat-card {
justify-content: space-between;
padding: 0.5rem 0.58rem;
padding: 0.42rem 0.5rem;
}
.point-copy,
.user-copy {
display: grid;
gap: 0.06rem;
min-width: 0;
}
.point-item {
align-items: flex-start;
gap: 0.42rem;
}
.point-item .point-copy strong,
.point-item .point-copy span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.selection-chips {
min-height: 0;
gap: 0.34rem;
gap: 0.28rem;
}
.chip {
display: grid;
gap: 0.12rem;
border-left: 3px solid var(--brand-2);
min-width: 94px;
padding: 0.34rem 0.42rem;
border-radius: 12px;
gap: 0.1rem;
border-left: 2px solid var(--brand-2);
min-width: 0;
max-width: 100%;
padding: 0.26rem 0.36rem;
border-radius: 10px;
flex: 1 1 150px;
}
.chip strong,
@@ -556,8 +571,21 @@ span {
font-size: 12px;
}
.chip strong,
.chip span {
font-size: 11px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chip strong {
font-size: 10px;
line-height: 1.15;
}
.chip span {
font-size: 10px;
line-height: 1.15;
}
.chart-panel {
@@ -572,6 +600,8 @@ span {
width: 100%;
min-height: 320px;
user-select: none;
cursor: crosshair;
touch-action: none;
}
.chart-wrapper.dragging {
@@ -641,7 +671,8 @@ span {
.latest-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
gap: 0.42rem;
}
.trend-layout {
@@ -674,6 +705,48 @@ span {
border-left: 4px solid var(--brand-2);
}
.latest-card {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 0.45rem;
min-width: 0;
}
.latest-card > div:first-child {
min-width: 0;
}
.latest-card small,
.latest-card strong,
.latest-card .value-stamp {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.latest-card small {
font-size: 9px;
}
.latest-card strong {
font-size: 11px;
line-height: 1.18;
}
.latest-card .stat-value {
justify-content: flex-end;
min-width: max-content;
font-size: 13px;
line-height: 1;
white-space: nowrap;
}
.latest-card .stat-value span {
font-size: 10px;
}
.stat-value {
display: flex;
align-items: baseline;
@@ -791,6 +864,12 @@ span {
border: 1px solid rgba(37, 99, 235, 0.22);
}
.warning-note {
margin: 0;
color: #c2410c;
font-size: 11px;
}
.checkbox-field {
display: flex;
align-items: center;
@@ -889,9 +968,14 @@ span {
.protocol-grid,
.inline-fields,
.inline-fields.multi-line,
.trend-layout {
.trend-layout,
.latest-card {
grid-template-columns: 1fr;
}
.latest-card .stat-value {
justify-content: flex-start;
}
}