diff --git a/package-lock.json b/package-lock.json
index c20c730..81c5e48 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index 2757ba3..cbea372 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/src/visualProgrammingUI/VisProgUI.tsx b/src/visualProgrammingUI/VisProgUI.tsx
index 48c987f..55c5a85 100644
--- a/src/visualProgrammingUI/VisProgUI.tsx
+++ b/src/visualProgrammingUI/VisProgUI.tsx
@@ -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 (
-
-
-
-
-
+
+
);
};
-export default VisualProgrammingUI
\ No newline at end of file
+function VisualProgrammingUI(){
+ return (
+
+
+
+ );
+}
+
+export default VisualProgrammingUI;
\ No newline at end of file
diff --git a/src/visualProgrammingUI/components/DragDropSidebar.tsx b/src/visualProgrammingUI/components/DragDropSidebar.tsx
new file mode 100644
index 0000000..9c1e3dd
--- /dev/null
+++ b/src/visualProgrammingUI/components/DragDropSidebar.tsx
@@ -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
(null);
+ const [position, setPosition] = useState({ 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 (
+
+ {children}
+
+ );
+}
+
+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 (
+
+ );
+}
\ No newline at end of file