mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
Converted canvas to React Flow
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
export default function Home() {
|
||||
return <div>Hello World</div>
|
||||
return <div>Go to /w/1</div>
|
||||
}
|
||||
|
||||
@@ -1,241 +1,132 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { BlockConfig } from '../components/blocks/types/block'
|
||||
import { WorkflowBlock } from '../components/blocks/components/workflow-block/workflow-block'
|
||||
import { useState, useCallback } from 'react'
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
Controls,
|
||||
NodeProps,
|
||||
NodeTypes,
|
||||
EdgeTypes,
|
||||
Connection,
|
||||
Edge,
|
||||
addEdge,
|
||||
useNodesState,
|
||||
useEdgesState,
|
||||
XYPosition,
|
||||
useReactFlow,
|
||||
ReactFlowProvider,
|
||||
} from 'reactflow'
|
||||
import 'reactflow/dist/style.css'
|
||||
import { getBlock } from '../components/blocks/configs'
|
||||
import { CoordinateTransformer } from '../lib/coordinates'
|
||||
import { WorkflowBlock } from '../components/blocks/components/workflow-block/workflow-block'
|
||||
|
||||
interface WorkflowBlock {
|
||||
id: string
|
||||
// Types
|
||||
interface WorkflowNodeData {
|
||||
type: string
|
||||
position: { x: number; y: number }
|
||||
config: BlockConfig
|
||||
config: any // Consider adding a proper type for config
|
||||
name: string
|
||||
}
|
||||
|
||||
const ZOOM_SPEED = 0.005
|
||||
const MIN_ZOOM = 0.5
|
||||
const MAX_ZOOM = 2
|
||||
const CANVAS_SIZE = 5000
|
||||
// Node Components
|
||||
const WorkflowNode = ({
|
||||
data,
|
||||
id,
|
||||
xPos,
|
||||
yPos,
|
||||
}: NodeProps<WorkflowNodeData>) => (
|
||||
<WorkflowBlock
|
||||
id={id}
|
||||
type={data.type}
|
||||
position={{ x: xPos, y: yPos }}
|
||||
config={data.config}
|
||||
name={data.name}
|
||||
/>
|
||||
)
|
||||
|
||||
export default function Workflow() {
|
||||
const [blocks, setBlocks] = useState<WorkflowBlock[]>([])
|
||||
const [zoom, setZoom] = useState(1)
|
||||
const [pan, setPan] = useState({ x: 0, y: 0 })
|
||||
const [isPanning, setIsPanning] = useState(false)
|
||||
const [startPanPos, setStartPanPos] = useState({ x: 0, y: 0 })
|
||||
const nodeTypes: NodeTypes = {
|
||||
workflowBlock: WorkflowNode,
|
||||
}
|
||||
|
||||
// Initialize pan position after mount
|
||||
useEffect(() => {
|
||||
const { width, height } = CoordinateTransformer.getViewportDimensions()
|
||||
setPan({
|
||||
x: (width - CANVAS_SIZE) / 2,
|
||||
y: (height - CANVAS_SIZE) / 2,
|
||||
})
|
||||
}, [])
|
||||
// Main Canvas Component
|
||||
function WorkflowCanvas() {
|
||||
// State
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState([])
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState([])
|
||||
const { project } = useReactFlow()
|
||||
|
||||
const constrainPan = useCallback(
|
||||
(newPan: { x: number; y: number }, currentZoom: number) => {
|
||||
const { width, height } = CoordinateTransformer.getViewportDimensions()
|
||||
|
||||
// Calculate the scaled canvas size
|
||||
const scaledCanvasWidth = CANVAS_SIZE * currentZoom
|
||||
const scaledCanvasHeight = CANVAS_SIZE * currentZoom
|
||||
|
||||
// Calculate the maximum allowed pan values
|
||||
const minX = Math.min(0, width - scaledCanvasWidth)
|
||||
const minY = Math.min(0, height - scaledCanvasHeight)
|
||||
|
||||
return {
|
||||
x: Math.min(0, Math.max(minX, newPan.x)),
|
||||
y: Math.min(0, Math.max(minY, newPan.y)),
|
||||
}
|
||||
},
|
||||
[]
|
||||
// Handlers
|
||||
const onConnect = useCallback(
|
||||
(connection: Connection) => setEdges((eds) => addEdge(connection, eds)),
|
||||
[setEdges]
|
||||
)
|
||||
|
||||
const handleDragOver = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
}
|
||||
const onDrop = useCallback(
|
||||
(event: React.DragEvent) => {
|
||||
event.preventDefault()
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const reactFlowBounds = event.currentTarget.getBoundingClientRect()
|
||||
const position = project({
|
||||
x: event.clientX - reactFlowBounds.left,
|
||||
y: event.clientY - reactFlowBounds.top,
|
||||
})
|
||||
|
||||
try {
|
||||
const { type } = JSON.parse(e.dataTransfer.getData('application/json'))
|
||||
const blockConfig = getBlock(type)
|
||||
const { type } = JSON.parse(
|
||||
event.dataTransfer.getData('application/json')
|
||||
)
|
||||
const blockConfig = getBlock(type)
|
||||
|
||||
if (!blockConfig) {
|
||||
console.error('Invalid block type:', type)
|
||||
return
|
||||
}
|
||||
if (!blockConfig) {
|
||||
console.error('Invalid block type:', type)
|
||||
return
|
||||
}
|
||||
|
||||
const canvasElement = e.currentTarget as HTMLElement
|
||||
const dropPoint = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
}
|
||||
|
||||
// Convert drop coordinates to canvas space
|
||||
const canvasPoint = CoordinateTransformer.viewportToCanvas(
|
||||
CoordinateTransformer.clientToViewport(dropPoint),
|
||||
canvasElement
|
||||
)
|
||||
|
||||
setBlocks((prev) => [
|
||||
...prev,
|
||||
{
|
||||
const newNode = {
|
||||
id: crypto.randomUUID(),
|
||||
type,
|
||||
position: canvasPoint,
|
||||
config: blockConfig,
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
console.error('Error dropping block:', err)
|
||||
}
|
||||
}
|
||||
type: 'workflowBlock',
|
||||
position,
|
||||
data: {
|
||||
type,
|
||||
config: blockConfig,
|
||||
name: `${blockConfig.toolbar.title} ${
|
||||
nodes.filter((n) => n.data.type === type).length + 1
|
||||
}`,
|
||||
},
|
||||
}
|
||||
|
||||
const handleWheel = useCallback(
|
||||
(e: React.WheelEvent) => {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault()
|
||||
const delta = -e.deltaY * ZOOM_SPEED
|
||||
setZoom((prevZoom) => {
|
||||
if (
|
||||
(prevZoom >= MAX_ZOOM && delta > 0) ||
|
||||
(prevZoom <= MIN_ZOOM && delta < 0)
|
||||
) {
|
||||
return prevZoom
|
||||
}
|
||||
const newZoom = Math.min(
|
||||
MAX_ZOOM,
|
||||
Math.max(MIN_ZOOM, prevZoom + delta)
|
||||
)
|
||||
setPan((prevPan) => constrainPan(prevPan, newZoom))
|
||||
return newZoom
|
||||
})
|
||||
} else {
|
||||
setPan((prevPan) =>
|
||||
constrainPan(
|
||||
{
|
||||
x: prevPan.x - e.deltaX,
|
||||
y: prevPan.y - e.deltaY,
|
||||
},
|
||||
zoom
|
||||
)
|
||||
)
|
||||
setNodes((nds) => [...nds, newNode])
|
||||
} catch (err) {
|
||||
console.error('Error dropping block:', err)
|
||||
}
|
||||
},
|
||||
[constrainPan, zoom]
|
||||
)
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (e.button === 1 || e.button === 0) {
|
||||
setIsPanning(true)
|
||||
const viewportPoint = CoordinateTransformer.clientToViewport({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
})
|
||||
setStartPanPos({
|
||||
x: viewportPoint.x - pan.x,
|
||||
y: viewportPoint.y - pan.y,
|
||||
})
|
||||
}
|
||||
},
|
||||
[pan]
|
||||
)
|
||||
|
||||
const handleMouseMove = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
if (isPanning) {
|
||||
const viewportPoint = CoordinateTransformer.clientToViewport({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
})
|
||||
setPan((prevPan) =>
|
||||
constrainPan(
|
||||
{
|
||||
x: viewportPoint.x - startPanPos.x,
|
||||
y: viewportPoint.y - startPanPos.y,
|
||||
},
|
||||
zoom
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
[isPanning, startPanPos, zoom, constrainPan]
|
||||
)
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setIsPanning(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const preventDefaultZoom = (e: WheelEvent) => {
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('wheel', preventDefaultZoom, { passive: false })
|
||||
return () => document.removeEventListener('wheel', preventDefaultZoom)
|
||||
}, [])
|
||||
|
||||
const updateBlockPosition = useCallback(
|
||||
(id: string, newPosition: { x: number; y: number }) => {
|
||||
setBlocks((prevBlocks) =>
|
||||
prevBlocks.map((block) =>
|
||||
block.id === id ? { ...block, position: newPosition } : block
|
||||
)
|
||||
)
|
||||
},
|
||||
[]
|
||||
[project, nodes, setNodes]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-[calc(100vh-56px)] overflow-hidden select-none"
|
||||
onWheel={handleWheel}
|
||||
>
|
||||
<div
|
||||
className="w-full h-full bg-[#F5F5F5] relative cursor-grab active:cursor-grabbing"
|
||||
style={{
|
||||
backgroundImage: `radial-gradient(#D9D9D9 1px, transparent 1px)`,
|
||||
backgroundSize: '20px 20px',
|
||||
width: CANVAS_SIZE,
|
||||
height: CANVAS_SIZE,
|
||||
transform: `scale(${zoom}) translate(${pan.x}px, ${pan.y}px)`,
|
||||
transformOrigin: '0 0',
|
||||
}}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={handleDrop}
|
||||
<div className="w-full h-[calc(100vh-56px)]">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
nodeTypes={nodeTypes}
|
||||
onDrop={onDrop}
|
||||
onDragOver={(e) => e.preventDefault()}
|
||||
fitView
|
||||
maxZoom={1}
|
||||
panOnScroll
|
||||
>
|
||||
{blocks.map((block, index) => {
|
||||
const typeCount = blocks
|
||||
.slice(0, index + 1)
|
||||
.filter((b) => b.type === block.type).length
|
||||
|
||||
return (
|
||||
<WorkflowBlock
|
||||
key={block.id}
|
||||
id={block.id}
|
||||
type={block.type}
|
||||
position={block.position}
|
||||
config={block.config}
|
||||
name={`${block.config.toolbar.title} ${typeCount}`}
|
||||
onPositionUpdate={updateBlockPosition}
|
||||
zoom={zoom}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<Background />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Workflow() {
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<WorkflowCanvas />
|
||||
</ReactFlowProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ export function ToolbarBlock({ config }: ToolbarBlockProps) {
|
||||
'application/json',
|
||||
JSON.stringify({ type: config.type })
|
||||
)
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,55 +1,132 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
import { CoordinateTransformer } from '@/app/w/lib/coordinates'
|
||||
import { useState, useCallback, useEffect } from 'react'
|
||||
|
||||
interface ConnectionPointProps {
|
||||
position: 'top' | 'bottom'
|
||||
}
|
||||
|
||||
interface ConnectionLine {
|
||||
start: { x: number; y: number }
|
||||
end: { x: number; y: number }
|
||||
}
|
||||
|
||||
export function ConnectionPoint({ position }: ConnectionPointProps) {
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const [isDrawing, setIsDrawing] = useState(false)
|
||||
const [line, setLine] = useState<ConnectionLine | null>(null)
|
||||
|
||||
const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
|
||||
const canvasElement = e.currentTarget.closest(
|
||||
'[style*="transform"]'
|
||||
) as HTMLElement
|
||||
if (!canvasElement) return
|
||||
|
||||
const elementPosition = CoordinateTransformer.getElementCanvasPosition(
|
||||
e.currentTarget,
|
||||
canvasElement
|
||||
)
|
||||
|
||||
// Test distance calculation
|
||||
const testPoint = {
|
||||
x: e.clientX + 100,
|
||||
y: e.clientY + 100,
|
||||
// Get raw client coordinates
|
||||
const startPoint = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
}
|
||||
|
||||
const distance = CoordinateTransformer.getTransformedDistance(
|
||||
{ x: e.clientX, y: e.clientY },
|
||||
testPoint,
|
||||
// Use the same conversion pattern as drag and drop
|
||||
const canvasPoint = CoordinateTransformer.viewportToCanvas(
|
||||
CoordinateTransformer.clientToViewport(startPoint),
|
||||
canvasElement
|
||||
)
|
||||
|
||||
console.log({
|
||||
viewport: CoordinateTransformer.getViewportDimensions(),
|
||||
canvasTransform: CoordinateTransformer.getCanvasTransform(canvasElement),
|
||||
elementPosition,
|
||||
transformedDistance: distance,
|
||||
setIsDrawing(true)
|
||||
setLine({
|
||||
start: canvasPoint,
|
||||
end: canvasPoint,
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleMouseMove = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (!isDrawing || !line) return
|
||||
|
||||
const canvasElement = document.querySelector(
|
||||
'[style*="transform"]'
|
||||
) as HTMLElement
|
||||
if (!canvasElement) return
|
||||
|
||||
// Convert current mouse position to canvas coordinates
|
||||
const currentPoint = CoordinateTransformer.viewportToCanvas(
|
||||
CoordinateTransformer.clientToViewport({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
}),
|
||||
canvasElement
|
||||
)
|
||||
|
||||
setLine((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
end: currentPoint,
|
||||
}
|
||||
: null
|
||||
)
|
||||
},
|
||||
[isDrawing, line]
|
||||
)
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setIsDrawing(false)
|
||||
setLine(null)
|
||||
}, [])
|
||||
|
||||
// Add and remove global mouse event listeners
|
||||
useEffect(() => {
|
||||
if (isDrawing) {
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
}
|
||||
}, [isDrawing, handleMouseMove, handleMouseUp])
|
||||
|
||||
return (
|
||||
<div
|
||||
data-connection-point
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
'absolute left-1/2 -translate-x-1/2 w-3 h-3',
|
||||
'bg-white rounded-full border opacity-0 group-hover:opacity-100',
|
||||
'transition-opacity duration-200 cursor-crosshair hover:border-blue-500',
|
||||
'hover:scale-110 hover:shadow-sm',
|
||||
position === 'top'
|
||||
? '-translate-y-1/2 top-0'
|
||||
: 'translate-y-1/2 bottom-0'
|
||||
<>
|
||||
<div
|
||||
data-connection-point
|
||||
onMouseDown={handleMouseDown}
|
||||
className={cn(
|
||||
'absolute left-1/2 -translate-x-1/2 w-3 h-3',
|
||||
'bg-white rounded-full border opacity-0 group-hover:opacity-100',
|
||||
'transition-opacity duration-200 cursor-crosshair hover:border-blue-500',
|
||||
'hover:scale-110 hover:shadow-sm',
|
||||
position === 'top'
|
||||
? '-translate-y-1/2 top-0'
|
||||
: 'translate-y-1/2 bottom-0'
|
||||
)}
|
||||
/>
|
||||
{line && (
|
||||
<svg
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
zIndex: 9999,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<line
|
||||
x1={line.start.x}
|
||||
y1={line.start.y}
|
||||
x2={line.end.x}
|
||||
y2={line.end.y}
|
||||
stroke="rgb(59 130 246)"
|
||||
strokeWidth="2"
|
||||
strokeDasharray="5,5"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,131 +1,53 @@
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { BlockConfig, SubBlockConfig } from '../../types/block'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SubBlock } from './components/sub-block/sub-block'
|
||||
import { useCallback, useState, MouseEvent, useEffect } from 'react'
|
||||
import { ConnectionPoint } from './components/connection/connection-point'
|
||||
import { Handle, Position } from 'reactflow'
|
||||
|
||||
export interface WorkflowBlockProps {
|
||||
interface WorkflowBlockProps {
|
||||
id: string
|
||||
type: string
|
||||
position: { x: number; y: number }
|
||||
config: BlockConfig
|
||||
name: string
|
||||
onPositionUpdate: (id: string, position: { x: number; y: number }) => void
|
||||
zoom: number
|
||||
}
|
||||
|
||||
function groupSubBlocks(subBlocks: SubBlockConfig[]) {
|
||||
const rows: SubBlockConfig[][] = []
|
||||
let currentRow: SubBlockConfig[] = []
|
||||
let currentRowWidth = 0
|
||||
export function WorkflowBlock({ id, type, config, name }: WorkflowBlockProps) {
|
||||
const { toolbar, workflow } = config
|
||||
|
||||
subBlocks.forEach((block) => {
|
||||
const blockWidth = block.layout === 'half' ? 0.5 : 1
|
||||
function groupSubBlocks(subBlocks: SubBlockConfig[]) {
|
||||
const rows: SubBlockConfig[][] = []
|
||||
let currentRow: SubBlockConfig[] = []
|
||||
let currentRowWidth = 0
|
||||
|
||||
if (currentRowWidth + blockWidth > 1) {
|
||||
rows.push([...currentRow])
|
||||
currentRow = [block]
|
||||
currentRowWidth = blockWidth
|
||||
} else {
|
||||
currentRow.push(block)
|
||||
currentRowWidth += blockWidth
|
||||
subBlocks.forEach((block) => {
|
||||
const blockWidth = block.layout === 'half' ? 0.5 : 1
|
||||
if (currentRowWidth + blockWidth > 1) {
|
||||
rows.push([...currentRow])
|
||||
currentRow = [block]
|
||||
currentRowWidth = blockWidth
|
||||
} else {
|
||||
currentRow.push(block)
|
||||
currentRowWidth += blockWidth
|
||||
}
|
||||
})
|
||||
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow)
|
||||
}
|
||||
})
|
||||
|
||||
if (currentRow.length > 0) {
|
||||
rows.push(currentRow)
|
||||
return rows
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
export function WorkflowBlock({
|
||||
id,
|
||||
type,
|
||||
position,
|
||||
config,
|
||||
name,
|
||||
onPositionUpdate,
|
||||
zoom,
|
||||
}: WorkflowBlockProps) {
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
// Don't handle drag if clicking on a connection point
|
||||
if ((e.target as HTMLElement).closest('[data-connection-point]')) {
|
||||
return
|
||||
}
|
||||
|
||||
e.stopPropagation()
|
||||
setIsDragging(true)
|
||||
|
||||
// Account for the sidebar width (344px) and control bar height (56px)
|
||||
const sidebarWidth = 344 // 72px (sidebar) + 272px (toolbar)
|
||||
const controlBarHeight = 56
|
||||
|
||||
const rect = e.currentTarget.getBoundingClientRect()
|
||||
setDragOffset({
|
||||
x: (e.clientX - sidebarWidth) / zoom - position.x,
|
||||
y: (e.clientY - controlBarHeight) / zoom - position.y,
|
||||
})
|
||||
},
|
||||
[zoom, position]
|
||||
)
|
||||
|
||||
const handleMouseMove = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (isDragging) {
|
||||
e.stopPropagation()
|
||||
|
||||
// Account for the sidebar width and control bar height
|
||||
const sidebarWidth = 344
|
||||
const controlBarHeight = 56
|
||||
|
||||
const newX = (e.clientX - sidebarWidth) / zoom - dragOffset.x
|
||||
const newY = (e.clientY - controlBarHeight) / zoom - dragOffset.y
|
||||
|
||||
onPositionUpdate(id, { x: newX, y: newY })
|
||||
}
|
||||
},
|
||||
[id, isDragging, dragOffset, onPositionUpdate, zoom]
|
||||
)
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setIsDragging(false)
|
||||
}, [])
|
||||
|
||||
// Add event listeners to handle dragging outside the block
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
document.addEventListener('mousemove', handleMouseMove as any)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove as any)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
}
|
||||
}, [isDragging, handleMouseMove, handleMouseUp])
|
||||
|
||||
const { toolbar, workflow } = config
|
||||
const subBlockRows = groupSubBlocks(workflow.subBlocks)
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={cn(
|
||||
'absolute w-[320px] shadow-md cursor-move select-none group',
|
||||
'transform -translate-x-1/2 -translate-y-1/2',
|
||||
isDragging && 'pointer-events-none'
|
||||
)}
|
||||
style={{
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
}}
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
<ConnectionPoint position="top" />
|
||||
<Card className="w-[320px] shadow-md select-none group">
|
||||
<Handle
|
||||
type="target"
|
||||
position={Position.Top}
|
||||
className="w-3 h-3 bg-white border-2 border-blue-500"
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-3 p-3 border-b">
|
||||
<div
|
||||
@@ -143,10 +65,9 @@ export function WorkflowBlock({
|
||||
{row.map((subBlock, blockIndex) => (
|
||||
<div
|
||||
key={`${id}-${rowIndex}-${blockIndex}`}
|
||||
className={cn(
|
||||
'space-y-1',
|
||||
className={`space-y-1 ${
|
||||
subBlock.layout === 'half' ? 'flex-1' : 'w-full'
|
||||
)}
|
||||
}`}
|
||||
>
|
||||
<SubBlock config={subBlock} />
|
||||
</div>
|
||||
@@ -155,7 +76,11 @@ export function WorkflowBlock({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<ConnectionPoint position="bottom" />
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Bottom}
|
||||
className="w-3 h-3 bg-white border-2 border-blue-500"
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
528
package-lock.json
generated
528
package-lock.json
generated
@@ -24,6 +24,7 @@
|
||||
"next": "15.1.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"reactflow": "^11.11.4",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
@@ -1831,6 +1832,108 @@
|
||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@reactflow/background": {
|
||||
"version": "11.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz",
|
||||
"integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.11.4",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/controls": {
|
||||
"version": "11.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz",
|
||||
"integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.11.4",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/core": {
|
||||
"version": "11.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz",
|
||||
"integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/d3-drag": "^3.0.1",
|
||||
"@types/d3-selection": "^3.0.3",
|
||||
"@types/d3-zoom": "^3.0.1",
|
||||
"classcat": "^5.0.3",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/minimap": {
|
||||
"version": "11.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz",
|
||||
"integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.11.4",
|
||||
"@types/d3-selection": "^3.0.3",
|
||||
"@types/d3-zoom": "^3.0.1",
|
||||
"classcat": "^5.0.3",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-zoom": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/node-resizer": {
|
||||
"version": "2.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz",
|
||||
"integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.11.4",
|
||||
"classcat": "^5.0.4",
|
||||
"d3-drag": "^3.0.0",
|
||||
"d3-selection": "^3.0.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactflow/node-toolbar": {
|
||||
"version": "1.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz",
|
||||
"integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/core": "11.11.4",
|
||||
"classcat": "^5.0.3",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
@@ -1846,6 +1949,265 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
|
||||
"integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/d3-axis": "*",
|
||||
"@types/d3-brush": "*",
|
||||
"@types/d3-chord": "*",
|
||||
"@types/d3-color": "*",
|
||||
"@types/d3-contour": "*",
|
||||
"@types/d3-delaunay": "*",
|
||||
"@types/d3-dispatch": "*",
|
||||
"@types/d3-drag": "*",
|
||||
"@types/d3-dsv": "*",
|
||||
"@types/d3-ease": "*",
|
||||
"@types/d3-fetch": "*",
|
||||
"@types/d3-force": "*",
|
||||
"@types/d3-format": "*",
|
||||
"@types/d3-geo": "*",
|
||||
"@types/d3-hierarchy": "*",
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-path": "*",
|
||||
"@types/d3-polygon": "*",
|
||||
"@types/d3-quadtree": "*",
|
||||
"@types/d3-random": "*",
|
||||
"@types/d3-scale": "*",
|
||||
"@types/d3-scale-chromatic": "*",
|
||||
"@types/d3-selection": "*",
|
||||
"@types/d3-shape": "*",
|
||||
"@types/d3-time": "*",
|
||||
"@types/d3-time-format": "*",
|
||||
"@types/d3-timer": "*",
|
||||
"@types/d3-transition": "*",
|
||||
"@types/d3-zoom": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-axis": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
|
||||
"integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-brush": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
|
||||
"integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-chord": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
|
||||
"integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-color": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
|
||||
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-contour": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
|
||||
"integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-array": "*",
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-delaunay": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
|
||||
"integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-dispatch": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz",
|
||||
"integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-drag": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
|
||||
"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-dsv": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
|
||||
"integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-ease": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
|
||||
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-fetch": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
|
||||
"integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-dsv": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-force": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
|
||||
"integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-format": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
|
||||
"integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-geo": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
|
||||
"integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-hierarchy": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
|
||||
"integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-interpolate": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
|
||||
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-color": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-path": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz",
|
||||
"integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-polygon": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
|
||||
"integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-quadtree": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
|
||||
"integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-random": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
|
||||
"integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-scale": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
|
||||
"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-time": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-scale-chromatic": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
|
||||
"integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-selection": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
|
||||
"integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-shape": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
|
||||
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-path": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-time": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
|
||||
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-time-format": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
|
||||
"integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-timer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
|
||||
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/d3-transition": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
|
||||
"integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-zoom": {
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
|
||||
"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/d3-interpolate": "*",
|
||||
"@types/d3-selection": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz",
|
||||
"integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz",
|
||||
@@ -2072,6 +2434,12 @@
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/classcat": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
|
||||
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/client-only": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
@@ -2653,6 +3021,111 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-dispatch": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
|
||||
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-drag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
|
||||
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-selection": "3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-ease": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
|
||||
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-interpolate": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
|
||||
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-selection": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-timer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
|
||||
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-transition": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
|
||||
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-color": "1 - 3",
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-ease": "1 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-timer": "1 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"d3-selection": "2 - 3"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-zoom": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
|
||||
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"d3-dispatch": "1 - 3",
|
||||
"d3-drag": "2 - 3",
|
||||
"d3-interpolate": "1 - 3",
|
||||
"d3-selection": "2 - 3",
|
||||
"d3-transition": "2 - 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
@@ -3518,6 +3991,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/reactflow": {
|
||||
"version": "11.11.4",
|
||||
"resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz",
|
||||
"integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactflow/background": "11.3.14",
|
||||
"@reactflow/controls": "11.2.14",
|
||||
"@reactflow/core": "11.11.4",
|
||||
"@reactflow/minimap": "11.7.14",
|
||||
"@reactflow/node-resizer": "2.2.14",
|
||||
"@reactflow/node-toolbar": "1.3.14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17",
|
||||
"react-dom": ">=17"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -4092,6 +4583,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@@ -4215,6 +4715,34 @@
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/zustand": {
|
||||
"version": "4.5.6",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz",
|
||||
"integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=16.8",
|
||||
"immer": ">=9.0.6",
|
||||
"react": ">=16.8"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"immer": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"next": "15.1.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"reactflow": "^11.11.4",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user