Files
uopi/web/src/Canvas.tsx
T
Martino Ferrari e83e183673 phase 10
2026-04-26 11:01:55 +02:00

100 lines
2.9 KiB
TypeScript

import { h } from 'preact';
import { useState } from 'preact/hooks';
import type { Interface, Widget, SignalRef } from './lib/types';
import TextView from './widgets/TextView';
import TextLabel from './widgets/TextLabel';
import Gauge from './widgets/Gauge';
import BarH from './widgets/BarH';
import BarV from './widgets/BarV';
import Led from './widgets/Led';
import MultiLed from './widgets/MultiLed';
import SetValue from './widgets/SetValue';
import Button from './widgets/Button';
import PlotWidget from './widgets/PlotWidget';
import ImageWidget from './widgets/ImageWidget';
import LinkWidget from './widgets/LinkWidget';
import ContextMenu from './ContextMenu';
const COMPONENTS: Record<string, any> = {
textview: TextView,
textlabel: TextLabel,
gauge: Gauge,
barh: BarH,
barv: BarV,
led: Led,
multiled: MultiLed,
setvalue: SetValue,
button: Button,
plot: PlotWidget,
image: ImageWidget,
link: LinkWidget,
};
interface CtxState {
visible: boolean;
x: number;
y: number;
signal: SignalRef | null;
}
interface Props {
iface: Interface | null;
onNavigate?: (interfaceId: string) => void;
timeRange?: { start: string; end: string } | null;
}
export default function Canvas({ iface, onNavigate, timeRange }: Props) {
const [ctxMenu, setCtxMenu] = useState<CtxState>({
visible: false, x: 0, y: 0, signal: null,
});
function onCtxMenu(e: MouseEvent, widget: Widget) {
e.preventDefault();
e.stopPropagation();
setCtxMenu({ visible: true, x: e.clientX, y: e.clientY, signal: widget.signals[0] ?? null });
}
if (!iface) {
return (
<div class="canvas-container">
<div class="placeholder">
<p>Select an interface from the left panel, or import one.</p>
</div>
</div>
);
}
return (
<div class="canvas-container">
<div class="canvas-area" style={`width:${iface.w}px;height:${iface.h}px;`}>
{iface.widgets.map(widget => {
const Comp = COMPONENTS[widget.type];
return Comp
? <Comp
key={widget.id}
widget={widget}
onContextMenu={(e: MouseEvent) => onCtxMenu(e, widget)}
onNavigate={onNavigate}
timeRange={timeRange}
/>
: (
<div
key={widget.id}
class="unknown-widget"
style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
title={`Unknown widget type: ${widget.type}`}
onContextMenu={(e: MouseEvent) => onCtxMenu(e, widget)}
>
<span class="unknown-label">{widget.type}</span>
</div>
);
})}
</div>
<ContextMenu
{...ctxMenu}
onClose={() => setCtxMenu(c => ({ ...c, visible: false }))}
/>
</div>
);
}