From 876b71dc19ebf0be16e7dcc5627c3bbc68aefbb5 Mon Sep 17 00:00:00 2001 From: Braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:23:50 +0200 Subject: [PATCH] refactor(core): use node/edge id as only dependency for nodes/edges list (#1447) * refactor(core): use node id to (re-)render nodes * refactor(core): use edge id to (re-)render nodes * chore(changeset): add * chore(core): cleanup * chore(core): cast cmp as any --- .changeset/calm-pigs-yawn.md | 5 + .changeset/four-singers-suffer.md | 5 + .../core/src/components/Edges/EdgeWrapper.ts | 189 +++++++------ .../core/src/components/Nodes/NodeWrapper.ts | 249 ++++++++++-------- packages/core/src/composables/useEdge.ts | 2 +- .../container/EdgeRenderer/EdgeRenderer.vue | 70 +---- .../container/NodeRenderer/NodeRenderer.vue | 79 +----- 7 files changed, 274 insertions(+), 325 deletions(-) create mode 100644 .changeset/calm-pigs-yawn.md create mode 100644 .changeset/four-singers-suffer.md diff --git a/.changeset/calm-pigs-yawn.md b/.changeset/calm-pigs-yawn.md new file mode 100644 index 00000000..e512bfbc --- /dev/null +++ b/.changeset/calm-pigs-yawn.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/core": minor +--- + +Return non-nullable edge from `useEdge` diff --git a/.changeset/four-singers-suffer.md b/.changeset/four-singers-suffer.md new file mode 100644 index 00000000..7904923f --- /dev/null +++ b/.changeset/four-singers-suffer.md @@ -0,0 +1,5 @@ +--- +"@vue-flow/core": minor +--- + +Use node/edge id as the only dependency to render nodes/edges. diff --git a/packages/core/src/components/Edges/EdgeWrapper.ts b/packages/core/src/components/Edges/EdgeWrapper.ts index b0c03ef2..2873d9ec 100644 --- a/packages/core/src/components/Edges/EdgeWrapper.ts +++ b/packages/core/src/components/Edges/EdgeWrapper.ts @@ -1,9 +1,8 @@ -import { computed, defineComponent, h, provide, ref } from 'vue' -import { useVModel } from '@vueuse/core' -import type { Connection, EdgeComponent, EdgeUpdatable, GraphEdge, HandleType, MouseTouchEvent } from '../../types' +import { computed, defineComponent, getCurrentInstance, h, inject, provide, ref, resolveComponent, toRef } from 'vue' +import type { Connection, EdgeComponent, HandleType, MouseTouchEvent } from '../../types' import { ConnectionMode, Position } from '../../types' -import { useEdgeHooks, useHandle, useVueFlow } from '../../composables' -import { EdgeId, EdgeRef } from '../../context' +import { useEdge, useEdgeHooks, useHandle, useVueFlow } from '../../composables' +import { EdgeId, EdgeRef, Slots } from '../../context' import { ARIA_EDGE_DESC_KEY, ErrorCode, @@ -17,18 +16,12 @@ import EdgeAnchor from './EdgeAnchor' interface Props { id: string - type: EdgeComponent | ((...args: any[]) => any) | object | false - name: string - selectable?: boolean - focusable?: boolean - updatable?: EdgeUpdatable - edge: GraphEdge } const EdgeWrapper = defineComponent({ name: 'Edge', compatConfig: { MODE: 3 }, - props: ['name', 'type', 'id', 'updatable', 'selectable', 'focusable', 'edge'], + props: ['id'], setup(props: Props) { const { id: vueFlowId, @@ -45,11 +38,18 @@ const EdgeWrapper = defineComponent({ isValidConnection, multiSelectionActive, disableKeyboardA11y, + elementsSelectable, + edgesUpdatable, + edgesFocusable, } = useVueFlow() - const hooks = useEdgeHooks(props.edge, emits) + const { edge } = useEdge(props.id) - const edge = useVModel(props, 'edge') + const hooks = useEdgeHooks(edge, emits) + + const slots = inject(Slots) + + const instance = getCurrentInstance() const mouseOver = ref(false) @@ -63,11 +63,45 @@ const EdgeWrapper = defineComponent({ const edgeEl = ref(null) + const isSelectable = toRef(() => (typeof edge.selectable === 'undefined' ? elementsSelectable.value : edge.selectable)) + + const isUpdatable = toRef(() => (typeof edge.updatable === 'undefined' ? edgesUpdatable.value : edge.updatable)) + + const isFocusable = toRef(() => (typeof edge.focusable === 'undefined' ? edgesFocusable.value : edge.focusable)) + provide(EdgeId, props.id) provide(EdgeRef, edgeEl) - const edgeClass = computed(() => (edge.value.class instanceof Function ? edge.value.class(edge.value) : edge.value.class)) - const edgeStyle = computed(() => (edge.value.style instanceof Function ? edge.value.style(edge.value) : edge.value.style)) + const edgeClass = computed(() => (edge.class instanceof Function ? edge.class(edge) : edge.class)) + const edgeStyle = computed(() => (edge.style instanceof Function ? edge.style(edge) : edge.style)) + + const edgeCmp = computed(() => { + const name = edge.type || 'default' + + const slot = slots?.[`edge-${name}`] + if (slot) { + return slot + } + + let edgeType = edge.template ?? getEdgeTypes.value[name] + + if (typeof edgeType === 'string') { + if (instance) { + const components = Object.keys(instance.appContext.components) + if (components && components.includes(name)) { + edgeType = resolveComponent(name, false) as EdgeComponent + } + } + } + + if (edgeType && typeof edgeType !== 'string') { + return edgeType + } + + emits.error(new VueFlowError(ErrorCode.EDGE_TYPE_MISSING, edgeType)) + + return false + }) const { handlePointerDown } = useHandle({ nodeId, @@ -80,29 +114,29 @@ const EdgeWrapper = defineComponent({ }) return () => { - const sourceNode = findNode(edge.value.source) - const targetNode = findNode(edge.value.target) - const pathOptions = 'pathOptions' in edge.value ? edge.value.pathOptions : {} + const sourceNode = findNode(edge.source) + const targetNode = findNode(edge.target) + const pathOptions = 'pathOptions' in edge ? edge.pathOptions : {} if (!sourceNode && !targetNode) { - emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_TARGET_MISSING, edge.value.id, edge.value.source, edge.value.target)) + emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_TARGET_MISSING, edge.id, edge.source, edge.target)) return null } if (!sourceNode) { - emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_MISSING, edge.value.id, edge.value.source)) + emits.error(new VueFlowError(ErrorCode.EDGE_SOURCE_MISSING, edge.id, edge.source)) return null } if (!targetNode) { - emits.error(new VueFlowError(ErrorCode.EDGE_TARGET_MISSING, edge.value.id, edge.value.target)) + emits.error(new VueFlowError(ErrorCode.EDGE_TARGET_MISSING, edge.id, edge.target)) return null } - if (!edge.value || edge.value.hidden || sourceNode.hidden || targetNode.hidden) { + if (!edge || edge.hidden || sourceNode.hidden || targetNode.hidden) { return null } @@ -113,7 +147,7 @@ const EdgeWrapper = defineComponent({ sourceNodeHandles = [...(sourceNode.handleBounds.source || []), ...(sourceNode.handleBounds.target || [])] } - const sourceHandle = getHandle(sourceNodeHandles, edge.value.sourceHandle) + const sourceHandle = getHandle(sourceNodeHandles, edge.sourceHandle) let targetNodeHandles if (connectionMode.value === ConnectionMode.Strict) { @@ -122,7 +156,7 @@ const EdgeWrapper = defineComponent({ targetNodeHandles = [...(targetNode.handleBounds.target || []), ...(targetNode.handleBounds.source || [])] } - const targetHandle = getHandle(targetNodeHandles, edge.value.targetHandle) + const targetHandle = getHandle(targetNodeHandles, edge.targetHandle) const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom @@ -137,10 +171,10 @@ const EdgeWrapper = defineComponent({ targetPosition, ) - edge.value.sourceX = sourceX - edge.value.sourceY = sourceY - edge.value.targetX = targetX - edge.value.targetY = targetY + edge.sourceX = sourceX + edge.sourceY = sourceY + edge.targetX = targetX + edge.targetY = targetY return h( 'g', @@ -150,14 +184,14 @@ const EdgeWrapper = defineComponent({ 'data-id': props.id, 'class': [ 'vue-flow__edge', - `vue-flow__edge-${props.type === false ? 'default' : props.name}`, + `vue-flow__edge-${edgeCmp.value === false ? 'default' : edge.type || 'default'}`, noPanClassName.value, edgeClass.value, { updating: mouseOver.value, - selected: edge.value.selected, - animated: edge.value.animated, - inactive: !props.selectable, + selected: edge.selected, + animated: edge.animated, + inactive: !isSelectable.value, }, ], 'onClick': onEdgeClick, @@ -166,52 +200,49 @@ const EdgeWrapper = defineComponent({ 'onMouseenter': onEdgeMouseEnter, 'onMousemove': onEdgeMouseMove, 'onMouseleave': onEdgeMouseLeave, - 'onKeyDown': props.focusable ? onKeyDown : undefined, - 'tabIndex': props.focusable ? 0 : undefined, - 'aria-label': - edge.value.ariaLabel === null - ? undefined - : edge.value.ariaLabel || `Edge from ${edge.value.source} to ${edge.value.target}`, - 'aria-describedby': props.focusable ? `${ARIA_EDGE_DESC_KEY}-${vueFlowId}` : undefined, - 'role': props.focusable ? 'button' : 'img', + 'onKeyDown': isFocusable.value ? onKeyDown : undefined, + 'tabIndex': isFocusable.value ? 0 : undefined, + 'aria-label': edge.ariaLabel === null ? undefined : edge.ariaLabel || `Edge from ${edge.source} to ${edge.target}`, + 'aria-describedby': isFocusable.value ? `${ARIA_EDGE_DESC_KEY}-${vueFlowId}` : undefined, + 'role': isFocusable.value ? 'button' : 'img', }, [ updating.value ? null - : h(props.type === false ? getEdgeTypes.value.default : props.type, { + : h(edgeCmp.value === false ? getEdgeTypes.value.default : (edgeCmp.value as any), { id: props.id, sourceNode, targetNode, - source: edge.value.source, - target: edge.value.target, - type: edge.value.type, - updatable: props.updatable, - selected: edge.value.selected, - animated: edge.value.animated, - label: edge.value.label, - labelStyle: edge.value.labelStyle, - labelShowBg: edge.value.labelShowBg, - labelBgStyle: edge.value.labelBgStyle, - labelBgPadding: edge.value.labelBgPadding, - labelBgBorderRadius: edge.value.labelBgBorderRadius, - data: edge.value.data, - events: { ...edge.value.events, ...hooks.on }, + source: edge.source, + target: edge.target, + type: edge.type, + updatable: isUpdatable.value, + selected: edge.selected, + animated: edge.animated, + label: edge.label, + labelStyle: edge.labelStyle, + labelShowBg: edge.labelShowBg, + labelBgStyle: edge.labelBgStyle, + labelBgPadding: edge.labelBgPadding, + labelBgBorderRadius: edge.labelBgBorderRadius, + data: edge.data, + events: { ...edge.events, ...hooks.on }, style: edgeStyle.value, - markerStart: `url('#${getMarkerId(edge.value.markerStart, vueFlowId)}')`, - markerEnd: `url('#${getMarkerId(edge.value.markerEnd, vueFlowId)}')`, + markerStart: `url('#${getMarkerId(edge.markerStart, vueFlowId)}')`, + markerEnd: `url('#${getMarkerId(edge.markerEnd, vueFlowId)}')`, sourcePosition, targetPosition, sourceX, sourceY, targetX, targetY, - sourceHandleId: edge.value.sourceHandle, - targetHandleId: edge.value.targetHandle, - interactionWidth: edge.value.interactionWidth, + sourceHandleId: edge.sourceHandle, + targetHandleId: edge.targetHandle, + interactionWidth: edge.interactionWidth, ...pathOptions, }), [ - props.updatable === 'source' || props.updatable === true + isUpdatable.value === 'source' || isUpdatable.value === true ? [ h( 'g', @@ -231,7 +262,7 @@ const EdgeWrapper = defineComponent({ ), ] : null, - props.updatable === 'target' || props.updatable === true + isUpdatable.value === 'target' || isUpdatable.value === true ? [ h( 'g', @@ -265,11 +296,11 @@ const EdgeWrapper = defineComponent({ } function onEdgeUpdate(event: MouseTouchEvent, connection: Connection) { - hooks.emit.update({ event, edge: edge.value, connection }) + hooks.emit.update({ event, edge, connection }) } function onEdgeUpdateEnd(event: MouseTouchEvent) { - hooks.emit.updateEnd({ event, edge: edge.value }) + hooks.emit.updateEnd({ event, edge }) updating.value = false } @@ -280,28 +311,28 @@ const EdgeWrapper = defineComponent({ updating.value = true - nodeId.value = isSourceHandle ? edge.value.target : edge.value.source - handleId.value = (isSourceHandle ? edge.value.targetHandle : edge.value.sourceHandle) ?? '' + nodeId.value = isSourceHandle ? edge.target : edge.source + handleId.value = (isSourceHandle ? edge.targetHandle : edge.sourceHandle) ?? '' edgeUpdaterType.value = isSourceHandle ? 'target' : 'source' - hooks.emit.updateStart({ event, edge: edge.value }) + hooks.emit.updateStart({ event, edge }) handlePointerDown(event) } function onEdgeClick(event: MouseEvent) { - const data = { event, edge: edge.value } + const data = { event, edge } - if (props.selectable) { + if (isSelectable.value) { nodesSelectionActive.value = false - if (edge.value.selected && multiSelectionActive.value) { - removeSelectedEdges([edge.value]) + if (edge.selected && multiSelectionActive.value) { + removeSelectedEdges([edge]) edgeEl.value?.blur() } else { - addSelectedEdges([edge.value]) + addSelectedEdges([edge]) } } @@ -309,23 +340,23 @@ const EdgeWrapper = defineComponent({ } function onEdgeContextMenu(event: MouseEvent) { - hooks.emit.contextMenu({ event, edge: edge.value }) + hooks.emit.contextMenu({ event, edge }) } function onDoubleClick(event: MouseEvent) { - hooks.emit.doubleClick({ event, edge: edge.value }) + hooks.emit.doubleClick({ event, edge }) } function onEdgeMouseEnter(event: MouseEvent) { - hooks.emit.mouseEnter({ event, edge: edge.value }) + hooks.emit.mouseEnter({ event, edge }) } function onEdgeMouseMove(event: MouseEvent) { - hooks.emit.mouseMove({ event, edge: edge.value }) + hooks.emit.mouseMove({ event, edge }) } function onEdgeMouseLeave(event: MouseEvent) { - hooks.emit.mouseLeave({ event, edge: edge.value }) + hooks.emit.mouseLeave({ event, edge }) } function onEdgeUpdaterSourceMouseDown(event: MouseEvent) { @@ -337,7 +368,7 @@ const EdgeWrapper = defineComponent({ } function onKeyDown(event: KeyboardEvent) { - if (!disableKeyboardA11y.value && elementSelectionKeys.includes(event.key) && props.selectable) { + if (!disableKeyboardA11y.value && elementSelectionKeys.includes(event.key) && isSelectable.value) { const unselect = event.key === 'Escape' if (unselect) { diff --git a/packages/core/src/components/Nodes/NodeWrapper.ts b/packages/core/src/components/Nodes/NodeWrapper.ts index 78d9f2ae..f3e69742 100644 --- a/packages/core/src/components/Nodes/NodeWrapper.ts +++ b/packages/core/src/components/Nodes/NodeWrapper.ts @@ -1,33 +1,41 @@ -import { computed, defineComponent, h, nextTick, onMounted, provide, ref, toRef, watch } from 'vue' -import { until, useVModel } from '@vueuse/core' +import { + computed, + defineComponent, + getCurrentInstance, + h, + inject, + nextTick, + onMounted, + provide, + ref, + resolveComponent, + toRef, + watch, +} from 'vue' +import { until } from '@vueuse/core' import { ARIA_NODE_DESC_KEY, + ErrorCode, + VueFlowError, arrowKeyDiffs, calcNextPosition, elementSelectionKeys, getXYZPos, handleNodeClick, } from '../../utils' -import type { GraphNode, HandleConnectable, NodeComponent } from '../../types' -import { NodeId, NodeRef } from '../../context' -import { isInputDOMNode, useDrag, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables' +import { NodeId, NodeRef, Slots } from '../../context' +import { isInputDOMNode, useDrag, useNode, useNodeHooks, useUpdateNodePositions, useVueFlow } from '../../composables' +import type { NodeComponent } from '../../types' interface Props { id: string - draggable: boolean - selectable: boolean - connectable: HandleConnectable - focusable: boolean - type: NodeComponent | ((...args: any[]) => any) | object | false - name: string - node: GraphNode resizeObserver: ResizeObserver } const NodeWrapper = defineComponent({ name: 'Node', compatConfig: { MODE: 3 }, - props: ['name', 'type', 'id', 'draggable', 'selectable', 'focusable', 'connectable', 'node', 'resizeObserver'], + props: ['id', 'resizeObserver'], setup(props: Props) { provide(NodeId, props.id) @@ -38,7 +46,6 @@ const NodeWrapper = defineComponent({ nodesSelectionActive, multiSelectionActive, emits, - findNode, removeSelectedNodes, addSelectedNodes, updateNodeDimensions, @@ -52,48 +59,86 @@ const NodeWrapper = defineComponent({ snapToGrid, snapGrid, nodeDragThreshold, - getConnectedEdges, + nodesDraggable, + elementsSelectable, + nodesConnectable, + nodesFocusable, } = useVueFlow() + const slots = inject(Slots) + + const instance = getCurrentInstance() + const updateNodePositions = useUpdateNodePositions() - const node = useVModel(props, 'node') - - const parentNode = computed(() => findNode(node.value?.parentNode)) - - const connectedEdges = computed(() => getConnectedEdges(node.value ? [node.value] : [])) + const { node, parentNode, connectedEdges } = useNode(props.id) const nodeElement = ref(null) - provide(NodeRef, nodeElement) - const { emit, on } = useNodeHooks(node.value, emits) + const isDraggable = toRef(() => (typeof node.draggable === 'undefined' ? nodesDraggable.value : node.draggable)) + + const isSelectable = toRef(() => (typeof node.selectable === 'undefined' ? elementsSelectable.value : node.selectable)) + + const isConnectable = toRef(() => (typeof node.connectable === 'undefined' ? nodesConnectable.value : node.connectable)) + + const isFocusable = toRef(() => (typeof node.focusable === 'undefined' ? nodesFocusable.value : node.focusable)) + + const nodeCmp = computed(() => { + const name = node.type || 'default' + + const slot = slots?.[`node-${name}`] + if (slot) { + return slot + } + + let nodeType = node.template || getNodeTypes.value[name] + + if (typeof nodeType === 'string') { + if (instance) { + const components = Object.keys(instance.appContext.components) + if (components && components.includes(name)) { + nodeType = resolveComponent(name, false) as NodeComponent + } + } + } + + if (nodeType && typeof nodeType !== 'string') { + return nodeType + } + + emits.error(new VueFlowError(ErrorCode.NODE_TYPE_MISSING, nodeType)) + + return false + }) + + const { emit, on } = useNodeHooks(node, emits) const dragging = useDrag({ id: props.id, el: nodeElement, - disabled: () => !props.draggable, - selectable: () => props.selectable, - dragHandle: () => node.value.dragHandle, + disabled: () => !isDraggable.value, + selectable: () => isSelectable.value, + dragHandle: () => node.dragHandle, onStart(args) { // todo: remove intersections from here - they are not needed and only reduce performance - emit.dragStart({ ...args, intersections: getIntersectingNodes(node.value) }) + emit.dragStart({ ...args, intersections: getIntersectingNodes(node) }) }, onDrag(args) { - emit.drag({ ...args, intersections: getIntersectingNodes(node.value) }) + emit.drag({ ...args, intersections: getIntersectingNodes(node) }) }, onStop(args) { - emit.dragStop({ ...args, intersections: getIntersectingNodes(node.value) }) + emit.dragStop({ ...args, intersections: getIntersectingNodes(node) }) }, }) - const getClass = computed(() => (node.value.class instanceof Function ? node.value.class(node.value) : node.value.class)) + const getClass = computed(() => (node.class instanceof Function ? node.class(node) : node.class)) const getStyle = computed(() => { - const styles = (node.value.style instanceof Function ? node.value.style(node.value) : node.value.style) || {} + const styles = (node.style instanceof Function ? node.style(node) : node.style) || {} - const width = node.value.width instanceof Function ? node.value.width(node.value) : node.value.width - const height = node.value.height instanceof Function ? node.value.height(node.value) : node.value.height + const width = node.width instanceof Function ? node.width(node) : node.width + const height = node.height instanceof Function ? node.height(node) : node.height if (width) { styles.width = typeof width === 'string' ? width : `${width}px` @@ -106,7 +151,7 @@ const NodeWrapper = defineComponent({ return styles }) - const zIndex = toRef(() => Number(node.value.zIndex ?? getStyle.value.zIndex ?? 0)) + const zIndex = toRef(() => Number(node.zIndex ?? getStyle.value.zIndex ?? 0)) onUpdateNodeInternals((updateIds) => { if (updateIds.includes(props.id)) { @@ -116,7 +161,7 @@ const NodeWrapper = defineComponent({ onMounted(() => { watch( - () => node.value.hidden, + () => node.hidden, (isHidden = false, _, onCleanup) => { if (!isHidden && nodeElement.value) { props.resizeObserver.observe(nodeElement.value) @@ -132,7 +177,7 @@ const NodeWrapper = defineComponent({ ) }) - watch([() => node.value.type, () => node.value.sourcePosition, () => node.value.targetPosition], () => { + watch([() => node.type, () => node.sourcePosition, () => node.targetPosition], () => { nextTick(() => { updateNodeDimensions([{ id: props.id, nodeElement: nodeElement.value as HTMLDivElement, forceUpdate: true }]) }) @@ -141,15 +186,15 @@ const NodeWrapper = defineComponent({ /** this watcher only updates XYZPosition (when dragging a parent etc) */ watch( [ - () => node.value.position.x, - () => node.value.position.y, + () => node.position.x, + () => node.position.y, () => parentNode.value?.computedPosition.x, () => parentNode.value?.computedPosition.y, () => parentNode.value?.computedPosition.z, zIndex, - () => node.value.selected, - () => node.value.dimensions.height, - () => node.value.dimensions.width, + () => node.selected, + () => node.dimensions.height, + () => node.dimensions.width, () => parentNode.value?.dimensions.height, () => parentNode.value?.dimensions.width, ], @@ -157,19 +202,19 @@ const NodeWrapper = defineComponent({ const xyzPos = { x: newX, y: newY, - z: nodeZIndex + (elevateNodesOnSelect.value ? (node.value.selected ? 1000 : 0) : 0), + z: nodeZIndex + (elevateNodesOnSelect.value ? (node.selected ? 1000 : 0) : 0), } if (typeof parentX !== 'undefined' && typeof parentY !== 'undefined') { - node.value.computedPosition = getXYZPos({ x: parentX, y: parentY, z: parentZ! }, xyzPos) + node.computedPosition = getXYZPos({ x: parentX, y: parentY, z: parentZ! }, xyzPos) } else { - node.value.computedPosition = xyzPos + node.computedPosition = xyzPos } }, { flush: 'post', immediate: true }, ) - watch([() => node.value.extent, nodeExtent], ([nodeExtent, globalExtent], [oldNodeExtent, oldGlobalExtent]) => { + watch([() => node.extent, nodeExtent], ([nodeExtent, globalExtent], [oldNodeExtent, oldGlobalExtent]) => { // update position if extent has actually changed if (nodeExtent !== oldNodeExtent || globalExtent !== oldGlobalExtent) { clampPosition() @@ -179,10 +224,10 @@ const NodeWrapper = defineComponent({ // clamp initial position to nodes' extent // if extent is parent, we need dimensions to properly clamp the position if ( - node.value.extent === 'parent' || - (typeof node.value.extent === 'object' && 'range' in node.value.extent && node.value.extent.range === 'parent') + node.extent === 'parent' || + (typeof node.extent === 'object' && 'range' in node.extent && node.extent.range === 'parent') ) { - until(() => node.value.initialized) + until(() => node.initialized) .toBe(true) .then(clampPosition) } @@ -192,7 +237,7 @@ const NodeWrapper = defineComponent({ } return () => { - if (node.value.hidden) { + if (node.hidden) { return null } @@ -200,31 +245,31 @@ const NodeWrapper = defineComponent({ 'div', { 'ref': nodeElement, - 'data-id': node.value.id, + 'data-id': node.id, 'class': [ 'vue-flow__node', - `vue-flow__node-${props.type === false ? 'default' : props.name}`, + `vue-flow__node-${nodeCmp.value === false ? 'default' : node.type || 'default'}`, { - [noPanClassName.value]: props.draggable, + [noPanClassName.value]: isDraggable.value, dragging: dragging?.value, - draggable: props.draggable, - selected: node.value.selected, - selectable: props.selectable, - parent: node.value.isParent, + draggable: isDraggable.value, + selected: node.selected, + selectable: isSelectable.value, + parent: node.isParent, }, getClass.value, ], 'style': { - visibility: node.value.initialized ? 'visible' : 'hidden', - zIndex: node.value.computedPosition.z ?? zIndex.value, - transform: `translate(${node.value.computedPosition.x}px,${node.value.computedPosition.y}px)`, - pointerEvents: props.selectable || props.draggable ? 'all' : 'none', + visibility: node.initialized ? 'visible' : 'hidden', + zIndex: node.computedPosition.z ?? zIndex.value, + transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`, + pointerEvents: isSelectable.value || isDraggable.value ? 'all' : 'none', ...getStyle.value, }, - 'tabIndex': props.focusable ? 0 : undefined, - 'role': props.focusable ? 'button' : undefined, + 'tabIndex': isFocusable.value ? 0 : undefined, + 'role': isFocusable.value ? 'button' : undefined, 'aria-describedby': disableKeyboardA11y.value ? undefined : `${ARIA_NODE_DESC_KEY}-${vueFlowId}`, - 'aria-label': node.value.ariaLabel, + 'aria-label': node.ariaLabel, 'onMouseenter': onMouseEnter, 'onMousemove': onMouseMove, 'onMouseleave': onMouseLeave, @@ -234,26 +279,26 @@ const NodeWrapper = defineComponent({ 'onKeydown': onKeyDown, }, [ - h(props.type === false ? getNodeTypes.value.default : props.type, { - id: node.value.id, - type: node.value.type, - data: node.value.data, - events: { ...node.value.events, ...on }, - selected: node.value.selected, - resizing: node.value.resizing, + h(nodeCmp.value === false ? getNodeTypes.value.default : (nodeCmp.value as any), { + id: node.id, + type: node.type, + data: node.data, + events: { ...node.events, ...on }, + selected: node.selected, + resizing: node.resizing, dragging: dragging.value, - connectable: props.connectable, - position: node.value.computedPosition, - dimensions: node.value.dimensions, - isValidTargetPos: node.value.isValidTargetPos, - isValidSourcePos: node.value.isValidSourcePos, - parent: node.value.parentNode, - parentNodeId: node.value.parentNode, - zIndex: node.value.computedPosition.z ?? zIndex.value, - targetPosition: node.value.targetPosition, - sourcePosition: node.value.sourcePosition, - label: node.value.label, - dragHandle: node.value.dragHandle, + connectable: isConnectable.value, + position: node.computedPosition, + dimensions: node.dimensions, + isValidTargetPos: node.isValidTargetPos, + isValidSourcePos: node.isValidSourcePos, + parent: node.parentNode, + parentNodeId: node.parentNode, + zIndex: node.computedPosition.z ?? zIndex.value, + targetPosition: node.targetPosition, + sourcePosition: node.sourcePosition, + label: node.label, + dragHandle: node.dragHandle, onUpdateNodeInternals: updateInternals, }), ], @@ -261,28 +306,22 @@ const NodeWrapper = defineComponent({ } /** this re-calculates the current position, necessary for clamping by a node's extent */ function clampPosition() { - const nextPos = node.value.computedPosition + const nextPos = node.computedPosition if (snapToGrid.value) { nextPos.x = snapGrid.value[0] * Math.round(nextPos.x / snapGrid.value[0]) nextPos.y = snapGrid.value[1] * Math.round(nextPos.y / snapGrid.value[1]) } - const { computedPosition, position } = calcNextPosition( - node.value, - nextPos, - emits.error, - nodeExtent.value, - parentNode.value, - ) + const { computedPosition, position } = calcNextPosition(node, nextPos, emits.error, nodeExtent.value, parentNode.value) // only overwrite positions if there are changes when clamping - if (node.value.computedPosition.x !== computedPosition.x || node.value.computedPosition.y !== computedPosition.y) { - node.value.computedPosition = { ...node.value.computedPosition, ...computedPosition } + if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) { + node.computedPosition = { ...node.computedPosition, ...computedPosition } } - if (node.value.position.x !== position.x || node.value.position.y !== position.y) { - node.value.position = position + if (node.position.x !== position.x || node.position.y !== position.y) { + node.position = position } } @@ -294,34 +333,34 @@ const NodeWrapper = defineComponent({ function onMouseEnter(event: MouseEvent) { if (!dragging?.value) { - emit.mouseEnter({ event, node: node.value, connectedEdges: connectedEdges.value }) + emit.mouseEnter({ event, node, connectedEdges: connectedEdges.value }) } } function onMouseMove(event: MouseEvent) { if (!dragging?.value) { - emit.mouseMove({ event, node: node.value, connectedEdges: connectedEdges.value }) + emit.mouseMove({ event, node, connectedEdges: connectedEdges.value }) } } function onMouseLeave(event: MouseEvent) { if (!dragging?.value) { - emit.mouseLeave({ event, node: node.value, connectedEdges: connectedEdges.value }) + emit.mouseLeave({ event, node, connectedEdges: connectedEdges.value }) } } function onContextMenu(event: MouseEvent) { - return emit.contextMenu({ event, node: node.value, connectedEdges: connectedEdges.value }) + return emit.contextMenu({ event, node, connectedEdges: connectedEdges.value }) } function onDoubleClick(event: MouseEvent) { - return emit.doubleClick({ event, node: node.value, connectedEdges: connectedEdges.value }) + return emit.doubleClick({ event, node, connectedEdges: connectedEdges.value }) } function onSelectNode(event: MouseEvent) { - if (props.selectable && (!selectNodesOnDrag.value || !props.draggable || nodeDragThreshold.value > 0)) { + if (isSelectable.value && (!selectNodesOnDrag.value || !isDraggable.value || nodeDragThreshold.value > 0)) { handleNodeClick( - node.value, + node, multiSelectionActive.value, addSelectedNodes, removeSelectedNodes, @@ -331,7 +370,7 @@ const NodeWrapper = defineComponent({ ) } - emit.click({ event, node: node.value, connectedEdges: connectedEdges.value }) + emit.click({ event, node, connectedEdges: connectedEdges.value }) } function onKeyDown(event: KeyboardEvent) { @@ -339,11 +378,11 @@ const NodeWrapper = defineComponent({ return } - if (elementSelectionKeys.includes(event.key) && props.selectable) { + if (elementSelectionKeys.includes(event.key) && isSelectable.value) { const unselect = event.key === 'Escape' handleNodeClick( - node.value, + node, multiSelectionActive.value, addSelectedNodes, removeSelectedNodes, @@ -351,9 +390,9 @@ const NodeWrapper = defineComponent({ unselect, nodeElement.value!, ) - } else if (props.draggable && node.value.selected && arrowKeyDiffs[event.key]) { + } else if (isDraggable.value && node.selected && arrowKeyDiffs[event.key]) { ariaLiveMessage.value = `Moved selected node ${event.key.replace('Arrow', '').toLowerCase()}. New position, x: ${~~node - .value.position.x}, y: ${~~node.value.position.y}` + .position.x}, y: ${~~node.position.y}` updateNodePositions( { diff --git a/packages/core/src/composables/useEdge.ts b/packages/core/src/composables/useEdge.ts index 742a76ec..8a1d9287 100644 --- a/packages/core/src/composables/useEdge.ts +++ b/packages/core/src/composables/useEdge.ts @@ -21,7 +21,7 @@ export function useEdge(edgeId) + const edge = findEdge(edgeId)! if (!edge) { emits.error(new VueFlowError(ErrorCode.EDGE_NOT_FOUND, edgeId)) diff --git a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue index 8e81ea11..ff3c0058 100644 --- a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue +++ b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue @@ -1,66 +1,11 @@