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:
Braks
2021-12-20 19:29:52 +01:00
parent f6007d026c
commit 81c8145f3a
19 changed files with 173 additions and 184 deletions
-2
View File
@@ -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>
+1 -1
View File
@@ -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'
+62 -53
View File
@@ -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 -1
View File
@@ -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'
+1
View File
@@ -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">
-1
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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))
-1
View File
@@ -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
View File
@@ -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'
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
-12
View File
@@ -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}`