refactor(nodes): reduce nodewrapper re-render
# What's changed? * use v-model for computed pos * remove computed from style and class getters
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { CSSProperties } from 'vue'
|
|
||||||
import { useVueFlow, useDrag } from '../../composables'
|
import { useVueFlow, useDrag } from '../../composables'
|
||||||
import { NodeComponent, SnapGrid } from '../../types'
|
import { NodeComponent, SnapGrid, XYZPosition } from '../../types'
|
||||||
import { NodeId } from '../../context'
|
import { NodeId } from '../../context'
|
||||||
import { getConnectedEdges, getHandleBounds, getXYZPos } from '../../utils'
|
import { getConnectedEdges, getHandleBounds, getXYZPos } from '../../utils'
|
||||||
|
|
||||||
const { id, type, name, draggable, selectable, connectable, snapGrid } = defineProps<{
|
const { id, type, name, draggable, selectable, connectable, snapGrid, ...props } = defineProps<{
|
||||||
id: string
|
id: string
|
||||||
|
modelValue: XYZPosition
|
||||||
draggable: boolean
|
draggable: boolean
|
||||||
selectable: boolean
|
selectable: boolean
|
||||||
connectable: boolean
|
connectable: boolean
|
||||||
@@ -15,6 +15,11 @@ const { id, type, name, draggable, selectable, connectable, snapGrid } = defineP
|
|||||||
name: string
|
name: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
let computedPosition = $(useVModel(props, 'modelValue', emit))
|
||||||
|
|
||||||
provide(NodeId, id)
|
provide(NodeId, id)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -95,7 +100,7 @@ onMounted(() => {
|
|||||||
onBeforeUnmount(() => observer.stop())
|
onBeforeUnmount(() => observer.stop())
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[() => node.position, () => parentNode?.computedPosition, () => node.selected, () => parentNode?.selected],
|
[() => node.position, () => parentNode?.computedPosition],
|
||||||
([pos, parent]) => {
|
([pos, parent]) => {
|
||||||
const xyzPos = {
|
const xyzPos = {
|
||||||
...pos,
|
...pos,
|
||||||
@@ -103,14 +108,14 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
node.computedPosition = getXYZPos(parent, xyzPos)
|
computedPosition = getXYZPos(parent, xyzPos)
|
||||||
} else {
|
} else {
|
||||||
node.computedPosition = xyzPos
|
computedPosition = xyzPos
|
||||||
}
|
}
|
||||||
|
|
||||||
node.handleBounds = getHandleBounds(nodeElement.value, viewport.zoom)
|
node.handleBounds = getHandleBounds(nodeElement.value, viewport.zoom)
|
||||||
},
|
},
|
||||||
{ deep: true, flush: 'post' },
|
{ immediate: true, deep: true, flush: 'post' },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -160,35 +165,19 @@ const onSelectNode = (event: MouseEvent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getClass = computed(() => {
|
const getClass = () => {
|
||||||
const extraClass = node.class instanceof Function ? node.class(node) : node.class
|
return node.class instanceof Function ? node.class(node) : node.class
|
||||||
return [
|
}
|
||||||
'vue-flow__node',
|
|
||||||
`vue-flow__node-${name}`,
|
|
||||||
noPanClassName,
|
|
||||||
{
|
|
||||||
dragging,
|
|
||||||
selected: node.selected,
|
|
||||||
selectable,
|
|
||||||
},
|
|
||||||
extraClass,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
const getStyle = computed(() => {
|
const getStyle = () => {
|
||||||
const styles = (node.style instanceof Function ? node.style(node) : node.style) || {}
|
const styles = (node.style instanceof Function ? node.style(node) : node.style) || {}
|
||||||
const width = node.width instanceof Function ? node.width(node) : node.width
|
const width = node.width instanceof Function ? node.width(node) : node.width
|
||||||
const height = node.height instanceof Function ? node.height(node) : node.height
|
const height = node.height instanceof Function ? node.height(node) : node.height
|
||||||
if (width) styles.width = typeof width === 'string' ? width : `${width}px`
|
if (width) styles.width = typeof width === 'string' ? width : `${width}px`
|
||||||
if (height) styles.height = typeof height === 'string' ? height : `${height}px`
|
if (height) styles.height = typeof height === 'string' ? height : `${height}px`
|
||||||
|
|
||||||
return {
|
return styles
|
||||||
zIndex: node.computedPosition.z,
|
}
|
||||||
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
|
|
||||||
pointerEvents: selectable || draggable ? 'all' : 'none',
|
|
||||||
...styles,
|
|
||||||
} as CSSProperties
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@@ -201,8 +190,23 @@ export default {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
ref="nodeElement"
|
ref="nodeElement"
|
||||||
:class="getClass"
|
:class="[
|
||||||
:style="getStyle"
|
'vue-flow__node',
|
||||||
|
`vue-flow__node-${name}`,
|
||||||
|
noPanClassName,
|
||||||
|
{
|
||||||
|
dragging,
|
||||||
|
selected: node.selected,
|
||||||
|
selectable,
|
||||||
|
},
|
||||||
|
getClass(),
|
||||||
|
]"
|
||||||
|
:style="{
|
||||||
|
zIndex: node.computedPosition.z ? node.computedPosition.z : node.selected ? 1000 : 0,
|
||||||
|
transform: `translate(${node.computedPosition.x}px,${node.computedPosition.y}px)`,
|
||||||
|
pointerEvents: selectable || draggable ? 'all' : 'none',
|
||||||
|
...getStyle(),
|
||||||
|
}"
|
||||||
:data-id="node.id"
|
:data-id="node.id"
|
||||||
@mouseenter="onMouseEnter"
|
@mouseenter="onMouseEnter"
|
||||||
@mousemove="onMouseMove"
|
@mousemove="onMouseMove"
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ const selectable = (s?: boolean) => (typeof s === 'undefined' ? elementsSelectab
|
|||||||
const connectable = (c?: boolean) => (typeof c === 'undefined' ? nodesConnectable : c)
|
const connectable = (c?: boolean) => (typeof c === 'undefined' ? nodesConnectable : c)
|
||||||
const hasSnapGrid = (sg?: SnapGrid) => (sg ?? snapToGrid ? snapGrid : undefined)
|
const hasSnapGrid = (sg?: SnapGrid) => (sg ?? snapToGrid ? snapGrid : undefined)
|
||||||
|
|
||||||
const getType = (node: GraphNode) => {
|
const getType = (type?: string, template?: GraphNode['template']) => {
|
||||||
const name = node.type || 'default'
|
const name = type || 'default'
|
||||||
let nodeType = node.template ?? getNodeTypes[name]
|
let nodeType = template ?? getNodeTypes[name]
|
||||||
const instance = getCurrentInstance()
|
const instance = getCurrentInstance()
|
||||||
|
|
||||||
if (typeof nodeType === 'string') {
|
if (typeof nodeType === 'string') {
|
||||||
@@ -40,7 +40,7 @@ const getType = (node: GraphNode) => {
|
|||||||
|
|
||||||
const slot = slots?.[`node-${name}`]
|
const slot = slots?.[`node-${name}`]
|
||||||
if (!slot?.({})) {
|
if (!slot?.({})) {
|
||||||
console.warn(`[vueflow]: Node type "${node.type}" not found and no node-slot detected. Using fallback type "default".`)
|
console.warn(`[vueflow]: Node type "${type}" not found and no node-slot detected. Using fallback type "default".`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,8 @@ export default {
|
|||||||
v-for="node of getNodes"
|
v-for="node of getNodes"
|
||||||
:id="node.id"
|
:id="node.id"
|
||||||
:key="node.id"
|
:key="node.id"
|
||||||
:type="getType(node)"
|
v-model="node.computedPosition"
|
||||||
|
:type="getType(node.type, node.template)"
|
||||||
:name="node.type || 'default'"
|
:name="node.type || 'default'"
|
||||||
:draggable="draggable(node.draggable)"
|
:draggable="draggable(node.draggable)"
|
||||||
:selectable="selectable(node.selectable)"
|
:selectable="selectable(node.selectable)"
|
||||||
|
|||||||
Reference in New Issue
Block a user