diff --git a/packages/core/src/components/Nodes/DefaultNode.ts b/packages/core/src/components/Nodes/DefaultNode.ts index 839e7a7f7..1eeee58c4 100644 --- a/packages/core/src/components/Nodes/DefaultNode.ts +++ b/packages/core/src/components/Nodes/DefaultNode.ts @@ -1,28 +1,25 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { Node, NodeProps } from '../../types' import { Position } from '../../types' -const DefaultNode: FunctionalComponent> = function ({ +const DefaultNode: FunctionalComponent>> = function ({ sourcePosition = Position.Bottom, targetPosition = Position.Top, - label: _label, - connectable = true, - isValidTargetPos, - isValidSourcePos, + isConnectable = true, data, }) { - const label = data.label || _label + const label = data.label return [ - h(Handle as Component, { type: 'target', position: targetPosition, connectable, isValidConnection: isValidTargetPos }), + h(Handle as Component, { type: 'target', position: targetPosition, isConnectable }), typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), - h(Handle as Component, { type: 'source', position: sourcePosition, connectable, isValidConnection: isValidSourcePos }), + h(Handle as Component, { type: 'source', position: sourcePosition, isConnectable }), ] } -DefaultNode.props = ['sourcePosition', 'targetPosition', 'label', 'isValidTargetPos', 'isValidSourcePos', 'connectable', 'data'] +DefaultNode.props = ['sourcePosition', 'targetPosition', 'isConnectable', 'data'] DefaultNode.inheritAttrs = false DefaultNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/components/Nodes/InputNode.ts b/packages/core/src/components/Nodes/InputNode.ts index c963faaf0..d61f1ebb3 100644 --- a/packages/core/src/components/Nodes/InputNode.ts +++ b/packages/core/src/components/Nodes/InputNode.ts @@ -1,25 +1,23 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { Node, NodeProps } from '../../types' import { Position } from '../../types' -const InputNode: FunctionalComponent> = function ({ +const InputNode: FunctionalComponent>> = function ({ sourcePosition = Position.Bottom, - label: _label, - connectable = true, - isValidSourcePos, + isConnectable = true, data, }) { - const label = data.label || _label + const label = data.label return [ typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), - h(Handle as Component, { type: 'source', position: sourcePosition, connectable, isValidConnection: isValidSourcePos }), + h(Handle as Component, { type: 'source', position: sourcePosition, isConnectable }), ] } -InputNode.props = ['sourcePosition', 'label', 'isValidSourcePos', 'connectable', 'data'] +InputNode.props = ['sourcePosition', 'isConnectable', 'data'] InputNode.inheritAttrs = false InputNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/components/Nodes/NodeWrapper.ts b/packages/core/src/components/Nodes/NodeWrapper.ts index 9c443ea49..a1e0846ba 100644 --- a/packages/core/src/components/Nodes/NodeWrapper.ts +++ b/packages/core/src/components/Nodes/NodeWrapper.ts @@ -82,7 +82,7 @@ const NodeWrapper = defineComponent({ const isFocusable = toRef(() => (typeof node.focusable === 'undefined' ? nodesFocusable.value : node.focusable)) - const isInit = toRef(() => !!node.dimensions.width && !!node.dimensions.height) + const isInit = toRef(() => !!node.measured.width && !!node.measured.height) const nodeCmp = computed(() => { const name = node.type || 'default' @@ -92,7 +92,7 @@ const NodeWrapper = defineComponent({ return slot } - let nodeType = node.template || getNodeTypes.value[name] + let nodeType = getNodeTypes.value[name] if (typeof nodeType === 'string') { if (instance) { @@ -112,7 +112,7 @@ const NodeWrapper = defineComponent({ return false }) - const { emit, on } = useNodeHooks(node, emits) + const { emit } = useNodeHooks(node, emits) const dragging = useDrag({ id: props.id, @@ -134,26 +134,7 @@ const NodeWrapper = defineComponent({ }, }) - const getClass = computed(() => (node.class instanceof Function ? node.class(node) : node.class)) - - const getStyle = computed(() => { - const styles = (node.style instanceof Function ? node.style(node) : node.style) || {} - - 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` - } - - if (height) { - styles.height = typeof height === 'string' ? height : `${height}px` - } - - return styles - }) - - const zIndex = toRef(() => Number(node.zIndex ?? getStyle.value.zIndex ?? 0)) + const zIndex = toRef(() => Number(node.zIndex ?? node.style?.zIndex ?? 0)) onUpdateNodeInternals((updateIds) => { // when no ids are passed, update all nodes @@ -191,27 +172,38 @@ const NodeWrapper = defineComponent({ [ () => node.position.x, () => node.position.y, - () => parentNode.value?.computedPosition.x, - () => parentNode.value?.computedPosition.y, - () => parentNode.value?.computedPosition.z, + () => parentNode.value?.internals.positionAbsolute.x, + () => parentNode.value?.internals.positionAbsolute.y, + () => parentNode.value?.internals.z, zIndex, () => node.selected, - () => node.dimensions.height, - () => node.dimensions.width, - () => parentNode.value?.dimensions.height, - () => parentNode.value?.dimensions.width, + () => node.measured.height, + () => node.measured.width, + () => parentNode.value?.measured.height, + () => parentNode.value?.measured.width, ], ([newX, newY, parentX, parentY, parentZ, nodeZIndex]) => { - const xyzPos = { - x: newX, - y: newY, - z: nodeZIndex + (elevateNodesOnSelect.value ? (node.selected ? 1000 : 0) : 0), - } + const nextZ = nodeZIndex + (elevateNodesOnSelect.value ? (node.selected ? 1000 : 0) : 0) if (typeof parentX !== 'undefined' && typeof parentY !== 'undefined') { - node.computedPosition = getXYZPos({ x: parentX, y: parentY, z: parentZ! }, xyzPos) + const { + x: absoluteX, + y: absoluteY, + z, + } = getXYZPos( + { x: parentX, y: parentY, z: parentZ! }, + { + x: newX, + y: newY, + z: nextZ, + }, + ) + + node.internals.positionAbsolute = { x: absoluteX, y: absoluteY } + node.internals.z = z } else { - node.computedPosition = xyzPos + node.internals.positionAbsolute = { x: newX, y: newY } + node.internals.z = nextZ } }, { flush: 'post', immediate: true }, @@ -258,17 +250,18 @@ const NodeWrapper = defineComponent({ draggable: isDraggable.value, selected: node.selected, selectable: isSelectable.value, - parent: node.isParent, }, - getClass.value, + node.class, + ], + 'style': [ + { + visibility: isInit.value ? 'visible' : 'hidden', + zIndex: node.internals.z ?? zIndex.value, + transform: `translate(${node.internals.positionAbsolute.x}px,${node.internals.positionAbsolute.y}px)`, + pointerEvents: isSelectable.value || isDraggable.value ? 'all' : 'none', + }, + node.style, ], - 'style': { - visibility: isInit.value ? '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': isFocusable.value ? 0 : undefined, 'role': isFocusable.value ? 'button' : undefined, 'aria-describedby': disableKeyboardA11y.value ? undefined : `${ARIA_NODE_DESC_KEY}-${vueFlowId}`, @@ -286,21 +279,17 @@ const NodeWrapper = defineComponent({ id: node.id, type: node.type, data: node.data, - events: { ...node.events, ...on }, selected: node.selected, - resizing: node.resizing, dragging: dragging.value, 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, + positionAbsoluteX: node.internals.positionAbsolute.x, + positionAbsoluteY: node.internals.positionAbsolute.y, + width: node.measured.width, + height: node.measured.height, + parentId: node.parentId, + zIndex: node.internals.z ?? zIndex.value, targetPosition: node.targetPosition, sourcePosition: node.sourcePosition, - label: node.label, dragHandle: node.dragHandle, onUpdateNodeInternals: updateInternals, }), @@ -309,7 +298,7 @@ const NodeWrapper = defineComponent({ } /** this re-calculates the current position, necessary for clamping by a node's extent */ function clampPosition() { - const nextPos = node.computedPosition + const nextPos = node.internals.positionAbsolute if (snapToGrid.value) { nextPos.x = snapGrid.value[0] * Math.round(nextPos.x / snapGrid.value[0]) @@ -319,8 +308,8 @@ const NodeWrapper = defineComponent({ const { computedPosition, position } = calcNextPosition(node, nextPos, emits.error, nodeExtent.value, parentNode.value) // only overwrite positions if there are changes when clamping - if (node.computedPosition.x !== computedPosition.x || node.computedPosition.y !== computedPosition.y) { - node.computedPosition = { ...node.computedPosition, ...computedPosition } + if (nextPos.x !== computedPosition.x || nextPos.y !== computedPosition.y) { + node.internals.positionAbsolute = { ...nextPos, ...computedPosition } } if (node.position.x !== position.x || node.position.y !== position.y) { diff --git a/packages/core/src/components/Nodes/OutputNode.ts b/packages/core/src/components/Nodes/OutputNode.ts index 02fb7933b..8e4a188bc 100644 --- a/packages/core/src/components/Nodes/OutputNode.ts +++ b/packages/core/src/components/Nodes/OutputNode.ts @@ -1,25 +1,23 @@ import type { Component, FunctionalComponent } from 'vue' import { h } from 'vue' import Handle from '../Handle/Handle.vue' -import type { NodeProps } from '../../types' +import type { Node, NodeProps } from '../../types' import { Position } from '../../types' -const OutputNode: FunctionalComponent> = function ({ +const OutputNode: FunctionalComponent>> = function ({ targetPosition = Position.Top, - label: _label, - connectable = true, - isValidTargetPos, + isConnectable = true, data, }) { - const label = data.label || _label + const label = data.label return [ - h(Handle as Component, { type: 'target', position: targetPosition, connectable, isValidConnection: isValidTargetPos }), + h(Handle as Component, { type: 'target', position: targetPosition, isConnectable }), typeof label !== 'string' && label ? h(label) : h('div', { innerHTML: label }), ] } -OutputNode.props = ['targetPosition', 'label', 'isValidTargetPos', 'connectable', 'data'] +OutputNode.props = ['targetPosition', 'isConnectable', 'data'] OutputNode.inheritAttrs = false OutputNode.compatConfig = { MODE: 3 } diff --git a/packages/core/src/composables/useNode.ts b/packages/core/src/composables/useNode.ts index 9fbb0756d..5f91bf243 100644 --- a/packages/core/src/composables/useNode.ts +++ b/packages/core/src/composables/useNode.ts @@ -1,7 +1,7 @@ import { computed, inject, ref } from 'vue' -import type { NodeBase } from '@xyflow/system' import { ErrorCode, VueFlowError, getConnectedEdges } from '../utils' import { NodeRef } from '../context' +import type { GraphNode, Node } from '../types' import { useVueFlow } from './useVueFlow' import { useNodeId } from './useNodeId' @@ -16,13 +16,13 @@ import { useNodeId } from './useNodeId' * @param id - The id of the node to access * @returns the node id, the node, the node dom element, it's parent and connected edges */ -export function useNode(id?: string) { +export function useNode(id?: string) { const nodeId = id ?? useNodeId() ?? '' const nodeEl = inject(NodeRef, ref(null)) - const { findNode, edges, emits } = useVueFlow() + const { getNode, edges, emits } = useVueFlow() - const node = findNode(nodeId)! + const node = getNode(nodeId) as GraphNode if (!node) { emits.error(new VueFlowError(ErrorCode.NODE_NOT_FOUND, nodeId)) @@ -32,7 +32,7 @@ export function useNode(id?: string) { id: nodeId, nodeEl, node, - parentNode: computed(() => findNode(node.parentId)), + parentNode: computed(() => getNode(node.parentId)), connectedEdges: computed(() => getConnectedEdges([node], edges.value)), } } diff --git a/packages/core/src/types/node.ts b/packages/core/src/types/node.ts index c756bfeee..d64f20217 100644 --- a/packages/core/src/types/node.ts +++ b/packages/core/src/types/node.ts @@ -34,6 +34,8 @@ export interface Node, 'connectable' | 'extent' | 'origin'> { /** Disable/enable connecting node */ connectable?: HandleConnectable + /** Disable/enable focusing node */ + focusable?: boolean /** define node extent, i.e. area in which node can be moved */ extent?: CoordinateExtent | CoordinateExtentRange | 'parent' /** Additional class names, can be a string or a callback returning a string (receives current flow element) */