Files
vue-flow/packages/core/src/utils/changes.ts
T

180 lines
5.4 KiB
TypeScript

import { isGraphNode } from './graph'
import type {
Edge,
EdgeAddChange,
EdgeChange,
EdgeRemoveChange,
EdgeSelectionChange,
ElementChange,
FlowElement,
FlowElements,
GraphEdge,
GraphNode,
Node,
NodeAddChange,
NodeChange,
NodeRemoveChange,
NodeSelectionChange,
} from '~/types'
function handleParentExpand(updateItem: GraphNode, parent: GraphNode) {
if (parent) {
const extendWidth = updateItem.position.x + updateItem.dimensions.width - parent.dimensions.width
const extendHeight = updateItem.position.y + updateItem.dimensions.height - parent.dimensions.height
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
parent.style = { ...parent.style } || {}
parent.style.width = parent.style.width ?? parent.dimensions.width
parent.style.height = parent.style.height ?? parent.dimensions.height
if (extendWidth > 0) {
if (typeof parent.style.width === 'string') {
const currWidth = parseInt(parent.style.width, 10)
parent.style.width = `${currWidth + extendWidth}px`
} else {
parent.style.width += extendWidth
}
}
if (extendHeight > 0) {
if (typeof parent.style.height === 'string') {
const currWidth = parseInt(parent.style.height, 10)
parent.style.height = `${currWidth + extendHeight}px`
} else {
parent.style.height += extendHeight
}
}
if (updateItem.position.x < 0) {
const xDiff = Math.abs(updateItem.position.x)
parent.position.x = parent.position.x - xDiff
if (typeof parent.style.width === 'string') {
const currWidth = parseInt(parent.style.width, 10)
parent.style.width = `${currWidth + xDiff}px`
} else {
;(parent.style as any).width += xDiff
}
updateItem.position.x = 0
}
if (updateItem.position.y < 0) {
const yDiff = Math.abs(updateItem.position.y)
parent.position.y = parent.position.y - yDiff
if (typeof parent.style.height === 'string') {
const currWidth = parseInt(parent.style.height, 10)
parent.style.height = `${currWidth + yDiff}px`
} else {
;(parent.style as any).height += yDiff
}
updateItem.position.y = 0
}
}
}
}
export const applyChanges = <
T extends Node | Edge | FlowElement = Node,
C extends ElementChange = T extends GraphNode ? NodeChange : EdgeChange,
>(
changes: C[],
elements: T[],
): T[] => {
let elementIds = elements.map((el) => el.id)
changes.forEach((change) => {
if (change.type === 'add') {
const item = <T>change.item
return elements.push(item)
}
const i = elementIds.indexOf((<any>change).id)
const el = elements[i]
switch (change.type) {
case 'select':
;(el as FlowElement).selected = change.selected
break
case 'position':
if (isGraphNode(el)) {
if (typeof change.position !== 'undefined') el.position = change.position
if (typeof change.computedPosition !== 'undefined') {
el.computedPosition = { ...el.computedPosition, ...change.computedPosition }
}
if (el.expandParent && el.parentNode) {
const parent = elements[elementIds.indexOf(el.parentNode)]
if (parent && isGraphNode(parent)) {
handleParentExpand(el, parent)
}
}
}
break
case 'dimensions':
if (isGraphNode(el)) {
if (typeof change.dimensions !== 'undefined') el.dimensions = change.dimensions
if (el.expandParent && el.parentNode) {
const parent = elements[elementIds.indexOf(el.parentNode)]
if (parent && isGraphNode(parent)) {
handleParentExpand(el, parent)
}
}
}
break
case 'remove':
if (elementIds.includes(change.id)) {
elements.splice(i, 1)
elementIds = elements.map((el) => el.id)
}
break
}
})
return elements
}
export const applyEdgeChanges = (changes: EdgeChange[], edges: GraphEdge[]) => applyChanges(changes, edges)
export const applyNodeChanges = (changes: NodeChange[], nodes: GraphNode[]) => applyChanges(changes, nodes)
export const createSelectionChange = (id: string, selected: boolean): NodeSelectionChange | EdgeSelectionChange => ({
id,
type: 'select',
selected,
})
export const createAdditionChange = <
T extends GraphNode | GraphEdge = GraphNode,
C extends NodeAddChange | EdgeAddChange = T extends GraphNode ? NodeAddChange : EdgeAddChange,
>(
item: T,
): C =>
<C>{
item,
type: 'add',
}
export const createRemoveChange = (id: string): NodeRemoveChange | EdgeRemoveChange => ({
id,
type: 'remove',
})
export const getSelectionChanges = (elements: FlowElements, selectedIds: string[]) => {
return elements.reduce(
(res, item) => {
const willBeSelected = selectedIds.includes(item.id)
const key = isGraphNode(item) ? 'changedNodes' : 'changedEdges'
if (!item.selected && willBeSelected) {
res[key].push(createSelectionChange(item.id, true))
} else if (item.selected && !willBeSelected) {
res[key].push(createSelectionChange(item.id, false))
}
return res
},
{ changedNodes: [], changedEdges: [] } as { changedNodes: NodeSelectionChange[]; changedEdges: EdgeSelectionChange[] },
)
}