Files
cameleer-server/ui/src/components/ProcessDiagram/NodeToolbar.tsx

104 lines
3.2 KiB
TypeScript
Raw Normal View History

import { useCallback, useRef, useState } from 'react';
import { Search, Footprints, Droplets, Ellipsis } from 'lucide-react';
import type { NodeAction, NodeConfig } from './types';
import styles from './ProcessDiagram.module.css';
const HIDE_DELAY = 150;
const TRACE_ACTIVE_COLOR = 'var(--running)';
interface NodeToolbarProps {
nodeId: string;
/** Screen-space position (already transformed by zoom/pan) */
screenX: number;
screenY: number;
onAction: (nodeId: string, action: NodeAction) => void;
onMouseEnter: () => void;
onMouseLeave: () => void;
/** Current config for this node (trace/tap state) */
nodeConfig?: NodeConfig;
}
export function NodeToolbar({
nodeId, screenX, screenY, onAction, onMouseEnter, onMouseLeave, nodeConfig,
}: NodeToolbarProps) {
const traceActive = !!nodeConfig?.traceEnabled;
const tapActive = !!nodeConfig?.tapExpression;
return (
<div
className={styles.nodeToolbar}
style={{ left: screenX, top: screenY }}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<button
className={styles.nodeToolbarBtn}
title="Inspect"
onClick={(e) => { e.stopPropagation(); onAction(nodeId, 'inspect'); }}
>
<Search size={14} />
</button>
<button
className={`${styles.nodeToolbarBtn} ${traceActive ? styles.nodeToolbarBtnActive : ''}`}
title={traceActive ? 'Disable tracing' : 'Enable tracing'}
onClick={(e) => { e.stopPropagation(); onAction(nodeId, 'toggle-trace'); }}
style={traceActive ? { color: TRACE_ACTIVE_COLOR } : undefined}
>
<Footprints size={14} />
</button>
<button
className={`${styles.nodeToolbarBtn} ${tapActive ? styles.nodeToolbarBtnActive : ''}`}
title={tapActive ? 'Edit tap' : 'Configure tap'}
onClick={(e) => { e.stopPropagation(); onAction(nodeId, 'configure-tap'); }}
style={tapActive ? { color: 'var(--purple)' } : undefined}
>
<Droplets size={14} />
</button>
<button
className={styles.nodeToolbarBtn}
title="Copy ID"
onClick={(e) => { e.stopPropagation(); onAction(nodeId, 'copy-id'); }}
>
<Ellipsis size={14} />
</button>
</div>
);
}
/** Hook to manage toolbar visibility with hide delay */
export function useToolbarHover() {
const [hoveredNodeId, setHoveredNodeId] = useState<string | null>(null);
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const onNodeEnter = useCallback((nodeId: string) => {
if (hideTimer.current) {
clearTimeout(hideTimer.current);
hideTimer.current = null;
}
setHoveredNodeId(nodeId);
}, []);
const onNodeLeave = useCallback(() => {
hideTimer.current = setTimeout(() => {
setHoveredNodeId(null);
hideTimer.current = null;
}, HIDE_DELAY);
}, []);
const onToolbarEnter = useCallback(() => {
if (hideTimer.current) {
clearTimeout(hideTimer.current);
hideTimer.current = null;
}
}, []);
const onToolbarLeave = useCallback(() => {
hideTimer.current = setTimeout(() => {
setHoveredNodeId(null);
hideTimer.current = null;
}, HIDE_DELAY);
}, []);
return { hoveredNodeId, onNodeEnter, onNodeLeave, onToolbarEnter, onToolbarLeave };
}