[backport core/1.32] Simplify Vue node resize to bottom-right corner only (#7063) (#7066)

## Summary
- Backport of #7063 to core/1.32
- Simplifies Vue node resize to bottom-right corner only

Cherry-picked from d76c59cb14

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7066-refactor-Backport-Simplify-Vue-node-resize-to-bottom-right-corner-only-7063-2bc6d73d365081fabcdde1c3876c5857)
by [Unito](https://www.unito.io)

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Christian Byrne
2025-11-30 19:53:15 -08:00
committed by GitHub
parent b879cbaaf8
commit e132eafe1b
6 changed files with 46 additions and 334 deletions

View File

@@ -117,17 +117,14 @@
</div>
</template>
<!-- Resize handles -->
<template v-if="!isCollapsed">
<div
v-for="handle in cornerResizeHandles"
:key="handle.id"
role="button"
:aria-label="handle.ariaLabel"
:class="cn(baseResizeHandleClasses, handle.classes)"
@pointerdown.stop="handleResizePointerDown(handle.direction)($event)"
/>
</template>
<!-- Resize handle (bottom-right only) -->
<div
v-if="!isCollapsed"
role="button"
:aria-label="t('g.resizeFromBottomRight')"
:class="cn(baseResizeHandleClasses, 'right-0 bottom-0 cursor-se-resize')"
@pointerdown.stop="handleResizePointerDown"
/>
</div>
</template>
@@ -150,7 +147,6 @@ import { useTelemetry } from '@/platform/telemetry'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
import { useTransformState } from '@/renderer/core/layout/transform/useTransformState'
import SlotConnectionDot from '@/renderer/extensions/vueNodes/components/SlotConnectionDot.vue'
import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers'
import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions'
@@ -172,7 +168,6 @@ import {
} from '@/utils/graphTraversalUtil'
import { cn } from '@/utils/tailwindUtil'
import type { ResizeHandleDirection } from '../interactions/resize/resizeMath'
import { useNodeResize } from '../interactions/resize/useNodeResize'
import LivePreview from './LivePreview.vue'
import NodeContent from './NodeContent.vue'
@@ -197,13 +192,6 @@ const { bringNodeToFront } = useNodeZIndex()
useVueElementTracking(() => nodeData.id, 'node')
const transformState = useTransformState()
if (!transformState) {
throw new Error(
'TransformState must be provided for node resize functionality'
)
}
const { selectedNodeIds } = storeToRefs(useCanvasStore())
const isSelected = computed(() => {
return selectedNodeIds.value.has(nodeData.id)
@@ -271,7 +259,7 @@ onErrorCaptured((error) => {
return false // Prevent error propagation
})
const { position, size, zIndex, moveNodeTo } = useNodeLayout(() => nodeData.id)
const { position, size, zIndex } = useNodeLayout(() => nodeData.id)
const { pointerHandlers } = useNodePointerInteractions(() => nodeData.id)
const { onPointerdown, ...remainingPointerHandlers } = pointerHandlers
const { startDrag } = useNodeDrag()
@@ -322,74 +310,23 @@ onMounted(() => {
const baseResizeHandleClasses =
'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40'
const POSITION_EPSILON = 0.01
type CornerResizeHandle = {
id: string
direction: ResizeHandleDirection
classes: string
ariaLabel: string
}
const cornerResizeHandles: CornerResizeHandle[] = [
{
id: 'se',
direction: { horizontal: 'right', vertical: 'bottom' },
classes: 'right-0 bottom-0 cursor-se-resize',
ariaLabel: t('g.resizeFromBottomRight')
},
{
id: 'ne',
direction: { horizontal: 'right', vertical: 'top' },
classes: 'right-0 top-0 cursor-ne-resize',
ariaLabel: t('g.resizeFromTopRight')
},
{
id: 'sw',
direction: { horizontal: 'left', vertical: 'bottom' },
classes: 'left-0 bottom-0 cursor-sw-resize',
ariaLabel: t('g.resizeFromBottomLeft')
},
{
id: 'nw',
direction: { horizontal: 'left', vertical: 'top' },
classes: 'left-0 top-0 cursor-nw-resize',
ariaLabel: t('g.resizeFromTopLeft')
}
]
const MIN_NODE_WIDTH = 225
const { startResize } = useNodeResize(
(result, element) => {
if (isCollapsed.value) return
const { startResize } = useNodeResize((result, element) => {
if (isCollapsed.value) return
// Clamp width to minimum to avoid conflicts with CSS min-width
const clampedWidth = Math.max(result.size.width, MIN_NODE_WIDTH)
// Clamp width to minimum to avoid conflicts with CSS min-width
const clampedWidth = Math.max(result.size.width, MIN_NODE_WIDTH)
// Apply size directly to DOM element - ResizeObserver will pick this up
element.style.setProperty('--node-width', `${clampedWidth}px`)
element.style.setProperty('--node-height', `${result.size.height}px`)
// Apply size directly to DOM element - ResizeObserver will pick this up
element.style.setProperty('--node-width', `${clampedWidth}px`)
element.style.setProperty('--node-height', `${result.size.height}px`)
})
const currentPosition = position.value
const deltaX = Math.abs(result.position.x - currentPosition.x)
const deltaY = Math.abs(result.position.y - currentPosition.y)
if (deltaX > POSITION_EPSILON || deltaY > POSITION_EPSILON) {
moveNodeTo(result.position)
}
},
{
transformState
}
)
const handleResizePointerDown = (direction: ResizeHandleDirection) => {
return (event: PointerEvent) => {
if (nodeData.flags?.pinned) return
startResize(event, direction, { ...position.value })
}
const handleResizePointerDown = (event: PointerEvent) => {
if (nodeData.flags?.pinned) return
startResize(event)
}
watch(isCollapsed, (collapsed) => {