refactor(core): replace arr reduce methods (#1467)

* refactor(core): replace arr reduce methods

* chore(core): cleanup
This commit is contained in:
Braks
2024-06-13 00:04:51 +02:00
parent 70b39cc78e
commit d658c1d357
10 changed files with 68 additions and 65 deletions
@@ -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>
+6 -7
View File
@@ -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) {
+3 -4
View File
@@ -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)
-2
View File
@@ -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: {},
+9 -3
View File
@@ -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 {
+21 -19
View File
@@ -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)
} }
+9 -4
View File
@@ -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 {
+10 -16
View File
@@ -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))