From 4b88bcb093e96edd9351aa63bf5de7b4dc753621 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 16:09:20 +0000 Subject: [PATCH] Generated by Spark: Thought there would be like little nodes on the side that I can click and drag to another cloud - like n8n --- src/components/FeatureIdeaCloud.tsx | 220 ++++++++++++++++++++++------ 1 file changed, 173 insertions(+), 47 deletions(-) diff --git a/src/components/FeatureIdeaCloud.tsx b/src/components/FeatureIdeaCloud.tsx index 399fb15..5132f72 100644 --- a/src/components/FeatureIdeaCloud.tsx +++ b/src/components/FeatureIdeaCloud.tsx @@ -207,6 +207,8 @@ export function FeatureIdeaCloud() { const [connectingFrom, setConnectingFrom] = useState(null) const [connectionType, setConnectionType] = useState('association') const [hoveredConnection, setHoveredConnection] = useState(null) + const [draggingConnection, setDraggingConnection] = useState<{ fromId: string, x: number, y: number } | null>(null) + const [hoveredNode, setHoveredNode] = useState(null) const safeIdeas = ideas || SEED_IDEAS const safeConnections = connections || SEED_CONNECTIONS @@ -408,10 +410,19 @@ export function FeatureIdeaCloud() { : idea ) ) + } else if (draggingConnection) { + setDraggingConnection({ + ...draggingConnection, + x: e.clientX, + y: e.clientY, + }) } } - const handleCanvasMouseUp = () => { + const handleCanvasMouseUp = (e: React.MouseEvent) => { + if (draggingConnection) { + setDraggingConnection(null) + } setIsPanning(false) setDraggedIdea(null) } @@ -447,6 +458,42 @@ export function FeatureIdeaCloud() { setPan({ x: newPanX, y: newPanY }) } + const handleConnectionNodeMouseDown = (ideaId: string, e: React.MouseEvent) => { + e.stopPropagation() + const idea = safeIdeas.find(i => i.id === ideaId) + if (idea) { + setDraggingConnection({ + fromId: ideaId, + x: e.clientX, + y: e.clientY, + }) + } + } + + const handleConnectionNodeMouseUp = (ideaId: string, e: React.MouseEvent) => { + e.stopPropagation() + if (draggingConnection && draggingConnection.fromId !== ideaId) { + const existingConnection = safeConnections.find( + c => c.fromId === draggingConnection.fromId && c.toId === ideaId + ) + + if (existingConnection) { + toast.error('Connection already exists') + } else { + const newConnection: Connection = { + id: `conn-${Date.now()}`, + fromId: draggingConnection.fromId, + toId: ideaId, + type: connectionType, + label: CONNECTION_LABELS[connectionType], + } + setConnections((current) => [...(current || []), newConnection]) + toast.success('Ideas connected!') + } + } + setDraggingConnection(null) + } + const renderArrowhead = (connection: Connection, x: number, y: number, angle: number) => { const style = CONNECTION_STYLES[connection.type] const size = 12 @@ -533,9 +580,9 @@ export function FeatureIdeaCloud() { const toIdea = safeIdeas.find(i => i.id === connection.toId) if (fromIdea && toIdea) { - const fromX = fromIdea.x * zoom + pan.x + 120 + const fromX = fromIdea.x * zoom + pan.x + 240 const fromY = fromIdea.y * zoom + pan.y + 80 - const toX = toIdea.x * zoom + pan.x + 120 + const toX = toIdea.x * zoom + pan.x const toY = toIdea.y * zoom + pan.y + 80 const dx = toX - fromX @@ -615,6 +662,41 @@ export function FeatureIdeaCloud() { } }) + if (draggingConnection) { + const fromIdea = safeIdeas.find(i => i.id === draggingConnection.fromId) + if (fromIdea) { + const fromX = fromIdea.x * zoom + pan.x + 240 + const fromY = fromIdea.y * zoom + pan.y + 80 + const toX = draggingConnection.x + const toY = draggingConnection.y + + const style = CONNECTION_STYLES[connectionType] + + elements.push( + + + + + ) + } + } + return elements } @@ -674,22 +756,21 @@ export function FeatureIdeaCloud() { - {tool === 'connect' && ( -
- -
- )} +
+ Type: + +
@@ -776,7 +857,8 @@ export function FeatureIdeaCloud() {

💡 Tip: Double-click ideas to view details

-

🔗 Use Connect tool to create UML-style relationships

+

🔗 Drag connection nodes on card sides to connect ideas

+

⚙️ Change connection type in toolbar before connecting

handleIdeaClick(idea, e)} onDoubleClick={(e) => handleIdeaDoubleClick(idea, e)} > - -
-
-

{idea.title}

- -
-

- {idea.description} -

-
- - {idea.category} - - - {idea.status} - +
+
{ + e.stopPropagation() + handleConnectionNodeMouseDown(idea.id, e) + }} + onMouseUp={(e) => { + e.stopPropagation() + handleConnectionNodeMouseUp(idea.id, e) + }} + onMouseEnter={() => setHoveredNode(`${idea.id}-left`)} + onMouseLeave={() => setHoveredNode(null)} + > +
+
- + +
{ + e.stopPropagation() + handleConnectionNodeMouseDown(idea.id, e) + }} + onMouseUp={(e) => { + e.stopPropagation() + handleConnectionNodeMouseUp(idea.id, e) + }} + onMouseEnter={() => setHoveredNode(`${idea.id}-right`)} + onMouseLeave={() => setHoveredNode(null)} + > +
+
+
+
+ + +
+
+

{idea.title}

+ +
+

+ {idea.description} +

+
+ + {idea.category} + + + {idea.status} + +
+
+
+
))}