Merging dev into main #49
@@ -1,137 +1,152 @@
|
||||
import { useDraggable } from '@neodrag/react';
|
||||
import {useDraggable} from '@neodrag/react';
|
||||
import {
|
||||
useReactFlow,
|
||||
type XYPosition
|
||||
useReactFlow,
|
||||
type XYPosition
|
||||
} from '@xyflow/react';
|
||||
import {
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useRef,
|
||||
useState
|
||||
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;
|
||||
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 });
|
||||
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,
|
||||
});
|
||||
},
|
||||
});
|
||||
// @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" ? "draggable-node" : "draggable-node" + "__" + className} ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className={className === "default" ? "draggable-node" : "draggable-node" + "__" + className}
|
||||
ref={draggableRef}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Sidebar() {
|
||||
const { setNodes, screenToFlowPosition } = useReactFlow();
|
||||
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;
|
||||
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);
|
||||
// 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()));
|
||||
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, screenToFlowPosition],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<aside style={{padding: '10px 10px',outline: '2.5pt solid black',borderRadius: '0pt 0pt 5pt 5pt', borderColor:'dimgrey', backgroundColor:'canvas', display:'flex', flexDirection: 'column', gap: '1rem'}}>
|
||||
<div className="description">
|
||||
You can drag these nodes to the pane to create new nodes.
|
||||
</div>
|
||||
<div className="DraggableNodeContainer" style={{backgroundColor:'canvas', display: 'flex', flexDirection: 'row', gap: '1rem', justifyContent: 'center'}}>
|
||||
<DraggableNode className="phase" nodeType="phase" onDrop={handleNodeDrop}>
|
||||
phase Node
|
||||
</DraggableNode>
|
||||
<DraggableNode className="norm" nodeType="norm" onDrop={handleNodeDrop}>
|
||||
norm Node
|
||||
</DraggableNode>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
setNodes((nds) => nds.concat(newNode()));
|
||||
}
|
||||
},
|
||||
[setNodes, screenToFlowPosition],
|
||||
);
|
||||
|
||||
return (
|
||||
<aside style={{
|
||||
padding: '10px 10px',
|
||||
outline: '2.5pt solid black',
|
||||
borderRadius: '0pt 0pt 5pt 5pt',
|
||||
borderColor: 'dimgrey',
|
||||
backgroundColor: 'canvas',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1rem'
|
||||
}}>
|
||||
<div className="description">
|
||||
You can drag these nodes to the pane to create new nodes.
|
||||
</div>
|
||||
<div className="DraggableNodeContainer" style={{
|
||||
backgroundColor: 'canvas',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '1rem',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<DraggableNode className="phase" nodeType="phase" onDrop={handleNodeDrop}>
|
||||
phase Node
|
||||
</DraggableNode>
|
||||
<DraggableNode className="norm" nodeType="norm" onDrop={handleNodeDrop}>
|
||||
norm Node
|
||||
</DraggableNode>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user