diff --git a/client/src/pages/platform/workflow-editor/components/workflow-nodes-tabs/WorkflowNodesTabsItem.tsx b/client/src/pages/platform/workflow-editor/components/workflow-nodes-tabs/WorkflowNodesTabsItem.tsx index 129a08971b..40c6c55d86 100644 --- a/client/src/pages/platform/workflow-editor/components/workflow-nodes-tabs/WorkflowNodesTabsItem.tsx +++ b/client/src/pages/platform/workflow-editor/components/workflow-nodes-tabs/WorkflowNodesTabsItem.tsx @@ -3,15 +3,17 @@ import { ComponentDefinitionBasic, TaskDispatcherDefinition, } from '@/shared/middleware/platform/configuration'; -import {ComponentIcon} from 'lucide-react'; -import {HTMLAttributes, MouseEvent, useEffect, useRef, useState} from 'react'; +import { ComponentIcon } from 'lucide-react'; +import { HTMLAttributes, MouseEvent, useEffect, useRef, useState } from 'react'; import InlineSVG from 'react-inlinesvg'; -import {twMerge} from 'tailwind-merge'; +import { twMerge } from 'tailwind-merge'; + interface DragEventI extends MouseEvent { dataTransfer: DataTransfer; } + interface WorkflowNodesTabsItemProps extends HTMLAttributes { handleClick?: () => void; node: (ComponentDefinitionBasic | TaskDispatcherDefinition | ClusterElementDefinitionBasic) & { @@ -20,27 +22,56 @@ interface WorkflowNodesTabsItemProps extends HTMLAttributes { trigger: boolean; }; selected?: boolean; + draggable?: boolean; } -const WorkflowNodesTabsItem = ({draggable, handleClick, node, selected}: WorkflowNodesTabsItemProps) => { +const WorkflowNodesTabsItem = ({ + draggable, + handleClick, + node, + selected, +}: WorkflowNodesTabsItemProps) => { const [isVisible, setIsVisible] = useState(false); const ref = useRef(null); + let nodeName = node.name; + if (node.trigger) nodeName = `${node.name}--trigger`; + if (node.taskDispatcher) nodeName = `${node.name}--taskDispatcher`; - if (node.trigger) { - nodeName = `${node.name}--trigger`; - } - - if (node.taskDispatcher) { - nodeName = `${node.name}--taskDispatcher'`; - } - + const onDragStart = (event: DragEventI) => { event.dataTransfer.setData('application/reactflow', nodeName); event.dataTransfer.effectAllowed = 'move'; + + const dragIcon = document.createElement('img'); + + if (node.icon) { + dragIcon.src = node.icon; + } else { + + dragIcon.src = + 'data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjI0IiBzdHJva2U9IiM4ODgiIHN0cm9rZS13aWR0aD0iMS41IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTIgNWMxLjM3IDAgMi41Ljg5IDIuODYgMi4xOGwxLjA0LTIuMDhjLS40Mi0uMTItLjg3LS4xOC0xLjMuMThBNC41IDQuNSAwIDAgMCAxMiA0Yy0yLjQ4IDAtNCAxLjUyLTQgMy41IDAgMi4zOCAxLjk1IDQuNS00LjUgNC41UzguNSA5Ljg4IDggOC41YTMuNSA0LjUgMCAwIDEgMy4wMi0zLjM1bC0uNzMtMS45OEExMiAxMiAwIDEgMSAxMiA1WiIvPjwvc3ZnPg=='; + } + + + dragIcon.style.width = '32px'; + dragIcon.style.height = '32px'; + dragIcon.style.position = 'absolute'; + dragIcon.style.top = '-1000px'; + dragIcon.style.left = '-1000px'; + document.body.appendChild(dragIcon); + + + event.dataTransfer.setDragImage(dragIcon, 16, 16); + + + setTimeout(() => { + document.body.removeChild(dragIcon); + }, 0); }; + useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { @@ -49,16 +80,10 @@ const WorkflowNodesTabsItem = ({draggable, handleClick, node, selected}: Workflo observer.disconnect(); } }, - { - rootMargin: '50px', - threshold: 0.1, - } + { rootMargin: '50px', threshold: 0.1 } ); - if (ref.current) { - observer.observe(ref.current); - } - + if (ref.current) observer.observe(ref.current); return () => observer.disconnect(); }, []); @@ -70,20 +95,17 @@ const WorkflowNodesTabsItem = ({draggable, handleClick, node, selected}: Workflo )} draggable={draggable} id={node?.title} + ref={ref} onClick={(event) => { - if (handleClick) { - handleClick(); - } - event.stopPropagation(); + handleClick?.(); }} onDragStart={(event) => onDragStart(event)} - ref={ref} > {node.icon ? ( isVisible ? ( } src={node.icon} @@ -98,7 +120,6 @@ const WorkflowNodesTabsItem = ({draggable, handleClick, node, selected}: Workflo

{node?.title}

-

{node?.description}