feat(kue705FO): scrolling für lange Modulnamen (48 Zeichen) + Version-Gate/Env-Override
- Unterstützt bis zu 48 Zeichen im Modulnamen; bei Überlänge automatische Laufschrift - Marquee via react-fast-marquee (SSR-sicher per next/dynamic) - Overflow-Erkennung + Tooltip mit vollem Namen - Version-Gate: aktiviert ab V4.30
This commit is contained in:
@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
|
||||
NEXT_PUBLIC_EXPORT_STATIC=false
|
||||
NEXT_PUBLIC_USE_CGI=false
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.844
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.845
|
||||
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
||||
|
||||
|
||||
@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
|
||||
NEXT_PUBLIC_EXPORT_STATIC=true
|
||||
NEXT_PUBLIC_USE_CGI=true
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.844
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.845
|
||||
NEXT_PUBLIC_CPL_MODE=production
|
||||
@@ -1,3 +1,8 @@
|
||||
## [1.6.845] – 2025-09-05
|
||||
|
||||
- feat: prepare KÜ 8 for scrolling text
|
||||
|
||||
---
|
||||
## [1.6.844] – 2025-09-05
|
||||
|
||||
- test: woodpecker
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
"use client"; // components/modules/kue705FO/Kue705FO.tsx
|
||||
import React, { useState, useMemo } from "react";
|
||||
import React, { useState, useMemo, useEffect, useRef } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const Marquee: any = dynamic(() => import("react-fast-marquee"), {
|
||||
ssr: false,
|
||||
});
|
||||
import { useSelector } from "react-redux";
|
||||
import KueModal from "./modals/SettingsModalWrapper";
|
||||
// import FallSensors from "../../fall-detection-sensors/FallSensors";
|
||||
@@ -54,6 +59,19 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
||||
// Admin authentication hook for security - using showModal as true for continuous auth check
|
||||
const { isAdminLoggedIn } = useAdminAuth(true);
|
||||
|
||||
// Modulname (max 48 Zeichen) vorbereiten
|
||||
const moduleNameRaw = useMemo(
|
||||
() => kueName?.[slotIndex] || `Modul ${slotIndex + 1}`,
|
||||
[kueName, slotIndex]
|
||||
);
|
||||
const moduleName48 = useMemo(
|
||||
() =>
|
||||
typeof moduleNameRaw === "string"
|
||||
? moduleNameRaw.slice(0, 48)
|
||||
: String(moduleNameRaw),
|
||||
[moduleNameRaw]
|
||||
);
|
||||
|
||||
const [activeButton, setActiveButton] = useState<"Schleife" | "TDR" | "ISO">(
|
||||
"Schleife"
|
||||
);
|
||||
@@ -206,6 +224,53 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
||||
);
|
||||
const { setCurrentModulName } = useModulName(slotIndex, modulName);
|
||||
//---------------------------------
|
||||
// Version-gate für Laufschrift: erst ab V4.30 aktiv
|
||||
const parseVersion = (v?: string): [number, number, number] => {
|
||||
if (!v) return [0, 0, 0];
|
||||
const m = String(v).match(/(\d+)(?:\.(\d+))?(?:\.(\d+))?/);
|
||||
if (!m) return [0, 0, 0];
|
||||
const major = parseInt(m[1] || "0", 10) || 0;
|
||||
const minor = parseInt(m[2] || "0", 10) || 0;
|
||||
const patch = parseInt(m[3] || "0", 10) || 0;
|
||||
return [major, minor, patch];
|
||||
};
|
||||
const gte = (a: [number, number, number], b: [number, number, number]) => {
|
||||
if (a[0] !== b[0]) return a[0] > b[0];
|
||||
if (a[1] !== b[1]) return a[1] > b[1];
|
||||
return a[2] >= b[2];
|
||||
};
|
||||
const marqueeOverride =
|
||||
process.env.NEXT_PUBLIC_ENABLE_KUE_MARQUEE === "1" ||
|
||||
process.env.NEXT_PUBLIC_ENABLE_KUE_MARQUEE === "true";
|
||||
const scrollFeatureEnabled = useMemo(
|
||||
() => marqueeOverride || gte(parseVersion(kueVersion), [4, 30, 0]),
|
||||
[kueVersion, marqueeOverride]
|
||||
);
|
||||
|
||||
// Überlängen-Erkennung für Laufschrift
|
||||
const nameContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const measureTextRef = useRef<HTMLSpanElement | null>(null);
|
||||
const [shouldScroll, setShouldScroll] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const measure = () => {
|
||||
if (!scrollFeatureEnabled) {
|
||||
setShouldScroll(false);
|
||||
return;
|
||||
}
|
||||
const container = nameContainerRef.current;
|
||||
const text = measureTextRef.current;
|
||||
if (!container || !text) {
|
||||
setShouldScroll(false);
|
||||
return;
|
||||
}
|
||||
const needs = text.scrollWidth > container.clientWidth + 2;
|
||||
setShouldScroll(needs);
|
||||
};
|
||||
measure();
|
||||
window.addEventListener("resize", measure);
|
||||
return () => window.removeEventListener("resize", measure);
|
||||
}, [moduleName48, scrollFeatureEnabled]);
|
||||
//---------------------------------
|
||||
const tdmChartData = useSelector(
|
||||
(state: RootState) => state.tdmChartSlice.data
|
||||
@@ -360,8 +425,38 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
||||
<div className="absolute top-0 left-[4.688rem] w-[0.188rem] h-full bg-white z-0"></div>
|
||||
<div className="absolute top-[2.5rem] left-[4.688rem] w-[2.5rem] h-[0.188rem] bg-white z-0"></div>
|
||||
|
||||
<div className="absolute bottom-[1.25rem] left-0 right-0 text-black text-[0.625rem] bg-gray-300 p-[0.063rem] text-center">
|
||||
{kueName?.[slotIndex] || `Modul ${slotIndex + 1}`}
|
||||
{/* Hidden measuring span for overflow detection (kept measurable) */}
|
||||
<span
|
||||
ref={measureTextRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: -9999,
|
||||
top: -9999,
|
||||
visibility: "hidden",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{moduleName48}
|
||||
</span>
|
||||
|
||||
<div
|
||||
ref={nameContainerRef}
|
||||
className="absolute bottom-[1.25rem] left-0 right-0 text-black text-[0.625rem] bg-gray-300 p-[0.063rem] overflow-hidden"
|
||||
>
|
||||
{shouldScroll && scrollFeatureEnabled ? (
|
||||
<Marquee pauseOnHover gradient={false} speed={40}>
|
||||
<span className="pr-8 whitespace-nowrap" title={moduleName48}>
|
||||
{moduleName48}
|
||||
</span>
|
||||
</Marquee>
|
||||
) : (
|
||||
<span
|
||||
className="block text-center whitespace-nowrap"
|
||||
title={moduleName48}
|
||||
>
|
||||
{moduleName48}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="absolute bottom-[0.063rem] right-[0.063rem] text-black text-[0.5rem]">
|
||||
|
||||
@@ -94,9 +94,9 @@ in Rot, wenn Schleifenfehler ansteht
|
||||
|
||||
# Folgende Erweiterung / Neuerungen:
|
||||
|
||||
[ ] TODO: Messverlauf bei Systemwerten (Temperatur und Spannungen) mit Datumsauswahl
|
||||
[x] TODO: Messverlauf bei Systemwerten (Temperatur und Spannungen) mit Datumsauswahl
|
||||
|
||||
[ ] TODO: Formatierung der Kabelüberwachungswerten in den visuellen Einschüben (Isowert mit Komma und 2 Nachkommastellen; RSL mit Komma und 3 Noachkommastellen) Nachkommastellen immer anzeigen und mit Nullen auffüllen.
|
||||
[x] TODO: Formatierung der Kabelüberwachungswerten in den visuellen Einschüben (Isowert mit Komma und 2 Nachkommastellen; RSL mit Komma und 3 Noachkommastellen) Nachkommastellen immer anzeigen und mit Nullen auffüllen.
|
||||
|
||||
[ ] TODO: Admin User nach einer Zeit von einer Stunde löschen (Cookie oder Local Storrage)
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ var win_kueLoopInterval = [
|
||||
//---------------------------------------------------
|
||||
//KÜ Modul Version soll /100 und davor V angezeigt werden z.B. 4.19V
|
||||
var win_kueVersion = [
|
||||
420, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419,
|
||||
420, 419, 419, 419, 419, 419, 419, 430, 419, 419, 419, 419, 419, 419, 419,
|
||||
419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419,
|
||||
419, 419,
|
||||
];
|
||||
|
||||
15
package-lock.json
generated
15
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.844",
|
||||
"version": "1.6.845",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.844",
|
||||
"version": "1.6.845",
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "^5.1.0",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
@@ -39,6 +39,7 @@
|
||||
"react-datepicker": "^8.0.0",
|
||||
"react-day-picker": "^9.6.7",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-fast-marquee": "^1.6.0",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-spinners": "^0.14.1",
|
||||
@@ -9161,6 +9162,16 @@
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-fast-marquee": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/react-fast-marquee/-/react-fast-marquee-1.6.5.tgz",
|
||||
"integrity": "sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0 || ^18.0.0",
|
||||
"react-dom": ">= 16.8.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-fit": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-fit/-/react-fit-2.0.1.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.844",
|
||||
"version": "1.6.845",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3000",
|
||||
@@ -60,6 +60,7 @@
|
||||
"next": "^14.2.23",
|
||||
"node-fetch": "^3.3.2",
|
||||
"react": "^18.3.1",
|
||||
"react-fast-marquee": "^1.6.0",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-date-picker": "^11.0.0",
|
||||
"react-datepicker": "^8.0.0",
|
||||
|
||||
17
types/react-fast-marquee.d.ts
vendored
Normal file
17
types/react-fast-marquee.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
declare module "react-fast-marquee" {
|
||||
import * as React from "react";
|
||||
interface MarqueeProps {
|
||||
speed?: number;
|
||||
pauseOnHover?: boolean;
|
||||
gradient?: boolean;
|
||||
gradientColor?: [number, number, number];
|
||||
gradientWidth?: number | string;
|
||||
direction?: "left" | "right";
|
||||
play?: boolean;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
const Marquee: React.FC<MarqueeProps>;
|
||||
export default Marquee;
|
||||
}
|
||||
Reference in New Issue
Block a user