FlowWeb / src /features /canvas /components /nodes /PathNode.jsx
danylokhodus's picture
feat: optimize canvas loading and saving with lazy-loaded drawing paths/images
e0fff03
Raw
History Blame Contribute Delete
2.86 kB
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;