From cdd61d71792c05845c5e0368fd02e345819f0e64 Mon Sep 17 00:00:00 2001 From: Anton Lavrenov Date: Thu, 5 Jun 2025 16:13:49 -0500 Subject: [PATCH] fix pointerleave bubbling --- src/Node.ts | 17 +++++++++++++++-- test/unit/Stage-test.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/Node.ts b/src/Node.ts index 058bb880..58f44384 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -88,6 +88,10 @@ const ABSOLUTE_OPACITY = 'absoluteOpacity', LISTENING = 'listening', MOUSEENTER = 'mouseenter', MOUSELEAVE = 'mouseleave', + POINTERENTER = 'pointerenter', + POINTERLEAVE = 'pointerleave', + TOUCHENTER = 'touchenter', + TOUCHLEAVE = 'touchleave', NAME = 'name', SET = 'set', SHAPE = 'Shape', @@ -2335,8 +2339,17 @@ export abstract class Node { evt.target = this; } + const nonBubbling = [ + MOUSEENTER, + MOUSELEAVE, + POINTERENTER, + POINTERLEAVE, + TOUCHENTER, + TOUCHLEAVE, + ]; + const shouldStop = - (eventType === MOUSEENTER || eventType === MOUSELEAVE) && + nonBubbling.indexOf(eventType) !== -1 && ((compareShape && (this === compareShape || (this.isAncestorOf && this.isAncestorOf(compareShape)))) || @@ -2347,7 +2360,7 @@ export abstract class Node { // simulate event bubbling const stopBubble = - (eventType === MOUSEENTER || eventType === MOUSELEAVE) && + nonBubbling.indexOf(eventType) !== -1 && compareShape && compareShape.isAncestorOf && compareShape.isAncestorOf(this) && diff --git a/test/unit/Stage-test.ts b/test/unit/Stage-test.ts index 76f23678..c3d48f73 100644 --- a/test/unit/Stage-test.ts +++ b/test/unit/Stage-test.ts @@ -8,6 +8,7 @@ import { simulateTouchStart, simulateTouchMove, simulateTouchEnd, + simulatePointerMove, compareCanvases, createCanvas, showHit, @@ -1222,6 +1223,36 @@ describe('Stage', function () { assert.equal(count, 2); }); + it('stage pointerleave should not fire when leaving a child', function () { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + + var circle = new Konva.Circle({ + fill: 'red', + radius: 30, + x: 50, + y: 50, + }); + layer.add(circle); + layer.draw(); + + var stageLeave = 0; + var circleLeave = 0; + stage.on('pointerleave', function () { + stageLeave += 1; + }); + circle.on('pointerleave', function () { + circleLeave += 1; + }); + + simulatePointerMove(stage, { x: 50, y: 50 }); + simulatePointerMove(stage, { x: 90, y: 50 }); + + assert.equal(circleLeave, 1, 'circle pointerleave should fire'); + assert.equal(stageLeave, 0, 'stage pointerleave should not fire'); + }); + it('toDataURL with hidden layer', function () { var stage = addStage(); var layer = new Konva.Layer();