87 lines
2.3 KiB
TypeScript
87 lines
2.3 KiB
TypeScript
import { h } from 'preact';
|
|
import { useEffect, useRef } from 'preact/hooks';
|
|
import type { SignalRef, SignalMeta } from './lib/types';
|
|
import { getMetaStore } from './lib/stores';
|
|
import { useState } from 'preact/hooks';
|
|
|
|
interface Props {
|
|
visible: boolean;
|
|
x: number;
|
|
y: number;
|
|
signal: SignalRef | null;
|
|
onClose: () => void;
|
|
onInfo?: () => void;
|
|
onPlot?: () => void;
|
|
}
|
|
|
|
export default function ContextMenu({ visible, x, y, signal, onClose, onInfo, onPlot }: Props) {
|
|
const [meta, setMeta] = useState<SignalMeta | null>(null);
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!signal) { setMeta(null); return; }
|
|
const unsub = getMetaStore(signal).subscribe(setMeta);
|
|
return unsub;
|
|
}, [signal?.ds, signal?.name]);
|
|
|
|
useEffect(() => {
|
|
if (!visible) return;
|
|
function handleClick(e: MouseEvent) {
|
|
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
|
onClose();
|
|
}
|
|
}
|
|
document.addEventListener('mousedown', handleClick);
|
|
return () => document.removeEventListener('mousedown', handleClick);
|
|
}, [visible, onClose]);
|
|
|
|
if (!visible) return null;
|
|
|
|
function copySignalName() {
|
|
if (signal) navigator.clipboard.writeText(signal.name).catch(() => {});
|
|
onClose();
|
|
}
|
|
|
|
function openInfo() {
|
|
onClose();
|
|
onInfo?.();
|
|
}
|
|
|
|
function openPlot() {
|
|
onClose();
|
|
onPlot?.();
|
|
}
|
|
|
|
function exportCSV() {
|
|
// Phase 5+: export CSV data
|
|
onClose();
|
|
}
|
|
|
|
return (
|
|
<div
|
|
class="ctx-menu"
|
|
ref={menuRef}
|
|
style={`left:${x}px;top:${y}px;`}
|
|
>
|
|
{signal ? (
|
|
<div>
|
|
<div class="ctx-signal-info">
|
|
<div>{signal.ds} / {signal.name}</div>
|
|
{meta && (
|
|
<div>{meta.type}{meta.unit ? ` [${meta.unit}]` : ''}</div>
|
|
)}
|
|
</div>
|
|
<div class="ctx-divider" />
|
|
<button class="ctx-item" onClick={openInfo}>Signal info</button>
|
|
<button class="ctx-item" onClick={openPlot}>Plot</button>
|
|
<div class="ctx-divider" />
|
|
<button class="ctx-item" onClick={copySignalName}>Copy signal name</button>
|
|
<button class="ctx-item" onClick={exportCSV}>Export data to CSV</button>
|
|
</div>
|
|
) : (
|
|
<button class="ctx-item" onClick={onClose}>Close</button>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|