refactor(types)!: Remove elements and replace with nodes and edges
Signed-off-by: Braks <78412429+bcakmakoglu@users.noreply.github.com>
This commit is contained in:
@@ -15,54 +15,31 @@ import {
|
|||||||
|
|
||||||
const onNodeDragStop = (e) => console.log('drag stop', e)
|
const onNodeDragStop = (e) => console.log('drag stop', e)
|
||||||
const onElementClick = (e) => console.log('click', e)
|
const onElementClick = (e) => console.log('click', e)
|
||||||
const elements = ref<Elements>([
|
const nodes = 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 } },
|
||||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
])
|
])
|
||||||
|
const edges = ref<Elements>([])
|
||||||
const vfInstance = ref<FlowInstance>()
|
const vfInstance = ref<FlowInstance>()
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => removeElements(elementsToRemove, elements.value)
|
|
||||||
const onConnect = (params: Edge | Connection) => addEdge(params, elements.value)
|
|
||||||
const onLoad = (flowInstance: FlowInstance) => {
|
const onLoad = (flowInstance: FlowInstance) => {
|
||||||
flowInstance.fitView({ padding: 0.1 })
|
flowInstance.fitView({ padding: 0.1 })
|
||||||
vfInstance.value = flowInstance
|
vfInstance.value = flowInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePos = () => {
|
|
||||||
elements.value = elements.value.map((el) => {
|
|
||||||
if (isNode(el)) {
|
|
||||||
el.position = {
|
|
||||||
x: Math.random() * 400,
|
|
||||||
y: Math.random() * 400,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return el
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const logToObject = () => console.log(vfInstance.value?.toObject())
|
const logToObject = () => console.log(vfInstance.value?.toObject())
|
||||||
const resetTransform = () => vfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 })
|
const resetTransform = () => vfInstance.value?.setTransform({ x: 0, y: 0, zoom: 1 })
|
||||||
|
|
||||||
const toggleclasss = () => {
|
|
||||||
elements.value = elements.value.map((el) => {
|
|
||||||
if (isNode(el)) el.class = el.class === 'light' ? 'dark' : 'light'
|
|
||||||
return el
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<VueFlow
|
<VueFlow
|
||||||
v-model="elements"
|
:nodes="nodes"
|
||||||
|
:edges="edges"
|
||||||
class="vue-flow-basic-example"
|
class="vue-flow-basic-example"
|
||||||
:default-zoom="1.5"
|
:default-zoom="1.5"
|
||||||
:min-zoom="0.2"
|
:min-zoom="0.2"
|
||||||
:max-zoom="4"
|
:max-zoom="4"
|
||||||
:zoom-on-scroll="false"
|
:zoom-on-scroll="false"
|
||||||
@elements-remove="onElementsRemove"
|
|
||||||
@connect="onConnect"
|
|
||||||
@node-drag-stop="onNodeDragStop"
|
@node-drag-stop="onNodeDragStop"
|
||||||
@node-click="onElementClick"
|
@node-click="onElementClick"
|
||||||
@load="onLoad"
|
@load="onLoad"
|
||||||
@@ -70,11 +47,5 @@ const toggleclasss = () => {
|
|||||||
<Background />
|
<Background />
|
||||||
<MiniMap />
|
<MiniMap />
|
||||||
<Controls />
|
<Controls />
|
||||||
<div style="position: absolute; right: 10px; top: 10px; z-index: 4">
|
|
||||||
<button style="margin-right: 5px" @click="resetTransform">reset transform</button>
|
|
||||||
<button style="margin-right: 5px" @click="updatePos">change pos</button>
|
|
||||||
<button style="margin-right: 5px" @click="toggleclasss">toggle classs</button>
|
|
||||||
<button @click="logToObject">toObject</button>
|
|
||||||
</div>
|
|
||||||
</VueFlow>
|
</VueFlow>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const instance = ref<FlowInstance>()
|
|||||||
const onLoad = (flowInstance: FlowInstance) => {
|
const onLoad = (flowInstance: FlowInstance) => {
|
||||||
flowInstance.fitView()
|
flowInstance.fitView()
|
||||||
instance.value = flowInstance
|
instance.value = flowInstance
|
||||||
console.log(flowInstance.getElements())
|
console.log(flowInstance.getNodes())
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialElements: Elements = getElements(50, 20)
|
const initialElements: Elements = getElements(50, 20)
|
||||||
|
|||||||
@@ -93,10 +93,8 @@ export default {
|
|||||||
>
|
>
|
||||||
<template v-for="node of store.getNodes" :key="`mini-map-node-${node.id}`">
|
<template v-for="node of store.getNodes" :key="`mini-map-node-${node.id}`">
|
||||||
<slot
|
<slot
|
||||||
:x="node.computedPosition.x"
|
:position="node.computedPosition"
|
||||||
:y="node.computedPosition.y"
|
:dimensions="node.dimensions"
|
||||||
:width="node.width"
|
|
||||||
:height="node.height"
|
|
||||||
:style="node.style"
|
:style="node.style"
|
||||||
:class="nodeClassNameFunc(node)"
|
:class="nodeClassNameFunc(node)"
|
||||||
:color="nodeColorFunc(node)"
|
:color="nodeColorFunc(node)"
|
||||||
@@ -106,10 +104,8 @@ export default {
|
|||||||
:shape-rendering="shapeRendering"
|
:shape-rendering="shapeRendering"
|
||||||
>
|
>
|
||||||
<MiniMapNode
|
<MiniMapNode
|
||||||
:x="node.computedPosition.x"
|
:position="node.computedPosition"
|
||||||
:y="node.computedPosition.y"
|
:dimensions="node.dimensions"
|
||||||
:width="node.width"
|
|
||||||
:height="node.height"
|
|
||||||
:style="node.style"
|
:style="node.style"
|
||||||
:class="nodeClassNameFunc(node)"
|
:class="nodeClassNameFunc(node)"
|
||||||
:color="nodeColorFunc(node)"
|
:color="nodeColorFunc(node)"
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { CSSProperties } from 'vue'
|
import { CSSProperties } from 'vue'
|
||||||
|
import type { MiniMapNodeProps } from '../../types/components'
|
||||||
interface MiniMapNodeProps {
|
|
||||||
x?: number
|
|
||||||
y?: number
|
|
||||||
width?: number
|
|
||||||
height?: number
|
|
||||||
borderRadius?: number
|
|
||||||
color?: string
|
|
||||||
shapeRendering?: CSSProperties['shapeRendering']
|
|
||||||
strokeColor?: string
|
|
||||||
strokeWidth?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<MiniMapNodeProps>(), {
|
const props = withDefaults(defineProps<MiniMapNodeProps>(), {
|
||||||
shapeRendering: 'geometricPrecision',
|
shapeRendering: 'geometricPrecision',
|
||||||
@@ -29,12 +18,12 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<rect
|
<rect
|
||||||
class="vue-flow__minimap-node"
|
class="vue-flow__minimap-node"
|
||||||
:x="props.x"
|
:x="props.position.x"
|
||||||
:y="props.y"
|
:y="props.position.y"
|
||||||
:rx="props.borderRadius"
|
:rx="props.borderRadius"
|
||||||
:ry="props.borderRadius"
|
:ry="props.borderRadius"
|
||||||
:width="props.width"
|
:width="props.dimensions.width"
|
||||||
:height="props.height"
|
:height="props.dimensions.height"
|
||||||
:fill="fill"
|
:fill="fill"
|
||||||
:stroke="props.strokeColor"
|
:stroke="props.strokeColor"
|
||||||
:stroke-width="props.strokeWidth"
|
:stroke-width="props.strokeWidth"
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ const sourceHandle =
|
|||||||
store.connectionHandleId && store.connectionHandleType
|
store.connectionHandleId && store.connectionHandleType
|
||||||
? props.sourceNode.handleBounds[store.connectionHandleType]?.find((d: HandleElement) => d.id === store.connectionHandleId)
|
? props.sourceNode.handleBounds[store.connectionHandleType]?.find((d: HandleElement) => d.id === store.connectionHandleId)
|
||||||
: store.connectionHandleType && props.sourceNode.handleBounds[store.connectionHandleType ?? 'source']?.[0]
|
: store.connectionHandleType && props.sourceNode.handleBounds[store.connectionHandleType ?? 'source']?.[0]
|
||||||
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : (props.sourceNode.width as number) / 2
|
const sourceHandleX = sourceHandle ? sourceHandle.x + sourceHandle.width / 2 : props.sourceNode.dimensions.width / 2
|
||||||
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : props.sourceNode.height
|
const sourceHandleY = sourceHandle ? sourceHandle.y + sourceHandle.height / 2 : props.sourceNode.dimensions.height
|
||||||
const sourceX = props.sourceNode.position.x + sourceHandleX
|
const sourceX = props.sourceNode.position.x + sourceHandleX
|
||||||
const sourceY = props.sourceNode.position.y + sourceHandleY
|
const sourceY = props.sourceNode.position.y + sourceHandleY
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DraggableEventListener, DraggableCore } from '@braks/revue-draggable'
|
import { DraggableEventListener, DraggableCore } from '@braks/revue-draggable'
|
||||||
import { useStore } from '../../composables'
|
import { useStore } from '../../composables'
|
||||||
import { Draggable, GraphNode, NodeDimensionUpdate, SnapGrid, XYPosition } from '../../types'
|
import { Draggable, GraphNode, SnapGrid, XYPosition } from '../../types'
|
||||||
import { NodeId } from '../../context'
|
import { NodeId } from '../../context'
|
||||||
import { calculateXYZPosition, clampPosition, getDimensions, getHandleBounds, isParentSelected } from '../../utils'
|
import { calculateXYZPosition, clampPosition, getDimensions, getHandleBounds, isParentSelected } from '../../utils'
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ interface NodeWrapperProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<NodeWrapperProps>()
|
const props = defineProps<NodeWrapperProps>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:node'])
|
||||||
const node = useVModel(props, 'node', emit)
|
const node = useVModel(props, 'node', emit)
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
provide(NodeId, props.node.id)
|
provide(NodeId, props.node.id)
|
||||||
@@ -65,7 +65,7 @@ const onDrag: DraggableEventListener = ({ event, data: { deltaX, deltaY } }) =>
|
|||||||
node.value.dragging = true
|
node.value.dragging = true
|
||||||
store.hooks.nodeDrag.trigger({ event, node: node.value })
|
store.hooks.nodeDrag.trigger({ event, node: node.value })
|
||||||
}
|
}
|
||||||
const onDragStop: DraggableEventListener = ({ event, data: { deltaX, deltaY } }) => {
|
const onDragStop: DraggableEventListener = ({ event }) => {
|
||||||
// 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 (!node.value.dragging) {
|
if (!node.value.dragging) {
|
||||||
@@ -80,7 +80,7 @@ const onDragStop: DraggableEventListener = ({ event, data: { deltaX, deltaY } })
|
|||||||
store.hooks.nodeDragStop.trigger({ event, node: node.value })
|
store.hooks.nodeDragStop.trigger({ event, node: node.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateNodePosition = (node: GraphNode, { x, y }: XYPosition) => {
|
const updateNodePosition = (node: GraphNode, { x, y }: XYPosition = { x: 0, y: 0 }) => {
|
||||||
const position = {
|
const position = {
|
||||||
x: node.position.x + x,
|
x: node.position.x + x,
|
||||||
y: node.position.y + y,
|
y: node.position.y + y,
|
||||||
@@ -88,13 +88,13 @@ const updateNodePosition = (node: GraphNode, { x, y }: XYPosition) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let extent = store.nodeExtent
|
let extent = store.nodeExtent
|
||||||
if (node.extent === 'parent' && node.parentNode && node.width && node.height) {
|
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) {
|
||||||
const parent = node.parentNode
|
const parent = node.parentNode
|
||||||
extent =
|
extent =
|
||||||
parent.width && parent.height
|
parent.dimensions.width && parent.dimensions.height
|
||||||
? [
|
? [
|
||||||
[0, 0],
|
[0, 0],
|
||||||
[parent.width - node.width, parent.height - node.height],
|
[parent.dimensions.width - node.dimensions.width, parent.dimensions.height - node.dimensions.height],
|
||||||
]
|
]
|
||||||
: extent
|
: extent
|
||||||
}
|
}
|
||||||
@@ -104,43 +104,25 @@ const updateNodePosition = (node: GraphNode, { x, y }: XYPosition) => {
|
|||||||
} else node.computedPosition = { ...node.position, z: position.z }
|
} else node.computedPosition = { ...node.position, z: position.z }
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateNodeDimensions = ({ nodeElement, forceUpdate }: NodeDimensionUpdate) => {
|
updateNodePosition(node.value)
|
||||||
const dimensions = getDimensions(nodeElement)
|
|
||||||
|
|
||||||
const doUpdate =
|
|
||||||
dimensions.width &&
|
|
||||||
dimensions.height &&
|
|
||||||
(node.value.width !== dimensions.width || node.value.height !== dimensions.height || forceUpdate)
|
|
||||||
|
|
||||||
if (doUpdate) {
|
|
||||||
const handleBounds = getHandleBounds(nodeElement, store.transform[2], store.id)
|
|
||||||
|
|
||||||
node.value = {
|
|
||||||
...node.value,
|
|
||||||
...dimensions,
|
|
||||||
handleBounds,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onRenderTriggered(() => console.log(node.value.id))
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
const dimensions = ref(getDimensions(nodeElement.value))
|
||||||
updateNodeDimensions({
|
useResizeObserver(nodeElement, () => (dimensions.value = getDimensions(nodeElement.value)))
|
||||||
id: props.node.id,
|
watch(
|
||||||
nodeElement: nodeElement.value,
|
dimensions,
|
||||||
forceUpdate: true,
|
({ width: w, height: h }) => {
|
||||||
})
|
if (w > 0 && h > 0) {
|
||||||
|
const handleBounds = getHandleBounds(nodeElement.value, store.transform[2], store.id)
|
||||||
|
|
||||||
useResizeObserver(nodeElement, (entries) =>
|
node.value.dimensions = {
|
||||||
entries.forEach((entry) => {
|
width: w,
|
||||||
updateNodeDimensions({
|
height: h,
|
||||||
id: entry.target.getAttribute('data-id') as string,
|
}
|
||||||
nodeElement: entry.target as HTMLDivElement,
|
node.value.handleBounds = handleBounds
|
||||||
})
|
}
|
||||||
}),
|
},
|
||||||
)
|
{ immediate: true },
|
||||||
})
|
)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -177,7 +159,7 @@ export default {
|
|||||||
zIndex: node.dragging || selected ? 1000 : node.computedPosition.z,
|
zIndex: node.dragging || selected ? 1000 : node.computedPosition.z,
|
||||||
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
|
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
|
||||||
pointerEvents: props.selectable || props.draggable ? 'all' : 'none',
|
pointerEvents: props.selectable || props.draggable ? 'all' : 'none',
|
||||||
opacity: node.width !== null && node.height !== null ? 1 : 0,
|
opacity: node.dimensions.width !== 0 && node.dimensions.height !== 0 ? 1 : 0,
|
||||||
...node.style,
|
...node.style,
|
||||||
}"
|
}"
|
||||||
:data-id="node.id"
|
:data-id="node.id"
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useStore, useWindow, useZoomPanHelper } from '../../composables'
|
import { useStore, useWindow, useZoomPanHelper } from '../../composables'
|
||||||
import { Elements, FlowInstance } from '../../types'
|
import { FlowInstance } from '../../types'
|
||||||
import { onLoadGetElements, onLoadProject, onLoadToObject } from '../../utils'
|
import { onLoadGetEdges, onLoadGetNodes, onLoadProject, onLoadToObject } from '../../utils'
|
||||||
|
|
||||||
interface RendererProps {
|
|
||||||
elements: Elements
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<RendererProps>()
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
||||||
// if there are preloaded elements we overwrite the current elements with the stored ones and reset the stored elements (so we can properly parse them)
|
|
||||||
await store.setElements(props.elements, false)
|
|
||||||
store.isReady = true
|
store.isReady = true
|
||||||
|
|
||||||
nextTick(async () => {
|
nextTick(async () => {
|
||||||
@@ -28,7 +20,8 @@ nextTick(async () => {
|
|||||||
zoomTo,
|
zoomTo,
|
||||||
setTransform,
|
setTransform,
|
||||||
project: onLoadProject(store),
|
project: onLoadProject(store),
|
||||||
getElements: onLoadGetElements(store),
|
getNodes: onLoadGetNodes(store),
|
||||||
|
getEdges: onLoadGetEdges(store),
|
||||||
toObject: onLoadToObject(store),
|
toObject: onLoadToObject(store),
|
||||||
}
|
}
|
||||||
store.hooks.load.trigger(instance)
|
store.hooks.load.trigger(instance)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { createHooks, initFlow } from '../../composables'
|
|||||||
import type { FlowProps } from '../../types/flow'
|
import type { FlowProps } from '../../types/flow'
|
||||||
|
|
||||||
const props = withDefaults(defineProps<FlowProps>(), {
|
const props = withDefaults(defineProps<FlowProps>(), {
|
||||||
modelValue: () => [],
|
|
||||||
snapToGrid: false,
|
snapToGrid: false,
|
||||||
onlyRenderVisibleElements: false,
|
onlyRenderVisibleElements: false,
|
||||||
edgesUpdatable: false,
|
edgesUpdatable: false,
|
||||||
@@ -24,38 +23,15 @@ const props = withDefaults(defineProps<FlowProps>(), {
|
|||||||
panOnScroll: false,
|
panOnScroll: false,
|
||||||
paneMoveable: true,
|
paneMoveable: true,
|
||||||
})
|
})
|
||||||
const emit = defineEmits([...Object.keys(createHooks()), 'update:modelValue'])
|
const emit = defineEmits([...Object.keys(createHooks()), 'update:nodes', 'update:edges'])
|
||||||
const store = initFlow(emit, props.id)
|
const store = initFlow(emit, props.id)
|
||||||
const elements = useVModel(props, 'modelValue', emit)
|
const { nodes, edges } = useVModels(props, emit)
|
||||||
store.setState(props)
|
store.setState(props)
|
||||||
watch(
|
watch(
|
||||||
() => props,
|
() => props,
|
||||||
() => store.setState(props),
|
() => store.setState(props),
|
||||||
{ flush: 'post', deep: true },
|
{ flush: 'post', deep: true },
|
||||||
)
|
)
|
||||||
const { pause: pauseModel, resume: resumeModel } = pausableWatch(
|
|
||||||
() => props.modelValue.length,
|
|
||||||
async () => await store.setElements(elements.value),
|
|
||||||
)
|
|
||||||
const { pause, resume } = pausableWatch(elements, async (val) => await store.setElements(val), { flush: 'post' })
|
|
||||||
|
|
||||||
until(() => store.isReady)
|
|
||||||
.toBe(true)
|
|
||||||
.then(() => {
|
|
||||||
watch(
|
|
||||||
() => store.elements,
|
|
||||||
(val) => {
|
|
||||||
pause()
|
|
||||||
pauseModel()
|
|
||||||
elements.value = val
|
|
||||||
setTimeout(() => {
|
|
||||||
resume()
|
|
||||||
resumeModel()
|
|
||||||
}, 1)
|
|
||||||
},
|
|
||||||
{ flush: 'post', deep: true },
|
|
||||||
)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
@@ -66,7 +42,7 @@ export default {
|
|||||||
<div class="vue-flow">
|
<div class="vue-flow">
|
||||||
<Suspense timeout="0">
|
<Suspense timeout="0">
|
||||||
<template #default>
|
<template #default>
|
||||||
<Renderer :key="`renderer-${store.id}`" :elements="elements">
|
<Renderer :key="`renderer-${store.id}`">
|
||||||
<ZoomPane :key="`zoom-pane-${store.id}`">
|
<ZoomPane :key="`zoom-pane-${store.id}`">
|
||||||
<template #default="zoomPaneProps">
|
<template #default="zoomPaneProps">
|
||||||
<SelectionPane :key="`selection-pane-${store.id}`">
|
<SelectionPane :key="`selection-pane-${store.id}`">
|
||||||
|
|||||||
+44
-16
@@ -1,15 +1,7 @@
|
|||||||
import { Elements, FlowActions, FlowGetters, FlowState } from '~/types'
|
import { CoordinateExtent, Edge, FlowActions, FlowGetters, FlowState, GraphNode, Node } from '~/types'
|
||||||
import { getConnectedEdges, getNodesInside, getRectOfNodes, isGraphNode, parseElements, processElements } from '~/utils'
|
import { getConnectedEdges, getNodesInside, getRectOfNodes, isNode, parseEdge, parseNode } from '~/utils'
|
||||||
|
|
||||||
export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
||||||
const setElements: FlowActions['setElements'] = async (elements, force = true) => {
|
|
||||||
const { nodes, edges } = parseElements(elements, state.elements, state.nodeExtent)
|
|
||||||
if (force) state.elements = []
|
|
||||||
await processElements([...nodes, ...edges], (processed) => {
|
|
||||||
state.elements = [...state.elements, ...processed]
|
|
||||||
})
|
|
||||||
state.hooks.elementsProcessed.trigger(state.elements)
|
|
||||||
}
|
|
||||||
const setUserSelection: FlowActions['setUserSelection'] = (mousePos) => {
|
const setUserSelection: FlowActions['setUserSelection'] = (mousePos) => {
|
||||||
state.selectionActive = true
|
state.selectionActive = true
|
||||||
state.userSelectionRect = {
|
state.userSelectionRect = {
|
||||||
@@ -26,7 +18,7 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
const startX = state.userSelectionRect.startX
|
const startX = state.userSelectionRect.startX
|
||||||
const startY = state.userSelectionRect.startY
|
const startY = state.userSelectionRect.startY
|
||||||
|
|
||||||
state.selectedElements?.forEach((el) => isGraphNode(el) && (el.selected = undefined))
|
state.selectedElements?.forEach((el) => isNode(el) && (el.selected = undefined))
|
||||||
const nextUserSelectRect: FlowState['userSelectionRect'] = {
|
const nextUserSelectRect: FlowState['userSelectionRect'] = {
|
||||||
...state.userSelectionRect,
|
...state.userSelectionRect,
|
||||||
x: mousePos.x < startX ? mousePos.x : state.userSelectionRect.x,
|
x: mousePos.x < startX ? mousePos.x : state.userSelectionRect.x,
|
||||||
@@ -38,7 +30,7 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
const selectedEdges = getConnectedEdges(selectedNodes, getters.getEdges.value)
|
const selectedEdges = getConnectedEdges(selectedNodes, getters.getEdges.value)
|
||||||
|
|
||||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
||||||
nextSelectedElements.forEach((el) => isGraphNode(el) && (el.selected = true))
|
nextSelectedElements.forEach((el) => isNode(el) && (el.selected = true))
|
||||||
state.userSelectionRect = nextUserSelectRect
|
state.userSelectionRect = nextUserSelectRect
|
||||||
state.selectedElements = nextSelectedElements
|
state.selectedElements = nextSelectedElements
|
||||||
}
|
}
|
||||||
@@ -103,10 +95,48 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
state.nodesConnectable = isInteractive
|
state.nodesConnectable = isInteractive
|
||||||
state.elementsSelectable = isInteractive
|
state.elementsSelectable = isInteractive
|
||||||
}
|
}
|
||||||
const addElements: FlowActions['addElements'] = (elements: Elements) => {
|
const setNodes = (nodes: Node[], extent: CoordinateExtent) => {
|
||||||
setElements(elements, false)
|
const parseChildren = (n: Node, arr: GraphNode[] = []) => {
|
||||||
|
arr.concat(parseNode(n, extent))
|
||||||
|
if (n.children && n.children.length) {
|
||||||
|
n.children.forEach((c) => parseChildren(c))
|
||||||
|
} else {
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nodes = nodes.flatMap((node) => {
|
||||||
|
console.log('flatmap')
|
||||||
|
const parsed = parseNode(node, extent)
|
||||||
|
const children: GraphNode[] = []
|
||||||
|
if (node.children && node.children.length) {
|
||||||
|
node.children.forEach((c) => parseChildren(c, children))
|
||||||
|
}
|
||||||
|
return [parsed, ...children]
|
||||||
|
})
|
||||||
|
state.nodes = <GraphNode[]>nodes
|
||||||
}
|
}
|
||||||
|
const setEdges = (edges: Edge[]) => {
|
||||||
|
state.edges = edges.flatMap((edge) => {
|
||||||
|
const parsed = parseEdge(edge)
|
||||||
|
|
||||||
|
const sourceNode = getters.getNode.value(edge.source)!
|
||||||
|
const targetNode = getters.getNode.value(edge.target)!
|
||||||
|
if (!sourceNode || typeof sourceNode === 'undefined')
|
||||||
|
console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`)
|
||||||
|
if (!targetNode || typeof targetNode === 'undefined')
|
||||||
|
console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...parsed,
|
||||||
|
sourceNode,
|
||||||
|
targetNode,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const setState: FlowActions['setState'] = (opts) => {
|
const setState: FlowActions['setState'] = (opts) => {
|
||||||
|
if (typeof opts.nodes !== 'undefined') setNodes(opts.nodes, opts.nodeExtent ?? state.nodeExtent)
|
||||||
|
if (typeof opts.edges !== 'undefined') setEdges(opts.edges)
|
||||||
if (typeof opts.loading !== 'undefined') state.loading = opts.loading
|
if (typeof opts.loading !== 'undefined') state.loading = opts.loading
|
||||||
if (typeof opts.panOnScroll !== 'undefined') state.panOnScroll = opts.panOnScroll
|
if (typeof opts.panOnScroll !== 'undefined') state.panOnScroll = opts.panOnScroll
|
||||||
if (typeof opts.panOnScrollMode !== 'undefined') state.panOnScrollMode = opts.panOnScrollMode
|
if (typeof opts.panOnScrollMode !== 'undefined') state.panOnScrollMode = opts.panOnScrollMode
|
||||||
@@ -151,7 +181,6 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
setElements,
|
|
||||||
setUserSelection,
|
setUserSelection,
|
||||||
updateUserSelection,
|
updateUserSelection,
|
||||||
unsetUserSelection,
|
unsetUserSelection,
|
||||||
@@ -165,7 +194,6 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
updateSize,
|
updateSize,
|
||||||
setConnectionNodeId,
|
setConnectionNodeId,
|
||||||
setInteractive,
|
setInteractive,
|
||||||
addElements,
|
|
||||||
setState,
|
setState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-13
@@ -1,5 +1,5 @@
|
|||||||
import { FlowGetters, FlowState, GraphEdge, GraphNode, Node } from '~/types'
|
import { FlowGetters, FlowState, GraphEdge, GraphNode } from '~/types'
|
||||||
import { defaultEdgeTypes, defaultNodeTypes, getNodesInside, getSourceTargetNodes, isEdge, isGraphNode } from '~/utils'
|
import { defaultEdgeTypes, defaultNodeTypes, getNodesInside, getSourceTargetNodes, isGraphNode } from '~/utils'
|
||||||
|
|
||||||
export default (state: FlowState): FlowGetters => {
|
export default (state: FlowState): FlowGetters => {
|
||||||
const getEdgeTypes = computed(() => {
|
const getEdgeTypes = computed(() => {
|
||||||
@@ -20,15 +20,8 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
|
|
||||||
const getNodes = computed<GraphNode[]>(() => {
|
const getNodes = computed<GraphNode[]>(() => {
|
||||||
if (state.isReady && state.dimensions.width && state.dimensions.height) {
|
if (state.isReady && state.dimensions.width && state.dimensions.height) {
|
||||||
console.log('getter')
|
console.log('getNodes')
|
||||||
const nodes: GraphNode[] = []
|
const nodes = state.nodes.filter((n) => !n.hidden)
|
||||||
const parseNode = (n: Node) => {
|
|
||||||
nodes.push(<GraphNode>n)
|
|
||||||
if (n.children && n.children.length) {
|
|
||||||
n.children.forEach((c) => parseNode(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.elements.filter((n) => isGraphNode(n) && !n.hidden).forEach((node) => parseNode(<GraphNode>node))
|
|
||||||
return state.onlyRenderVisibleElements
|
return state.onlyRenderVisibleElements
|
||||||
? nodes &&
|
? nodes &&
|
||||||
getNodesInside(
|
getNodesInside(
|
||||||
@@ -48,9 +41,8 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const getEdges = computed<GraphEdge[]>(() => {
|
const getEdges = computed<GraphEdge[]>(() => {
|
||||||
const edges = state.elements.filter((e) => isEdge(e) && !e.hidden) as GraphEdge[]
|
const edges = state.edges.filter((e) => !e.hidden)
|
||||||
if (state.isReady && state.dimensions.width && state.dimensions.height) {
|
if (state.isReady && state.dimensions.width && state.dimensions.height) {
|
||||||
console.log('getter2')
|
|
||||||
return (
|
return (
|
||||||
edges
|
edges
|
||||||
.map((edge) => {
|
.map((edge) => {
|
||||||
@@ -72,7 +64,12 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
|
|
||||||
const getSelectedNodes = computed<GraphNode[]>(() => state.selectedElements?.filter(isGraphNode) ?? [])
|
const getSelectedNodes = computed<GraphNode[]>(() => state.selectedElements?.filter(isGraphNode) ?? [])
|
||||||
|
|
||||||
|
const getNode: FlowGetters['getNode'] = computed(() => (id: string) => state.nodes.find((node) => node.id === id))
|
||||||
|
const getEdge: FlowGetters['getEdge'] = computed(() => (id: string) => state.edges.find((edge) => edge.id === id))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
getNode,
|
||||||
|
getEdge,
|
||||||
getEdgeTypes,
|
getEdgeTypes,
|
||||||
getNodeTypes,
|
getNodeTypes,
|
||||||
getEdges,
|
getEdges,
|
||||||
|
|||||||
+5
-48
@@ -1,9 +1,8 @@
|
|||||||
import { Component, CSSProperties, DefineComponent } from 'vue'
|
import { Component, CSSProperties, DefineComponent } from 'vue'
|
||||||
import { BackgroundVariant, Dimensions, Elements, FitViewParams, FlowOptions, Position, XYPosition } from './flow'
|
import { BackgroundVariant, Dimensions, FitViewParams, Position, XYPosition } from './flow'
|
||||||
import { Connection, ConnectionLineType, ConnectionMode } from './connection'
|
import { Connection, ConnectionLineType } from './connection'
|
||||||
import { GraphNode, Node, NodeProps, CoordinateExtent } from './node'
|
import { GraphNode, Node, NodeProps } from './node'
|
||||||
import { EdgeProps } from './edge'
|
import { EdgeProps } from './edge'
|
||||||
import { KeyCode, PanOnScrollMode } from './zoom'
|
|
||||||
|
|
||||||
export type DefaultEdgeTypes = { [key in 'default' | 'straight' | 'smoothstep' | 'step']: Component<EdgeProps> }
|
export type DefaultEdgeTypes = { [key in 'default' | 'straight' | 'smoothstep' | 'step']: Component<EdgeProps> }
|
||||||
export type EdgeTypes = (keyof DefaultEdgeTypes | string)[]
|
export type EdgeTypes = (keyof DefaultEdgeTypes | string)[]
|
||||||
@@ -63,10 +62,8 @@ export interface MiniMapProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MiniMapNodeProps {
|
export interface MiniMapNodeProps {
|
||||||
x?: number
|
position: XYPosition
|
||||||
y?: number
|
dimensions: Dimensions
|
||||||
width?: number
|
|
||||||
height?: number
|
|
||||||
borderRadius?: number
|
borderRadius?: number
|
||||||
color?: string
|
color?: string
|
||||||
shapeRendering?: CSSProperties['shapeRendering']
|
shapeRendering?: CSSProperties['shapeRendering']
|
||||||
@@ -103,43 +100,3 @@ export interface CustomConnectionLineProps {
|
|||||||
sourceNode: GraphNode
|
sourceNode: GraphNode
|
||||||
sourceHandle: HandleElement
|
sourceHandle: HandleElement
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowProps extends FlowOptions {
|
|
||||||
id?: string
|
|
||||||
modelValue?: Elements
|
|
||||||
nodeTypes?: NodeTypes
|
|
||||||
edgeTypes?: EdgeTypes
|
|
||||||
connectionMode?: ConnectionMode
|
|
||||||
connectionLineType?: ConnectionLineType
|
|
||||||
connectionLineStyle?: CSSProperties
|
|
||||||
deleteKeyCode?: KeyCode
|
|
||||||
selectionKeyCode?: KeyCode
|
|
||||||
multiSelectionKeyCode?: KeyCode
|
|
||||||
zoomActivationKeyCode?: KeyCode
|
|
||||||
preventScrolling?: boolean
|
|
||||||
snapToGrid?: boolean
|
|
||||||
snapGrid?: [number, number]
|
|
||||||
onlyRenderVisibleElements?: boolean
|
|
||||||
nodesDraggable?: boolean
|
|
||||||
nodesConnectable?: boolean
|
|
||||||
elementsSelectable?: boolean
|
|
||||||
selectNodesOnDrag?: boolean
|
|
||||||
paneMoveable?: boolean
|
|
||||||
minZoom?: number
|
|
||||||
maxZoom?: number
|
|
||||||
defaultZoom?: number
|
|
||||||
defaultPosition?: [number, number]
|
|
||||||
translateExtent?: CoordinateExtent
|
|
||||||
nodeExtent?: CoordinateExtent
|
|
||||||
arrowHeadColor?: string
|
|
||||||
markerEndId?: string
|
|
||||||
zoomOnScroll?: boolean
|
|
||||||
zoomOnPinch?: boolean
|
|
||||||
panOnScroll?: boolean
|
|
||||||
panOnScrollSpeed?: number
|
|
||||||
panOnScrollMode?: PanOnScrollMode
|
|
||||||
zoomOnDoubleClick?: boolean
|
|
||||||
edgeUpdaterRadius?: number
|
|
||||||
storageKey?: string
|
|
||||||
loading?: string
|
|
||||||
}
|
|
||||||
|
|||||||
+15
-13
@@ -1,6 +1,6 @@
|
|||||||
import { CSSProperties } from 'vue'
|
import { CSSProperties } from 'vue'
|
||||||
import { Edge, GraphEdge } from './edge'
|
import { GraphEdge, Edge } from './edge'
|
||||||
import { GraphNode, Node, CoordinateExtent } from './node'
|
import { GraphNode, CoordinateExtent, Node } from './node'
|
||||||
import { ConnectionLineType, ConnectionMode } from './connection'
|
import { ConnectionLineType, ConnectionMode } from './connection'
|
||||||
import { KeyCode, PanOnScrollMode } from './zoom'
|
import { KeyCode, PanOnScrollMode } from './zoom'
|
||||||
import { EdgeTypes, NodeTypes } from './components'
|
import { EdgeTypes, NodeTypes } from './components'
|
||||||
@@ -82,8 +82,9 @@ export type FitViewParams = {
|
|||||||
nodes?: string[]
|
nodes?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FlowExportObject<T = any> = {
|
export type FlowExportObject<DataNode = any, DataEdge = DataNode> = {
|
||||||
elements: FlowElements<T>
|
nodes: GraphNode<DataNode>[]
|
||||||
|
edges: GraphEdge<DataEdge>[]
|
||||||
position: [number, number]
|
position: [number, number]
|
||||||
zoom: number
|
zoom: number
|
||||||
}
|
}
|
||||||
@@ -96,21 +97,24 @@ export type FlowTransform = {
|
|||||||
|
|
||||||
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void
|
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void
|
||||||
export type ProjectFunc = (position: XYPosition) => XYPosition
|
export type ProjectFunc = (position: XYPosition) => XYPosition
|
||||||
export type ToObjectFunc<T = any> = () => FlowExportObject<T>
|
export type ToObjectFunc<DataNode = any, DataEdge = DataNode> = () => FlowExportObject<DataNode, DataEdge>
|
||||||
|
|
||||||
export type FlowInstance<T = any> = {
|
export type FlowInstance<DataNode = any, DataEdge = DataNode> = {
|
||||||
zoomIn: () => void
|
zoomIn: () => void
|
||||||
zoomOut: () => void
|
zoomOut: () => void
|
||||||
zoomTo: (zoomLevel: number) => void
|
zoomTo: (zoomLevel: number) => void
|
||||||
fitView: FitViewFunc
|
fitView: FitViewFunc
|
||||||
project: ProjectFunc
|
project: ProjectFunc
|
||||||
getElements: () => FlowElements<T>
|
getNodes: () => GraphNode[]
|
||||||
|
getEdges: () => GraphEdge[]
|
||||||
setTransform: (transform: FlowTransform) => void
|
setTransform: (transform: FlowTransform) => void
|
||||||
toObject: ToObjectFunc<T>
|
toObject: ToObjectFunc<DataNode, DataEdge>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowProps {
|
export interface FlowProps<DataNode = any, DataEdge = DataNode> {
|
||||||
modelValue?: Elements
|
nodes: GraphNode<DataNode>[]
|
||||||
|
edges?: GraphEdge<DataEdge>[]
|
||||||
|
elements?: Elements
|
||||||
id?: string
|
id?: string
|
||||||
nodeTypes?: NodeTypes
|
nodeTypes?: NodeTypes
|
||||||
edgeTypes?: EdgeTypes
|
edgeTypes?: EdgeTypes
|
||||||
@@ -150,6 +154,4 @@ export interface FlowProps {
|
|||||||
loading?: string
|
loading?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowOptions extends Omit<FlowProps, 'modelValue'> {
|
export type FlowOptions = FlowProps
|
||||||
elements?: Elements
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-6
@@ -1,5 +1,5 @@
|
|||||||
import { DraggableOptions } from '@braks/revue-draggable'
|
import { DraggableOptions } from '@braks/revue-draggable'
|
||||||
import { XYPosition, Position, SnapGrid, Element, XYZPosition } from './flow'
|
import { XYPosition, Position, SnapGrid, Element, XYZPosition, Dimensions } from './flow'
|
||||||
import { HandleElement, ValidConnectionFunc } from './components'
|
import { HandleElement, ValidConnectionFunc } from './components'
|
||||||
|
|
||||||
export type CoordinateExtent = [[number, number], [number, number]]
|
export type CoordinateExtent = [[number, number], [number, number]]
|
||||||
@@ -19,8 +19,7 @@ export interface Node<T = any> extends Element<T> {
|
|||||||
isValidSourcePos?: ValidConnectionFunc
|
isValidSourcePos?: ValidConnectionFunc
|
||||||
extent?: 'parent' | CoordinateExtent
|
extent?: 'parent' | CoordinateExtent
|
||||||
children?: Node[]
|
children?: Node[]
|
||||||
width?: number
|
dimensions?: Dimensions
|
||||||
height?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphNode<T = any> extends Node<T> {
|
export interface GraphNode<T = any> extends Node<T> {
|
||||||
@@ -33,8 +32,7 @@ export interface GraphNode<T = any> extends Node<T> {
|
|||||||
computedPosition: XYZPosition
|
computedPosition: XYZPosition
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
dragging?: boolean
|
dragging?: boolean
|
||||||
width: number
|
dimensions: Dimensions
|
||||||
height: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeProps<T = any> extends GraphNode {
|
export interface NodeProps<T = any> extends GraphNode {
|
||||||
@@ -60,5 +58,5 @@ export interface NodeProps<T = any> extends GraphNode {
|
|||||||
export type NodeDimensionUpdate = {
|
export type NodeDimensionUpdate = {
|
||||||
id: string
|
id: string
|
||||||
nodeElement: HTMLDivElement
|
nodeElement: HTMLDivElement
|
||||||
forceUpdate?: boolean
|
dimensions: Dimensions
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -23,7 +23,9 @@ export interface FlowState extends Omit<FlowOptions, 'elements' | 'id'> {
|
|||||||
hooks: FlowHooks
|
hooks: FlowHooks
|
||||||
instance?: FlowInstance
|
instance?: FlowInstance
|
||||||
|
|
||||||
elements: FlowElements
|
elements: Elements
|
||||||
|
nodes: GraphNode[]
|
||||||
|
edges: GraphEdge[]
|
||||||
nodeTypes: NodeTypes
|
nodeTypes: NodeTypes
|
||||||
edgeTypes: EdgeTypes
|
edgeTypes: EdgeTypes
|
||||||
|
|
||||||
@@ -83,7 +85,6 @@ export interface FlowState extends Omit<FlowOptions, 'elements' | 'id'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowActions {
|
export interface FlowActions {
|
||||||
setElements: (elements: Elements, init?: boolean) => Promise<void>
|
|
||||||
setUserSelection: (mousePos: XYPosition) => void
|
setUserSelection: (mousePos: XYPosition) => void
|
||||||
updateUserSelection: (mousePos: XYPosition) => void
|
updateUserSelection: (mousePos: XYPosition) => void
|
||||||
unsetUserSelection: () => void
|
unsetUserSelection: () => void
|
||||||
@@ -97,7 +98,6 @@ export interface FlowActions {
|
|||||||
updateSize: (size: Dimensions) => void
|
updateSize: (size: Dimensions) => void
|
||||||
setConnectionNodeId: (payload: SetConnectionId) => void
|
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||||
setInteractive: (isInteractive: boolean) => void
|
setInteractive: (isInteractive: boolean) => void
|
||||||
addElements: (elements: Elements) => void
|
|
||||||
setState: (state: Partial<FlowOptions>) => void
|
setState: (state: Partial<FlowOptions>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +106,8 @@ export interface FlowGetters {
|
|||||||
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
|
getNodeTypes: ComputedRef<Record<string, NodeComponent>>
|
||||||
getNodes: ComputedRef<GraphNode[]>
|
getNodes: ComputedRef<GraphNode[]>
|
||||||
getEdges: ComputedRef<GraphEdge[]>
|
getEdges: ComputedRef<GraphEdge[]>
|
||||||
|
getNode: ComputedRef<(id: string) => GraphNode | undefined>
|
||||||
|
getEdge: ComputedRef<(id: string) => GraphEdge | undefined>
|
||||||
getSelectedNodes: ComputedRef<GraphNode[]>
|
getSelectedNodes: ComputedRef<GraphNode[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -4,8 +4,8 @@ import { Edge, EdgePositions, GraphNode, HandleElement, Position, Transform, XYP
|
|||||||
export function getHandlePosition(position: Position, node: GraphNode, handle?: HandleElement): XYPosition {
|
export function getHandlePosition(position: Position, node: GraphNode, handle?: HandleElement): XYPosition {
|
||||||
const x = (handle?.x ?? 0) + node.position.x
|
const x = (handle?.x ?? 0) + node.position.x
|
||||||
const y = (handle?.y ?? 0) + node.position.y
|
const y = (handle?.y ?? 0) + node.position.y
|
||||||
const width = handle?.width ?? node.width
|
const width = handle?.width ?? node.dimensions.width
|
||||||
const height = handle?.height ?? node.height
|
const height = handle?.height ?? node.dimensions.height
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case Position.Top:
|
case Position.Top:
|
||||||
|
|||||||
+13
-10
@@ -169,8 +169,10 @@ export const parseNode = (node: Node, nodeExtent: CoordinateExtent): GraphNode =
|
|||||||
...node,
|
...node,
|
||||||
id: node.id.toString(),
|
id: node.id.toString(),
|
||||||
type: node.type ?? 'default',
|
type: node.type ?? 'default',
|
||||||
width: 0,
|
dimensions: {
|
||||||
height: 0,
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
handleBounds: {
|
handleBounds: {
|
||||||
source: [],
|
source: [],
|
||||||
target: [],
|
target: [],
|
||||||
@@ -179,6 +181,7 @@ export const parseNode = (node: Node, nodeExtent: CoordinateExtent): GraphNode =
|
|||||||
z: typeof node.style?.zIndex === 'string' ? parseInt(node.style?.zIndex) : node.style?.zIndex ?? 0,
|
z: typeof node.style?.zIndex === 'string' ? parseInt(node.style?.zIndex) : node.style?.zIndex ?? 0,
|
||||||
...clampPosition(node.position, nodeExtent),
|
...clampPosition(node.position, nodeExtent),
|
||||||
},
|
},
|
||||||
|
isParent: !!(node.children && node.children.length),
|
||||||
dragging: false,
|
dragging: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -219,13 +222,12 @@ export const getBoundsofRects = (rect1: Rect, rect2: Rect) => boxToRect(getBound
|
|||||||
|
|
||||||
export const getRectOfNodes = (nodes: GraphNode[]) => {
|
export const getRectOfNodes = (nodes: GraphNode[]) => {
|
||||||
const box = nodes.reduce(
|
const box = nodes.reduce(
|
||||||
(currBox, { position = { x: 0, y: 0 }, width = 0, height = 0 } = {} as any) =>
|
(currBox, { position = { x: 0, y: 0 }, dimensions = { width: 0, height: 0 } } = {} as any) =>
|
||||||
getBoundsOfBoxes(
|
getBoundsOfBoxes(
|
||||||
currBox,
|
currBox,
|
||||||
rectToBox({
|
rectToBox({
|
||||||
...position,
|
...position,
|
||||||
width,
|
...dimensions,
|
||||||
height,
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity },
|
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity },
|
||||||
@@ -249,13 +251,13 @@ export const getNodesInside = (nodes: GraphNode[], rect: Rect, [tx, ty, tScale]:
|
|||||||
|
|
||||||
return nodes.filter((node) => {
|
return nodes.filter((node) => {
|
||||||
if (!node || node.selectable === false) return false
|
if (!node || node.selectable === false) return false
|
||||||
const { position = { x: 0, y: 0 }, width = 0, height = 0, dragging = false } = node
|
const { position = { x: 0, y: 0 }, dimensions = { width: 0, height: 0 }, dragging = false } = node
|
||||||
const nBox = rectToBox({ ...position, width, height } as any)
|
const nBox = rectToBox({ ...position, ...dimensions })
|
||||||
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x))
|
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x))
|
||||||
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y))
|
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y))
|
||||||
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||||
|
|
||||||
if (width === null || height === null || dragging) {
|
if (dimensions.width === null || dimensions.height === null || dragging) {
|
||||||
// nodes are initialized with width and height = null
|
// nodes are initialized with width and height = null
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -264,7 +266,7 @@ export const getNodesInside = (nodes: GraphNode[], rect: Rect, [tx, ty, tScale]:
|
|||||||
return overlappingArea > 0
|
return overlappingArea > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const area = width * height
|
const area = dimensions.width * dimensions.height
|
||||||
|
|
||||||
return overlappingArea >= area
|
return overlappingArea >= area
|
||||||
})
|
})
|
||||||
@@ -275,7 +277,8 @@ export const getConnectedEdges = (nodes: GraphNode[], edges: GraphEdge[]) => {
|
|||||||
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
|
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const onLoadGetElements = (currentStore: FlowStore) => (): FlowElements => currentStore.elements
|
export const onLoadGetNodes = (currentStore: FlowStore) => (): FlowElements => currentStore.nodes
|
||||||
|
export const onLoadGetEdges = (currentStore: FlowStore) => (): FlowElements => currentStore.edges
|
||||||
|
|
||||||
export const onLoadToObject = (currentStore: FlowState) => (): FlowExportObject => {
|
export const onLoadToObject = (currentStore: FlowState) => (): FlowExportObject => {
|
||||||
// we have to stringify/parse so objects containing refs (like nodes and edges) can potentially be saved in a storage
|
// we have to stringify/parse so objects containing refs (like nodes and edges) can potentially be saved in a storage
|
||||||
|
|||||||
+7
-3
@@ -16,16 +16,20 @@ export const defaultEdgeTypes: DefaultEdgeTypes = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const initialState = (): FlowState => ({
|
export const initialState = (): FlowState => ({
|
||||||
|
elements: [],
|
||||||
|
nodes: [],
|
||||||
|
edges: [],
|
||||||
|
nodeTypes: [],
|
||||||
|
edgeTypes: [],
|
||||||
|
|
||||||
isReady: false,
|
isReady: false,
|
||||||
instance: undefined,
|
instance: undefined,
|
||||||
|
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
},
|
},
|
||||||
transform: [0, 0, 1],
|
transform: [0, 0, 1],
|
||||||
elements: [],
|
|
||||||
nodeTypes: [],
|
|
||||||
edgeTypes: [],
|
|
||||||
selectedElements: undefined,
|
selectedElements: undefined,
|
||||||
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
|
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user