Skip to content

Commit e132eaf

Browse files
christian-byrnegithub-actions
andauthored
[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 d76c59c ┆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>
1 parent b879cba commit e132eaf

File tree

6 files changed

+46
-334
lines changed

6 files changed

+46
-334
lines changed
-874 Bytes
Loading
-833 Bytes
Loading

src/renderer/extensions/vueNodes/components/LGraphNode.vue

Lines changed: 20 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,14 @@
117117
</div>
118118
</template>
119119

120-
<!-- Resize handles -->
121-
<template v-if="!isCollapsed">
122-
<div
123-
v-for="handle in cornerResizeHandles"
124-
:key="handle.id"
125-
role="button"
126-
:aria-label="handle.ariaLabel"
127-
:class="cn(baseResizeHandleClasses, handle.classes)"
128-
@pointerdown.stop="handleResizePointerDown(handle.direction)($event)"
129-
/>
130-
</template>
120+
<!-- Resize handle (bottom-right only) -->
121+
<div
122+
v-if="!isCollapsed"
123+
role="button"
124+
:aria-label="t('g.resizeFromBottomRight')"
125+
:class="cn(baseResizeHandleClasses, 'right-0 bottom-0 cursor-se-resize')"
126+
@pointerdown.stop="handleResizePointerDown"
127+
/>
131128
</div>
132129
</template>
133130

@@ -150,7 +147,6 @@ import { useTelemetry } from '@/platform/telemetry'
150147
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'
151148
import { useCanvasInteractions } from '@/renderer/core/canvas/useCanvasInteractions'
152149
import { layoutStore } from '@/renderer/core/layout/store/layoutStore'
153-
import { useTransformState } from '@/renderer/core/layout/transform/useTransformState'
154150
import SlotConnectionDot from '@/renderer/extensions/vueNodes/components/SlotConnectionDot.vue'
155151
import { useNodeEventHandlers } from '@/renderer/extensions/vueNodes/composables/useNodeEventHandlers'
156152
import { useNodePointerInteractions } from '@/renderer/extensions/vueNodes/composables/useNodePointerInteractions'
@@ -172,7 +168,6 @@ import {
172168
} from '@/utils/graphTraversalUtil'
173169
import { cn } from '@/utils/tailwindUtil'
174170
175-
import type { ResizeHandleDirection } from '../interactions/resize/resizeMath'
176171
import { useNodeResize } from '../interactions/resize/useNodeResize'
177172
import LivePreview from './LivePreview.vue'
178173
import NodeContent from './NodeContent.vue'
@@ -197,13 +192,6 @@ const { bringNodeToFront } = useNodeZIndex()
197192
198193
useVueElementTracking(() => nodeData.id, 'node')
199194
200-
const transformState = useTransformState()
201-
if (!transformState) {
202-
throw new Error(
203-
'TransformState must be provided for node resize functionality'
204-
)
205-
}
206-
207195
const { selectedNodeIds } = storeToRefs(useCanvasStore())
208196
const isSelected = computed(() => {
209197
return selectedNodeIds.value.has(nodeData.id)
@@ -271,7 +259,7 @@ onErrorCaptured((error) => {
271259
return false // Prevent error propagation
272260
})
273261
274-
const { position, size, zIndex, moveNodeTo } = useNodeLayout(() => nodeData.id)
262+
const { position, size, zIndex } = useNodeLayout(() => nodeData.id)
275263
const { pointerHandlers } = useNodePointerInteractions(() => nodeData.id)
276264
const { onPointerdown, ...remainingPointerHandlers } = pointerHandlers
277265
const { startDrag } = useNodeDrag()
@@ -322,74 +310,23 @@ onMounted(() => {
322310
323311
const baseResizeHandleClasses =
324312
'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40'
325-
const POSITION_EPSILON = 0.01
326-
327-
type CornerResizeHandle = {
328-
id: string
329-
direction: ResizeHandleDirection
330-
classes: string
331-
ariaLabel: string
332-
}
333-
334-
const cornerResizeHandles: CornerResizeHandle[] = [
335-
{
336-
id: 'se',
337-
direction: { horizontal: 'right', vertical: 'bottom' },
338-
classes: 'right-0 bottom-0 cursor-se-resize',
339-
ariaLabel: t('g.resizeFromBottomRight')
340-
},
341-
{
342-
id: 'ne',
343-
direction: { horizontal: 'right', vertical: 'top' },
344-
classes: 'right-0 top-0 cursor-ne-resize',
345-
ariaLabel: t('g.resizeFromTopRight')
346-
},
347-
{
348-
id: 'sw',
349-
direction: { horizontal: 'left', vertical: 'bottom' },
350-
classes: 'left-0 bottom-0 cursor-sw-resize',
351-
ariaLabel: t('g.resizeFromBottomLeft')
352-
},
353-
{
354-
id: 'nw',
355-
direction: { horizontal: 'left', vertical: 'top' },
356-
classes: 'left-0 top-0 cursor-nw-resize',
357-
ariaLabel: t('g.resizeFromTopLeft')
358-
}
359-
]
360313
361314
const MIN_NODE_WIDTH = 225
362315
363-
const { startResize } = useNodeResize(
364-
(result, element) => {
365-
if (isCollapsed.value) return
316+
const { startResize } = useNodeResize((result, element) => {
317+
if (isCollapsed.value) return
366318
367-
// Clamp width to minimum to avoid conflicts with CSS min-width
368-
const clampedWidth = Math.max(result.size.width, MIN_NODE_WIDTH)
319+
// Clamp width to minimum to avoid conflicts with CSS min-width
320+
const clampedWidth = Math.max(result.size.width, MIN_NODE_WIDTH)
369321
370-
// Apply size directly to DOM element - ResizeObserver will pick this up
371-
element.style.setProperty('--node-width', `${clampedWidth}px`)
372-
element.style.setProperty('--node-height', `${result.size.height}px`)
373-
374-
const currentPosition = position.value
375-
const deltaX = Math.abs(result.position.x - currentPosition.x)
376-
const deltaY = Math.abs(result.position.y - currentPosition.y)
377-
378-
if (deltaX > POSITION_EPSILON || deltaY > POSITION_EPSILON) {
379-
moveNodeTo(result.position)
380-
}
381-
},
382-
{
383-
transformState
384-
}
385-
)
386-
387-
const handleResizePointerDown = (direction: ResizeHandleDirection) => {
388-
return (event: PointerEvent) => {
389-
if (nodeData.flags?.pinned) return
322+
// Apply size directly to DOM element - ResizeObserver will pick this up
323+
element.style.setProperty('--node-width', `${clampedWidth}px`)
324+
element.style.setProperty('--node-height', `${result.size.height}px`)
325+
})
390326
391-
startResize(event, direction, { ...position.value })
392-
}
327+
const handleResizePointerDown = (event: PointerEvent) => {
328+
if (nodeData.flags?.pinned) return
329+
startResize(event)
393330
}
394331
395332
watch(isCollapsed, (collapsed) => {

src/renderer/extensions/vueNodes/interactions/resize/resizeMath.ts

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/renderer/extensions/vueNodes/interactions/resize/useNodeResize.ts

Lines changed: 26 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,60 +4,34 @@ import { ref } from 'vue'
44
import type { Point, Size } from '@/renderer/core/layout/types'
55
import { useNodeSnap } from '@/renderer/extensions/vueNodes/composables/useNodeSnap'
66
import { useShiftKeySync } from '@/renderer/extensions/vueNodes/composables/useShiftKeySync'
7-
8-
import type { ResizeHandleDirection } from './resizeMath'
9-
import { createResizeSession, toCanvasDelta } from './resizeMath'
10-
import type { useTransformState } from '@/renderer/core/layout/transform/useTransformState'
11-
12-
interface UseNodeResizeOptions {
13-
/** Transform state for coordinate conversion */
14-
transformState: ReturnType<typeof useTransformState>
15-
}
7+
import { useTransformState } from '@/renderer/core/layout/transform/useTransformState'
168

179
interface ResizeCallbackPayload {
1810
size: Size
19-
position: Point
2011
}
2112

2213
/**
23-
* Composable for node resizing functionality
14+
* Composable for node resizing functionality (bottom-right corner only)
2415
*
2516
* Provides resize handle interaction that integrates with the layout system.
2617
* Handles pointer capture, coordinate calculations, and size constraints.
2718
*/
2819
export function useNodeResize(
29-
resizeCallback: (
30-
payload: ResizeCallbackPayload,
31-
element: HTMLElement
32-
) => void,
33-
options: UseNodeResizeOptions
20+
resizeCallback: (payload: ResizeCallbackPayload, element: HTMLElement) => void
3421
) {
35-
const { transformState } = options
22+
const transformState = useTransformState()
3623

3724
const isResizing = ref(false)
3825
const resizeStartPointer = ref<Point | null>(null)
39-
const resizeSession = ref<
40-
| ((
41-
delta: Point,
42-
snapFn?: (size: Size) => Size
43-
) => {
44-
size: Size
45-
position: Point
46-
})
47-
| null
48-
>(null)
26+
const resizeStartSize = ref<Size | null>(null)
4927

5028
// Snap-to-grid functionality
5129
const { shouldSnap, applySnapToSize } = useNodeSnap()
5230

5331
// Shift key sync for LiteGraph canvas preview
5432
const { trackShiftKey } = useShiftKeySync()
5533

56-
const startResize = (
57-
event: PointerEvent,
58-
handle: ResizeHandleDirection,
59-
startPosition: Point
60-
) => {
34+
const startResize = (event: PointerEvent) => {
6135
event.preventDefault()
6236
event.stopPropagation()
6337

@@ -83,45 +57,44 @@ export function useNodeResize(
8357

8458
isResizing.value = true
8559
resizeStartPointer.value = { x: event.clientX, y: event.clientY }
86-
resizeSession.value = createResizeSession({
87-
startSize,
88-
startPosition: { ...startPosition },
89-
handle
90-
})
60+
resizeStartSize.value = startSize
9161

9262
const handlePointerMove = (moveEvent: PointerEvent) => {
9363
if (
9464
!isResizing.value ||
9565
!resizeStartPointer.value ||
96-
!resizeSession.value
97-
)
66+
!resizeStartSize.value
67+
) {
9868
return
69+
}
9970

100-
const startPointer = resizeStartPointer.value
101-
const session = resizeSession.value
71+
const scale = transformState.camera.z
72+
const deltaX =
73+
(moveEvent.clientX - resizeStartPointer.value.x) / (scale || 1)
74+
const deltaY =
75+
(moveEvent.clientY - resizeStartPointer.value.y) / (scale || 1)
10276

103-
const delta = toCanvasDelta(
104-
startPointer,
105-
{ x: moveEvent.clientX, y: moveEvent.clientY },
106-
transformState.camera.z
107-
)
77+
let newSize: Size = {
78+
width: resizeStartSize.value.width + deltaX,
79+
height: resizeStartSize.value.height + deltaY
80+
}
81+
82+
// Apply snap if shift is held
83+
if (shouldSnap(moveEvent)) {
84+
newSize = applySnapToSize(newSize)
85+
}
10886

10987
const nodeElement = target.closest('[data-node-id]')
11088
if (nodeElement instanceof HTMLElement) {
111-
const outcome = session(
112-
delta,
113-
shouldSnap(moveEvent) ? applySnapToSize : undefined
114-
)
115-
116-
resizeCallback(outcome, nodeElement)
89+
resizeCallback({ size: newSize }, nodeElement)
11790
}
11891
}
11992

12093
const handlePointerUp = (upEvent: PointerEvent) => {
12194
if (isResizing.value) {
12295
isResizing.value = false
12396
resizeStartPointer.value = null
124-
resizeSession.value = null
97+
resizeStartSize.value = null
12598

12699
// Stop tracking shift key state
127100
stopShiftSync()

0 commit comments

Comments
 (0)