refactor: pass props to edge/nodewrapper separately

Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
Braks
2021-11-22 01:22:27 +01:00
parent a03f658794
commit c35673c622
18 changed files with 204 additions and 148 deletions
+7 -7
View File
@@ -16,8 +16,8 @@ import {
useVueFlow, useVueFlow,
} from '~/index' } from '~/index'
const onNodeDragStop = ({ node }: { node: Node }) => console.log('drag stop', node) const onNodeDragStop = (e) => console.log('drag stop', e)
const onElementClick = ({ node }: { node: Node }) => console.log('click', node) const onElementClick = (e) => console.log('click', e)
const elements = ref<Elements>([ const elements = ref<Elements>([
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }, { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } }, { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
@@ -26,12 +26,12 @@ const elements = ref<Elements>([
{ id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e1-2', source: '1', target: '2', animated: true },
{ id: 'e1-3', source: '1', target: '3' }, { id: 'e1-3', source: '1', target: '3' },
]) ])
const rfInstance = ref<FlowInstance | null>(null) const vfInstance = ref<FlowInstance>()
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value)) const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
const onConnect = (params: Edge | Connection) => (elements.value = addEdge(params, elements.value)) const onConnect = (params: Edge | Connection) => (elements.value = addEdge(params, elements.value))
const onLoad = (flowInstance: FlowInstance) => { const onLoad = (flowInstance: FlowInstance) => {
flowInstance.fitView({ padding: 0.1 }) flowInstance.fitView({ padding: 0.1 })
rfInstance.value = flowInstance vfInstance.value = flowInstance
} }
const updatePos = () => { const updatePos = () => {
@@ -46,8 +46,8 @@ const updatePos = () => {
}) })
} }
const logToObject = () => console.log(rfInstance.value?.toObject()) const logToObject = () => console.log(vfInstance.value?.toObject())
const resetTransform = () => rfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 }) const resetTransform = () => vfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 })
const toggleclasss = () => { const toggleclasss = () => {
elements.value = elements.value.map((el) => { elements.value = elements.value.map((el) => {
@@ -64,11 +64,11 @@ const toggleclasss = () => {
:min-zoom="0.2" :min-zoom="0.2"
:max-zoom="4" :max-zoom="4"
:zoom-on-scroll="false" :zoom-on-scroll="false"
:worker="true"
@elements-remove="onElementsRemove" @elements-remove="onElementsRemove"
@connect="onConnect" @connect="onConnect"
@node-drag-stop="onNodeDragStop" @node-drag-stop="onNodeDragStop"
@node-click="onElementClick" @node-click="onElementClick"
@elementClick="onElementClick"
@load="onLoad" @load="onLoad"
> >
<MiniMap /> <MiniMap />
-1
View File
@@ -37,7 +37,6 @@
"d3": "^7.1.1", "d3": "^7.1.1",
"d3-selection": "^3.0.0", "d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0", "d3-zoom": "^3.0.0",
"defu": "^5.0.0",
"microdiff": "^1.2.0", "microdiff": "^1.2.0",
"pinia": "^2.0.0-rc.10" "pinia": "^2.0.0-rc.10"
}, },
+4 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Component, CSSProperties, DefineComponent } from 'vue' import { CSSProperties, DefineComponent } from 'vue'
import { ArrowHeadType, EdgeProps, ElementId, Position } from '../../types' import { ArrowHeadType, EdgeProps, EdgeTextProps, ElementId, Position } from '../../types'
import { getCenter, getMarkerEnd, getBezierPath } from './utils' import { getCenter, getMarkerEnd, getBezierPath } from './utils'
import EdgeText from './EdgeText.vue' import EdgeText from './EdgeText.vue'
@@ -19,8 +19,8 @@ interface BezierEdgeProps extends EdgeProps {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent<EdgeTextProps>
props?: Record<string, any> props?: EdgeTextProps
} }
labelStyle?: CSSProperties labelStyle?: CSSProperties
labelShowBg?: boolean labelShowBg?: boolean
+2 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Component, CSSProperties, DefineComponent } from 'vue' import { CSSProperties, DefineComponent } from 'vue'
interface EdgeTextProps { interface EdgeTextProps {
x: number x: number
@@ -7,7 +7,7 @@ interface EdgeTextProps {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent
props?: Record<string, any> props?: Record<string, any>
} }
labelStyle?: CSSProperties labelStyle?: CSSProperties
+70 -43
View File
@@ -1,12 +1,39 @@
<script lang="ts" setup> <script lang="ts" setup>
import { CSSProperties, DefineComponent } from 'vue'
import { useHandle, useStore } from '../../composables' import { useHandle, useStore } from '../../composables'
import { ConnectionMode, Edge, EdgePositions, Position } from '../../types' import { ArrowHeadType, ConnectionMode, Edge, EdgePositions, EdgeTextProps, ElementId, Position } from '../../types'
import { isEdge, getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../utils' import { isEdge, getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../utils'
import EdgeAnchor from './EdgeAnchor.vue' import EdgeAnchor from './EdgeAnchor.vue'
interface EdgeWrapper { interface EdgeWrapper {
id: ElementId
edge: Edge edge: Edge
type?: string
source: ElementId
target: ElementId
sourceHandle?: ElementId | null
targetHandle?: ElementId | null
selected?: boolean
sourcePosition?: Position
targetPosition?: Position
label?:
| string
| {
component: DefineComponent<EdgeTextProps>
props?: EdgeTextProps
}
labelStyle?: any
labelShowBg?: boolean
labelBgStyle?: any
labelBgPadding?: [number, number]
labelBgBorderRadius?: number
style?: CSSProperties | unknown
animated?: boolean
arrowHeadType?: ArrowHeadType
markerEndId?: string markerEndId?: string
data?: any
class?: string
isHidden?: boolean
edgeUpdaterRadius?: number edgeUpdaterRadius?: number
} }
@@ -15,7 +42,7 @@ const props = withDefaults(defineProps<EdgeWrapper>(), {})
const store = useStore() const store = useStore()
const type = computed(() => { const type = computed(() => {
const t = props.edge.type ?? 'default' const t = props.type ?? 'default'
let edge = store.getEdgeTypes[t] let edge = store.getEdgeTypes[t]
if (!edge) { if (!edge) {
edge = store.getEdgeTypes.default edge = store.getEdgeTypes.default
@@ -53,8 +80,8 @@ const onEdgeMouseLeave = (event: MouseEvent) => store.hooks.edgeMouseLeave.trigg
const handler = useHandle() const handler = useHandle()
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => { const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const nodeId = isSourceHandle ? props.edge.target : props.edge.source const nodeId = isSourceHandle ? props.target : props.source
const handleId = (isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle) ?? '' const handleId = (isSourceHandle ? props.targetHandle : props.sourceHandle) ?? ''
const isValidConnection = () => true const isValidConnection = () => true
const isTarget = isSourceHandle const isTarget = isSourceHandle
@@ -92,11 +119,11 @@ const nodes = computed(() => {
const n = getSourceTargetNodes(props.edge, store.nodes) const n = getSourceTargetNodes(props.edge, store.nodes)
if (!n.sourceNode) { if (!n.sourceNode) {
console.warn(`couldn't create edge for source id: ${props.edge.source}; edge id: ${props.edge.id}`) console.warn(`couldn't create edge for source id: ${props.source}; edge id: ${props.id}`)
} }
if (!n.targetNode) { if (!n.targetNode) {
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`) console.warn(`couldn't create edge for target id: ${props.target}; edge id: ${props.id}`)
} }
return n return n
@@ -111,17 +138,17 @@ const targetNodeHandles = computed(() =>
const sourceHandle = computed(() => { const sourceHandle = computed(() => {
if (nodes.value.sourceNode && nodes.value.sourceNode.__vf?.handleBounds?.source) if (nodes.value.sourceNode && nodes.value.sourceNode.__vf?.handleBounds?.source)
return getHandle(nodes.value.sourceNode.__vf.handleBounds.source, props.edge.sourceHandle ?? null) return getHandle(nodes.value.sourceNode.__vf.handleBounds.source, props.sourceHandle ?? null)
else return null else return null
}) })
const targetHandle = computed(() => { const targetHandle = computed(() => {
if (targetNodeHandles.value) return getHandle(targetNodeHandles.value, props.edge.targetHandle ?? null) if (targetNodeHandles.value) return getHandle(targetNodeHandles.value, props.targetHandle ?? null)
else return null else return null
}) })
const sourcePosition = computed(() => (sourceHandle.value ? sourceHandle.value.position : Position.Bottom)) const sourcePosition = computed(() => (sourceHandle.value ? sourceHandle.value.position : Position.Bottom))
const targetPosition = computed(() => (targetHandle.value ? targetHandle.value.position : Position.Top)) const targetPosition = computed(() => (targetHandle.value ? targetHandle.value.position : Position.Top))
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) ?? false) const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.id) ?? false)
const edgePos = computed(() => const edgePos = computed(() =>
getEdgePositions( getEdgePositions(
nodes.value.sourceNode, nodes.value.sourceNode,
@@ -141,17 +168,17 @@ export default {
</script> </script>
<template> <template>
<g <g
v-show="!props.edge.isHidden && isVisible(edgePos)" v-show="!props.isHidden && isVisible(edgePos)"
:class="[ :class="[
'vue-flow__edge', 'vue-flow__edge',
`vue-flow__edge-${props.edge.type || 'default'}`, `vue-flow__edge-${props.type || 'default'}`,
{ {
selected: isSelected, selected: isSelected,
animated: props.edge.animated, animated: props.animated,
inactive: !elementsSelectable, inactive: !elementsSelectable,
updating, updating,
}, },
props.edge.class, props.class,
]" ]"
@click="onEdgeClick" @click="onEdgeClick"
@dblClick="onDoubleClick" @dblClick="onDoubleClick"
@@ -163,20 +190,20 @@ export default {
<slot <slot
v-if="edgePos.sourceX && edgePos.sourceY && edgePos.targetX && edgePos.targetY" v-if="edgePos.sourceX && edgePos.sourceY && edgePos.targetX && edgePos.targetY"
v-bind="{ v-bind="{
id: props.edge.id, id: props.id,
source: props.edge.source, source: props.source,
target: props.edge.target, target: props.target,
selected: isSelected, selected: isSelected,
animated: props.edge.animated, animated: props.animated,
label: props.edge.label, label: props.label,
labelStyle: props.edge.labelStyle, labelStyle: props.labelStyle,
labelShowBg: props.edge.labelShowBg, labelShowBg: props.labelShowBg,
labelBgStyle: props.edge.labelBgStyle, labelBgStyle: props.labelBgStyle,
labelBgPadding: props.edge.labelBgPadding, labelBgPadding: props.labelBgPadding,
labelBgBorderRadius: props.edge.labelBgBorderRadius, labelBgBorderRadius: props.labelBgBorderRadius,
data: props.edge.data, data: props.data,
style: props.edge.style, style: props.style,
arrowHeadType: props.edge.arrowHeadType, arrowHeadType: props.arrowHeadType,
sourcePosition, sourcePosition,
targetPosition, targetPosition,
sourceX: edgePos.sourceX, sourceX: edgePos.sourceX,
@@ -184,28 +211,28 @@ export default {
targetX: edgePos.targetX, targetX: edgePos.targetX,
targetY: edgePos.targetY, targetY: edgePos.targetY,
markerEndId: props.markerEndId, markerEndId: props.markerEndId,
sourceHandleId: props.edge.sourceHandle, sourceHandleId: props.sourceHandle,
targetHandleId: props.edge.targetHandle, targetHandleId: props.targetHandle,
}" }"
> >
<component <component
:is="type" :is="type"
v-if="typeof type !== 'boolean'" v-if="typeof type !== 'boolean'"
v-bind="{ v-bind="{
id: props.edge.id, id: props.id,
source: props.edge.source, source: props.source,
target: props.edge.target, target: props.target,
selected: isSelected, selected: isSelected,
animated: props.edge.animated, animated: props.animated,
label: props.edge.label, label: props.label,
labelStyle: props.edge.labelStyle, labelStyle: props.labelStyle,
labelShowBg: props.edge.labelShowBg, labelShowBg: props.labelShowBg,
labelBgStyle: props.edge.labelBgStyle, labelBgStyle: props.labelBgStyle,
labelBgPadding: props.edge.labelBgPadding, labelBgPadding: props.labelBgPadding,
labelBgBorderRadius: props.edge.labelBgBorderRadius, labelBgBorderRadius: props.labelBgBorderRadius,
data: props.edge.data, data: props.data,
style: props.edge.style, style: props.style,
arrowHeadType: props.edge.arrowHeadType, arrowHeadType: props.arrowHeadType,
sourcePosition, sourcePosition,
targetPosition, targetPosition,
sourceX: edgePos.sourceX, sourceX: edgePos.sourceX,
@@ -213,8 +240,8 @@ export default {
targetX: edgePos.targetX, targetX: edgePos.targetX,
targetY: edgePos.targetY, targetY: edgePos.targetY,
markerEndId: props.markerEndId, markerEndId: props.markerEndId,
sourceHandleId: props.edge.sourceHandle, sourceHandleId: props.sourceHandle,
targetHandleId: props.edge.targetHandle, targetHandleId: props.targetHandle,
}" }"
/> />
</slot> </slot>
+4 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Component, CSSProperties, DefineComponent } from 'vue' import { CSSProperties, DefineComponent } from 'vue'
import { ArrowHeadType, EdgeProps, ElementId, Position } from '../../types' import { ArrowHeadType, EdgeProps, EdgeTextProps, ElementId, Position } from '../../types'
import EdgeText from './EdgeText.vue' import EdgeText from './EdgeText.vue'
import { getCenter, getMarkerEnd, getSmoothStepPath } from './utils' import { getCenter, getMarkerEnd, getSmoothStepPath } from './utils'
@@ -19,8 +19,8 @@ interface SmoothStepEdgeProps extends EdgeProps {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent<EdgeTextProps>
props?: Record<string, any> props?: EdgeTextProps
} }
labelStyle?: CSSProperties labelStyle?: CSSProperties
labelShowBg?: boolean labelShowBg?: boolean
+5 -5
View File
@@ -1,9 +1,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Component, CSSProperties, DefineComponent } from 'vue' import { CSSProperties, DefineComponent } from 'vue'
import { ArrowHeadType, EdgeProps, ElementId, Position } from '../../types' import { ArrowHeadType, EdgeProps, EdgeTextProps, ElementId, Position } from '../../types'
import SmoothStepEdge from './SmoothStepEdge.vue' import SmoothStepEdge from './SmoothStepEdge.vue'
export interface StepEdgeProps extends EdgeProps { interface StepEdgeProps extends EdgeProps {
id: ElementId id: ElementId
source: ElementId source: ElementId
target: ElementId target: ElementId
@@ -18,8 +18,8 @@ export interface StepEdgeProps extends EdgeProps {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent<EdgeTextProps>
props?: Record<string, any> props?: EdgeTextProps
} }
labelStyle?: CSSProperties labelStyle?: CSSProperties
labelShowBg?: boolean labelShowBg?: boolean
+4 -4
View File
@@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Component, CSSProperties, DefineComponent } from 'vue' import { CSSProperties, DefineComponent } from 'vue'
import { ArrowHeadType, EdgeProps, ElementId, Position } from '../../types' import { ArrowHeadType, EdgeProps, EdgeTextProps, ElementId, Position } from '../../types'
import EdgeText from './EdgeText.vue' import EdgeText from './EdgeText.vue'
import { getMarkerEnd, getBezierPath } from './utils' import { getMarkerEnd, getBezierPath } from './utils'
@@ -19,8 +19,8 @@ interface StraightEdgeProps extends EdgeProps {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent<EdgeTextProps>
props?: Record<string, any> props?: EdgeTextProps
} }
labelStyle?: CSSProperties labelStyle?: CSSProperties
labelShowBg?: boolean labelShowBg?: boolean
+55 -44
View File
@@ -1,13 +1,28 @@
<script lang="ts" setup> <script lang="ts" setup>
import { DraggableEventListener, DraggableCore } from '@braks/revue-draggable' import { DraggableEventListener, DraggableCore } from '@braks/revue-draggable'
import { CSSProperties } from 'vue'
import { useStore } from '../../composables' import { useStore } from '../../composables'
import { FlowEvents, GraphNode, SnapGrid } from '../../types' import { ElementId, FlowEvents, GraphNode, Position, SnapGrid, VFInternals, XYPosition } from '../../types'
import { NodeId } from '../../context' import { NodeId } from '../../context'
interface NodeWrapperProps { interface NodeWrapperProps {
node: GraphNode node: GraphNode
vf: VFInternals
selectNodesOnDrag?: boolean selectNodesOnDrag?: boolean
snapGrid?: SnapGrid snapGrid?: SnapGrid
id: ElementId
position: XYPosition
type?: string
class?: string
style?: CSSProperties
data?: any
targetPosition?: Position
sourcePosition?: Position
isHidden?: boolean
draggable?: boolean
selectable?: boolean
connectable?: boolean
dragHandle?: string
} }
interface NodeEvents { interface NodeEvents {
@@ -25,12 +40,12 @@ const props = withDefaults(defineProps<NodeWrapperProps>(), {
}) })
const store = useStore() const store = useStore()
provide(NodeId, props.node.id) provide(NodeId, props.id)
const nodeElement = templateRef<HTMLDivElement>('node-element', null) const nodeElement = templateRef<HTMLDivElement>('node-element', null)
const type = computed(() => { const type = computed(() => {
const t = props.node.type ?? 'default' const t = props.type ?? 'default'
let node = store.getNodeTypes[t] let node = store.getNodeTypes[t]
if (!node) { if (!node) {
node = store.getNodeTypes.default node = store.getNodeTypes.default
@@ -39,24 +54,20 @@ const type = computed(() => {
return node return node
}) })
const selectable = computed(() => const selectable = computed(() => (typeof props.selectable === 'undefined' ? store.elementsSelectable : props.selectable))
typeof props.node.selectable === 'undefined' ? store.elementsSelectable : props.node.selectable, const draggable = computed(() => (typeof props.draggable === 'undefined' ? store.nodesDraggable : props.draggable))
) const connectable = computed(() => (typeof props.connectable === 'undefined' ? store.nodesConnectable : props.connectable))
const draggable = computed(() => (typeof props.node.draggable === 'undefined' ? store.nodesDraggable : props.node.draggable))
const connectable = computed(() =>
typeof props.node.connectable === 'undefined' ? store.nodesConnectable : props.node.connectable,
)
const scale = computed(() => store.transform[2]) const scale = computed(() => store.transform[2])
const selected = computed(() => selectable.value && store.selectedElements?.some(({ id }) => id === props.node.id)) const selected = computed(() => selectable.value && store.selectedElements?.some(({ id }) => id === props.id))
const onMouseEnterHandler = () => const onMouseEnterHandler = () =>
props.node.__vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseEnter.trigger({ event, node: props.node })) props.vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseEnter.trigger({ event, node: props.node }))
const onMouseMoveHandler = () => const onMouseMoveHandler = () =>
props.node.__vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseMove.trigger({ event, node: props.node })) props.vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseMove.trigger({ event, node: props.node }))
const onMouseLeaveHandler = () => const onMouseLeaveHandler = () =>
props.node.__vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseLeave.trigger({ event, node: props.node })) props.vf.isDragging && ((event: MouseEvent) => store.hooks.nodeMouseLeave.trigger({ event, node: props.node }))
const onContextMenuHandler = () => (event: MouseEvent) => store.hooks.nodeContextMenu.trigger({ event, node: props.node }) const onContextMenuHandler = () => (event: MouseEvent) => store.hooks.nodeContextMenu.trigger({ event, node: props.node })
@@ -69,9 +80,8 @@ const onSelectNodeHandler = (event: MouseEvent) => {
store.unsetNodesSelection() store.unsetNodesSelection()
if (!selected.value) store.addSelectedElements([n]) if (!selected.value) store.addSelectedElements([n])
} }
store.hooks.nodeClick.trigger({ event, node: n })
} }
store.hooks.elementClick.trigger({ event, element: n })
store.hooks.nodeClick.trigger({ event, node: n })
} }
const onDragStart: DraggableEventListener = ({ event }) => { const onDragStart: DraggableEventListener = ({ event }) => {
@@ -95,7 +105,7 @@ const onDrag: DraggableEventListener = ({ event, data }) => {
store.hooks.nodeDrag.trigger({ event, node: n }) store.hooks.nodeDrag.trigger({ event, node: n })
store.updateNodePosDiff({ store.updateNodePosDiff({
id: props.node.id, id: props.id,
diff: { diff: {
x: data.deltaX, x: data.deltaX,
y: data.deltaY, y: data.deltaY,
@@ -108,11 +118,12 @@ const onDragStop: DraggableEventListener = ({ event }) => {
const n = props.node const n = props.node
// onDragStop also gets called when user just clicks on a node. // 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 // Because of that we set dragging to true inside the onDrag handler and handle the click here
if (!props.node.__vf.isDragging) { if (!props.vf.isDragging) {
if (selectable.value && !props.selectNodesOnDrag && !selected.value) { if (selectable.value && !props.selectNodesOnDrag && !selected.value) {
store.addSelectedElements([n]) store.addSelectedElements([n])
} }
store.hooks.nodeClick.trigger({ event, node: n }) store.hooks.nodeClick.trigger({ event, node: n })
store.hooks.elementClick.trigger({ event, element: n })
return return
} }
@@ -127,7 +138,7 @@ const onDragStop: DraggableEventListener = ({ event }) => {
onMounted(() => { onMounted(() => {
store.updateNodeDimensions({ store.updateNodeDimensions({
id: props.node.id, id: props.id,
nodeElement: nodeElement.value, nodeElement: nodeElement.value,
forceUpdate: true, forceUpdate: true,
}) })
@@ -141,10 +152,10 @@ onMounted(() => {
}), }),
) )
watch([() => props.node.type, () => props.node.sourcePosition, () => props.node.targetPosition], () => { watch([() => props.type, () => props.sourcePosition, () => props.targetPosition], () => {
nextTick(() => { nextTick(() => {
store.updateNodeDimensions({ store.updateNodeDimensions({
id: props.node.id, id: props.id,
nodeElement: nodeElement.value, nodeElement: nodeElement.value,
forceUpdate: true, forceUpdate: true,
}) })
@@ -160,7 +171,7 @@ export default {
<template> <template>
<DraggableCore <DraggableCore
cancel=".nodrag" cancel=".nodrag"
:handle="props.node.dragHandle" :handle="props.dragHandle"
:disabled="!draggable" :disabled="!draggable"
:scale="scale" :scale="scale"
:grid="props.snapGrid" :grid="props.snapGrid"
@@ -173,21 +184,21 @@ export default {
ref="node-element" ref="node-element"
:class="[ :class="[
'vue-flow__node', 'vue-flow__node',
`vue-flow__node-${props.node.type}`, `vue-flow__node-${props.type}`,
{ {
selected, selected,
selectable: selectable, selectable: selectable,
}, },
props.node.class, props.class,
]" ]"
:style="{ :style="{
zIndex: selected ? 10 : 3, zIndex: selected ? 10 : 3,
transform: `translate(${props.node.__vf?.position?.x}px,${props.node.__vf?.position?.y}px)`, transform: `translate(${props.vf.position.x}px,${props.vf.position.y}px)`,
pointerEvents: selectable || draggable ? 'all' : 'none', pointerEvents: selectable || draggable ? 'all' : 'none',
opacity: props.node.__vf?.width !== null && props.node.__vf?.height !== null ? 1 : 0, opacity: props.vf.width !== null && props.vf.height !== null ? 1 : 0,
...props.node.style, ...props.style,
}" }"
:data-id="props.node.id" :data-id="props.id"
@mouseenter="onMouseEnterHandler" @mouseenter="onMouseEnterHandler"
@mousemove="onMouseMoveHandler" @mousemove="onMouseMoveHandler"
@mouseleave="onMouseLeaveHandler" @mouseleave="onMouseLeaveHandler"
@@ -197,31 +208,31 @@ export default {
> >
<slot <slot
v-bind="{ v-bind="{
id: props.node.id, id: props.id,
data: props.node.data, data: props.data,
type: props.node.type, type: props.type,
xPos: props.node.__vf?.position?.x, xPos: props.vf.position.x,
yPos: props.node.__vf?.position?.y, yPos: props.vf.position.y,
selected, selected,
connectable, connectable,
sourcePosition: props.node.sourcePosition, sourcePosition: props.sourcePosition,
targetPosition: props.node.targetPosition, targetPosition: props.targetPosition,
dragging: props.node.__vf?.isDragging, dragging: props.vf.isDragging,
}" }"
> >
<component <component
:is="type" :is="type"
v-bind="{ v-bind="{
id: props.node.id, id: props.id,
data: props.node.data, data: props.data,
type: props.node.type, type: props.type,
xPos: props.node.__vf?.position?.x, xPos: props.vf.position.x,
yPos: props.node.__vf?.position?.y, yPos: props.vf.position.y,
selected, selected,
connectable, connectable,
sourcePosition: props.node.sourcePosition, sourcePosition: props.sourcePosition,
targetPosition: props.node.targetPosition, targetPosition: props.targetPosition,
dragging: props.node.__vf?.isDragging, dragging: props.vf.isDragging,
}" }"
/> />
</slot> </slot>
+22 -1
View File
@@ -40,9 +40,30 @@ export default {
<g :transform="transform"> <g :transform="transform">
<Edge <Edge
v-for="(edge, i) of store.getEdges" v-for="(edge, i) of store.getEdges"
:id="edge.id"
:key="`${edge.id}-${i}`" :key="`${edge.id}-${i}`"
:edge="edge" :edge="edge"
:marker-end-id="props.markerEndId" :type="edge.type"
:source="edge.source"
:target="edge.target"
:source-handle="edge.sourceHandle"
:target-handle="edge.targetHandle"
:selected="edge.selected"
:source-position="edge.sourcePosition"
:target-position="edge.targetPosition"
:label="edge.label"
:label-tyle="edge.labelStyle"
:label-show-bg="edge.labelShowBg"
:label-bg-style="edge.labelBgStyle"
:label-bg-padding="edge.labelBgPadding"
:label-bg-border-radius="edge.labelBgBorderRadius"
:style="edge.style"
:animated="edge.animated"
:arrow-head-type="edge.arrowHeadType"
:data="edge.data"
:class="edge.class"
:is-hidden="edge.isHidden"
:marker-end-id="edge.markerEndId ?? props.markerEndId"
:edge-updater-radius="props.edgeUpdaterRadius" :edge-updater-radius="props.edgeUpdaterRadius"
> >
<template #default="edgeProps"> <template #default="edgeProps">
+16 -2
View File
@@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useStore } from '../../composables' import { useStore } from '../../composables'
import Node from '../../components/Nodes/NodeWrapper.vue'
import { SnapGrid } from '../../types' import { SnapGrid } from '../../types'
import Node from '../../components/Nodes/NodeWrapper.vue'
interface NodeRendererProps { interface NodeRendererProps {
selectNodesOnDrag?: boolean selectNodesOnDrag?: boolean
@@ -20,15 +20,29 @@ const snapGrid = computed(() => (props.snapToGrid || store.snapToGrid ? props.sn
</script> </script>
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'Nodes', name: 'Node',
} }
</script> </script>
<template> <template>
<div class="vue-flow__nodes" :style="{ transform }"> <div class="vue-flow__nodes" :style="{ transform }">
<Node <Node
v-for="node of store.getNodes" v-for="node of store.getNodes"
:id="node.id"
:key="node.id" :key="node.id"
:node="node" :node="node"
:vf="node.__vf"
:position="node.position"
:type="node.type"
:class="node.class"
:style="node.style"
:data="node.data"
:target-position="node.targetPosition"
:source-position="node.sourcePosition"
:is-hidden="node.isHidden"
:draggable="node.draggable"
:selectable="node.selectable"
:connectable="node.connectable"
:drag-handle="node.dragHandle"
:snap-grid="snapGrid" :snap-grid="snapGrid"
:select-nodes-on-drag="props.selectNodesOnDrag" :select-nodes-on-drag="props.selectNodesOnDrag"
> >
+6 -8
View File
@@ -116,15 +116,16 @@ const options = Object.assign({}, store.$state, props)
const init = async (state: FlowState) => { const init = async (state: FlowState) => {
// set state variables // set state variables
const skip = ['modelValue', 'd3Zoom', 'd3Selection', 'd3ZoomHandler', 'minZoom', 'maxZoom', 'translateExtent', 'nodeExtent']
for (const opt of Object.keys(state)) { for (const opt of Object.keys(state)) {
const val = state[opt as keyof FlowState] const val = state[opt as keyof FlowState]
if (typeof val !== 'undefined' && opt !== 'modelValue' && opt !== 'elements') { if (typeof val !== 'undefined' && !skip.includes(opt)) {
if (typeof val === 'object' && !Array.isArray(val)) { if (typeof val === 'object' && !Array.isArray(val)) {
;(store as any)[opt] = { ...(store as any)[opt], ...val } ;(store as any)[opt] = { ...(store as any)[opt], ...val }
} else (store as any)[opt] = val } else (store as any)[opt] = val
} }
} }
await until(() => store.d3Zoom).not.toBeUndefined() if (!store.isReady) await until(() => store.d3Zoom).not.toBeUndefined()
store.setMinZoom(state.minZoom) store.setMinZoom(state.minZoom)
store.setMaxZoom(state.maxZoom) store.setMaxZoom(state.maxZoom)
store.setTranslateExtent(state.translateExtent) store.setTranslateExtent(state.translateExtent)
@@ -155,12 +156,6 @@ invoke(async () => {
} }
store.hooks.load.trigger(instance) store.hooks.load.trigger(instance)
store.instance = instance store.instance = instance
})
throttledWatch(elements, store.setElements, { flush: 'post', deep: true, throttle: 10 })
throttledWatch(store.elements, (val) => (elements.value = val), { flush: 'post', deep: true, throttle: 10 })
onMounted(() => {
watch( watch(
() => props, () => props,
() => init({ ...store.$state, ...props } as FlowState), () => init({ ...store.$state, ...props } as FlowState),
@@ -168,6 +163,9 @@ onMounted(() => {
) )
}) })
throttledWatch(elements, store.setElements, { flush: 'post', deep: true, throttle: 10 })
throttledWatch(store.elements, (val) => (elements.value = val), { flush: 'post', deep: true, throttle: 10 })
const transitionName = computed(() => { const transitionName = computed(() => {
let name = '' let name = ''
if (typeof store.loading === 'object' && store.loading.transition) { if (typeof store.loading === 'object' && store.loading.transition) {
+3 -5
View File
@@ -87,11 +87,9 @@ export default (id: string, preloadedState: FlowState) => {
next = res next = res
} else next = await parseElements(elements, this.nodes, this.edges, this.nodeExtent) } else next = await parseElements(elements, this.nodes, this.edges, this.nodeExtent)
} }
if (next) { this.elements = [...next.nextNodes, ...next.nextEdges]
this.elements = [...next.nextNodes, ...next.nextEdges] this.nodes = next.nextNodes ?? []
this.nodes = next?.nextNodes ?? [] this.edges = next.nextEdges ?? []
this.edges = next?.nextEdges ?? []
}
}, },
updateNodeDimensions({ id, nodeElement, forceUpdate }) { updateNodeDimensions({ id, nodeElement, forceUpdate }) {
const i = this.nodes.map((x) => x.id).indexOf(id) const i = this.nodes.map((x) => x.id).indexOf(id)
+3 -2
View File
@@ -1,5 +1,6 @@
import { Component, CSSProperties, DefineComponent } from 'vue' import { Component, CSSProperties, DefineComponent } from 'vue'
import { ArrowHeadType, ElementId, Position } from './flow' import { ArrowHeadType, ElementId, Position } from './flow'
import { EdgeTextProps } from '~/types/components'
export interface Edge<T = any> { export interface Edge<T = any> {
id: ElementId id: ElementId
@@ -14,8 +15,8 @@ export interface Edge<T = any> {
label?: label?:
| string | string
| { | {
component: Component | DefineComponent component: DefineComponent<EdgeTextProps>
props?: Record<string, any> props?: EdgeTextProps
} }
labelStyle?: any labelStyle?: any
labelShowBg?: boolean labelShowBg?: boolean
+1 -1
View File
@@ -2,7 +2,7 @@ import { Component, CSSProperties, DefineComponent } from 'vue'
import { XYPosition, ElementId, Position } from './flow' import { XYPosition, ElementId, Position } from './flow'
import { HandleElement } from './components' import { HandleElement } from './components'
interface VFInternals { export interface VFInternals {
position: XYPosition position: XYPosition
isDragging?: boolean isDragging?: boolean
width: number width: number
+1 -5
View File
@@ -114,11 +114,7 @@ export const parseElements = async (elements: Elements, nodes: GraphNode[], edge
...storeNode, ...storeNode,
...element, ...element,
} }
if (!updatedNode.__vf) updatedNode.__vf = {} as any updatedNode.__vf!.position = element.position
if (storeNode.position.x !== element.position.x || storeNode.position.y !== element.position.y) {
updatedNode.__vf!.position = element.position
}
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) { 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. // we reset the elements dimensions here in order to force a re-calculation of the bounds.
+1 -5
View File
@@ -49,11 +49,7 @@ export default () =>
if (storeNode) { if (storeNode) {
const updatedNode = Object.assign(storeNode, element) const updatedNode = Object.assign(storeNode, element)
if (!updatedNode.__vf) updatedNode.__vf = {} as any updatedNode.__vf!.position = element.position
if (storeNode.position.x !== element.position.x || storeNode.position.y !== element.position.y) {
updatedNode.__vf!.position = element.position
}
if (typeof element.type !== 'undefined' && element.type !== storeNode.type) { 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. // we reset the elements dimensions here in order to force a re-calculation of the bounds.
-5
View File
@@ -1974,11 +1974,6 @@ define-properties@^1.1.3:
dependencies: dependencies:
object-keys "^1.0.12" object-keys "^1.0.12"
defu@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/defu/-/defu-5.0.0.tgz#5768f0d402a555bfc4c267246b20f82ce8b5a10b"
integrity sha512-VHg73EDeRXlu7oYWRmmrNp/nl7QkdXUxkQQKig0Zk8daNmm84AbGoC8Be6/VVLJEKxn12hR0UBmz8O+xQiAPKQ==
del@^6.0.0: del@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952"