chore(lint): Update eslint rules
* Remove semi
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
id: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -25,6 +25,6 @@ export default defineComponent({
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</marker>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Marker from './Marker';
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import Marker from './Marker'
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
color: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -38,6 +38,6 @@ export default defineComponent({
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue';
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
|
||||
import ConnectionLine from '../../components/ConnectionLine';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import Edge from '../../components/Edges/Edge';
|
||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue'
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types'
|
||||
import ConnectionLine from '../../components/ConnectionLine'
|
||||
import MarkerDefinitions from './MarkerDefinitions'
|
||||
import Edge from '../../components/Edges/Edge'
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edgeTypes: any;
|
||||
connectionLineType: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
connectionLineComponent?: ConnectionLineComponent;
|
||||
connectionMode?: ConnectionMode;
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
onlyRenderVisibleElements: boolean;
|
||||
edgeUpdaterRadius?: number;
|
||||
edgeTypes: any
|
||||
connectionLineType: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
connectionLineComponent?: ConnectionLineComponent
|
||||
connectionMode?: ConnectionMode
|
||||
arrowHeadColor: string
|
||||
markerEndId?: string
|
||||
onlyRenderVisibleElements: boolean
|
||||
edgeUpdaterRadius?: number
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -70,7 +70,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
return () => (
|
||||
<svg width={store.width} height={store.height} class="revue-flow__edges">
|
||||
@@ -94,6 +94,6 @@ export default defineComponent({
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
||||
import { rectToBox } from '../../utils/graph';
|
||||
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types';
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||
import { rectToBox } from '../../utils/graph'
|
||||
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types'
|
||||
|
||||
export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<string, EdgeType> {
|
||||
const standardTypes: Record<string, EdgeType> = {
|
||||
@@ -8,75 +8,75 @@ export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<str
|
||||
straight: edgeTypes.bezier || StraightEdge,
|
||||
step: edgeTypes.step || StepEdge,
|
||||
smoothstep: edgeTypes.step || SmoothStepEdge
|
||||
};
|
||||
}
|
||||
|
||||
const wrappedTypes = {} as Record<string, EdgeType>;
|
||||
const wrappedTypes = {} as Record<string, EdgeType>
|
||||
const specialTypes: Record<string, EdgeType> = Object.keys(edgeTypes)
|
||||
.filter((k) => !['default', 'bezier'].includes(k))
|
||||
.reduce((res, key) => {
|
||||
res[key] = edgeTypes[key] || BezierEdge;
|
||||
res[key] = edgeTypes[key] || BezierEdge
|
||||
|
||||
return res;
|
||||
}, wrappedTypes);
|
||||
return res
|
||||
}, wrappedTypes)
|
||||
|
||||
return {
|
||||
...standardTypes,
|
||||
...specialTypes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
||||
const x = (handle?.x || 0) + node.__rf.position.x;
|
||||
const y = (handle?.y || 0) + node.__rf.position.y;
|
||||
const width = handle?.width || node.__rf.width;
|
||||
const height = handle?.height || node.__rf.height;
|
||||
const x = (handle?.x || 0) + node.__rf.position.x
|
||||
const y = (handle?.y || 0) + node.__rf.position.y
|
||||
const width = handle?.width || node.__rf.width
|
||||
const height = handle?.height || node.__rf.height
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y
|
||||
};
|
||||
}
|
||||
case Position.Right:
|
||||
return {
|
||||
x: x + width,
|
||||
y: y + height / 2
|
||||
};
|
||||
}
|
||||
case Position.Bottom:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y: y + height
|
||||
};
|
||||
}
|
||||
case Position.Left:
|
||||
return {
|
||||
x,
|
||||
y: y + height / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null {
|
||||
if (!bounds) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
// there is no handleId when there are no multiple handles/ handles with ids
|
||||
// so we just pick the first one
|
||||
let handle = null;
|
||||
let handle = null
|
||||
if (bounds.length === 1 || !handleId) {
|
||||
handle = bounds[0];
|
||||
handle = bounds[0]
|
||||
} else if (handleId) {
|
||||
handle = bounds.find((d) => d.id === handleId);
|
||||
handle = bounds.find((d) => d.id === handleId)
|
||||
}
|
||||
|
||||
return typeof handle === 'undefined' ? null : handle;
|
||||
return typeof handle === 'undefined' ? null : handle
|
||||
}
|
||||
|
||||
interface EdgePositions {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
}
|
||||
|
||||
export const getEdgePositions = (
|
||||
@@ -87,23 +87,23 @@ export const getEdgePositions = (
|
||||
targetHandle: HandleElement | unknown,
|
||||
targetPosition: Position
|
||||
): EdgePositions => {
|
||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
|
||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle);
|
||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle)
|
||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle)
|
||||
|
||||
return {
|
||||
sourceX: sourceHandlePos.x,
|
||||
sourceY: sourceHandlePos.y,
|
||||
targetX: targetHandlePos.x,
|
||||
targetY: targetHandlePos.y
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface IsEdgeVisibleParams {
|
||||
sourcePos: XYPosition;
|
||||
targetPos: XYPosition;
|
||||
width: number;
|
||||
height: number;
|
||||
transform: Transform;
|
||||
sourcePos: XYPosition
|
||||
targetPos: XYPosition
|
||||
width: number
|
||||
height: number
|
||||
transform: Transform
|
||||
}
|
||||
|
||||
export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }: IsEdgeVisibleParams): boolean {
|
||||
@@ -112,14 +112,14 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
||||
y: Math.min(sourcePos.y, targetPos.y),
|
||||
x2: Math.max(sourcePos.x, targetPos.x),
|
||||
y2: Math.max(sourcePos.y, targetPos.y)
|
||||
};
|
||||
}
|
||||
|
||||
if (edgeBox.x === edgeBox.x2) {
|
||||
edgeBox.x2 += 1;
|
||||
edgeBox.x2 += 1
|
||||
}
|
||||
|
||||
if (edgeBox.y === edgeBox.y2) {
|
||||
edgeBox.y2 += 1;
|
||||
edgeBox.y2 += 1
|
||||
}
|
||||
|
||||
const viewBox = rectToBox({
|
||||
@@ -127,31 +127,31 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
||||
y: (0 - transform[1]) / transform[2],
|
||||
width: width / transform[2],
|
||||
height: height / transform[2]
|
||||
});
|
||||
})
|
||||
|
||||
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x));
|
||||
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y));
|
||||
const overlappingArea = Math.ceil(xOverlap * yOverlap);
|
||||
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x))
|
||||
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y))
|
||||
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||
|
||||
return overlappingArea > 0;
|
||||
return overlappingArea > 0
|
||||
}
|
||||
|
||||
type SourceTargetNode = {
|
||||
sourceNode: Node | null;
|
||||
targetNode: Node | null;
|
||||
};
|
||||
sourceNode: Node | null
|
||||
targetNode: Node | null
|
||||
}
|
||||
|
||||
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
|
||||
return nodes.reduce(
|
||||
(res, node) => {
|
||||
if (node.id === edge.source) {
|
||||
res.sourceNode = node;
|
||||
res.sourceNode = node
|
||||
}
|
||||
if (node.id === edge.target) {
|
||||
res.targetNode = node;
|
||||
res.targetNode = node
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
},
|
||||
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
import NodesSelection from '../../components/NodesSelection';
|
||||
import { RevueFlowStore, GraphViewProps } from '../../types';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { computed, defineComponent, inject, PropType } from 'vue'
|
||||
import ZoomPane from '../ZoomPane'
|
||||
import UserSelection from '../../components/UserSelection'
|
||||
import NodesSelection from '../../components/NodesSelection'
|
||||
import { RevueFlowStore, GraphViewProps } from '../../types'
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'
|
||||
import useKeyPress from '../../hooks/useKeyPress'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
type FlowRendererProps = Omit<
|
||||
GraphViewProps,
|
||||
@@ -18,7 +18,7 @@ type FlowRendererProps = Omit<
|
||||
| 'arrowHeadColor'
|
||||
| 'onlyRenderVisibleElements'
|
||||
| 'selectNodesOnDrag'
|
||||
>;
|
||||
>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FlowRenderer',
|
||||
@@ -101,28 +101,28 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode)
|
||||
|
||||
useGlobalKeyHandler({
|
||||
store,
|
||||
onElementsRemove: hooks.elementsRemove.trigger,
|
||||
deleteKeyCode: props.deleteKeyCode || '',
|
||||
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
|
||||
});
|
||||
})
|
||||
|
||||
const onClick = (event: MouseEvent) => {
|
||||
hooks.paneClick.trigger(event);
|
||||
store.unsetNodesSelection();
|
||||
store.resetSelectedElements();
|
||||
};
|
||||
hooks.paneClick.trigger(event)
|
||||
store.unsetNodesSelection()
|
||||
store.resetSelectedElements()
|
||||
}
|
||||
|
||||
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event);
|
||||
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event)
|
||||
|
||||
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event);
|
||||
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
|
||||
|
||||
const selectionActive = computed(() => store.nodesSelectionActive);
|
||||
const selectionActive = computed(() => store.nodesSelectionActive)
|
||||
|
||||
return () => (
|
||||
<ZoomPane
|
||||
@@ -145,6 +145,6 @@ export default defineComponent({
|
||||
{selectionActive.value ? <NodesSelection /> : ''}
|
||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
||||
</ZoomPane>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import FlowRenderer from '../FlowRenderer';
|
||||
import NodeRenderer from '../NodeRenderer';
|
||||
import EdgeRenderer from '../EdgeRenderer';
|
||||
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph';
|
||||
import { GraphViewProps, RevueFlowStore } from '../../types';
|
||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { tryOnMounted } from '@vueuse/core';
|
||||
import { defineComponent, inject, PropType } from 'vue'
|
||||
import FlowRenderer from '../FlowRenderer'
|
||||
import NodeRenderer from '../NodeRenderer'
|
||||
import EdgeRenderer from '../EdgeRenderer'
|
||||
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'
|
||||
import { GraphViewProps, RevueFlowStore } from '../../types'
|
||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
import { tryOnMounted } from '@vueuse/core'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GraphView',
|
||||
@@ -177,10 +177,10 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
|
||||
const { onReady } = useZoomPanHelper();
|
||||
const { onReady } = useZoomPanHelper()
|
||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
||||
hooks.load.trigger({
|
||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||
@@ -191,52 +191,52 @@ export default defineComponent({
|
||||
project: onLoadProject(store),
|
||||
getElements: onLoadGetElements(store),
|
||||
toObject: onLoadToObject(store)
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
if (typeof props.snapToGrid !== 'undefined') {
|
||||
store.snapToGrid = props.snapToGrid;
|
||||
store.snapToGrid = props.snapToGrid
|
||||
}
|
||||
|
||||
if (typeof props.snapGrid !== 'undefined') {
|
||||
store.snapGrid = props.snapGrid;
|
||||
store.snapGrid = props.snapGrid
|
||||
}
|
||||
|
||||
if (typeof props.minZoom !== 'undefined') {
|
||||
store.setMinZoom(props.minZoom);
|
||||
store.setMinZoom(props.minZoom)
|
||||
}
|
||||
|
||||
if (typeof props.maxZoom !== 'undefined') {
|
||||
store.setMaxZoom(props.maxZoom);
|
||||
store.setMaxZoom(props.maxZoom)
|
||||
}
|
||||
|
||||
if (typeof props.translateExtent !== 'undefined') {
|
||||
store.setTranslateExtent(props.translateExtent);
|
||||
store.setTranslateExtent(props.translateExtent)
|
||||
}
|
||||
|
||||
if (typeof props.nodeExtent !== 'undefined') {
|
||||
store.setNodeExtent(props.nodeExtent);
|
||||
store.setNodeExtent(props.nodeExtent)
|
||||
}
|
||||
|
||||
if (typeof props.nodesConnectable !== 'undefined') {
|
||||
store.nodesConnectable = !!props.nodesConnectable;
|
||||
store.nodesConnectable = !!props.nodesConnectable
|
||||
}
|
||||
if (typeof props.elementsSelectable !== 'undefined') {
|
||||
store.elementsSelectable = !!props.elementsSelectable;
|
||||
store.elementsSelectable = !!props.elementsSelectable
|
||||
}
|
||||
if (typeof props.nodesDraggable !== 'undefined') {
|
||||
store.nodesDraggable = !!props.nodesDraggable;
|
||||
store.nodesDraggable = !!props.nodesDraggable
|
||||
}
|
||||
|
||||
if (typeof props.connectionMode !== 'undefined') {
|
||||
store.connectionMode = props.connectionMode;
|
||||
store.connectionMode = props.connectionMode
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tryOnMounted(() => {
|
||||
init();
|
||||
});
|
||||
init()
|
||||
})
|
||||
|
||||
return () => (
|
||||
<FlowRenderer
|
||||
@@ -275,6 +275,6 @@ export default defineComponent({
|
||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
||||
/>
|
||||
</FlowRenderer>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import Node from '../../components/Nodes/Node';
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeType, RevueFlowStore } from '../../types';
|
||||
import { defineComponent, inject, PropType } from 'vue'
|
||||
import Node from '../../components/Nodes/Node'
|
||||
import { getNodesInside } from '../../utils/graph'
|
||||
import { NodeType, RevueFlowStore } from '../../types'
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: Record<string, NodeType>;
|
||||
selectNodesOnDrag: boolean;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
onlyRenderVisibleElements: boolean;
|
||||
nodeTypes: Record<string, NodeType>
|
||||
selectNodesOnDrag: boolean
|
||||
snapToGrid: boolean
|
||||
snapGrid: [number, number]
|
||||
onlyRenderVisibleElements: boolean
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -42,7 +42,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
const getNodes = () => {
|
||||
return props.onlyRenderVisibleElements
|
||||
@@ -58,30 +58,8 @@ export default defineComponent({
|
||||
store.transform,
|
||||
true
|
||||
)
|
||||
: store.nodes;
|
||||
};
|
||||
|
||||
const visibleNodes = () =>
|
||||
getNodes().map((node, i) => {
|
||||
const nodeType = node.type || 'default';
|
||||
if (props.nodeTypes) {
|
||||
const type = props.nodeTypes[nodeType] || props.nodeTypes.default;
|
||||
if (!props.nodeTypes[nodeType]) {
|
||||
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`);
|
||||
}
|
||||
|
||||
return (
|
||||
<Node
|
||||
node={node}
|
||||
snapGrid={props.snapGrid}
|
||||
snapToGrid={props.snapToGrid}
|
||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
||||
type={type}
|
||||
key={node.id + i}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
: store.nodes
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
@@ -90,8 +68,27 @@ export default defineComponent({
|
||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||
}}
|
||||
>
|
||||
{visibleNodes()}
|
||||
{getNodes().map((node, i) => {
|
||||
const nodeType = node.type || 'default'
|
||||
if (props.nodeTypes) {
|
||||
const type = props.nodeTypes[nodeType] || props.nodeTypes.default
|
||||
if (!props.nodeTypes[nodeType]) {
|
||||
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`)
|
||||
}
|
||||
|
||||
return (
|
||||
<Node
|
||||
node={node}
|
||||
snapGrid={props.snapGrid}
|
||||
snapToGrid={props.snapToGrid}
|
||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
||||
type={type}
|
||||
key={node.id + i}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
||||
import InputNode from '../../components/Nodes/InputNode';
|
||||
import OutputNode from '../../components/Nodes/OutputNode';
|
||||
import { NodeType } from '../../types';
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||
import InputNode from '../../components/Nodes/InputNode'
|
||||
import OutputNode from '../../components/Nodes/OutputNode'
|
||||
import { NodeType } from '../../types'
|
||||
|
||||
export function createNodeTypes(nodeTypes: Record<string, NodeType>): Record<string, NodeType> {
|
||||
const standardTypes: Record<string, NodeType> = {
|
||||
input: nodeTypes.input || InputNode,
|
||||
default: nodeTypes.default || DefaultNode,
|
||||
output: nodeTypes.output || OutputNode
|
||||
};
|
||||
}
|
||||
|
||||
const wrappedTypes = {} as NodeType;
|
||||
const wrappedTypes = {} as NodeType
|
||||
const specialTypes: NodeType = Object.keys(nodeTypes)
|
||||
.filter((k) => !['input', 'default', 'output'].includes(k))
|
||||
.reduce((res, key) => {
|
||||
res[key] = nodeTypes[key] || DefaultNode;
|
||||
res[key] = nodeTypes[key] || DefaultNode
|
||||
|
||||
return res;
|
||||
}, wrappedTypes);
|
||||
return res
|
||||
}, wrappedTypes)
|
||||
|
||||
return {
|
||||
...standardTypes,
|
||||
...specialTypes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import { defineComponent, onBeforeUnmount, onUpdated, PropType, provide, watch } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import '../../style.css';
|
||||
import '../../theme-default.css';
|
||||
import GraphView from '../GraphView';
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
||||
import InputNode from '../../components/Nodes/InputNode';
|
||||
import OutputNode from '../../components/Nodes/OutputNode';
|
||||
import { createNodeTypes } from '../NodeRenderer/utils';
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
||||
import { createEdgeTypes } from '../EdgeRenderer/utils';
|
||||
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types';
|
||||
import { initialState } from '../../store';
|
||||
import configureStore from '../../store/configure-store';
|
||||
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks';
|
||||
import { PropType } from 'vue'
|
||||
import '../../style.css'
|
||||
import '../../theme-default.css'
|
||||
import GraphView from '../GraphView'
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||
import InputNode from '../../components/Nodes/InputNode'
|
||||
import OutputNode from '../../components/Nodes/OutputNode'
|
||||
import { createNodeTypes } from '../NodeRenderer/utils'
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||
import { createEdgeTypes } from '../EdgeRenderer/utils'
|
||||
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types'
|
||||
import { initialState } from '../../store'
|
||||
import configureStore from '../../store/configure-store'
|
||||
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
const defaultNodeTypes = {
|
||||
input: InputNode,
|
||||
default: DefaultNode,
|
||||
output: OutputNode
|
||||
};
|
||||
}
|
||||
|
||||
const defaultEdgeTypes = {
|
||||
default: BezierEdge,
|
||||
straight: StraightEdge,
|
||||
step: StepEdge,
|
||||
smoothstep: SmoothStepEdge
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RevueFlow',
|
||||
@@ -220,25 +219,25 @@ export default defineComponent({
|
||||
},
|
||||
emits: Object.keys(useRevueFlow()),
|
||||
setup(props, { emit, slots }) {
|
||||
const store = configureStore(initialState)();
|
||||
provide<RevueFlowStore>('store', store);
|
||||
const hooks = useRevueFlow().bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', hooks);
|
||||
const elements = useVModel(props, 'modelValue', emit);
|
||||
const store = configureStore(initialState)()
|
||||
provide<RevueFlowStore>('store', store)
|
||||
const hooks = useRevueFlow().bind(emit)
|
||||
provide<RevueFlowHooks>('hooks', hooks)
|
||||
const elements = useVModel(props, 'modelValue', emit)
|
||||
|
||||
store.setElements(elements.value);
|
||||
store.setElements(elements.value)
|
||||
watch(elements.value, () => {
|
||||
store.setElements(elements.value);
|
||||
});
|
||||
store.setElements(elements.value)
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
store.setElements(elements.value);
|
||||
});
|
||||
store.setElements(elements.value)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.$reset();
|
||||
store.setElements([]);
|
||||
});
|
||||
store.$reset()
|
||||
store.setElements([])
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="revue-flow">
|
||||
@@ -279,6 +278,6 @@ export default defineComponent({
|
||||
/>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom';
|
||||
import { select, pointer } from 'd3-selection';
|
||||
import { clamp } from '../../utils';
|
||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types';
|
||||
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { templateRef } from '@vueuse/core';
|
||||
import { ZoomBehavior } from 'd3';
|
||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
||||
import { select, pointer } from 'd3-selection'
|
||||
import { clamp } from '../../utils'
|
||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types'
|
||||
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue'
|
||||
import useKeyPress from '../../hooks/useKeyPress'
|
||||
import useResizeHandler from '../../hooks/useResizeHandler'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
import { templateRef } from '@vueuse/core'
|
||||
import { ZoomBehavior } from 'd3'
|
||||
|
||||
interface ZoomPaneProps {
|
||||
elementsSelectable?: boolean;
|
||||
zoomOnScroll?: boolean;
|
||||
zoomOnPinch?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
panOnScrollMode?: PanOnScrollMode;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
paneMoveable?: boolean;
|
||||
defaultPosition?: [number, number];
|
||||
defaultZoom?: number;
|
||||
translateExtent?: TranslateExtent;
|
||||
zoomActivationKeyCode?: KeyCode;
|
||||
selectionKeyCode?: KeyCode;
|
||||
elementsSelectable?: boolean
|
||||
zoomOnScroll?: boolean
|
||||
zoomOnPinch?: boolean
|
||||
panOnScroll?: boolean
|
||||
panOnScrollSpeed?: number
|
||||
panOnScrollMode?: PanOnScrollMode
|
||||
zoomOnDoubleClick?: boolean
|
||||
paneMoveable?: boolean
|
||||
defaultPosition?: [number, number]
|
||||
defaultZoom?: number
|
||||
translateExtent?: TranslateExtent
|
||||
zoomActivationKeyCode?: KeyCode
|
||||
selectionKeyCode?: KeyCode
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -95,35 +95,35 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
||||
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k;
|
||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
||||
|
||||
const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => ({
|
||||
x: eventTransform.x,
|
||||
y: eventTransform.y,
|
||||
zoom: eventTransform.k
|
||||
});
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
const { onReady } = useResizeHandler(zoomPane, store.updateSize);
|
||||
const { onReady } = useResizeHandler(zoomPane, store.updateSize)
|
||||
onReady(() => {
|
||||
if (props.defaultPosition && props.defaultZoom) {
|
||||
const currentTranslateExtent =
|
||||
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent;
|
||||
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent);
|
||||
const selection = select(zoomPane.value as Element).call(d3ZoomInstance);
|
||||
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent
|
||||
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent)
|
||||
const selection = select(zoomPane.value as Element).call(d3ZoomInstance)
|
||||
|
||||
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]);
|
||||
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]);
|
||||
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom);
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
||||
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0])
|
||||
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1])
|
||||
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom)
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||
|
||||
d3ZoomInstance.transform(selection, updatedTransform);
|
||||
d3ZoomInstance.transform(selection, updatedTransform)
|
||||
|
||||
store.initD3Zoom({
|
||||
d3Zoom: d3ZoomInstance,
|
||||
@@ -131,106 +131,106 @@ export default defineComponent({
|
||||
d3ZoomHandler: selection.on('wheel.zoom'),
|
||||
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
||||
transform: [clampedX, clampedY, clampedZoom]
|
||||
});
|
||||
applyZoomHandlers(d3ZoomInstance);
|
||||
applyZoomFilter(d3ZoomInstance);
|
||||
})
|
||||
applyZoomHandlers(d3ZoomInstance)
|
||||
applyZoomFilter(d3ZoomInstance)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
||||
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
const flowTransform = eventToFlowTransform(event.transform)
|
||||
prevTransform.value = flowTransform
|
||||
|
||||
hooks.moveStart.trigger(flowTransform);
|
||||
hooks.moveStart.trigger(flowTransform)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
const flowTransform = eventToFlowTransform(event.transform)
|
||||
prevTransform.value = flowTransform
|
||||
|
||||
hooks.moveEnd.trigger(flowTransform);
|
||||
hooks.moveEnd.trigger(flowTransform)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
||||
if (keyPress) {
|
||||
d3Zoom.on('zoom', null);
|
||||
d3Zoom.on('zoom', null)
|
||||
} else {
|
||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k];
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform));
|
||||
});
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k]
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform))
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
||||
if (props.panOnScroll && keyPress) {
|
||||
store.d3Selection
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1
|
||||
|
||||
if (event.ctrlKey && props.zoomOnPinch) {
|
||||
const point = pointer(event);
|
||||
const point = pointer(event)
|
||||
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
|
||||
const zoom = currentZoom * Math.pow(2, pinchDelta);
|
||||
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point);
|
||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
||||
const zoom = currentZoom * Math.pow(2, pinchDelta)
|
||||
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point)
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// increase scroll speed in firefox
|
||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
|
||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
|
||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||
|
||||
if (store.d3Selection && props.panOnScrollSpeed)
|
||||
store.d3Zoom?.translateBy(
|
||||
store.d3Selection,
|
||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
||||
);
|
||||
)
|
||||
})
|
||||
.on('wheel.zoom', null);
|
||||
.on('wheel.zoom', null)
|
||||
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
||||
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler);
|
||||
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom));
|
||||
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom))
|
||||
|
||||
const applyZoomFilter = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
||||
const keyPress = useKeyPress(props.selectionKeyCode);
|
||||
const keyPress = useKeyPress(props.selectionKeyCode)
|
||||
d3Zoom.filter((event: MouseEvent) => {
|
||||
const zoomScroll = props.zoomOnScroll;
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
|
||||
const zoomScroll = props.zoomOnScroll
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
||||
|
||||
// if all interactions are disabled, we prevent all zoom events
|
||||
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// during a selection we prevent all other interactions
|
||||
if (keyPress.value) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// if zoom on double click is disabled, we prevent the double click event
|
||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// when the target element is a node, we still allow zooming
|
||||
@@ -238,40 +238,40 @@ export default defineComponent({
|
||||
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
||||
event.type !== 'wheel'
|
||||
) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// when the target element is a node selection, we still allow zooming
|
||||
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// when there is no scroll handling enabled, we prevent all wheel events
|
||||
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
||||
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// default filter for d3-zoom
|
||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
||||
});
|
||||
};
|
||||
};
|
||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
|
||||
return () => (
|
||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user