update!: graphnode and node typing
* graphnode is a node containing internal Vue Flow data * move util files to util directory * move flow actions type into store file * rename panel type file to zoom * rename types file to flow * Rename Node to NodeWrapper * Rename Edge to EdgeWrapper Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -35,7 +35,7 @@ const onLoad = (flowInstance: FlowInstance) => {
|
||||
}
|
||||
|
||||
const updatePos = () => {
|
||||
elements.value = elements.value.map((el: FlowElement) => {
|
||||
elements.value = elements.value.map((el) => {
|
||||
if (isNode(el)) {
|
||||
el.position = {
|
||||
x: Math.random() * 400,
|
||||
@@ -50,7 +50,7 @@ const logToObject = () => console.log(rfInstance.value?.toObject())
|
||||
const resetTransform = () => rfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 })
|
||||
|
||||
const toggleclasss = () => {
|
||||
elements.value = elements.value.map((el: FlowElement) => {
|
||||
elements.value = elements.value.map((el) => {
|
||||
if (isNode(el)) el.class = el.class === 'light' ? 'dark' : 'light'
|
||||
return el
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { getEdgeCenter, getBezierPath, getMarkerEnd, ArrowHeadType, Edge, ElementId, Position, useVueFlow } from '~/index'
|
||||
import { getEdgeCenter, getBezierPath, getMarkerEnd, ArrowHeadType, ElementId, Position, useVueFlow, EdgeProps } from '~/index'
|
||||
|
||||
interface CustomEdgeProps<T = any> extends Edge<T> {
|
||||
interface CustomEdgeProps<T = any> extends EdgeProps<T> {
|
||||
id: ElementId
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
|
||||
@@ -44,8 +44,7 @@ const nodeClassNameFunc = props.nodeClassName instanceof Function ? props.nodeCl
|
||||
|
||||
const shapeRendering: ShapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'
|
||||
|
||||
const nodes = computed(() => store.nodes.filter((node) => !node.isHidden))
|
||||
const bb = computed(() => getRectOfNodes(nodes.value))
|
||||
const bb = computed(() => getRectOfNodes(store.getNodes))
|
||||
const viewBB = computed(() => ({
|
||||
x: -store.transform[0] / store.transform[2],
|
||||
y: -store.transform[1] / store.transform[2],
|
||||
@@ -90,7 +89,7 @@ const d = computed(() => {
|
||||
:viewBox="`${viewBox.x || 0} ${viewBox.y || 0} ${viewBox.width || 0} ${viewBox.height || 0}`"
|
||||
class="vue-flow__minimap"
|
||||
>
|
||||
<template v-for="node of nodes" :key="`mini-map-node-${node.id}`">
|
||||
<template v-for="node of store.getNodes" :key="`mini-map-node-${node.id}`">
|
||||
<slot
|
||||
:x="node.__vf.position.x"
|
||||
:y="node.__vf.position.y"
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { CSSProperties } from 'vue'
|
||||
import { getBezierPath, getSmoothStepPath } from '../Edges/utils'
|
||||
import { useStore } from '../../composables'
|
||||
import { ConnectionLineType, HandleElement, Node, Position } from '../../types'
|
||||
import { ConnectionLineType, HandleElement, GraphNode, Position } from '../../types'
|
||||
|
||||
interface ConnectionLineProps {
|
||||
sourceNode: Node
|
||||
sourceNode: GraphNode
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
}
|
||||
@@ -19,29 +19,29 @@ const store = useStore()
|
||||
|
||||
const sourceHandle =
|
||||
store.connectionHandleId && store.connectionHandleType
|
||||
? props.sourceNode.__vf?.handleBounds[store.connectionHandleType].find(
|
||||
? props.sourceNode.__vf.handleBounds[store.connectionHandleType]?.find(
|
||||
(d: HandleElement) => d.id === store.connectionHandleId,
|
||||
)
|
||||
: store.connectionHandleType && props.sourceNode.__vf?.handleBounds[store.connectionHandleType ?? 'source'][0]
|
||||
: store.connectionHandleType && props.sourceNode.__vf.handleBounds[store.connectionHandleType ?? 'source']?.[0]
|
||||
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : (props.sourceNode.__vf?.width as number) / 2
|
||||
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : props.sourceNode.__vf?.height
|
||||
const sourceX = props.sourceNode.__vf?.position?.x + sourceHandleX
|
||||
const sourceY = props.sourceNode.__vf?.position?.y + sourceHandleY
|
||||
|
||||
const isRightOrLeft = sourceHandle.position === Position.Left || sourceHandle.position === Position.Right
|
||||
const isRightOrLeft = sourceHandle?.position === Position.Left || sourceHandle?.position === Position.Right
|
||||
const targetPosition = isRightOrLeft ? Position.Left : Position.Top
|
||||
|
||||
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
|
||||
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
|
||||
|
||||
const dAttr = computed(() => {
|
||||
let path = `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`
|
||||
let path = `M${sourceX},${sourceY} ${targetX.value},${targetY.value}`
|
||||
switch (props.connectionLineType) {
|
||||
case ConnectionLineType.Bezier:
|
||||
path = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle.position,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX: targetX.value,
|
||||
targetY: targetY.value,
|
||||
targetPosition,
|
||||
@@ -51,7 +51,7 @@ const dAttr = computed(() => {
|
||||
path = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle.position,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX: targetX.value,
|
||||
targetY: targetY.value,
|
||||
targetPosition,
|
||||
@@ -62,7 +62,7 @@ const dAttr = computed(() => {
|
||||
path = getSmoothStepPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle.position,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX: targetX.value,
|
||||
targetY: targetY.value,
|
||||
targetPosition,
|
||||
@@ -78,7 +78,7 @@ const dAttr = computed(() => {
|
||||
v-bind="{
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourcePosition: sourceHandle.position,
|
||||
sourcePosition: sourceHandle?.position,
|
||||
targetX,
|
||||
targetY,
|
||||
targetPosition,
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { useHandle, useStore } from '../../composables'
|
||||
import { ConnectionMode, Edge, EdgePositions, Position } from '../../types'
|
||||
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils'
|
||||
import { isEdge } from '../../utils'
|
||||
import { isEdge, getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../utils'
|
||||
import EdgeAnchor from './EdgeAnchor.vue'
|
||||
|
||||
interface EdgeProps {
|
||||
interface EdgeWrapper {
|
||||
edge: Edge
|
||||
markerEndId?: string
|
||||
edgeUpdaterRadius?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<EdgeProps>(), {})
|
||||
const props = withDefaults(defineProps<EdgeWrapper>(), {})
|
||||
|
||||
const store = useStore()
|
||||
|
||||
@@ -4,4 +4,4 @@ export { default as SmoothStepEdge } from './SmoothStepEdge.vue'
|
||||
export { default as StraightEdge } from './StraightEdge.vue'
|
||||
export { default as EdgeAnchor } from './EdgeAnchor.vue'
|
||||
export { default as EdgeText } from './EdgeText.vue'
|
||||
export { default as EdgeWrapper } from './Edge.vue'
|
||||
export { default as EdgeWrapper } from './EdgeWrapper.vue'
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { DraggableEventListener, DraggableCore } from '@braks/revue-draggable'
|
||||
import { useStore } from '../../composables'
|
||||
import { Node, SnapGrid } from '../../types'
|
||||
import { GraphNode, SnapGrid } from '../../types'
|
||||
import { NodeId } from '../../context'
|
||||
|
||||
interface NodeProps {
|
||||
node: Node
|
||||
interface NodeWrapperProps {
|
||||
node: GraphNode
|
||||
selectNodesOnDrag?: boolean
|
||||
snapGrid?: SnapGrid
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<NodeProps>(), {
|
||||
const props = withDefaults(defineProps<NodeWrapperProps>(), {
|
||||
selected: false,
|
||||
selectNodesOnDrag: true,
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
export { default as DefaultNode } from './DefaultNode.vue'
|
||||
export { default as InputNode } from './InputNode.vue'
|
||||
export { default as OutputNode } from './OutputNode.vue'
|
||||
export { default as NodeWrapper } from './Node.vue'
|
||||
export { default as NodeWrapper } from './NodeWrapper.vue'
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
|
||||
import { useStore } from '../../composables'
|
||||
import { Node } from '../../types'
|
||||
import { getRectOfNodes, isNode } from '../../utils'
|
||||
import { GraphNode } from '../../types'
|
||||
import { getRectOfNodes, isGraphNode } from '../../utils'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const selectedNodes = store.selectedElements
|
||||
? store.selectedElements.filter(isNode).map((selectedNode) => {
|
||||
const selectedNodes: GraphNode[] = store.selectedElements
|
||||
? store.selectedElements.filter(isGraphNode).map((selectedNode) => {
|
||||
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id)
|
||||
|
||||
return {
|
||||
...matchingNode,
|
||||
position: matchingNode?.__vf?.position,
|
||||
} as Node
|
||||
} as GraphNode
|
||||
})
|
||||
: []
|
||||
|
||||
|
||||
7
src/components/index.ts
Normal file
7
src/components/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './Nodes'
|
||||
export * from './Edges'
|
||||
export * from './ConnectionLine/ConnectionLine.vue'
|
||||
export * from './Handle/Handle.vue'
|
||||
export * from './Loading/LoadingIndicator.vue'
|
||||
export * from './NodesSelection/NodesSelection.vue'
|
||||
export * from './UserSelection/UserSelection.vue'
|
||||
@@ -1,61 +1,44 @@
|
||||
import {
|
||||
Connection,
|
||||
Edge,
|
||||
Elements,
|
||||
EmitFunc,
|
||||
FlowHooks,
|
||||
FlowTransform,
|
||||
Node,
|
||||
OnConnectStartParams,
|
||||
FlowInstance,
|
||||
FlowEvents,
|
||||
FlowStore,
|
||||
} from '~/types'
|
||||
import { EmitFunc, FlowHooks, FlowEvents, FlowStore } from '~/types'
|
||||
|
||||
// flow event hooks
|
||||
export const createHooks = (): FlowHooks => {
|
||||
return {
|
||||
elementClick: createEventHook<{ event: MouseEvent; element: Node | Edge }>(),
|
||||
elementsRemove: createEventHook<Elements>(),
|
||||
nodeDoubleClick: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeClick: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeMouseEnter: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeMouseMove: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeMouseLeave: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeContextMenu: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeDragStart: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeDrag: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
nodeDragStop: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||
connect: createEventHook<Connection>(),
|
||||
connectStart: createEventHook<{
|
||||
event: MouseEvent
|
||||
params: OnConnectStartParams
|
||||
}>(),
|
||||
connectStop: createEventHook<MouseEvent>(),
|
||||
connectEnd: createEventHook<MouseEvent>(),
|
||||
load: createEventHook<FlowInstance>(),
|
||||
move: createEventHook<FlowTransform | undefined>(),
|
||||
moveStart: createEventHook<FlowTransform | undefined>(),
|
||||
moveEnd: createEventHook<FlowTransform | undefined>(),
|
||||
selectionChange: createEventHook<Elements | null>(),
|
||||
selectionDragStart: createEventHook<{ event: MouseEvent; nodes: Node[] }>(),
|
||||
selectionDrag: createEventHook<{ event: MouseEvent; nodes: Node[] }>(),
|
||||
selectionDragStop: createEventHook<{ event: MouseEvent; nodes: Node[] }>(),
|
||||
selectionContextMenu: createEventHook<{ event: MouseEvent; nodes: Node[] }>(),
|
||||
paneScroll: createEventHook<WheelEvent | undefined>(),
|
||||
paneClick: createEventHook<MouseEvent>(),
|
||||
paneContextMenu: createEventHook<MouseEvent>(),
|
||||
edgeUpdate: createEventHook<{ edge: Edge; connection: Connection }>(),
|
||||
edgeContextMenu: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeMouseEnter: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeMouseMove: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeMouseLeave: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeDoubleClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeUpdateStart: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeUpdateEnd: createEventHook<MouseEvent>(),
|
||||
}
|
||||
}
|
||||
export const createHooks = (): FlowHooks => ({
|
||||
elementClick: createEventHook(),
|
||||
elementsRemove: createEventHook(),
|
||||
nodeDoubleClick: createEventHook(),
|
||||
nodeClick: createEventHook(),
|
||||
nodeMouseEnter: createEventHook(),
|
||||
nodeMouseMove: createEventHook(),
|
||||
nodeMouseLeave: createEventHook(),
|
||||
nodeContextMenu: createEventHook(),
|
||||
nodeDragStart: createEventHook(),
|
||||
nodeDrag: createEventHook(),
|
||||
nodeDragStop: createEventHook(),
|
||||
connect: createEventHook(),
|
||||
connectStart: createEventHook(),
|
||||
connectStop: createEventHook(),
|
||||
connectEnd: createEventHook(),
|
||||
load: createEventHook(),
|
||||
move: createEventHook(),
|
||||
moveStart: createEventHook(),
|
||||
moveEnd: createEventHook(),
|
||||
selectionChange: createEventHook(),
|
||||
selectionDragStart: createEventHook(),
|
||||
selectionDrag: createEventHook(),
|
||||
selectionDragStop: createEventHook(),
|
||||
selectionContextMenu: createEventHook(),
|
||||
paneScroll: createEventHook(),
|
||||
paneClick: createEventHook(),
|
||||
paneContextMenu: createEventHook(),
|
||||
edgeUpdate: createEventHook(),
|
||||
edgeContextMenu: createEventHook(),
|
||||
edgeMouseEnter: createEventHook(),
|
||||
edgeMouseMove: createEventHook(),
|
||||
edgeMouseLeave: createEventHook(),
|
||||
edgeDoubleClick: createEventHook(),
|
||||
edgeClick: createEventHook(),
|
||||
edgeUpdateStart: createEventHook(),
|
||||
edgeUpdateEnd: createEventHook(),
|
||||
})
|
||||
|
||||
const bind = (emit: EmitFunc, hooks: FlowHooks) => {
|
||||
for (const [key, value] of Object.entries(hooks)) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FlowExportObject, FlowOptions } from '~/types'
|
||||
import { useStateStore, initialState } from '~/store'
|
||||
import { FlowExportObject, FlowOptions, FlowState } from '~/types'
|
||||
import { useStateStore } from '~/store'
|
||||
import { Store } from '~/context'
|
||||
import { onLoadToObject } from '~/utils'
|
||||
import { onLoadToObject, initialState } from '~/utils'
|
||||
|
||||
let id = 0
|
||||
export default (options?: Partial<FlowOptions>, key?: string) => {
|
||||
@@ -15,10 +15,10 @@ export default (options?: Partial<FlowOptions>, key?: string) => {
|
||||
const storageKey = key ?? `vue-flow-${id++}`
|
||||
const preloadedState = {
|
||||
...initial,
|
||||
...options,
|
||||
...(options as FlowState),
|
||||
}
|
||||
if (withStorage) {
|
||||
storedState = useStorage(storageKey, {} as FlowExportObject)
|
||||
storedState = useStorage<FlowExportObject>(storageKey, { elements: [], position: [0, 0], zoom: 0 })
|
||||
if (storedState.value) {
|
||||
preloadedState.elements = storedState.value.elements ?? options?.elements ?? []
|
||||
if (storedState.value.position && storedState.value.zoom)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @ts-nocheck
|
||||
import { zoomIdentity } from 'd3-zoom'
|
||||
import useStore from './useStore'
|
||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '~/utils'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { CSSProperties } from 'vue'
|
||||
import { ConnectionLineType } from '../../types'
|
||||
import { useStore } from '../../composables'
|
||||
import Edge from '../../components/Edges/Edge.vue'
|
||||
import Edge from '../../components/Edges/EdgeWrapper.vue'
|
||||
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue'
|
||||
import MarkerDefinitions from './MarkerDefinitions.vue'
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { useStore } from '../../composables'
|
||||
import Node from '../../components/Nodes/Node.vue'
|
||||
import Node from '../../components/Nodes/NodeWrapper.vue'
|
||||
import { SnapGrid } from '../../types'
|
||||
|
||||
interface NodeRendererProps {
|
||||
selectNodesOnDrag?: boolean
|
||||
snapToGrid?: boolean
|
||||
snapGrid?: SnapGrid
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<NodeRendererProps>(), {
|
||||
@@ -13,7 +16,7 @@ const props = withDefaults(defineProps<NodeRendererProps>(), {
|
||||
const store = useStore()
|
||||
|
||||
const transform = computed(() => `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`)
|
||||
const snapGrid = computed(() => (store.snapToGrid ? store.snapGrid : undefined))
|
||||
const snapGrid = computed(() => (props.snapToGrid || store.snapToGrid ? props.snapGrid ?? store.snapGrid : undefined))
|
||||
</script>
|
||||
<template>
|
||||
<div class="vue-flow__nodes" :style="{ transform }">
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ElementId, FlowElement, KeyCode } from '../../types'
|
||||
import { useStore, useKeyPress } from '../../composables'
|
||||
import NodesSelection from '../../components/NodesSelection/NodesSelection.vue'
|
||||
import UserSelection from '../../components/UserSelection/UserSelection.vue'
|
||||
import { getConnectedEdges, isNode } from '../../utils'
|
||||
import { getConnectedEdges, isGraphNode } from '../../utils'
|
||||
|
||||
interface SelectionPaneProps {
|
||||
selectionKeyCode?: KeyCode
|
||||
@@ -33,7 +33,7 @@ const selectionKeyPresed = ref(false)
|
||||
onMounted(() => {
|
||||
useKeyPress(props.deleteKeyCode, (keyPressed) => {
|
||||
if (keyPressed && store.selectedElements) {
|
||||
const selectedNodes = store.selectedElements.filter(isNode)
|
||||
const selectedNodes = store.selectedElements.filter(isGraphNode)
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, store.edges)
|
||||
const elementsToRemove = [...store.selectedElements, ...connectedEdges].reduce(
|
||||
(res, item) => res.set(item.id, item),
|
||||
|
||||
@@ -181,7 +181,6 @@ const transitionName = computed(() => {
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<ZoomPane
|
||||
v-show="store.isReady"
|
||||
key="zoom-pane"
|
||||
:selection-key-code="store.selectionKeyCode"
|
||||
:zoom-activation-key-code="store.zoomActivationKeyCode"
|
||||
|
||||
@@ -20,11 +20,10 @@ export {
|
||||
isInputDOMNode,
|
||||
graphPosToZoomedPos,
|
||||
} from './utils/graph'
|
||||
export { defaultEdgeTypes, defaultNodeTypes } from './utils/store'
|
||||
export { default as useZoomPanHelper } from './composables/useZoomPanHelper'
|
||||
export { default as useVueFlow } from './composables/useVueFlow'
|
||||
export { default as useHandle } from './composables/useHandle'
|
||||
export { default as useKeyPress } from './composables/useKeyPress'
|
||||
export { defaultEdgeTypes, defaultNodeTypes } from './store'
|
||||
|
||||
export * from './additional-components'
|
||||
export * from './types'
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { default as useStateStore } from './stateStore'
|
||||
export { defaultEdgeTypes, defaultNodeTypes, initialState } from './utils'
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'
|
||||
import microDiff from 'microdiff'
|
||||
import { parseElements, defaultNodeTypes, defaultEdgeTypes, deepUnref, NextElements } from './utils'
|
||||
import { FlowState, Node, FlowActions, Elements, FlowGetters, Edge } from '~/types'
|
||||
import { clampPosition, getDimensions, getConnectedEdges, getNodesInside, getRectOfNodes, isNode } from '~/utils'
|
||||
import { getHandleBounds } from '~/components/Nodes/utils'
|
||||
import { FlowState, Node, FlowActions, Elements, FlowGetters, Edge, GraphNode, NextElements } from '~/types'
|
||||
import {
|
||||
clampPosition,
|
||||
getDimensions,
|
||||
getConnectedEdges,
|
||||
getNodesInside,
|
||||
getRectOfNodes,
|
||||
isNode,
|
||||
parseElements,
|
||||
defaultNodeTypes,
|
||||
defaultEdgeTypes,
|
||||
deepUnref,
|
||||
getHandleBounds,
|
||||
isGraphNode,
|
||||
} from '~/utils'
|
||||
import parseElementsWorker from '~/workers/parseElements'
|
||||
|
||||
const pinia = createPinia()
|
||||
@@ -81,7 +92,7 @@ export default function flowStore(
|
||||
} else next = await parseElements(elements, this.nodes, this.edges, this.nodeExtent)
|
||||
}
|
||||
if (next) {
|
||||
this.elements = elements ?? []
|
||||
this.elements = [...next.nextNodes, ...next.nextEdges]
|
||||
this.nodes = next?.nextNodes ?? []
|
||||
this.edges = next?.nextEdges ?? []
|
||||
}
|
||||
@@ -102,7 +113,6 @@ export default function flowStore(
|
||||
this.nodes.splice(i, 1, {
|
||||
...node,
|
||||
__vf: {
|
||||
position: { x: 0, y: 0 },
|
||||
...node.__vf,
|
||||
...dimensions,
|
||||
handleBounds,
|
||||
@@ -125,21 +135,16 @@ export default function flowStore(
|
||||
this.nodes.splice(i, 1, {
|
||||
...node,
|
||||
__vf: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
...node.__vf,
|
||||
position: pos,
|
||||
},
|
||||
})
|
||||
},
|
||||
updateNodePosDiff({ id, diff, isDragging }) {
|
||||
const update = (node: Node, i: number) => {
|
||||
const updatedNode: Node = {
|
||||
const update = (node: GraphNode, i: number) => {
|
||||
const updatedNode: GraphNode = {
|
||||
...node,
|
||||
__vf: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
position: { x: 0, y: 0 },
|
||||
...node.__vf,
|
||||
isDragging,
|
||||
},
|
||||
@@ -161,8 +166,8 @@ export default function flowStore(
|
||||
if (!id) {
|
||||
const selectedNodes = this.nodes.filter((x) => this.selectedElements?.find((sNode) => sNode?.id === x.id))
|
||||
selectedNodes.forEach((node) => {
|
||||
const i = this.nodes.map((x) => x.id).indexOf((node as Node).id)
|
||||
update(node as Node, i)
|
||||
const i = this.nodes.map((x) => x.id).indexOf(node.id)
|
||||
update(node, i)
|
||||
})
|
||||
} else {
|
||||
const i = this.nodes.map((x) => x.id).indexOf(id)
|
||||
@@ -202,7 +207,7 @@ export default function flowStore(
|
||||
this.selectedElements = nextSelectedElements
|
||||
},
|
||||
unsetUserSelection() {
|
||||
const selectedNodes = this.selectedElements?.filter((node) => node && isNode(node) && node.__vf) as Node[]
|
||||
const selectedNodes = this.selectedElements?.filter((node) => node && isGraphNode(node) && node.__vf) as GraphNode[]
|
||||
|
||||
this.selectionActive = false
|
||||
this.userSelectionRect.draw = false
|
||||
@@ -243,8 +248,6 @@ export default function flowStore(
|
||||
return {
|
||||
...node,
|
||||
__vf: {
|
||||
height: 0,
|
||||
width: 0,
|
||||
...node.__vf,
|
||||
position: node.__vf?.position ? clampPosition(node.__vf.position, nodeExtent) : { x: 0, y: 0 },
|
||||
},
|
||||
@@ -272,7 +275,7 @@ export default function flowStore(
|
||||
},
|
||||
async addElements(elements: Elements) {
|
||||
const { nextNodes, nextEdges } = await parseElements(elements, this.nodes, this.edges, this.nodeExtent)
|
||||
this.elements = [...this.elements, ...elements]
|
||||
this.elements = [...this.elements, ...nextNodes, ...nextEdges]
|
||||
this.nodes = [...this.nodes, ...nextNodes]
|
||||
this.edges = [...this.edges, ...nextEdges]
|
||||
},
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Dimensions, Elements, XYPosition } from './types'
|
||||
import { NodeDiffUpdate, NodeDimensionUpdate, NodeExtent, NodePosUpdate, TranslateExtent } from './node'
|
||||
import { InitD3ZoomPayload } from './panel'
|
||||
import { SetConnectionId } from './connection'
|
||||
|
||||
export interface FlowActions {
|
||||
setElements: (elements: Elements) => void
|
||||
updateNodeDimensions: (update: NodeDimensionUpdate) => void
|
||||
updateNodePos: (payload: NodePosUpdate) => void
|
||||
updateNodePosDiff: (payload: NodeDiffUpdate) => void
|
||||
setUserSelection: (mousePos: XYPosition) => void
|
||||
updateUserSelection: (mousePos: XYPosition) => void
|
||||
unsetUserSelection: () => void
|
||||
addSelectedElements: (elements: Elements) => void
|
||||
initD3Zoom: (payload: InitD3ZoomPayload) => void
|
||||
setMinZoom: (zoom: number) => void
|
||||
setMaxZoom: (zoom: number) => void
|
||||
setTranslateExtent: (translateExtent: TranslateExtent) => void
|
||||
setNodeExtent: (nodeExtent: NodeExtent) => void
|
||||
resetSelectedElements: () => void
|
||||
unsetNodesSelection: () => void
|
||||
updateSize: (size: Dimensions) => void
|
||||
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||
setInteractive: (isInteractive: boolean) => void
|
||||
addElements: (elements: Elements) => void
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElementId, Position } from './types'
|
||||
import { ElementId, Position } from './flow'
|
||||
import { HandleType } from './handle'
|
||||
|
||||
export enum ConnectionLineType {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, CSSProperties, DefineComponent } from 'vue'
|
||||
import { ArrowHeadType, ElementId, Position } from './types'
|
||||
import { ArrowHeadType, ElementId, Position } from './flow'
|
||||
|
||||
export interface Edge<T = any> {
|
||||
id: ElementId
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { CSSProperties } from 'vue'
|
||||
import { Edge, EdgeTypes } from './edge'
|
||||
import { Node, NodeExtent, NodeTypes, TranslateExtent } from './node'
|
||||
import { NodeExtent, GraphNode, NodeTypes, TranslateExtent, Node } from './node'
|
||||
import { ConnectionLineType, ConnectionMode } from './connection'
|
||||
import { KeyCode, PanOnScrollMode } from './panel'
|
||||
import { KeyCode, PanOnScrollMode } from './zoom'
|
||||
import { FlowStore } from './store'
|
||||
|
||||
export type ElementId = string
|
||||
|
||||
export type FlowElement<T = any> = Node<T> | Edge<T>
|
||||
|
||||
export type Elements<T = any> = FlowElement<T>[]
|
||||
export type FlowElement<T = any> = GraphNode<T> | Edge<T>
|
||||
export type FlowElements<T = any> = FlowElement<T>[]
|
||||
export type Elements<T = any> = (Node<T> | Edge<T>)[]
|
||||
export type NextElements = {
|
||||
nextNodes: GraphNode[]
|
||||
nextEdges: Edge[]
|
||||
}
|
||||
|
||||
export type Transform = [number, number, number]
|
||||
|
||||
@@ -63,7 +66,7 @@ export type FitViewParams = {
|
||||
}
|
||||
|
||||
export type FlowExportObject<T = any> = {
|
||||
elements: Elements<T>
|
||||
elements: FlowElements<T>
|
||||
position: [number, number]
|
||||
zoom: number
|
||||
}
|
||||
@@ -98,14 +101,13 @@ export type FlowInstance<T = any> = {
|
||||
zoomTo: (zoomLevel: number) => void
|
||||
fitView: FitViewFunc
|
||||
project: ProjectFunc
|
||||
getElements: () => Elements<T>
|
||||
getElements: () => FlowElements<T>
|
||||
setTransform: (transform: FlowTransform) => void
|
||||
toObject: ToObjectFunc<T>
|
||||
}
|
||||
|
||||
export interface FlowOptions {
|
||||
id?: string
|
||||
store?: FlowStore
|
||||
elements?: Elements
|
||||
nodeTypes?: NodeTypes
|
||||
edgeTypes?: EdgeTypes
|
||||
@@ -142,4 +144,5 @@ export interface FlowOptions {
|
||||
storageKey?: string
|
||||
loading?: Loading
|
||||
worker?: boolean
|
||||
store?: FlowStore
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Dimensions, ElementId, Position, XYPosition } from './types'
|
||||
import { Connection } from '~/types/connection'
|
||||
import { Dimensions, ElementId, Position, XYPosition } from './flow'
|
||||
import { Connection } from './connection'
|
||||
|
||||
export type HandleType = 'source' | 'target'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventHook } from '@vueuse/core'
|
||||
import { Elements, FlowTransform, FlowInstance } from './types'
|
||||
import { FlowTransform, FlowInstance, FlowElements } from './flow'
|
||||
import { Edge } from './edge'
|
||||
import { Node } from './node'
|
||||
import { Connection, OnConnectStartParams } from './connection'
|
||||
@@ -8,7 +8,7 @@ export type FlowHook<T = any> = EventHook<T>
|
||||
|
||||
export interface FlowEvents {
|
||||
elementClick: { event: MouseEvent; element: Node | Edge }
|
||||
elementsRemove: Elements
|
||||
elementsRemove: FlowElements
|
||||
nodeDoubleClick: { event: MouseEvent; node: Node }
|
||||
nodeClick: { event: MouseEvent; node: Node }
|
||||
nodeMouseEnter: { event: MouseEvent; node: Node }
|
||||
@@ -29,7 +29,7 @@ export interface FlowEvents {
|
||||
move: FlowTransform | undefined
|
||||
moveStart: FlowTransform | undefined
|
||||
moveEnd: FlowTransform | undefined
|
||||
selectionChange: Elements | null
|
||||
selectionChange: FlowElements | null
|
||||
selectionDragStart: { event: MouseEvent; nodes: Node[] }
|
||||
selectionDrag: { event: MouseEvent; nodes: Node[] }
|
||||
selectionDragStop: { event: MouseEvent; nodes: Node[] }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
export * from './types'
|
||||
export * from './actions'
|
||||
export * from './flow'
|
||||
export * from './connection'
|
||||
export * from './edge'
|
||||
export * from './handle'
|
||||
export * from './node'
|
||||
export * from './panel'
|
||||
export * from './zoom'
|
||||
export * from './store'
|
||||
export * from './hooks'
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { Component, CSSProperties, DefineComponent } from 'vue'
|
||||
import { XYPosition, ElementId, Position } from './types'
|
||||
import { HandleElement } from '~/types/handle'
|
||||
import { XYPosition, ElementId, Position } from './flow'
|
||||
import { HandleElement } from './handle'
|
||||
|
||||
interface VFInternals {
|
||||
position: XYPosition
|
||||
isDragging?: boolean
|
||||
width: number
|
||||
height: number
|
||||
handleBounds: {
|
||||
source: HandleElement[] | null
|
||||
target: HandleElement[] | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface Node<T = any> {
|
||||
id: ElementId
|
||||
position: XYPosition
|
||||
type?: string
|
||||
__vf?: {
|
||||
position: XYPosition
|
||||
isDragging?: boolean
|
||||
width: number
|
||||
height: number
|
||||
handleBounds?: {
|
||||
source?: HandleElement[] | null
|
||||
target?: HandleElement[] | null
|
||||
}
|
||||
}
|
||||
class?: string
|
||||
style?: CSSProperties
|
||||
data?: T
|
||||
@@ -27,6 +28,10 @@ export interface Node<T = any> {
|
||||
connectable?: boolean
|
||||
}
|
||||
|
||||
export interface GraphNode<T = any> extends Node<T> {
|
||||
__vf: VFInternals
|
||||
}
|
||||
|
||||
export type NodePosUpdate = {
|
||||
id: ElementId
|
||||
pos: XYPosition
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Dimensions,
|
||||
ElementId,
|
||||
Elements,
|
||||
FlowElements,
|
||||
FlowInstance,
|
||||
FlowOptions,
|
||||
Rect,
|
||||
@@ -10,20 +11,26 @@ import {
|
||||
SnapGrid,
|
||||
Transform,
|
||||
XYPosition,
|
||||
} from './types'
|
||||
} from './flow'
|
||||
import { HandleType } from './handle'
|
||||
import { ConnectionMode, OnConnectEndFunc, OnConnectFunc, OnConnectStartFunc, OnConnectStopFunc } from './connection'
|
||||
import {
|
||||
ConnectionMode,
|
||||
OnConnectEndFunc,
|
||||
OnConnectFunc,
|
||||
OnConnectStartFunc,
|
||||
OnConnectStopFunc,
|
||||
SetConnectionId,
|
||||
} from './connection'
|
||||
import { Edge, EdgeComponent } from './edge'
|
||||
import { Node, NodeComponent, NodeExtent, TranslateExtent } from './node'
|
||||
import { FlowActions } from './actions'
|
||||
import { D3Selection, D3Zoom, D3ZoomHandler } from './panel'
|
||||
import { NodeComponent, NodeDiffUpdate, NodeDimensionUpdate, NodeExtent, GraphNode, NodePosUpdate, TranslateExtent } from './node'
|
||||
import { D3Selection, D3Zoom, D3ZoomHandler, InitD3ZoomPayload } from './zoom'
|
||||
import { FlowHooks } from './hooks'
|
||||
|
||||
export interface FlowState extends FlowOptions {
|
||||
elements: Elements
|
||||
nodes: Node[]
|
||||
elements: FlowElements
|
||||
nodes: GraphNode[]
|
||||
edges: Edge[]
|
||||
selectedElements?: Elements
|
||||
selectedElements?: FlowElements
|
||||
selectedNodesBbox: Rect
|
||||
|
||||
d3Zoom?: D3Zoom
|
||||
@@ -66,10 +73,32 @@ export interface FlowState extends FlowOptions {
|
||||
vueFlowVersion: string
|
||||
}
|
||||
|
||||
export interface FlowActions {
|
||||
setElements: (elements: Elements) => Promise<void>
|
||||
updateNodeDimensions: (update: NodeDimensionUpdate) => void
|
||||
updateNodePos: (payload: NodePosUpdate) => void
|
||||
updateNodePosDiff: (payload: NodeDiffUpdate) => void
|
||||
setUserSelection: (mousePos: XYPosition) => void
|
||||
updateUserSelection: (mousePos: XYPosition) => void
|
||||
unsetUserSelection: () => void
|
||||
addSelectedElements: (elements: FlowElements) => void
|
||||
initD3Zoom: (payload: InitD3ZoomPayload) => void
|
||||
setMinZoom: (zoom: number) => void
|
||||
setMaxZoom: (zoom: number) => void
|
||||
setTranslateExtent: (translateExtent: TranslateExtent) => void
|
||||
setNodeExtent: (nodeExtent: NodeExtent) => void
|
||||
resetSelectedElements: () => void
|
||||
unsetNodesSelection: () => void
|
||||
updateSize: (size: Dimensions) => void
|
||||
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||
setInteractive: (isInteractive: boolean) => void
|
||||
addElements: (elements: Elements) => Promise<void>
|
||||
}
|
||||
|
||||
export interface FlowGetters {
|
||||
getEdgeTypes: () => Record<string, EdgeComponent>
|
||||
getNodeTypes: () => Record<string, NodeComponent>
|
||||
getNodes: () => Node[]
|
||||
getNodes: () => GraphNode[]
|
||||
getEdges: () => Edge[]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Selection, ZoomBehavior } from 'd3'
|
||||
import { FitViewFunc, FlowTransform, Rect, XYPosition } from './types'
|
||||
import { FitViewFunc, FlowTransform, Rect, XYPosition } from './flow'
|
||||
|
||||
export type D3Zoom = ZoomBehavior<HTMLDivElement, unknown>
|
||||
export type D3Selection = Selection<HTMLDivElement, unknown, any, any>
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Edge, EdgePositions, ElementId, HandleElement, Node, Position, Transform, XYPosition } from '../../types'
|
||||
import { rectToBox } from '~/utils'
|
||||
import { rectToBox } from './graph'
|
||||
import { Edge, EdgePositions, ElementId, GraphNode, HandleElement, Position, Transform, XYPosition } from '~/types'
|
||||
|
||||
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
||||
const x = (handle?.x || 0) + node.__vf?.position?.x
|
||||
const y = (handle?.y || 0) + node.__vf?.position?.y
|
||||
const width = handle?.width || node.__vf?.width
|
||||
const height = handle?.height || node.__vf?.height
|
||||
export function getHandlePosition(position: Position, node: GraphNode, handle: any | null = null): XYPosition {
|
||||
const x = (handle?.x || 0) + node.__vf.position.x
|
||||
const y = (handle?.y || 0) + node.__vf.position.y
|
||||
const width = handle?.width || node.__vf.width
|
||||
const height = handle?.height || node.__vf.height
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
@@ -47,10 +47,10 @@ export function getHandle(bounds: HandleElement[], handleId: ElementId | null):
|
||||
}
|
||||
|
||||
export const getEdgePositions = (
|
||||
sourceNode: Node,
|
||||
sourceNode: GraphNode,
|
||||
sourceHandle: HandleElement | unknown,
|
||||
sourcePosition: Position,
|
||||
targetNode: Node,
|
||||
targetNode: GraphNode,
|
||||
targetHandle: HandleElement | unknown,
|
||||
targetPosition: Position,
|
||||
): EdgePositions => {
|
||||
@@ -104,11 +104,11 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
||||
}
|
||||
|
||||
type SourceTargetNode = {
|
||||
sourceNode: Node
|
||||
targetNode: Node
|
||||
sourceNode: GraphNode
|
||||
targetNode: GraphNode
|
||||
}
|
||||
|
||||
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
|
||||
export const getSourceTargetNodes = (edge: Edge, nodes: GraphNode[]): SourceTargetNode => {
|
||||
return nodes.reduce(
|
||||
(res, node) => {
|
||||
if (node.id === edge.source) {
|
||||
@@ -12,12 +12,20 @@ import {
|
||||
NodeExtent,
|
||||
Dimensions,
|
||||
FlowStore,
|
||||
GraphNode,
|
||||
FlowElements,
|
||||
FlowElement,
|
||||
} from '~/types'
|
||||
import { useWindow } from '~/composables'
|
||||
|
||||
const isHTMLElement = (el: EventTarget): el is HTMLElement => ('nodeName' || 'hasAttribute') in el
|
||||
|
||||
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
|
||||
const target = e.target as HTMLElement
|
||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
||||
const target = e.target
|
||||
if (target && isHTMLElement(target)) {
|
||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const getDimensions = (node: HTMLElement): Dimensions => ({
|
||||
@@ -40,40 +48,30 @@ export const getHostForElement = (element: HTMLElement): Document => {
|
||||
else return window.document
|
||||
}
|
||||
|
||||
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||
export const isEdge = (element: Node | FlowElement | Connection): element is Edge =>
|
||||
'id' in element && 'source' in element && 'target' in element
|
||||
|
||||
export const isNode = (element: Node | Connection | Edge): element is Node =>
|
||||
export const isNode = (element: Node | FlowElement | Connection): element is Node =>
|
||||
'id' in element && !('source' in element) && !('target' in element)
|
||||
|
||||
export const getOutgoers = (node: Node, elements: Elements): Node[] => {
|
||||
if (!isNode(node)) {
|
||||
return []
|
||||
}
|
||||
export const isGraphNode = (element: Node | FlowElement | Connection): element is GraphNode =>
|
||||
isNode(element) && '__vf' in element
|
||||
|
||||
const outgoerIds = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => (e as Edge).target)
|
||||
return elements.filter((e) => outgoerIds.includes(e.id)) as Node[]
|
||||
const getConnectedElements = (node: GraphNode, elements: Elements, dir: 'source' | 'target') => {
|
||||
if (!isNode(node)) return []
|
||||
const ids = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => isEdge(e) && e[dir])
|
||||
return elements.filter((e) => ids.includes(e.id))
|
||||
}
|
||||
export const getOutgoers = (node: GraphNode, elements: Elements) => getConnectedElements(node, elements, 'target')
|
||||
|
||||
export const getIncomers = (node: Node, elements: Elements): Node[] => {
|
||||
if (!isNode(node)) {
|
||||
return []
|
||||
}
|
||||
export const getIncomers = (node: GraphNode, elements: Elements) => getConnectedElements(node, elements, 'source')
|
||||
|
||||
const incomersIds = elements.filter((e) => isEdge(e) && e.target === node.id).map((e) => (e as Edge).source)
|
||||
return elements.filter((e) => incomersIds.includes(e.id)) as Node[]
|
||||
}
|
||||
|
||||
export const removeElements = (elementsToRemove: Elements, elements: Elements): Elements => {
|
||||
export const removeElements = (elementsToRemove: Elements, elements: Elements) => {
|
||||
const nodeIdsToRemove = elementsToRemove.map((n) => n.id)
|
||||
|
||||
return elements.filter((element) => {
|
||||
const edgeElement = element as Edge
|
||||
return !(
|
||||
nodeIdsToRemove.includes(element.id) ||
|
||||
nodeIdsToRemove.includes(edgeElement.target) ||
|
||||
nodeIdsToRemove.includes(edgeElement.source)
|
||||
)
|
||||
const { target, source } = isEdge(element) ? element : { target: '', source: '' }
|
||||
return !(nodeIdsToRemove.includes(element.id) || nodeIdsToRemove.includes(target) || nodeIdsToRemove.includes(source))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -91,13 +89,13 @@ const connectionExists = (edge: Edge, elements: Elements) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
|
||||
export const addEdge = (edgeParams: Edge | Connection, elements: Elements) => {
|
||||
if (!edgeParams.source || !edgeParams.target) {
|
||||
console.log("Can't create edge. An edge needs a source and a target.")
|
||||
return elements
|
||||
}
|
||||
|
||||
let edge: Edge
|
||||
let edge
|
||||
if (isEdge(edgeParams)) {
|
||||
edge = { ...edgeParams }
|
||||
} else {
|
||||
@@ -114,13 +112,13 @@ export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elem
|
||||
return elements.concat(edge)
|
||||
}
|
||||
|
||||
export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements): Elements => {
|
||||
export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements) => {
|
||||
if (!newConnection.source || !newConnection.target) {
|
||||
console.warn("Can't create new edge. An edge needs a source and a target.")
|
||||
return elements
|
||||
}
|
||||
|
||||
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id) as Edge
|
||||
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id)
|
||||
|
||||
if (!foundEdge) {
|
||||
console.warn(`The old edge with id=${oldEdge.id} does not exist.`)
|
||||
@@ -128,14 +126,14 @@ export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: E
|
||||
}
|
||||
|
||||
// Remove old edge and create the new edge with parameters of old edge.
|
||||
const edge = {
|
||||
const edge: Edge = {
|
||||
...oldEdge,
|
||||
id: getEdgeId(newConnection),
|
||||
source: newConnection.source,
|
||||
target: newConnection.target,
|
||||
sourceHandle: newConnection.sourceHandle,
|
||||
targetHandle: newConnection.targetHandle,
|
||||
} as Edge
|
||||
}
|
||||
|
||||
return elements.filter((e) => e.id !== oldEdge.id).concat(edge)
|
||||
}
|
||||
@@ -145,7 +143,7 @@ export const pointToRendererPoint = (
|
||||
[tx, ty, tScale]: Transform,
|
||||
snapToGrid: boolean,
|
||||
[snapX, snapY]: [number, number],
|
||||
): XYPosition => {
|
||||
) => {
|
||||
const position: XYPosition = {
|
||||
x: (x - tx) / tScale,
|
||||
y: (y - ty) / tScale,
|
||||
@@ -161,12 +159,10 @@ export const pointToRendererPoint = (
|
||||
return position
|
||||
}
|
||||
|
||||
export const onLoadProject =
|
||||
(currentStore: FlowStore) =>
|
||||
(position: XYPosition): XYPosition =>
|
||||
pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
||||
export const onLoadProject = (currentStore: FlowStore) => (position: XYPosition) =>
|
||||
pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
||||
|
||||
export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => ({
|
||||
export const parseNode = (node: Node, nodeExtent: NodeExtent): GraphNode => ({
|
||||
...node,
|
||||
id: node.id.toString(),
|
||||
type: node.type || 'default',
|
||||
@@ -174,7 +170,10 @@ export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => ({
|
||||
position: clampPosition(node.position, nodeExtent),
|
||||
width: 0,
|
||||
height: 0,
|
||||
handleBounds: {},
|
||||
handleBounds: {
|
||||
source: null,
|
||||
target: null,
|
||||
},
|
||||
isDragging: false,
|
||||
},
|
||||
})
|
||||
@@ -210,10 +209,9 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
|
||||
height: y2 - y,
|
||||
})
|
||||
|
||||
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect =>
|
||||
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)))
|
||||
export const getBoundsofRects = (rect1: Rect, rect2: Rect) => boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)))
|
||||
|
||||
export const getRectOfNodes = (nodes: Node[]): Rect => {
|
||||
export const getRectOfNodes = (nodes: GraphNode[]) => {
|
||||
const box = nodes.reduce(
|
||||
(currBox, { __vf: { position = { x: 0, y: 0 }, width = 0, height = 0 } = {} }) =>
|
||||
getBoundsOfBoxes(
|
||||
@@ -235,7 +233,7 @@ export const graphPosToZoomedPos = ({ x, y }: XYPosition, [tx, ty, tScale]: Tran
|
||||
y: y * tScale + ty,
|
||||
})
|
||||
|
||||
export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false): Node[] => {
|
||||
export const getNodesInside = (nodes: GraphNode[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false) => {
|
||||
const rBox = rectToBox({
|
||||
x: (rect.x - tx) / tScale,
|
||||
y: (rect.y - ty) / tScale,
|
||||
@@ -266,13 +264,12 @@ export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Tran
|
||||
})
|
||||
}
|
||||
|
||||
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
|
||||
export const getConnectedEdges = (nodes: GraphNode[], edges: Edge[]) => {
|
||||
const nodeIds = nodes.map((node) => node.id)
|
||||
|
||||
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
|
||||
}
|
||||
|
||||
export const onLoadGetElements = (currentStore: FlowStore) => (): Elements => currentStore.elements
|
||||
export const onLoadGetElements = (currentStore: FlowStore) => (): FlowElements => currentStore.elements
|
||||
|
||||
export const onLoadToObject = (currentStore: FlowStore) => (): FlowExportObject => {
|
||||
// we have to stringify/parse so objects containing refs (like nodes and edges) can potentially be saved in a storage
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
export * from './edge'
|
||||
export * from './graph'
|
||||
export * from './node'
|
||||
export * from './store'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HandleElement, Position } from '~/types'
|
||||
import { getDimensions } from '~/utils'
|
||||
import { getDimensions } from '~/utils/graph'
|
||||
|
||||
export const getHandleBoundsByHandleType = (
|
||||
selector: string,
|
||||
@@ -19,7 +19,7 @@ export const getHandleBoundsByHandleType = (
|
||||
const bounds = handle.getBoundingClientRect()
|
||||
const dimensions = getDimensions(handle)
|
||||
const handleId = handle.getAttribute('data-handleid')
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as Position
|
||||
|
||||
return {
|
||||
id: handleId,
|
||||
@@ -1,15 +1,20 @@
|
||||
import { Component } from 'vue'
|
||||
import { ConnectionMode, Edge, EdgeProps, Elements, FlowState, Node, NodeExtent, NodeProps, PanOnScrollMode } from '~/types'
|
||||
import { isEdge, isNode, parseEdge, parseNode } from '~/utils'
|
||||
import { DefaultNode, InputNode, OutputNode } from '~/components/Nodes'
|
||||
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components/Edges'
|
||||
import { isEdge, isNode, parseEdge, parseNode } from './graph'
|
||||
import {
|
||||
ConnectionMode,
|
||||
Edge,
|
||||
EdgeProps,
|
||||
Elements,
|
||||
FlowState,
|
||||
NextElements,
|
||||
NodeExtent,
|
||||
GraphNode,
|
||||
NodeProps,
|
||||
PanOnScrollMode,
|
||||
} from '~/types'
|
||||
import { DefaultNode, InputNode, OutputNode, BezierEdge, SmoothStepEdge, StepEdge, StraightEdge } from '~/components'
|
||||
import { createHooks } from '~/composables'
|
||||
|
||||
export type NextElements = {
|
||||
nextNodes: Node[]
|
||||
nextEdges: Edge[]
|
||||
}
|
||||
|
||||
export const defaultNodeTypes: Record<string, Component<NodeProps>> = {
|
||||
input: InputNode,
|
||||
default: DefaultNode,
|
||||
@@ -94,7 +99,7 @@ export const initialState = (): FlowState => ({
|
||||
vueFlowVersion: typeof __VUE_FLOW_VERSION__ !== 'undefined' ? __VUE_FLOW_VERSION__ : '-',
|
||||
})
|
||||
|
||||
export const parseElements = async (elements: Elements, nodes: Node[], edges: Edge[], nodeExtent: NodeExtent) =>
|
||||
export const parseElements = async (elements: Elements, nodes: GraphNode[], edges: Edge[], nodeExtent: NodeExtent) =>
|
||||
new Promise<NextElements>((resolve) => {
|
||||
const nextElements: NextElements = {
|
||||
nextNodes: [],
|
||||
@@ -105,20 +110,20 @@ export const parseElements = async (elements: Elements, nodes: Node[], edges: Ed
|
||||
const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
|
||||
|
||||
if (storeNode) {
|
||||
const updatedNode: Node = {
|
||||
const updatedNode: GraphNode = {
|
||||
...storeNode,
|
||||
...element,
|
||||
}
|
||||
if (!updatedNode.__vf) updatedNode.__vf = {}
|
||||
if (!updatedNode.__vf) updatedNode.__vf = {} as any
|
||||
|
||||
if (storeNode.position.x !== element.position.x || storeNode.position.y !== element.position.y) {
|
||||
updatedNode.__vf.position = element.position
|
||||
updatedNode.__vf!.position = element.position
|
||||
}
|
||||
|
||||
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
|
||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||
updatedNode.__vf.width = undefined
|
||||
updatedNode.__vf!.width = 0
|
||||
}
|
||||
|
||||
nextElements.nextNodes.push(updatedNode)
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Connection, Edge, Elements, Node, NodeExtent, XYPosition } from '~/types'
|
||||
import { Connection, Edge, Elements, Node, NodeExtent, GraphNode, XYPosition, NextElements, FlowElement } from '~/types'
|
||||
|
||||
export default () =>
|
||||
useWebWorkerFn((elements: Elements, nodes: Node[], edges: Edge[], nodeExtent: NodeExtent) => {
|
||||
useWebWorkerFn((elements: Elements, nodes: GraphNode[], edges: Edge[], nodeExtent: NodeExtent): NextElements => {
|
||||
const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
|
||||
|
||||
const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
|
||||
@@ -9,14 +9,14 @@ export default () =>
|
||||
y: clamp(position.y, extent[0][1], extent[1][1]),
|
||||
})
|
||||
|
||||
const parseNode = (node: Node, nodeExtent: NodeExtent): Node =>
|
||||
Object.assign(node, {
|
||||
const parseNode = (node: Node, nodeExtent: NodeExtent): GraphNode =>
|
||||
Object.assign(node as GraphNode, {
|
||||
id: node.id.toString(),
|
||||
type: node.type || 'default',
|
||||
__vf: {
|
||||
position: clampPosition(node.position, nodeExtent),
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
width: 0,
|
||||
height: 0,
|
||||
handleBounds: {},
|
||||
isDragging: false,
|
||||
},
|
||||
@@ -32,13 +32,13 @@ export default () =>
|
||||
type: edge.type || 'default',
|
||||
})
|
||||
|
||||
const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||
const isEdge = (element: Node | FlowElement | Connection): element is Edge =>
|
||||
'id' in element && 'source' in element && 'target' in element
|
||||
|
||||
const isNode = (element: Node | Connection | Edge): element is Node =>
|
||||
const isNode = (element: Node | FlowElement | Connection): element is Node =>
|
||||
'id' in element && !('source' in element) && !('target' in element)
|
||||
|
||||
const nextElements: any = {
|
||||
const nextElements: NextElements = {
|
||||
nextNodes: [],
|
||||
nextEdges: [],
|
||||
}
|
||||
@@ -48,17 +48,17 @@ export default () =>
|
||||
const storeNode = nodes[nodes.map((x) => x.id).indexOf(element.id)]
|
||||
|
||||
if (storeNode) {
|
||||
const updatedNode: Node = Object.assign(storeNode, element)
|
||||
if (!updatedNode.__vf) updatedNode.__vf = {}
|
||||
const updatedNode = Object.assign(storeNode, element)
|
||||
if (!updatedNode.__vf) updatedNode.__vf = {} as any
|
||||
|
||||
if (storeNode.position.x !== element.position.x || storeNode.position.y !== element.position.y) {
|
||||
updatedNode.__vf.position = element.position
|
||||
updatedNode.__vf!.position = element.position
|
||||
}
|
||||
|
||||
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) {
|
||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||
updatedNode.__vf.width = undefined
|
||||
updatedNode.__vf!.width = 0
|
||||
}
|
||||
|
||||
nextElements.nextNodes.push(updatedNode)
|
||||
|
||||
Reference in New Issue
Block a user