Spaces:
Running
Running
| import React, { useState, useEffect } from 'react'; | |
| import { Handle, Position } from '@xyflow/react'; | |
| import { Loader2 } from 'lucide-react'; | |
| import { useTheme } from '../../../../context/ThemeContext'; | |
| import axios from 'axios'; | |
| const PathNode = ({ id, data, selected }) => { | |
| const { isDarkMode } = useTheme(); | |
| const [path, setPath] = useState(data.path || ""); | |
| const [loading, setLoading] = useState(false); | |
| // Extract properties from data | |
| const color = data.color || (isDarkMode ? '#ffffff' : '#31363F'); | |
| const strokeWidth = data.strokeWidth || 3; | |
| const width = data.width || 100; | |
| const height = data.height || 100; | |
| // Load large path data from backend on mount if not loaded | |
| useEffect(() => { | |
| if (!path && data.hasLargeData) { | |
| setLoading(true); | |
| const API_URL = import.meta.env.VITE_API_URL; | |
| axios.get(`${API_URL}/TaskNodes/${id}/data`) | |
| .then(res => { | |
| if (res.data && res.data.data) { | |
| setPath(res.data.data); | |
| if (data.updateNodeData) { | |
| data.updateNodeData(id, { path: res.data.data, isLoadedFromBackend: true }); | |
| } | |
| } | |
| }) | |
| .catch(err => console.error('[PathNode] Error loading large path data:', err)) | |
| .finally(() => setLoading(false)); | |
| } | |
| }, [id, path, data.hasLargeData]); | |
| // Sync if data changes from remote | |
| useEffect(() => { | |
| if (data.path && data.path !== path) { | |
| setPath(data.path); | |
| } | |
| }, [data.path]); | |
| return ( | |
| <div | |
| className={`relative group ${selected ? 'ring-2 ring-primary/20 rounded-md' : ''}`} | |
| style={{ width, height }} | |
| > | |
| {loading ? ( | |
| <div className="w-full h-full flex items-center justify-center bg-secondary/5 rounded border border-dashed border-primary/10"> | |
| <Loader2 size={16} className="text-primary animate-spin" /> | |
| </div> | |
| ) : ( | |
| /* SVG Path Renderer */ | |
| <svg | |
| width={width} | |
| height={height} | |
| viewBox={`0 0 ${width} ${height}`} | |
| style={{ | |
| display: 'block', | |
| overflow: 'visible' | |
| }} | |
| > | |
| {path && ( | |
| <path | |
| d={path} | |
| fill="none" | |
| stroke={color} | |
| strokeWidth={strokeWidth} | |
| strokeLinecap="round" | |
| strokeLinejoin="round" | |
| style={{ | |
| pointerEvents: 'visibleStroke', // Allows selecting the path by clicking the stroke | |
| cursor: 'pointer' | |
| }} | |
| /> | |
| )} | |
| </svg> | |
| )} | |
| {/* Invisible Handles */} | |
| <Handle type="target" position={Position.Top} className="!opacity-0 !pointer-events-none" /> | |
| <Handle type="source" position={Position.Bottom} className="!opacity-0 !pointer-events-none" /> | |
| </div> | |
| ); | |
| }; | |
| export default PathNode; | |