import type { ReactNode } from "react";
import type {
DigitalDisplayMode,
DigitalDisplayProps,
} from "./digital-display.types.js";
const DEFAULT_WIDTH_PX = 120;
const DEFAULT_HEIGHT_PX = 32;
const DEFAULT_SPEED = 30;
const DEFAULT_WINDOW = 8;
function formatNumeric(
value: number,
precision: number | undefined,
units: string | undefined
): string {
const p = precision ?? 0;
const body = Number.isFinite(value) ? value.toFixed(p) : "—";
return units !== undefined && units !== "" ? `${body} ${units}` : body;
}
function renderBody(
mode: DigitalDisplayMode,
value: number,
widthPx: number,
heightPx: number
): ReactNode {
if (mode.kind === "numeric") {
return (
{formatNumeric(value, mode.precision, mode.units)}
);
}
if (mode.kind === "text") {
return {mode.text};
}
const window = mode.windowChars ?? DEFAULT_WINDOW;
const speed = mode.speedPxPerSec ?? DEFAULT_SPEED;
const overflowing = mode.text.length > window;
if (!overflowing) {
return (
{mode.text}
);
}
const pixelDistance = mode.text.length * (heightPx * 0.55);
const durationSec = pixelDistance / speed;
const animationName = "cs-display-scroll";
return (
<>
{mode.text}
>
);
}
/**
* Read-only 7-segment / LCD-style display. Three modes:
* `numeric` (renders `value.toFixed(precision)` with optional units),
* `text` (static string), `scrolling` (overflow → CSS keyframe scroll
* with `prefers-reduced-motion` honoured).
*/
export function DigitalDisplay(props: DigitalDisplayProps): ReactNode {
const {
mode,
value,
widthPx = DEFAULT_WIDTH_PX,
heightPx = DEFAULT_HEIGHT_PX,
label,
ariaLabel,
disabled,
className,
} = props;
const ariaContent =
mode.kind === "numeric"
? formatNumeric(value, mode.precision, mode.units)
: mode.text;
return (
);
}