refactor(core): replace arr reduce methods (#1467)
* refactor(core): replace arr reduce methods * chore(core): cleanup
This commit is contained in:
@@ -172,7 +172,7 @@ const NodeWrapper = defineComponent({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, flush: 'post' },
|
{ immediate: true, flush: 'pre' },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ function useKeyOrCode(code: string, keysToWatch: string | string[]) {
|
|||||||
* @internal
|
* @internal
|
||||||
* @param keyFilter - Can be a boolean, a string or an array of strings. If it's a boolean, it will always return that value. If it's a string, it will return true if the key is pressed. If it's an array of strings, it will return true if any of the keys are pressed, or a combination is pressed (e.g. ['ctrl+a', 'ctrl+b'])
|
* @param keyFilter - Can be a boolean, a string or an array of strings. If it's a boolean, it will always return that value. If it's a string, it will return true if the key is pressed. If it's an array of strings, it will return true if any of the keys are pressed, or a combination is pressed (e.g. ['ctrl+a', 'ctrl+b'])
|
||||||
* @param onChange - Callback function that will be called when the key state changes
|
* @param onChange - Callback function that will be called when the key state changes
|
||||||
|
* @param options - Options object
|
||||||
*/
|
*/
|
||||||
export function useKeyPress(
|
export function useKeyPress(
|
||||||
keyFilter: MaybeRefOrGetter<KeyFilter | null>,
|
keyFilter: MaybeRefOrGetter<KeyFilter | null>,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { EdgeMarkerType, MarkerProps, MarkerType } from '../../types/edge'
|
import type { EdgeMarkerType, MarkerProps, MarkerType } from '../../types'
|
||||||
import { useVueFlow } from '../../composables'
|
import { useVueFlow } from '../../composables'
|
||||||
import { getMarkerId } from '../../utils'
|
import { getMarkerId } from '../../utils'
|
||||||
import MarkerSymbols from './MarkerSymbols.vue'
|
import MarkerSymbols from './MarkerSymbols.vue'
|
||||||
@@ -8,20 +8,21 @@ import MarkerSymbols from './MarkerSymbols.vue'
|
|||||||
const { id: vueFlowId, edges, connectionLineOptions, defaultMarkerColor: defaultColor } = useVueFlow()
|
const { id: vueFlowId, edges, connectionLineOptions, defaultMarkerColor: defaultColor } = useVueFlow()
|
||||||
|
|
||||||
const markers = computed(() => {
|
const markers = computed(() => {
|
||||||
const ids: string[] = []
|
const ids: Set<string> = new Set()
|
||||||
const markers: MarkerProps[] = []
|
const markers: MarkerProps[] = []
|
||||||
|
|
||||||
const createMarkers = (marker?: EdgeMarkerType) => {
|
const createMarkers = (marker?: EdgeMarkerType) => {
|
||||||
if (marker) {
|
if (marker) {
|
||||||
const markerId = getMarkerId(marker, vueFlowId)
|
const markerId = getMarkerId(marker, vueFlowId)
|
||||||
if (!ids.includes(markerId)) {
|
|
||||||
|
if (!ids.has(markerId)) {
|
||||||
if (typeof marker === 'object') {
|
if (typeof marker === 'object') {
|
||||||
markers.push({ ...marker, id: markerId, color: marker.color || defaultColor.value })
|
markers.push({ ...marker, id: markerId, color: marker.color || defaultColor.value })
|
||||||
} else {
|
} else {
|
||||||
markers.push({ id: markerId, color: defaultColor.value, type: marker as MarkerType })
|
markers.push({ id: markerId, color: defaultColor.value, type: marker as MarkerType })
|
||||||
}
|
}
|
||||||
|
|
||||||
ids.push(markerId)
|
ids.add(markerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,15 +31,13 @@ const markers = computed(() => {
|
|||||||
createMarkers(marker)
|
createMarkers(marker)
|
||||||
}
|
}
|
||||||
|
|
||||||
edges.value.reduce<MarkerProps[]>((markers, edge) => {
|
for (const edge of edges.value) {
|
||||||
for (const marker of [edge.markerStart, edge.markerEnd]) {
|
for (const marker of [edge.markerStart, edge.markerEnd]) {
|
||||||
createMarkers(marker)
|
createMarkers(marker)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return markers.sort((a, b) => a.id.localeCompare(b.id))
|
return markers.sort((a, b) => a.id.localeCompare(b.id))
|
||||||
}, markers)
|
|
||||||
|
|
||||||
return markers
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -50,15 +50,14 @@ useKeyPress(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodesToRemove = getNodes.value.reduce<GraphNode[]>((res, node) => {
|
const nodesToRemove: GraphNode[] = []
|
||||||
if (!node.selected && node.parentNode && res.find((n) => n.id === node.parentNode)) {
|
for (const node of getNodes.value) {
|
||||||
res.push(node)
|
if (!node.selected && node.parentNode && nodesToRemove.some((n) => n.id === node.parentNode)) {
|
||||||
|
nodesToRemove.push(node)
|
||||||
} else if (node.selected) {
|
} else if (node.selected) {
|
||||||
res.push(node)
|
nodesToRemove.push(node)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return res
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (nodesToRemove || getSelectedEdges.value) {
|
if (nodesToRemove || getSelectedEdges.value) {
|
||||||
if (getSelectedEdges.value.length > 0) {
|
if (getSelectedEdges.value.length > 0) {
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export function useActions(
|
|||||||
const style = window.getComputedStyle(viewportNode)
|
const style = window.getComputedStyle(viewportNode)
|
||||||
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform)
|
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform)
|
||||||
|
|
||||||
const changes: NodeDimensionChange[] = Array.from({ length: updates.length })
|
const changes: NodeDimensionChange[] = []
|
||||||
|
|
||||||
for (let i = 0; i < updates.length; ++i) {
|
for (let i = 0; i < updates.length; ++i) {
|
||||||
const update = updates[i]
|
const update = updates[i]
|
||||||
@@ -378,7 +378,7 @@ export function useActions(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state.nodes = createGraphNodes(nextNodes, state.nodes, findNode, state.hooks.error.trigger)
|
state.nodes = createGraphNodes(nextNodes, findNode, state.hooks.error.trigger)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setEdges: Actions['setEdges'] = (edges) => {
|
const setEdges: Actions['setEdges'] = (edges) => {
|
||||||
@@ -419,7 +419,7 @@ export function useActions(
|
|||||||
let nextNodes = nodes instanceof Function ? nodes(state.nodes) : nodes
|
let nextNodes = nodes instanceof Function ? nodes(state.nodes) : nodes
|
||||||
nextNodes = Array.isArray(nextNodes) ? nextNodes : [nextNodes]
|
nextNodes = Array.isArray(nextNodes) ? nextNodes : [nextNodes]
|
||||||
|
|
||||||
const graphNodes = createGraphNodes(nextNodes, state.nodes, findNode, state.hooks.error.trigger)
|
const graphNodes = createGraphNodes(nextNodes, findNode, state.hooks.error.trigger)
|
||||||
|
|
||||||
const changes: NodeAddChange<any>[] = []
|
const changes: NodeAddChange<any>[] = []
|
||||||
for (const node of graphNodes) {
|
for (const node of graphNodes) {
|
||||||
@@ -665,7 +665,6 @@ export function useActions(
|
|||||||
return [nodeRect, node, isRectObj]
|
return [nodeRect, node, isRectObj]
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: rename to `findIntersectingNodes`
|
|
||||||
const getIntersectingNodes: Actions['getIntersectingNodes'] = (nodeOrRect, partially = true, nodes = state.nodes) => {
|
const getIntersectingNodes: Actions['getIntersectingNodes'] = (nodeOrRect, partially = true, nodes = state.nodes) => {
|
||||||
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect)
|
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect)
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ export function useState(): State {
|
|||||||
return {
|
return {
|
||||||
vueFlowRef: null,
|
vueFlowRef: null,
|
||||||
viewportRef: null,
|
viewportRef: null,
|
||||||
// todo: change this to a Set
|
|
||||||
nodes: [],
|
nodes: [],
|
||||||
// todo: change this to a Set
|
|
||||||
edges: [],
|
edges: [],
|
||||||
connectionLookup: new Map(),
|
connectionLookup: new Map(),
|
||||||
nodeTypes: {},
|
nodeTypes: {},
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ import type { VueFlowStore } from './store'
|
|||||||
// todo: should be object type
|
// todo: should be object type
|
||||||
export type ElementData = any
|
export type ElementData = any
|
||||||
|
|
||||||
/** A flow element (after parsing into state) */
|
/**
|
||||||
|
* @deprecated - will be removed in the next major version
|
||||||
|
* A flow element (after parsing into state)
|
||||||
|
*/
|
||||||
export type FlowElement<
|
export type FlowElement<
|
||||||
NodeData = ElementData,
|
NodeData = ElementData,
|
||||||
EdgeData = ElementData,
|
EdgeData = ElementData,
|
||||||
@@ -31,6 +34,10 @@ export type FlowElement<
|
|||||||
EdgeEvents extends Record<string, CustomEvent> = any,
|
EdgeEvents extends Record<string, CustomEvent> = any,
|
||||||
> = GraphNode<NodeData, NodeEvents> | GraphEdge<EdgeData, EdgeEvents>
|
> = GraphNode<NodeData, NodeEvents> | GraphEdge<EdgeData, EdgeEvents>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated - will be removed in the next major version
|
||||||
|
* An array of flow elements (after parsing into state)
|
||||||
|
*/
|
||||||
export type FlowElements<
|
export type FlowElements<
|
||||||
NodeData = ElementData,
|
NodeData = ElementData,
|
||||||
EdgeData = ElementData,
|
EdgeData = ElementData,
|
||||||
@@ -54,6 +61,7 @@ export type Elements<
|
|||||||
> = Element<NodeData, EdgeData, NodeEvents, EdgeEvents>[]
|
> = Element<NodeData, EdgeData, NodeEvents, EdgeEvents>[]
|
||||||
|
|
||||||
export type MaybeElement = Node | Edge | Connection | FlowElement | Element
|
export type MaybeElement = Node | Edge | Connection | FlowElement | Element
|
||||||
|
|
||||||
export interface CustomThemeVars {
|
export interface CustomThemeVars {
|
||||||
[key: string]: string | number | undefined
|
[key: string]: string | number | undefined
|
||||||
}
|
}
|
||||||
@@ -82,13 +90,11 @@ export enum Position {
|
|||||||
Bottom = 'bottom',
|
Bottom = 'bottom',
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: Rename to `Point`
|
|
||||||
export interface XYPosition {
|
export interface XYPosition {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: Rename to `AbsolutePoint`
|
|
||||||
export type XYZPosition = XYPosition & { z: number }
|
export type XYZPosition = XYPosition & { z: number }
|
||||||
|
|
||||||
export interface Dimensions {
|
export interface Dimensions {
|
||||||
|
|||||||
@@ -69,22 +69,18 @@ export function getHostForElement(element: HTMLElement): Document {
|
|||||||
return window.document
|
return window.document
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refactor generic to use MaybeElement
|
|
||||||
export function isEdge<Data = ElementData>(element: MaybeElement): element is Edge<Data> {
|
export function isEdge<Data = ElementData>(element: MaybeElement): element is Edge<Data> {
|
||||||
return element && typeof element === 'object' && 'id' in element && 'source' in element && 'target' in element
|
return element && typeof element === 'object' && 'id' in element && 'source' in element && 'target' in element
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refactor generic to use MaybeElement
|
|
||||||
export function isGraphEdge<Data = ElementData>(element: MaybeElement): element is GraphEdge<Data> {
|
export function isGraphEdge<Data = ElementData>(element: MaybeElement): element is GraphEdge<Data> {
|
||||||
return isEdge(element) && 'sourceNode' in element && 'targetNode' in element
|
return isEdge(element) && 'sourceNode' in element && 'targetNode' in element
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refactor generic to use MaybeElement
|
|
||||||
export function isNode<Data = ElementData>(element: MaybeElement): element is Node<Data> {
|
export function isNode<Data = ElementData>(element: MaybeElement): element is Node<Data> {
|
||||||
return element && typeof element === 'object' && 'id' in element && 'position' in element && !isEdge(element)
|
return element && typeof element === 'object' && 'id' in element && 'position' in element && !isEdge(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refactor generic to use MaybeElement
|
|
||||||
export function isGraphNode<Data = ElementData>(element: MaybeElement): element is GraphNode<Data> {
|
export function isGraphNode<Data = ElementData>(element: MaybeElement): element is GraphNode<Data> {
|
||||||
return isNode(element) && 'computedPosition' in element
|
return isNode(element) && 'computedPosition' in element
|
||||||
}
|
}
|
||||||
@@ -105,15 +101,15 @@ export function parseNode(node: Node, existingNode?: GraphNode, parentNode?: str
|
|||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
}),
|
}),
|
||||||
|
computedPosition: markRaw({
|
||||||
|
z: 0,
|
||||||
|
...node.position,
|
||||||
|
}),
|
||||||
// todo: shouldn't be defined initially, as we want to use handleBounds to check if a node was actually initialized or not
|
// todo: shouldn't be defined initially, as we want to use handleBounds to check if a node was actually initialized or not
|
||||||
handleBounds: {
|
handleBounds: {
|
||||||
source: [],
|
source: [],
|
||||||
target: [],
|
target: [],
|
||||||
},
|
},
|
||||||
computedPosition: markRaw({
|
|
||||||
z: 0,
|
|
||||||
...node.position,
|
|
||||||
}),
|
|
||||||
draggable: undefined,
|
draggable: undefined,
|
||||||
selectable: undefined,
|
selectable: undefined,
|
||||||
connectable: undefined,
|
connectable: undefined,
|
||||||
@@ -356,17 +352,23 @@ export function getBoundsofRects(rect1: Rect, rect2: Rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getRectOfNodes(nodes: GraphNode[]) {
|
export function getRectOfNodes(nodes: GraphNode[]) {
|
||||||
const box = nodes.reduce(
|
let box: Box = {
|
||||||
(currBox, { computedPosition = { x: 0, y: 0 }, dimensions = { width: 0, height: 0 } } = {} as any) =>
|
x: Number.POSITIVE_INFINITY,
|
||||||
getBoundsOfBoxes(
|
y: Number.POSITIVE_INFINITY,
|
||||||
currBox,
|
x2: Number.NEGATIVE_INFINITY,
|
||||||
rectToBox({
|
y2: Number.NEGATIVE_INFINITY,
|
||||||
...computedPosition,
|
}
|
||||||
...dimensions,
|
|
||||||
} as Rect),
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
),
|
const node = nodes[i]
|
||||||
{ x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY, x2: Number.NEGATIVE_INFINITY, y2: Number.NEGATIVE_INFINITY },
|
box = getBoundsOfBoxes(
|
||||||
)
|
box,
|
||||||
|
rectToBox({
|
||||||
|
...node.computedPosition,
|
||||||
|
...node.dimensions,
|
||||||
|
} as Rect),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return boxToRect(box)
|
return boxToRect(box)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,8 +222,12 @@ interface GetHandleLookupParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHandleLookupParams) {
|
export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHandleLookupParams) {
|
||||||
return nodes.reduce<ConnectionHandle[]>((res, node) => {
|
const handleLookup: ConnectionHandle[] = []
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const node = nodes[i]
|
||||||
|
|
||||||
const { handleBounds } = node
|
const { handleBounds } = node
|
||||||
|
|
||||||
let sourceHandles: ConnectionHandle[] = []
|
let sourceHandles: ConnectionHandle[] = []
|
||||||
let targetHandles: ConnectionHandle[] = []
|
let targetHandles: ConnectionHandle[] = []
|
||||||
|
|
||||||
@@ -232,9 +236,10 @@ export function getHandleLookup({ nodes, nodeId, handleId, handleType }: GetHand
|
|||||||
targetHandles = getHandles(node, handleBounds, 'target', `${nodeId}-${handleId}-${handleType}`)
|
targetHandles = getHandles(node, handleBounds, 'target', `${nodeId}-${handleId}-${handleType}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.push(...sourceHandles, ...targetHandles)
|
handleLookup.push(...sourceHandles, ...targetHandles)
|
||||||
return res
|
}
|
||||||
}, [])
|
|
||||||
|
return handleLookup
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHandleType(edgeUpdaterType: HandleType | undefined, handleDomNode: Element | null): HandleType | null {
|
export function getHandleType(edgeUpdaterType: HandleType | undefined, handleDomNode: Element | null): HandleType | null {
|
||||||
|
|||||||
@@ -89,22 +89,18 @@ export function updateEdgeAction(
|
|||||||
return newEdge
|
return newEdge
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createGraphNodes(
|
export function createGraphNodes(nodes: Node[], findNode: Actions['findNode'], triggerError: State['hooks']['error']['trigger']) {
|
||||||
nodes: Node[],
|
|
||||||
currGraphNodes: GraphNode[],
|
|
||||||
findNode: Actions['findNode'],
|
|
||||||
triggerError: State['hooks']['error']['trigger'],
|
|
||||||
) {
|
|
||||||
const parentNodes: Record<string, true> = {}
|
const parentNodes: Record<string, true> = {}
|
||||||
|
|
||||||
const nextNodes = nodes.reduce((nextNodes, node, currentIndex) => {
|
const nextNodes: GraphNode[] = []
|
||||||
// make sure we don't try to add invalid nodes
|
for (let i = 0; i < nodes.length; ++i) {
|
||||||
|
const node = nodes[i]
|
||||||
|
|
||||||
if (!isNode(node)) {
|
if (!isNode(node)) {
|
||||||
triggerError(
|
triggerError(
|
||||||
new VueFlowError(ErrorCode.NODE_INVALID, (node as undefined | Record<any, any>)?.id) ||
|
new VueFlowError(ErrorCode.NODE_INVALID, (node as undefined | Record<any, any>)?.id) || `[ID UNKNOWN|INDEX ${i}]`,
|
||||||
`[ID UNKNOWN|INDEX ${currentIndex}]`,
|
|
||||||
)
|
)
|
||||||
return nextNodes
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = parseNode(node, findNode(node.id), node.parentNode)
|
const parsed = parseNode(node, findNode(node.id), node.parentNode)
|
||||||
@@ -113,13 +109,11 @@ export function createGraphNodes(
|
|||||||
parentNodes[node.parentNode] = true
|
parentNodes[node.parentNode] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return nextNodes.concat(parsed)
|
nextNodes[i] = parsed
|
||||||
}, [] as GraphNode[])
|
}
|
||||||
|
|
||||||
const allNodes = [...nextNodes, ...currGraphNodes]
|
|
||||||
|
|
||||||
for (const node of nextNodes) {
|
for (const node of nextNodes) {
|
||||||
const parentNode = allNodes.find((n) => n.id === node.parentNode)
|
const parentNode = findNode(node.parentNode) || nextNodes.find((n) => n.id === node.parentNode)
|
||||||
|
|
||||||
if (node.parentNode && !parentNode) {
|
if (node.parentNode && !parentNode) {
|
||||||
triggerError(new VueFlowError(ErrorCode.NODE_MISSING_PARENT, node.id, node.parentNode))
|
triggerError(new VueFlowError(ErrorCode.NODE_MISSING_PARENT, node.id, node.parentNode))
|
||||||
|
|||||||
Reference in New Issue
Block a user