feat: Transform react-flow to vue-jsx

This commit is contained in:
Braks
2021-07-08 23:37:11 +02:00
parent 4025ec82cb
commit 52e1188d04
46 changed files with 5463 additions and 94 deletions
+41
View File
@@ -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;
+35
View File
@@ -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;
+35
View File
@@ -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;
+41
View File
@@ -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
};
});
};
+201
View File
@@ -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>
);
}
});
};