Merging dev into main #49
7
package-lock.json
generated
7
package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "pepperplus-ui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@neodrag/react": "^2.3.1",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1"
|
||||
@@ -1006,6 +1007,12 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@neodrag/react": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@neodrag/react/-/react-2.3.1.tgz",
|
||||
"integrity": "sha512-mOVefo3mFmaVLs9PB5F5wMXnnclG81qjOaPHyf8YZUnw/Ciz0pAqyJDwDJk0nPTIK5I2x1JdjXSchGNdCxZNRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@neodrag/react": "^2.3.1",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1"
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Background,
|
||||
Controls,
|
||||
ReactFlow,
|
||||
ReactFlowProvider,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
reconnectEdge,
|
||||
@@ -23,6 +24,8 @@ import {
|
||||
PhaseNode
|
||||
} from "./components/NodeDefinitions.tsx";
|
||||
|
||||
import { Sidebar } from './components/DragDropSidebar.tsx';
|
||||
|
||||
const nodeTypes = {
|
||||
start: StartNode,
|
||||
end: EndNode,
|
||||
@@ -59,7 +62,7 @@ const defaultEdgeOptions = {
|
||||
},
|
||||
};
|
||||
|
||||
const VisualProgrammingUI = ()=> {
|
||||
const VisProgUI = ()=> {
|
||||
const edgeReconnectSuccessful = useRef(true);
|
||||
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
@@ -89,27 +92,41 @@ const VisualProgrammingUI = ()=> {
|
||||
}, [setEdges]);
|
||||
|
||||
return (
|
||||
<div style={{outlineStyle: 'solid', borderRadius: '10pt' ,marginInline: 'auto',width: '60vw', height: '60vh' }}>
|
||||
<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 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>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default VisualProgrammingUI
|
||||
function VisualProgrammingUI(){
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<VisProgUI />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default VisualProgrammingUI;
|
||||
104
src/visualProgrammingUI/components/DragDropSidebar.tsx
Normal file
104
src/visualProgrammingUI/components/DragDropSidebar.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
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-ignore
|
||||
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 = {
|
||||
id: getId(),
|
||||
type: nodeType,
|
||||
position,
|
||||
data: { label: `${nodeType} 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>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user