feat(store, flow)!: Add apply changes handlers
* flow will not update changes unless a handler to apply changes is passed * a default handler is available in the useVueFlow composable * removeElements export removed
This commit is contained in:
@@ -42,7 +42,6 @@ const onSelectNodeHandler = (event: MouseEvent) => {
|
|||||||
store.nodesSelectionActive = false
|
store.nodesSelectionActive = false
|
||||||
if (!node.value.selected) store.addSelectedNodes([node.value])
|
if (!node.value.selected) store.addSelectedNodes([node.value])
|
||||||
}
|
}
|
||||||
store.hooks.elementClick.trigger({ event, element: node.value })
|
|
||||||
store.hooks.nodeClick.trigger({ event, node: node.value })
|
store.hooks.nodeClick.trigger({ event, node: node.value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +70,6 @@ const onDragStop: DraggableEventListener = ({ event, data: { deltaX, deltaY } })
|
|||||||
store.addSelectedNodes([node.value])
|
store.addSelectedNodes([node.value])
|
||||||
}
|
}
|
||||||
store.hooks.nodeClick.trigger({ event, node: node.value })
|
store.hooks.nodeClick.trigger({ event, node: node.value })
|
||||||
store.hooks.elementClick.trigger({ event, element: node.value })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
store.updateNodePosition({ id: node.value.id, diff: { x: deltaX, y: deltaY }, dragging: false })
|
store.updateNodePosition({ id: node.value.id, diff: { x: deltaX, y: deltaY }, dragging: false })
|
||||||
|
|||||||
@@ -2,31 +2,27 @@
|
|||||||
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
|
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
|
||||||
import { useVueFlow } from '../../composables'
|
import { useVueFlow } from '../../composables'
|
||||||
import { getRectOfNodes } from '../../utils'
|
import { getRectOfNodes } from '../../utils'
|
||||||
import { GraphNode } from '../../types'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
nodes: GraphNode[]
|
|
||||||
}
|
|
||||||
const { store } = useVueFlow()
|
const { store } = useVueFlow()
|
||||||
const props = defineProps<Props>()
|
const selectedNodesBBox = computed(() => getRectOfNodes(store.getSelectedNodes))
|
||||||
const selectedNodesBBox = computed(() => getRectOfNodes(props.nodes))
|
|
||||||
const innerStyle = computed(() => ({
|
const innerStyle = computed(() => ({
|
||||||
width: `${selectedNodesBBox.value.width}px`,
|
width: `${selectedNodesBBox.value.width}px`,
|
||||||
height: `${selectedNodesBBox.value.height}px`,
|
height: `${selectedNodesBBox.value.height}px`,
|
||||||
top: `${selectedNodesBBox.value.y}px`,
|
top: `${selectedNodesBBox.value.y}px`,
|
||||||
left: `${selectedNodesBBox.value.x}px`,
|
left: `${selectedNodesBBox.value.x}px`,
|
||||||
}))
|
}))
|
||||||
watch(selectedNodesBBox, (v) => (store.selectedNodesBbox = v))
|
watch(selectedNodesBBox, (v) => nextTick(() => (store.selectedNodesBbox = v)))
|
||||||
const onStart: DraggableEventListener = ({ event }) => store.hooks.selectionDragStart.trigger({ event, nodes: props.nodes })
|
const onStart: DraggableEventListener = ({ event }) =>
|
||||||
|
store.hooks.selectionDragStart.trigger({ event, nodes: store.getSelectedNodes })
|
||||||
const onDrag: DraggableEventListener = ({ event, data: { deltaX, deltaY } }) => {
|
const onDrag: DraggableEventListener = ({ event, data: { deltaX, deltaY } }) => {
|
||||||
store.hooks.selectionDrag.trigger({ event, nodes: props.nodes })
|
store.hooks.selectionDrag.trigger({ event, nodes: store.getSelectedNodes })
|
||||||
store.updateNodePosition({ diff: { x: deltaX, y: deltaY }, dragging: true })
|
store.updateNodePosition({ diff: { x: deltaX, y: deltaY }, dragging: true })
|
||||||
}
|
}
|
||||||
const onStop: DraggableEventListener = ({ event }) => {
|
const onStop: DraggableEventListener = ({ event }) => {
|
||||||
store.hooks.selectionDragStop.trigger({ event, nodes: props.nodes })
|
store.hooks.selectionDragStop.trigger({ event, nodes: store.getSelectedNodes })
|
||||||
props.nodes.forEach((node) => (node.dragging = false))
|
store.getSelectedNodes.forEach((node) => (node.dragging = false))
|
||||||
}
|
}
|
||||||
const onContextMenu = (event: MouseEvent) => store.hooks.selectionContextMenu.trigger({ event, nodes: props.nodes })
|
const onContextMenu = (event: MouseEvent) => store.hooks.selectionContextMenu.trigger({ event, nodes: store.getSelectedNodes })
|
||||||
|
|
||||||
const transform = computed(() => `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`)
|
const transform = computed(() => `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ export { default as useHandle } from './useHandle'
|
|||||||
export { default as useKeyPress } from './useKeyPress'
|
export { default as useKeyPress } from './useKeyPress'
|
||||||
export { default as useZoomPanHelper } from './useZoomPanHelper'
|
export { default as useZoomPanHelper } from './useZoomPanHelper'
|
||||||
export { default as useWindow } from './useWindow'
|
export { default as useWindow } from './useWindow'
|
||||||
export { default as useVueFlow, initFlow } from './useVueFlow'
|
export { default as useVueFlow } from './useVueFlow'
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
EdgeChange,
|
EdgeChange,
|
||||||
EmitFunc,
|
|
||||||
FlowOptions,
|
FlowOptions,
|
||||||
FlowStore,
|
|
||||||
NodeChange,
|
NodeChange,
|
||||||
UseVueFlow,
|
UseVueFlow,
|
||||||
FlowElements,
|
FlowElements,
|
||||||
@@ -10,59 +8,45 @@ import {
|
|||||||
GraphNode,
|
GraphNode,
|
||||||
GraphEdge,
|
GraphEdge,
|
||||||
SelectionChange,
|
SelectionChange,
|
||||||
|
NodeDimensionChange,
|
||||||
|
CreatePositionChangeParams,
|
||||||
} from '~/types'
|
} from '~/types'
|
||||||
import { useStore, useHooks } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { isGraphNode } from '~/utils'
|
import { clampPosition, isGraphNode } from '~/utils'
|
||||||
|
|
||||||
const applyChanges = (changes: NodeChange[] | EdgeChange[], elements: FlowElements): FlowElements => {
|
const applyChanges = <T extends FlowElement = GraphNode, C extends NodeChange = T extends GraphNode ? NodeChange : EdgeChange>(
|
||||||
const initElements: FlowElements = []
|
changes: C[],
|
||||||
|
elements: T[],
|
||||||
return elements.reduce((res: FlowElements, item: FlowElement) => {
|
): T[] => {
|
||||||
const currentChange = changes.find((c) => c.id === item.id)
|
let elementIds = elements.map((el) => el.id)
|
||||||
|
changes.forEach((change) => {
|
||||||
if (currentChange) {
|
const i = elementIds.indexOf(change.id)
|
||||||
switch (currentChange.type) {
|
const el = elements[i]
|
||||||
case 'select': {
|
if (el) {
|
||||||
res.push({ ...item, selected: currentChange.selected })
|
switch (change.type) {
|
||||||
return res
|
case 'select':
|
||||||
}
|
el.selected = change.selected
|
||||||
case 'dimensions': {
|
break
|
||||||
const updateItem = { ...item }
|
case 'dimensions':
|
||||||
|
if (isGraphNode(el)) {
|
||||||
if (isGraphNode(updateItem)) {
|
if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions
|
||||||
if (typeof currentChange.dimensions !== 'undefined') {
|
if (typeof change.position !== 'undefined') el.position = change.position
|
||||||
updateItem.dimensions.width = currentChange.dimensions.width
|
if (typeof change.dragging !== 'undefined') el.dragging = change.dragging
|
||||||
updateItem.dimensions.height = currentChange.dimensions.height
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof currentChange.position !== 'undefined') {
|
|
||||||
updateItem.position = currentChange.position
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof currentChange.dragging !== 'undefined') {
|
|
||||||
updateItem.dragging = currentChange.dragging
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break
|
||||||
res.push(updateItem)
|
case 'remove':
|
||||||
return res
|
elements.splice(i, 1)
|
||||||
}
|
elementIds = elements.map((el) => el.id)
|
||||||
case 'remove': {
|
break
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
res.push(item)
|
return elements
|
||||||
return res
|
|
||||||
}, initElements)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]): GraphNode[] =>
|
const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes)
|
||||||
<GraphNode[]>applyChanges(changes, nodes)
|
const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges)
|
||||||
|
|
||||||
export const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]): GraphEdge[] =>
|
|
||||||
<GraphEdge[]>applyChanges(changes, edges)
|
|
||||||
|
|
||||||
export const createSelectionChange = (id: string, selected: boolean): SelectionChange => ({
|
export const createSelectionChange = (id: string, selected: boolean): SelectionChange => ({
|
||||||
id,
|
id,
|
||||||
@@ -70,6 +54,37 @@ export const createSelectionChange = (id: string, selected: boolean): SelectionC
|
|||||||
selected,
|
selected,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const createPositionChange = ({ node, diff, dragging, nodeExtent }: CreatePositionChangeParams): NodeDimensionChange => {
|
||||||
|
const change: NodeDimensionChange = {
|
||||||
|
id: node.id,
|
||||||
|
type: 'dimensions',
|
||||||
|
dragging: !!dragging,
|
||||||
|
handleBounds: node.handleBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff) {
|
||||||
|
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y }
|
||||||
|
let currentExtent = nodeExtent || node.extent
|
||||||
|
|
||||||
|
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) {
|
||||||
|
currentExtent =
|
||||||
|
node.parentNode?.dimensions.width && node.parentNode?.dimensions.height
|
||||||
|
? [
|
||||||
|
[0, 0],
|
||||||
|
[
|
||||||
|
node.parentNode.dimensions.width - node.dimensions.width,
|
||||||
|
node.parentNode.dimensions.height - node.dimensions.height,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
: currentExtent
|
||||||
|
}
|
||||||
|
|
||||||
|
change.position = currentExtent ? clampPosition(nextPosition, currentExtent) : nextPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
return change
|
||||||
|
}
|
||||||
|
|
||||||
export const getSelectionChanges = (items: FlowElements, selectedIds: string[]) => {
|
export const getSelectionChanges = (items: FlowElements, selectedIds: string[]) => {
|
||||||
return items.reduce((res, item) => {
|
return items.reduce((res, item) => {
|
||||||
const willBeSelected =
|
const willBeSelected =
|
||||||
@@ -87,12 +102,6 @@ export const getSelectionChanges = (items: FlowElements, selectedIds: string[])
|
|||||||
}, [] as SelectionChange[])
|
}, [] as SelectionChange[])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initFlow = (emit: EmitFunc, id?: string): FlowStore => {
|
|
||||||
const store = useStore({ id })
|
|
||||||
useHooks(store, emit)
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (options?: FlowOptions): UseVueFlow => {
|
export default (options?: FlowOptions): UseVueFlow => {
|
||||||
const store = useStore(options)
|
const store = useStore(options)
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ onMounted(() => {
|
|||||||
store.hooks.nodesChange.trigger(nodeChanges)
|
store.hooks.nodesChange.trigger(nodeChanges)
|
||||||
store.hooks.edgesChange.trigger(edgeChanges)
|
store.hooks.edgesChange.trigger(edgeChanges)
|
||||||
store.nodesSelectionActive = false
|
store.nodesSelectionActive = false
|
||||||
|
|
||||||
store.resetSelectedElements()
|
store.resetSelectedElements()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -56,9 +57,8 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<UserSelection v-if="store.selectionActive" :key="`user-selection-${store.id}`" />
|
<UserSelection v-if="store.selectionActive" :key="`user-selection-${store.id}`" />
|
||||||
<NodesSelection
|
<NodesSelection
|
||||||
v-if="store.selectedNodes.length && !store.selectionActive && store.nodesSelectionActive"
|
v-if="store.getSelectedNodes.length && !store.selectionActive && store.nodesSelectionActive"
|
||||||
:key="`nodes-selction-${store.id}`"
|
:key="`nodes-selction-${store.id}`"
|
||||||
:nodes="store.selectedNodes"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
:key="`flow-pane-${store.id}`"
|
:key="`flow-pane-${store.id}`"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { createHooks, initFlow } from '../../composables'
|
import { createHooks, initFlow } from '../../store'
|
||||||
import type { FlowProps } from '../../types/flow'
|
import type { FlowProps } from '../../types/flow'
|
||||||
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
import ZoomPane from '../ZoomPane/ZoomPane.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ nextTick(async () => {
|
|||||||
}
|
}
|
||||||
store.hooks.paneReady.trigger(instance)
|
store.hooks.paneReady.trigger(instance)
|
||||||
store.instance = instance
|
store.instance = instance
|
||||||
|
store.fitViewOnInit && instance.fitView()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export { getBezierPath, getSmoothStepPath, getMarkerEnd, getCenter as getEdgeCen
|
|||||||
export {
|
export {
|
||||||
isNode,
|
isNode,
|
||||||
isEdge,
|
isEdge,
|
||||||
removeElements,
|
|
||||||
addEdge,
|
addEdge,
|
||||||
getOutgoers,
|
getOutgoers,
|
||||||
getIncomers,
|
getIncomers,
|
||||||
|
|||||||
+36
-47
@@ -1,5 +1,17 @@
|
|||||||
import { CoordinateExtent, FlowActions, FlowGetters, FlowState, GraphNode, Node, XYPosition } from '~/types'
|
import {
|
||||||
|
CoordinateExtent,
|
||||||
|
EdgeChange,
|
||||||
|
FlowActions,
|
||||||
|
FlowGetters,
|
||||||
|
FlowState,
|
||||||
|
GraphNode,
|
||||||
|
Node,
|
||||||
|
NodeChange,
|
||||||
|
NodeDimensionChange,
|
||||||
|
XYPosition,
|
||||||
|
} from '~/types'
|
||||||
import { clampPosition, isEdge, isGraphEdge, isGraphNode, isNode, isParentSelected, parseEdge, parseNode } from '~/utils'
|
import { clampPosition, isEdge, isGraphEdge, isGraphNode, isNode, isParentSelected, parseEdge, parseNode } from '~/utils'
|
||||||
|
import { createPositionChange, createSelectionChange, getSelectionChanges } from '~/composables/useVueFlow'
|
||||||
|
|
||||||
const getParent = (root: Node[], id: string): GraphNode | undefined => {
|
const getParent = (root: Node[], id: string): GraphNode | undefined => {
|
||||||
let node
|
let node
|
||||||
@@ -11,67 +23,45 @@ const getParent = (root: Node[], id: string): GraphNode | undefined => {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePosition = (node: GraphNode, { x, y }: XYPosition = { x: 0, y: 0 }, extent: CoordinateExtent, dragging?: boolean) => {
|
|
||||||
let position = {
|
|
||||||
x: node.position.x + x,
|
|
||||||
y: node.position.y + y,
|
|
||||||
z: node.computedPosition.z,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.extent === 'parent' && node.parentNode && node.dimensions.width && node.dimensions.height) {
|
|
||||||
const parent = node.parentNode
|
|
||||||
extent =
|
|
||||||
parent.dimensions.width && parent.dimensions.height
|
|
||||||
? [
|
|
||||||
[0, 0],
|
|
||||||
[parent.dimensions.width - node.dimensions.width, parent.dimensions.height - node.dimensions.height],
|
|
||||||
]
|
|
||||||
: extent
|
|
||||||
}
|
|
||||||
node.dragging = dragging
|
|
||||||
const clamped = clampPosition(position, extent)
|
|
||||||
position = { ...position, ...clamped }
|
|
||||||
node.position = position
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
||||||
const updateNodePosition: FlowActions['updateNodePosition'] = ({ id, diff = { x: 0, y: 0 }, dragging }) => {
|
const updateNodePosition: FlowActions['updateNodePosition'] = ({ id, diff = { x: 0, y: 0 }, dragging }) => {
|
||||||
|
const changes: NodeDimensionChange[] = []
|
||||||
|
|
||||||
state.nodes.forEach((node) => {
|
state.nodes.forEach((node) => {
|
||||||
if (node.selected) {
|
if (node.selected) {
|
||||||
if (!node.parentNode) {
|
if (!node.parentNode) {
|
||||||
updatePosition(node, diff, state.nodeExtent, dragging)
|
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }))
|
||||||
} else if (!isParentSelected(node)) {
|
} else if (!isParentSelected(node)) {
|
||||||
updatePosition(node, diff, state.nodeExtent, dragging)
|
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }))
|
||||||
}
|
}
|
||||||
} else if (node.id === id) {
|
} else if (node.id === id) {
|
||||||
updatePosition(node, diff, state.nodeExtent, dragging)
|
changes.push(createPositionChange({ node, diff, nodeExtent: state.nodeExtent, dragging }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (changes.length) state.hooks.nodesChange.trigger(changes)
|
||||||
}
|
}
|
||||||
const emit = ref(true)
|
|
||||||
const addSelectedNodes: FlowActions['addSelectedNodes'] = (nodes) => {
|
const addSelectedNodes: FlowActions['addSelectedNodes'] = (nodes) => {
|
||||||
const selectedElementsUpdated = nodes.filter((el) => !state.selectedNodes.some((e) => el.id === e.id)).length
|
const selectedNodesIds = nodes.map((n) => n.id)
|
||||||
const selectedNodes = !nodes.length || selectedElementsUpdated ? nodes : state.selectedNodes
|
|
||||||
const selectedNodesIds = selectedNodes.map((n) => n.id)
|
let changedNodes: NodeChange[]
|
||||||
state.nodes.forEach(
|
if (state.multiSelectionActive) changedNodes = selectedNodesIds.map((nodeId) => createSelectionChange(nodeId, true))
|
||||||
(n) => (n.selected = selectedNodesIds.includes(n.id) || (n.parentNode && selectedNodesIds.includes(n.parentNode?.id))),
|
else changedNodes = getSelectionChanges(state.nodes, selectedNodesIds)
|
||||||
)
|
|
||||||
state.selectedNodes = selectedNodes
|
if (changedNodes.length) state.hooks.nodesChange.trigger(changedNodes)
|
||||||
if (emit.value && selectedElementsUpdated) state.hooks.selectionChange.trigger({ nodes })
|
|
||||||
}
|
}
|
||||||
const addSelectedEdges: FlowActions['addSelectedEdges'] = (edges) => {
|
const addSelectedEdges: FlowActions['addSelectedEdges'] = (edges) => {
|
||||||
const selectedElementsUpdated = edges.filter((el) => !state.selectedEdges.some((e) => el.id === e.id)).length
|
const selectedEdgesIds = edges.map((e) => e.id)
|
||||||
const selectedEdges = !edges.length || selectedElementsUpdated ? edges : state.selectedEdges
|
|
||||||
const selectedEdgesIds = selectedEdges.map((e) => e.id)
|
let changedEdges: EdgeChange[]
|
||||||
state.edges.forEach((e) => (e.selected = selectedEdgesIds.includes(e.id)))
|
if (state.multiSelectionActive) changedEdges = selectedEdgesIds.map((nodeId) => createSelectionChange(nodeId, true))
|
||||||
state.selectedEdges = selectedEdges
|
else changedEdges = getSelectionChanges(state.nodes, selectedEdgesIds)
|
||||||
if (emit.value && selectedElementsUpdated) state.hooks.selectionChange.trigger({ edges })
|
|
||||||
|
if (changedEdges.length) state.hooks.edgesChange.trigger(changedEdges)
|
||||||
}
|
}
|
||||||
const addSelectedElements: FlowActions['addSelectedElements'] = (elements) => {
|
const addSelectedElements: FlowActions['addSelectedElements'] = (elements) => {
|
||||||
emit.value = false
|
|
||||||
addSelectedNodes(elements.filter(isGraphNode))
|
addSelectedNodes(elements.filter(isGraphNode))
|
||||||
addSelectedEdges(elements.filter(isGraphEdge))
|
addSelectedEdges(elements.filter(isGraphEdge))
|
||||||
state.hooks.selectionChange.trigger({ nodes: state.selectedNodes, edges: state.selectedEdges })
|
|
||||||
}
|
}
|
||||||
const setMinZoom: FlowActions['setMinZoom'] = (minZoom: any) => {
|
const setMinZoom: FlowActions['setMinZoom'] = (minZoom: any) => {
|
||||||
state.d3Zoom?.scaleExtent([minZoom, state.maxZoom])
|
state.d3Zoom?.scaleExtent([minZoom, state.maxZoom])
|
||||||
@@ -116,10 +106,10 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setNodes: FlowActions['setNodes'] = (nodes, extent: CoordinateExtent) => {
|
const setNodes: FlowActions['setNodes'] = (nodes, extent?: CoordinateExtent) => {
|
||||||
nodes = nodes.flatMap((node) => {
|
nodes = nodes.flatMap((node) => {
|
||||||
const children: GraphNode[] = []
|
const children: GraphNode[] = []
|
||||||
parseChildren(node, undefined, children, extent)
|
parseChildren(node, undefined, children, extent ?? state.nodeExtent)
|
||||||
return children
|
return children
|
||||||
})
|
})
|
||||||
state.nodes = <GraphNode[]>nodes
|
state.nodes = <GraphNode[]>nodes
|
||||||
@@ -151,7 +141,6 @@ export default (state: FlowState, getters: FlowGetters): FlowActions => {
|
|||||||
if (typeof opts.elements !== 'undefined') setElements(opts.elements, opts.nodeExtent ?? state.nodeExtent)
|
if (typeof opts.elements !== 'undefined') setElements(opts.elements, opts.nodeExtent ?? state.nodeExtent)
|
||||||
if (typeof opts.nodes !== 'undefined') setNodes(opts.nodes, opts.nodeExtent ?? state.nodeExtent)
|
if (typeof opts.nodes !== 'undefined') setNodes(opts.nodes, opts.nodeExtent ?? state.nodeExtent)
|
||||||
if (typeof opts.edges !== 'undefined') setEdges(opts.edges)
|
if (typeof opts.edges !== 'undefined') setEdges(opts.edges)
|
||||||
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
|
||||||
if (typeof opts.panOnScrollSpeed !== 'undefined') state.panOnScrollSpeed = opts.panOnScrollSpeed
|
if (typeof opts.panOnScrollSpeed !== 'undefined') state.panOnScrollSpeed = opts.panOnScrollSpeed
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ export default (state: FlowState): FlowGetters => {
|
|||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
const getSelectedElements: FlowGetters['getSelectedElements'] = computed(() => [
|
|
||||||
...(state.selectedNodes ?? []),
|
|
||||||
...(state.selectedEdges ?? []),
|
|
||||||
])
|
|
||||||
const getSelectedNodes: FlowGetters['getSelectedNodes'] = computed(() => state.nodes.filter((n) => n.selected))
|
const getSelectedNodes: FlowGetters['getSelectedNodes'] = computed(() => state.nodes.filter((n) => n.selected))
|
||||||
const getSelectedEdges: FlowGetters['getSelectedEdges'] = computed(() => state.edges.filter((e) => e.selected))
|
const getSelectedEdges: FlowGetters['getSelectedEdges'] = computed(() => state.edges.filter((e) => e.selected))
|
||||||
|
const getSelectedElements: FlowGetters['getSelectedElements'] = computed(() => [
|
||||||
|
...(getSelectedNodes.value ?? []),
|
||||||
|
...(getSelectedEdges.value ?? []),
|
||||||
|
])
|
||||||
|
|
||||||
const nodeIds = computed(() => state.nodes.map((n) => n.id))
|
const nodeIds = computed(() => state.nodes.map((n) => n.id))
|
||||||
const edgeIds = computed(() => state.edges.map((e) => e.id))
|
const edgeIds = computed(() => state.edges.map((e) => e.id))
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { EmitFunc, FlowHooks, FlowEvents, FlowStore } from '~/types'
|
|||||||
export const createHooks = (): FlowHooks => ({
|
export const createHooks = (): FlowHooks => ({
|
||||||
edgesChange: createEventHook(),
|
edgesChange: createEventHook(),
|
||||||
nodesChange: createEventHook(),
|
nodesChange: createEventHook(),
|
||||||
elementClick: createEventHook(),
|
|
||||||
nodeDoubleClick: createEventHook(),
|
nodeDoubleClick: createEventHook(),
|
||||||
nodeClick: createEventHook(),
|
nodeClick: createEventHook(),
|
||||||
nodeMouseEnter: createEventHook(),
|
nodeMouseEnter: createEventHook(),
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
export { default as useHooks } from './hooks'
|
export { default as useHooks, createHooks } from './hooks'
|
||||||
export { default as useStore } from './store'
|
export { default as useStore, initFlow } from './store'
|
||||||
export * from './state'
|
export * from './state'
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ export const initialState = (): FlowState => ({
|
|||||||
height: 0,
|
height: 0,
|
||||||
},
|
},
|
||||||
transform: [0, 0, 1],
|
transform: [0, 0, 1],
|
||||||
selectedNodes: [],
|
|
||||||
selectedEdges: [],
|
|
||||||
|
|
||||||
d3Zoom: undefined,
|
d3Zoom: undefined,
|
||||||
d3Selection: undefined,
|
d3Selection: undefined,
|
||||||
|
|||||||
+8
-1
@@ -2,9 +2,10 @@ import { getCurrentInstance } from 'vue'
|
|||||||
import useState, { initialState } from './state'
|
import useState, { initialState } from './state'
|
||||||
import useActions from './actions'
|
import useActions from './actions'
|
||||||
import useGetters from './getters'
|
import useGetters from './getters'
|
||||||
import { FlowExportObject, FlowHooksOn, FlowOptions, FlowState, FlowStore, Store } from '~/types'
|
import { EmitFunc, FlowExportObject, FlowHooksOn, FlowOptions, FlowState, FlowStore, Store } from '~/types'
|
||||||
import { StoreSymbol } from '~/context'
|
import { StoreSymbol } from '~/context'
|
||||||
import { onLoadToObject } from '~/utils'
|
import { onLoadToObject } from '~/utils'
|
||||||
|
import { useHooks, useStore } from '~/store/index'
|
||||||
|
|
||||||
const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
const useFlowStore = (id: string, preloadedState: FlowState): Store => {
|
||||||
const state = reactive(useState(preloadedState))
|
const state = reactive(useState(preloadedState))
|
||||||
@@ -61,6 +62,12 @@ export const createStore = (options?: FlowOptions) => {
|
|||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const initFlow = (emit: EmitFunc, id?: string): FlowStore => {
|
||||||
|
const store = useStore({ id })
|
||||||
|
useHooks(store, emit)
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
export default (options?: FlowOptions): FlowStore => {
|
export default (options?: FlowOptions): FlowStore => {
|
||||||
const currentInstance = getCurrentInstance()
|
const currentInstance = getCurrentInstance()
|
||||||
let store = currentInstance ? inject(StoreSymbol, undefined) : false
|
let store = currentInstance ? inject(StoreSymbol, undefined) : false
|
||||||
|
|||||||
+1
-1
@@ -114,7 +114,7 @@ export interface FlowProps<N = any, E = N> {
|
|||||||
preventScrolling?: boolean
|
preventScrolling?: boolean
|
||||||
edgeUpdaterRadius?: number
|
edgeUpdaterRadius?: number
|
||||||
storageKey?: string
|
storageKey?: string
|
||||||
loading?: string
|
fitViewOnInit?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FlowOptions<N = any, E = N> = FlowProps<N, E>
|
export type FlowOptions<N = any, E = N> = FlowProps<N, E>
|
||||||
|
|||||||
+43
-36
@@ -1,8 +1,8 @@
|
|||||||
import { EventHook } from '@vueuse/core'
|
import { EventHook } from '@vueuse/core'
|
||||||
import { MouseTouchEvent } from '@braks/revue-draggable'
|
import { MouseTouchEvent } from '@braks/revue-draggable'
|
||||||
import { FlowInstance, Dimensions, XYPosition, XYZPosition } from './flow'
|
import { FlowInstance, Dimensions, XYPosition } from './flow'
|
||||||
import { GraphEdge } from './edge'
|
import { GraphEdge } from './edge'
|
||||||
import { GraphNode, HandleBounds } from './node'
|
import { CoordinateExtent, GraphNode, HandleBounds } from './node'
|
||||||
import { Connection, OnConnectStartParams } from './connection'
|
import { Connection, OnConnectStartParams } from './connection'
|
||||||
import { FlowTransform } from './zoom'
|
import { FlowTransform } from './zoom'
|
||||||
|
|
||||||
@@ -12,72 +12,79 @@ export type NodeDimensionChange = {
|
|||||||
dimensions?: Dimensions
|
dimensions?: Dimensions
|
||||||
position?: XYPosition
|
position?: XYPosition
|
||||||
handleBounds?: HandleBounds
|
handleBounds?: HandleBounds
|
||||||
computedPosition?: XYZPosition
|
|
||||||
dragging?: boolean
|
dragging?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeSelectionChange = {
|
export type NodeSelectionChange = {
|
||||||
id: string
|
id: string
|
||||||
type: 'select'
|
type: 'select'
|
||||||
selected: boolean
|
selected: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeRemoveChange = {
|
export type NodeRemoveChange = {
|
||||||
id: string
|
id: string
|
||||||
type: 'remove'
|
type: 'remove'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeChange = NodeDimensionChange | NodeSelectionChange | NodeRemoveChange
|
export type NodeChange = NodeDimensionChange | NodeSelectionChange | NodeRemoveChange
|
||||||
|
|
||||||
export type EdgeSelectionChange = NodeSelectionChange
|
export type EdgeSelectionChange = NodeSelectionChange
|
||||||
export type EdgeRemoveChange = NodeRemoveChange
|
export type EdgeRemoveChange = NodeRemoveChange
|
||||||
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange
|
export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange
|
||||||
|
|
||||||
export interface FlowEvents {
|
export type SelectionChange = EdgeSelectionChange | NodeSelectionChange
|
||||||
|
export type RemoveChange = EdgeRemoveChange | NodeRemoveChange
|
||||||
|
|
||||||
|
export type CreatePositionChangeParams = {
|
||||||
|
node: GraphNode
|
||||||
|
nodeExtent: CoordinateExtent
|
||||||
|
diff?: XYPosition
|
||||||
|
dragging?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowEvents<N = any, E = N> {
|
||||||
nodesChange: NodeChange[]
|
nodesChange: NodeChange[]
|
||||||
edgesChange: EdgeChange[]
|
edgesChange: EdgeChange[]
|
||||||
elementClick: { event: MouseTouchEvent; element: GraphNode | GraphEdge }
|
nodeDoubleClick: { event: MouseTouchEvent; node: GraphNode<N> }
|
||||||
nodeDoubleClick: { event: MouseTouchEvent; node: GraphNode }
|
nodeClick: { event: MouseTouchEvent; node: GraphNode<N> }
|
||||||
nodeClick: { event: MouseTouchEvent; node: GraphNode }
|
nodeMouseEnter: { event: MouseEvent; node: GraphNode<N> }
|
||||||
nodeMouseEnter: { event: MouseEvent; node: GraphNode }
|
nodeMouseMove: { event: MouseEvent; node: GraphNode<N> }
|
||||||
nodeMouseMove: { event: MouseEvent; node: GraphNode }
|
nodeMouseLeave: { event: MouseEvent; node: GraphNode<N> }
|
||||||
nodeMouseLeave: { event: MouseEvent; node: GraphNode }
|
nodeContextMenu: { event: MouseEvent; node: GraphNode<N> }
|
||||||
nodeContextMenu: { event: MouseEvent; node: GraphNode }
|
nodeDragStart: { event: MouseTouchEvent; node: GraphNode<N> }
|
||||||
nodeDragStart: { event: MouseTouchEvent; node: GraphNode }
|
nodeDrag: { event: MouseTouchEvent; node: GraphNode<N> }
|
||||||
nodeDrag: { event: MouseTouchEvent; node: GraphNode }
|
nodeDragStop: { event: MouseTouchEvent; node: GraphNode<N> }
|
||||||
nodeDragStop: { event: MouseTouchEvent; node: GraphNode }
|
|
||||||
connect: Connection
|
connect: Connection
|
||||||
connectStart: {
|
connectStart: {
|
||||||
event: MouseEvent
|
event: MouseEvent
|
||||||
} & { [key in keyof OnConnectStartParams]: OnConnectStartParams[key] }
|
} & OnConnectStartParams
|
||||||
connectStop: MouseEvent
|
connectStop: MouseEvent
|
||||||
connectEnd: MouseEvent
|
connectEnd: MouseEvent
|
||||||
paneReady: FlowInstance
|
paneReady: FlowInstance<N, E>
|
||||||
move: FlowTransform | undefined
|
move: FlowTransform | undefined
|
||||||
moveStart: FlowTransform | undefined
|
moveStart: FlowTransform | undefined
|
||||||
moveEnd: FlowTransform | undefined
|
moveEnd: FlowTransform | undefined
|
||||||
selectionChange: { nodes?: GraphNode[]; edges?: GraphEdge[] }
|
selectionDragStart: { event: MouseTouchEvent; nodes: GraphNode<N>[] }
|
||||||
selectionDragStart: { event: MouseTouchEvent; nodes: GraphNode[] }
|
selectionDrag: { event: MouseTouchEvent; nodes: GraphNode<N>[] }
|
||||||
selectionDrag: { event: MouseTouchEvent; nodes: GraphNode[] }
|
selectionDragStop: { event: MouseTouchEvent; nodes: GraphNode<N>[] }
|
||||||
selectionDragStop: { event: MouseTouchEvent; nodes: GraphNode[] }
|
selectionContextMenu: { event: MouseEvent; nodes: GraphNode<N>[] }
|
||||||
selectionContextMenu: { event: MouseEvent; nodes: GraphNode[] }
|
|
||||||
paneScroll: WheelEvent | undefined
|
paneScroll: WheelEvent | undefined
|
||||||
paneClick: MouseEvent
|
paneClick: MouseEvent
|
||||||
paneContextMenu: MouseEvent
|
paneContextMenu: MouseEvent
|
||||||
edgeContextMenu: { event: MouseEvent; edge: GraphEdge }
|
edgeContextMenu: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeMouseEnter: { event: MouseEvent; edge: GraphEdge }
|
edgeMouseEnter: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeMouseMove: { event: MouseEvent; edge: GraphEdge }
|
edgeMouseMove: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeMouseLeave: { event: MouseEvent; edge: GraphEdge }
|
edgeMouseLeave: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeDoubleClick: { event: MouseEvent; edge: GraphEdge }
|
edgeDoubleClick: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeClick: { event: MouseEvent; edge: GraphEdge }
|
edgeClick: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeUpdateStart: { event: MouseEvent; edge: GraphEdge }
|
edgeUpdateStart: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
edgeUpdate: { edge: GraphEdge; connection: Connection }
|
edgeUpdate: { edge: GraphEdge<E>; connection: Connection }
|
||||||
edgeUpdateEnd: { event: MouseEvent; edge: GraphEdge }
|
edgeUpdateEnd: { event: MouseEvent; edge: GraphEdge<E> }
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FlowHook<T = any> = EventHook<T>
|
export type FlowEvent = FlowEvents[keyof FlowEvents]
|
||||||
|
export type FlowHook<T extends FlowEvent = any> = EventHook<T>
|
||||||
|
|
||||||
export type FlowHooks = { [key in keyof FlowEvents]: FlowHook<FlowEvents[key]> }
|
export type FlowHooks<N = any, E = N> = { [key in keyof FlowEvents<N, E>]: FlowHook<FlowEvents<N, E>[key]> }
|
||||||
export type FlowHooksOn = { [key in keyof FlowEvents as `On${Capitalize<key>}`]: FlowHook<FlowEvents[key]>['on'] }
|
export type FlowHooksOn<N = any, E = N> = {
|
||||||
|
[key in keyof FlowEvents<N, E> as `On${Capitalize<key>}`]: FlowHook<FlowEvents<N, E>[key]>['on']
|
||||||
|
}
|
||||||
|
|
||||||
export type EmitFunc<T extends FlowHooks = FlowHooks> = (name: keyof T, ...args: any[]) => void
|
export type EmitFunc<T extends FlowHooks = FlowHooks> = (name: keyof T, ...args: any[]) => void
|
||||||
|
|||||||
+2
-4
@@ -30,8 +30,6 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, 'id'>
|
|||||||
defaultPosition: [number, number]
|
defaultPosition: [number, number]
|
||||||
|
|
||||||
selectedNodesBbox: Rect
|
selectedNodesBbox: Rect
|
||||||
selectedNodes: GraphNode[]
|
|
||||||
selectedEdges: GraphEdge[]
|
|
||||||
nodesSelectionActive: boolean
|
nodesSelectionActive: boolean
|
||||||
selectionActive: boolean
|
selectionActive: boolean
|
||||||
multiSelectionActive: boolean
|
multiSelectionActive: boolean
|
||||||
@@ -72,8 +70,8 @@ export interface FlowState<N = any, E = N> extends Omit<FlowOptions<N, E>, 'id'>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FlowActions<N = any, E = N> {
|
export interface FlowActions<N = any, E = N> {
|
||||||
setElements: (elements: Elements<N, E>, extent: CoordinateExtent) => void
|
setElements: (elements: Elements<N, E>, extent?: CoordinateExtent) => void
|
||||||
setNodes: (nodes: Node<N>[], extent: CoordinateExtent) => void
|
setNodes: (nodes: Node<N>[], extent?: CoordinateExtent) => void
|
||||||
setEdges: (edges: Edge<E>[]) => void
|
setEdges: (edges: Edge<E>[]) => void
|
||||||
addSelectedElements: (elements: FlowElements<N, E>) => void
|
addSelectedElements: (elements: FlowElements<N, E>) => void
|
||||||
addSelectedEdges: (edges: GraphEdge<E>[]) => void
|
addSelectedEdges: (edges: GraphEdge<E>[]) => void
|
||||||
|
|||||||
+2
-2
@@ -136,8 +136,8 @@ export const groupEdgesByZLevel = (edges: GraphEdge[], nodes: GraphNode[]) => {
|
|||||||
const z = edge.z
|
const z = edge.z
|
||||||
? edge.z
|
? edge.z
|
||||||
: Math.max(
|
: Math.max(
|
||||||
nodes[nodeIds.indexOf(edge.source)].computedPosition.z || 0,
|
nodes[nodeIds.indexOf(edge.source)]?.computedPosition.z || 0,
|
||||||
nodes[nodeIds.indexOf(edge.target)].computedPosition.z || 0,
|
nodes[nodeIds.indexOf(edge.target)]?.computedPosition.z || 0,
|
||||||
)
|
)
|
||||||
if (tree[z]) {
|
if (tree[z]) {
|
||||||
tree[z].push(edge)
|
tree[z].push(edge)
|
||||||
|
|||||||
@@ -68,18 +68,6 @@ export const getOutgoers = (node: GraphNode, elements: Elements) => getConnected
|
|||||||
|
|
||||||
export const getIncomers = (node: GraphNode, elements: Elements) => getConnectedElements(node, elements, 'source')
|
export const getIncomers = (node: GraphNode, elements: Elements) => getConnectedElements(node, elements, 'source')
|
||||||
|
|
||||||
export const removeElements = (elementsToRemove: Elements, elements: Elements) => {
|
|
||||||
const nodeIdsToRemove = elementsToRemove.map((n) => n.id)
|
|
||||||
const shouldRemove = (element: Node | Edge) => {
|
|
||||||
const { target, source } = isEdge(element) ? element : { target: '', source: '' }
|
|
||||||
return nodeIdsToRemove.includes(element.id) || nodeIdsToRemove.includes(target) || nodeIdsToRemove.includes(source)
|
|
||||||
}
|
|
||||||
elements.forEach((element, i) => {
|
|
||||||
if (shouldRemove(element)) elements.splice(i, 1)
|
|
||||||
})
|
|
||||||
return elements.filter((element) => !shouldRemove(element))
|
|
||||||
}
|
|
||||||
|
|
||||||
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection) =>
|
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection) =>
|
||||||
`vueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`
|
`vueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user