feat: show connected robots in ui when getting cb pings #15
@@ -1,7 +0,0 @@
|
|||||||
.default-node {
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: canvas;
|
|
||||||
outline-style: solid;
|
|
||||||
border-radius: 5pt;
|
|
||||||
outline-width: 1pt;
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import './VisProgUI.css'
|
|
||||||
|
|
||||||
import {
|
|
||||||
useCallback,
|
|
||||||
useRef
|
|
||||||
} from 'react';
|
|
||||||
import {
|
|
||||||
Background,
|
|
||||||
Controls,
|
|
||||||
ReactFlow,
|
|
||||||
ReactFlowProvider,
|
|
||||||
useNodesState,
|
|
||||||
useEdgesState,
|
|
||||||
reconnectEdge,
|
|
||||||
addEdge,
|
|
||||||
MarkerType,
|
|
||||||
type Edge,
|
|
||||||
type Connection,
|
|
||||||
} from '@xyflow/react';
|
|
||||||
import '@xyflow/react/dist/style.css';
|
|
||||||
import {
|
|
||||||
StartNode,
|
|
||||||
EndNode,
|
|
||||||
PhaseNode,
|
|
||||||
NormNode
|
|
||||||
} from "./components/NodeDefinitions.tsx";
|
|
||||||
|
|
||||||
import { Sidebar } from './components/DragDropSidebar.tsx';
|
|
||||||
|
|
||||||
const nodeTypes = {
|
|
||||||
start: StartNode,
|
|
||||||
end: EndNode,
|
|
||||||
phase: PhaseNode,
|
|
||||||
norm: NormNode
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialNodes = [
|
|
||||||
{
|
|
||||||
id: 'start',
|
|
||||||
type: 'start',
|
|
||||||
position: {x: 0, y: 0},
|
|
||||||
data: {label: 'start'}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'genericPhase',
|
|
||||||
type: 'phase',
|
|
||||||
position: {x: 0, y: 150},
|
|
||||||
data: {label: 'Generic Phase', number: 1},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'end',
|
|
||||||
type: 'end',
|
|
||||||
position: {x: 0, y: 300},
|
|
||||||
data: {label: 'End'}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const initialEdges = [{id: 'start-end', source: 'start', target: 'end'}];
|
|
||||||
|
|
||||||
const defaultEdgeOptions = {
|
|
||||||
type: 'floating',
|
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
color: '#505050',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const VisProgUI = ()=> {
|
|
||||||
const edgeReconnectSuccessful = useRef(true);
|
|
||||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
||||||
|
|
||||||
const onConnect = useCallback(
|
|
||||||
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
|
|
||||||
[setEdges],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onReconnectStart = useCallback(() => {
|
|
||||||
edgeReconnectSuccessful.current = false;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
|
|
||||||
edgeReconnectSuccessful.current = true;
|
|
||||||
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
|
||||||
}, [setEdges]);
|
|
||||||
|
|
||||||
const onReconnectEnd = useCallback((_: unknown, edge: { id: string; }) => {
|
|
||||||
if (!edgeReconnectSuccessful.current) {
|
|
||||||
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeReconnectSuccessful.current = true;
|
|
||||||
}, [setEdges]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{marginInline: 'auto',display: 'flex',justifySelf: 'center', padding:'10px', alignItems: 'center', width: '80vw', height: '60vh'}}>
|
|
||||||
<div style={{outlineStyle: 'solid', borderRadius: '10pt', marginInline: '1em',width: '70%', height:'100%' }}>
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
defaultEdgeOptions={defaultEdgeOptions}
|
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
nodeTypes={nodeTypes}
|
|
||||||
snapToGrid
|
|
||||||
onReconnect={onReconnect}
|
|
||||||
onReconnectStart={onReconnectStart}
|
|
||||||
onReconnectEnd={onReconnectEnd}
|
|
||||||
onConnect={onConnect}
|
|
||||||
fitView
|
|
||||||
proOptions={{hideAttribution: true }}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
<Background />
|
|
||||||
</ReactFlow>
|
|
||||||
</div>
|
|
||||||
<div style={{width: '20%', height: '100%'}}>
|
|
||||||
<Sidebar />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function VisualProgrammingUI(){
|
|
||||||
return (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<VisProgUI />
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default VisualProgrammingUI;
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import { useDraggable } from '@neodrag/react';
|
|
||||||
import {
|
|
||||||
useReactFlow,
|
|
||||||
type XYPosition
|
|
||||||
} from '@xyflow/react';
|
|
||||||
import {
|
|
||||||
type ReactNode,
|
|
||||||
useCallback,
|
|
||||||
useRef,
|
|
||||||
useState
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
|
|
||||||
// improve later to create better automatic IDs
|
|
||||||
let id = 0;
|
|
||||||
const getId = () => `dndnode_${id++}`;
|
|
||||||
|
|
||||||
|
|
||||||
interface DraggableNodeProps {
|
|
||||||
className?: string;
|
|
||||||
children: ReactNode;
|
|
||||||
nodeType: string;
|
|
||||||
onDrop: (nodeType: string, position: XYPosition) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DraggableNode({ className, children, nodeType, onDrop }: DraggableNodeProps) {
|
|
||||||
const draggableRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [position, setPosition] = useState<XYPosition>({ x: 0, y: 0 });
|
|
||||||
|
|
||||||
// @ts-expect-error we expect the null referece here.
|
|
||||||
useDraggable(draggableRef, {
|
|
||||||
position: position,
|
|
||||||
onDrag: ({ offsetX, offsetY }) => {
|
|
||||||
// Calculate position relative to the viewport
|
|
||||||
setPosition({
|
|
||||||
x: offsetX,
|
|
||||||
y: offsetY,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onDragEnd: ({ event }) => {
|
|
||||||
setPosition({ x: 0, y: 0 });
|
|
||||||
onDrop(nodeType, {
|
|
||||||
x: event.clientX,
|
|
||||||
y: event.clientY,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={className === "default" ? "default-node" : "default-node" + "__" + className} ref={draggableRef}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Sidebar() {
|
|
||||||
const { setNodes, screenToFlowPosition } = useReactFlow();
|
|
||||||
|
|
||||||
const handleNodeDrop = useCallback(
|
|
||||||
(nodeType: string, screenPosition: XYPosition) => {
|
|
||||||
const flow = document.querySelector('.react-flow');
|
|
||||||
const flowRect = flow?.getBoundingClientRect();
|
|
||||||
const isInFlow =
|
|
||||||
flowRect &&
|
|
||||||
screenPosition.x >= flowRect.left &&
|
|
||||||
screenPosition.x <= flowRect.right &&
|
|
||||||
screenPosition.y >= flowRect.top &&
|
|
||||||
screenPosition.y <= flowRect.bottom;
|
|
||||||
|
|
||||||
// Create a new node and add it to the flow
|
|
||||||
if (isInFlow) {
|
|
||||||
const position = screenToFlowPosition(screenPosition);
|
|
||||||
|
|
||||||
const newNode = () => {
|
|
||||||
switch (nodeType) {
|
|
||||||
case "phase":
|
|
||||||
return {
|
|
||||||
id: getId(),
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
data: {label: `"new"`, number: (-1)},
|
|
||||||
};
|
|
||||||
case "start":
|
|
||||||
return {
|
|
||||||
id: getId(),
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
data: {label: `new start node`},
|
|
||||||
};
|
|
||||||
case "end":
|
|
||||||
return {
|
|
||||||
id: getId(),
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
data: {label: `new end node`},
|
|
||||||
};
|
|
||||||
case "norm":
|
|
||||||
return {
|
|
||||||
id: getId(),
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
data: {label: `new norm node`},
|
|
||||||
};
|
|
||||||
default: {
|
|
||||||
return {
|
|
||||||
id: getId(),
|
|
||||||
type: nodeType,
|
|
||||||
position,
|
|
||||||
data: {label: `new default node`},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setNodes((nds) => nds.concat(newNode()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setNodes, screenToFlowPosition],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<aside>
|
|
||||||
<div className="description">
|
|
||||||
You can drag these nodes to the pane to create new nodes.
|
|
||||||
</div>
|
|
||||||
<DraggableNode className="default" nodeType="start" onDrop={handleNodeDrop}>
|
|
||||||
start Node
|
|
||||||
</DraggableNode>
|
|
||||||
<DraggableNode className="default" nodeType="end" onDrop={handleNodeDrop}>
|
|
||||||
end Node
|
|
||||||
</DraggableNode>
|
|
||||||
<DraggableNode className="default" nodeType="phase" onDrop={handleNodeDrop}>
|
|
||||||
phase Node
|
|
||||||
</DraggableNode>
|
|
||||||
<DraggableNode className="default" nodeType="norm" onDrop={handleNodeDrop}>
|
|
||||||
norm Node
|
|
||||||
</DraggableNode>
|
|
||||||
</aside>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
import {Handle, NodeToolbar, Position, useReactFlow} from '@xyflow/react';
|
|
||||||
import '@xyflow/react/dist/style.css';
|
|
||||||
import '../VisProgUI.css';
|
|
||||||
|
|
||||||
// Datatypes for NodeTypes
|
|
||||||
|
|
||||||
type defaultNodeData = {
|
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type startNodeData = defaultNodeData;
|
|
||||||
type endNodeData = defaultNodeData;
|
|
||||||
type normNodeData = defaultNodeData;
|
|
||||||
type phaseNodeData = defaultNodeData & {
|
|
||||||
number: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type nodeData = defaultNodeData | startNodeData | phaseNodeData | endNodeData;
|
|
||||||
|
|
||||||
// Node Toolbar definition
|
|
||||||
|
|
||||||
type ToolbarProps= {
|
|
||||||
nodeId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function Toolbar({nodeId}:ToolbarProps) {
|
|
||||||
const { setNodes, setEdges } = useReactFlow();
|
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
setNodes((nds) => nds.filter((n) => n.id !== nodeId));
|
|
||||||
setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId));
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<NodeToolbar >
|
|
||||||
<button className="Node-toolbar__deletebutton" onClick={handleDelete}>delete</button>
|
|
||||||
</NodeToolbar>);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Definitions of Nodes
|
|
||||||
|
|
||||||
type StartNodeProps = {
|
|
||||||
id: string;
|
|
||||||
data: startNodeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StartNode= ({ id, data }: StartNodeProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Toolbar nodeId={id} />
|
|
||||||
<div className="default-node">
|
|
||||||
<div> data test {data.label} </div>
|
|
||||||
<Handle type="source" position={Position.Right} id="start" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type EndNodeProps = {
|
|
||||||
id: string;
|
|
||||||
data: endNodeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EndNode= ({ id, data }: EndNodeProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Toolbar nodeId={id}/>
|
|
||||||
<div className="default-node">
|
|
||||||
<div> {data.label} </div>
|
|
||||||
<Handle type="target" position={Position.Left} id="end" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
type PhaseNodeProps = {
|
|
||||||
id: string;
|
|
||||||
data: phaseNodeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PhaseNode= ({ id, data }: PhaseNodeProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Toolbar nodeId={id}/>
|
|
||||||
<div className="default-node">
|
|
||||||
<div> phase {data.number} {data.label} </div>
|
|
||||||
<Handle type="target" position={Position.Left} id="target" />
|
|
||||||
<Handle type="target" position={Position.Bottom } id="norms" />
|
|
||||||
<Handle type="source" position={Position.Right} id="source" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
type NormNodeProps = {
|
|
||||||
id: string;
|
|
||||||
data: normNodeData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const NormNode= ({ id, data }: NormNodeProps) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Toolbar nodeId={id} />
|
|
||||||
<div className="default-node">
|
|
||||||
<div> Norm {data.label} </div>
|
|
||||||
<Handle type="source" position={Position.Right} id="NormSource" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user