feat: Transform react-flow to vue-jsx
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
const DefaultNode = defineComponent({
|
||||
name: 'DefaultNode',
|
||||
components: { Handle },
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<NodeProps['data']>,
|
||||
required: false,
|
||||
default: undefined as any
|
||||
},
|
||||
isConnectable: {
|
||||
type: Boolean as PropType<NodeProps['isConnectable']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
},
|
||||
targetPosition: {
|
||||
type: String as PropType<NodeProps['targetPosition']>,
|
||||
required: false,
|
||||
default: Position.Top
|
||||
},
|
||||
sourcePosition: {
|
||||
type: String as PropType<NodeProps['sourcePosition']>,
|
||||
required: false,
|
||||
default: Position.Bottom
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
return () => (
|
||||
<>
|
||||
<Handle type="target" position={props.targetPosition} isConnectable={props.isConnectable} />
|
||||
{props.data?.label}
|
||||
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default DefaultNode;
|
||||
@@ -0,0 +1,35 @@
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
const InputNode = defineComponent({
|
||||
name: 'InputNode',
|
||||
components: { Handle },
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<NodeProps['data']>,
|
||||
required: false,
|
||||
default: undefined as any
|
||||
},
|
||||
isConnectable: {
|
||||
type: Boolean as PropType<NodeProps['isConnectable']>,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
sourcePosition: {
|
||||
type: String as PropType<NodeProps['sourcePosition']>,
|
||||
required: false,
|
||||
default: Position.Bottom
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
return () => (
|
||||
<>
|
||||
{props.data?.label}
|
||||
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default InputNode;
|
||||
@@ -0,0 +1,35 @@
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
const OutputNode = defineComponent({
|
||||
name: 'OutputNode',
|
||||
components: { Handle },
|
||||
props: {
|
||||
data: {
|
||||
type: Object as PropType<NodeProps['data']>,
|
||||
required: false,
|
||||
default: undefined as any
|
||||
},
|
||||
isConnectable: {
|
||||
type: Boolean as PropType<NodeProps['isConnectable']>,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
targetPosition: {
|
||||
type: String as PropType<NodeProps['targetPosition']>,
|
||||
required: false,
|
||||
default: Position.Bottom
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
return () => (
|
||||
<>
|
||||
{props.data?.label}
|
||||
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default OutputNode;
|
||||
@@ -0,0 +1,41 @@
|
||||
import { HandleElement, Position } from '../../types';
|
||||
import { getDimensions } from '../../utils';
|
||||
|
||||
export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
|
||||
const bounds = nodeElement.getBoundingClientRect();
|
||||
|
||||
return {
|
||||
source: getHandleBoundsByHandleType('.source', nodeElement, bounds, scale),
|
||||
target: getHandleBoundsByHandleType('.target', nodeElement, bounds, scale)
|
||||
};
|
||||
};
|
||||
|
||||
export const getHandleBoundsByHandleType = (
|
||||
selector: string,
|
||||
nodeElement: HTMLDivElement,
|
||||
parentBounds: ClientRect | DOMRect,
|
||||
k: number
|
||||
): HandleElement[] | null => {
|
||||
const handles = nodeElement.querySelectorAll(selector);
|
||||
|
||||
if (!handles || !handles.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handlesArray = Array.from(handles) as HTMLDivElement[];
|
||||
|
||||
return handlesArray.map((handle): HandleElement => {
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const dimensions = getDimensions(handle);
|
||||
const handleId = handle.getAttribute('data-handleid');
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position;
|
||||
|
||||
return {
|
||||
id: handleId,
|
||||
position: handlePosition,
|
||||
x: (bounds.left - parentBounds.left) / k,
|
||||
y: (bounds.top - parentBounds.top) / k,
|
||||
...dimensions
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
import { Node, WrapNodeProps } from '../../types';
|
||||
import { computed, CSSProperties, defineComponent, onMounted, provide, ref } from 'vue';
|
||||
import store from '../../store';
|
||||
|
||||
export default (NodeComponent: any) => {
|
||||
return defineComponent({
|
||||
props: WrapNodeProps,
|
||||
setup(props) {
|
||||
const pinia = store();
|
||||
provide('NodeIdContext', props.id);
|
||||
|
||||
const nodeElement = ref<HTMLDivElement | null>(null);
|
||||
|
||||
const node = computed(() => ({
|
||||
id: props.id,
|
||||
type: props.type,
|
||||
position: { x: props.xPos, y: props.yPos },
|
||||
data: props.data
|
||||
}));
|
||||
// const grid = computed(() => (props.snapToGrid ? props.snapGrid : [1, 1])! as [number, number]);
|
||||
|
||||
const nodeStyle = computed<CSSProperties>(() => ({
|
||||
zIndex: props.selected ? 10 : 3,
|
||||
transform: `translate(${props.xPos}px,${props.yPos}px)`,
|
||||
pointerEvents:
|
||||
props.isSelectable ||
|
||||
props.isDraggable ||
|
||||
props.onClick ||
|
||||
props.onMouseEnter ||
|
||||
props.onMouseMove ||
|
||||
props.onMouseLeave
|
||||
? 'all'
|
||||
: 'none',
|
||||
// prevents jumping of nodes on start
|
||||
opacity: props.isInitialized ? 1 : 0,
|
||||
...props.style
|
||||
}));
|
||||
const onMouseEnterHandler = () => {
|
||||
if (!props.onMouseEnter || props.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (event: MouseEvent) => props.onMouseEnter && props.onMouseEnter(event, node.value as Node);
|
||||
};
|
||||
|
||||
const onMouseMoveHandler = () => {
|
||||
if (!props.onMouseMove || props.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (event: MouseEvent) => props.onMouseMove && props.onMouseMove(event, node.value as Node);
|
||||
};
|
||||
|
||||
const onMouseLeaveHandler = () => {
|
||||
if (!props.onMouseLeave || props.isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (event: MouseEvent) => props.onMouseLeave && props.onMouseLeave(event, node.value as Node);
|
||||
};
|
||||
|
||||
const onContextMenuHandler = () => {
|
||||
if (!props.onContextMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (event: MouseEvent) => props.onContextMenu && props.onContextMenu(event, node.value as Node);
|
||||
};
|
||||
|
||||
const onSelectNodeHandler = (event: MouseEvent) => {
|
||||
if (!props.isDraggable) {
|
||||
if (props.isSelectable) {
|
||||
pinia.unsetNodesSelection();
|
||||
|
||||
if (!props.selected) {
|
||||
pinia.addSelectedElements([node.value as Node]);
|
||||
}
|
||||
}
|
||||
|
||||
props.onClick?.(event, node.value as Node);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
const onDragStart = (event: DraggableEvent) => {
|
||||
onNodeDragStart?.(event as MouseEvent, node);
|
||||
|
||||
if (selectNodesOnDrag && isSelectable) {
|
||||
unsetNodesSelection();
|
||||
|
||||
if (!selected) {
|
||||
addSelectedElements(node);
|
||||
}
|
||||
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
||||
unsetNodesSelection();
|
||||
addSelectedElements([]);
|
||||
}
|
||||
};
|
||||
|
||||
const onDrag = useCallback(
|
||||
(event: DraggableEvent, draggableData: DraggableData) => {
|
||||
if (onNodeDrag) {
|
||||
node.position.x += draggableData.deltaX;
|
||||
node.position.y += draggableData.deltaY;
|
||||
onNodeDrag(event as MouseEvent, node);
|
||||
}
|
||||
|
||||
updateNodePosDiff({
|
||||
id,
|
||||
diff: {
|
||||
x: draggableData.deltaX,
|
||||
y: draggableData.deltaY
|
||||
},
|
||||
isDragging: true
|
||||
});
|
||||
},
|
||||
[id, node, onNodeDrag]
|
||||
);
|
||||
|
||||
const onDragStop = useCallback(
|
||||
(event: DraggableEvent) => {
|
||||
// onDragStop also gets called when user just clicks on a node.
|
||||
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
||||
if (!isDragging) {
|
||||
if (isSelectable && !selectNodesOnDrag && !selected) {
|
||||
addSelectedElements(node);
|
||||
}
|
||||
|
||||
onClick?.(event as MouseEvent, node);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateNodePosDiff({
|
||||
id: node.id,
|
||||
isDragging: false
|
||||
});
|
||||
|
||||
onNodeDragStop?.(event as MouseEvent, node);
|
||||
},
|
||||
[node, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, isDragging, selected]
|
||||
);
|
||||
*/
|
||||
|
||||
onMounted(() => {
|
||||
if (nodeElement.value && !props.isHidden) {
|
||||
pinia.updateNodeDimensions([{ id: props.id || '', nodeElement: nodeElement.value, forceUpdate: true }]);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (nodeElement.value && typeof props.resizeObserver?.observe === 'function') {
|
||||
const currNode = nodeElement.value;
|
||||
props.resizeObserver?.observe(currNode);
|
||||
|
||||
return () => props.resizeObserver?.unobserve(currNode);
|
||||
}
|
||||
});
|
||||
|
||||
if (props.isHidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nodeClasses = [
|
||||
'react-flow__node',
|
||||
`react-flow__node-${props.type}`,
|
||||
{
|
||||
selected: props.selected,
|
||||
selectable: props.isSelectable
|
||||
}
|
||||
];
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={nodeClasses}
|
||||
ref={nodeElement}
|
||||
style={nodeStyle.value}
|
||||
onMouseenter={onMouseEnterHandler}
|
||||
onMousemove={onMouseMoveHandler}
|
||||
onMouseleave={onMouseLeaveHandler}
|
||||
onContextmenu={onContextMenuHandler}
|
||||
onClick={onSelectNodeHandler}
|
||||
data-id={props.id}
|
||||
>
|
||||
<NodeComponent
|
||||
id={props.id}
|
||||
data={props.data}
|
||||
type={props.type}
|
||||
xPos={props.xPos}
|
||||
yPos={props.yPos}
|
||||
selected={props.selected}
|
||||
isConnectable={props.isConnectable}
|
||||
sourcePosition={props.sourcePosition}
|
||||
targetPosition={props.targetPosition}
|
||||
isDragging={props.isDragging}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user