fix: preserve canvas receiver when invoking deferred onSelectionChange

Synchronous callsites use this.onSelectionChange?.(...) which binds the
LGraphCanvas instance as the callback's receiver. The deferred RAF
invocation called the saved reference as a bare function, so this would
be undefined inside the callback under strict mode — silently changing
the contract for an extension-facing callback only on the drag-start
path.

Use Function.prototype.call so the deferred invocation matches every
other onSelectionChange call in the file.

Adds a test that pins the receiver contract.
This commit is contained in:
Glary-Bot
2026-05-06 20:10:43 +00:00
parent d3dad95ce7
commit 8a3f06e2b7
2 changed files with 15 additions and 1 deletions

View File

@@ -132,6 +132,18 @@ describe('_startDraggingItems defers onSelectionChange', () => {
expect(onSelectionChange).not.toHaveBeenCalled()
})
it('invokes the deferred onSelectionChange with the canvas as receiver', () => {
const receivedThis: unknown[] = []
canvas.onSelectionChange = function (this: unknown) {
receivedThis.push(this)
}
canvas['_startDraggingItems'](node, pointer, true)
vi.advanceTimersByTime(16)
expect(receivedThis).toEqual([canvas])
})
it('restores onSelectionChange even when processSelect throws', () => {
const onSelectionChange = vi.fn()
canvas.onSelectionChange = onSelectionChange

View File

@@ -3629,7 +3629,9 @@ export class LGraphCanvas implements CustomEventDispatcher<LGraphCanvasEventMap>
this.onSelectionChange = onSelectionChange
}
if (onSelectionChange && selectionNotified) {
requestAnimationFrame(() => onSelectionChange(this.selected_nodes))
requestAnimationFrame(() => {
onSelectionChange.call(this, this.selected_nodes)
})
}
this.isDragging = true