From 2afff952fc4fe655520f363dfa548cef724641fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:49:33 +0000 Subject: [PATCH] fix(touch): allow pointerDown veto to stop tracking and free scroll --- package-lock.json | 85 ------------------- .../igniteui-angular/core/src/core/touch.ts | 34 +++++++- .../navigation-drawer.component.ts | 10 ++- 3 files changed, 39 insertions(+), 90 deletions(-) diff --git a/package-lock.json b/package-lock.json index 190d960190f..288d89db75f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5632,7 +5632,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5653,7 +5652,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5674,7 +5672,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5695,7 +5692,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5716,7 +5712,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5737,7 +5732,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5758,7 +5752,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5779,7 +5772,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5800,7 +5792,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5821,7 +5812,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5842,7 +5832,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5863,7 +5852,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5884,7 +5872,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -23065,33 +23052,6 @@ "sassdoc-extras": "^2.5.0" } }, - "node_modules/sassdoc-theme-default/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/sassdoc-theme-default/node_modules/commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", @@ -23120,21 +23080,6 @@ "jsonfile": "^2.1.0" } }, - "node_modules/sassdoc-theme-default/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/sassdoc-theme-default/node_modules/jsonfile": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", @@ -23171,36 +23116,6 @@ } } }, - "node_modules/sassdoc-theme-default/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/sassdoc-theme-default/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/sassdoc/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", diff --git a/projects/igniteui-angular/core/src/core/touch.ts b/projects/igniteui-angular/core/src/core/touch.ts index 30f019cd800..d9f1bc2691b 100644 --- a/projects/igniteui-angular/core/src/core/touch.ts +++ b/projects/igniteui-angular/core/src/core/touch.ts @@ -41,8 +41,14 @@ export interface IgxGestureEvent { * @internal */ export interface IgxTouchManagerCallbacks { - /** Fired on pointer down once the pointer type passes the configured filter, before any movement. */ - pointerDown?: (event: IgxGestureEvent) => void; + /** + * Fired on pointer down once the pointer type passes the configured filter, before any movement. + * + * Return `false` to veto the gesture (e.g. when the touch does not start in an active zone). + * The manager then stops tracking immediately and best-effort releases the pointer capture, so + * normal page/component scrolling is not blocked by the `touchmove` listener. + */ + pointerDown?: (event: IgxGestureEvent) => boolean | void; /** Fired on the first pointer move of a tracked gesture, once movement begins (mirrors Hammer's `panstart`). */ panStart?: (event: IgxGestureEvent) => void; /** Fired on each pointer move while a gesture is tracked. */ @@ -211,7 +217,12 @@ export class IgxTouchManager { } } - this.callbacks.pointerDown?.(this._createEvent(event)); + // Let the consumer veto the gesture (e.g. the touch did not start in an active + // zone). Returning `false` stops tracking immediately so the `touchmove` listener + // does not block normal scrolling and other gestures are not interfered with. + if (this.callbacks.pointerDown?.(this._createEvent(event)) === false) { + this._stopTracking(event.pointerId); + } }; private _onPointerMove = (event: PointerEvent) => { @@ -265,4 +276,21 @@ export class IgxTouchManager { event.preventDefault(); } } + + /** Stops tracking the current gesture and best-effort releases the pointer capture. */ + private _stopTracking(pointerId: number): void { + this._tracking = false; + this._panStarted = false; + this._pointerId = null; + this._startTarget = null; + + if (this._setPointerCapture && typeof (this.target as Element).releasePointerCapture === 'function') { + try { + (this.target as Element).releasePointerCapture(pointerId); + } catch { + // `releasePointerCapture` can throw when the pointer is no longer captured. + // Releasing is a best-effort cleanup, so ignore it. + } + } + } } diff --git a/projects/igniteui-angular/navigation-drawer/src/navigation-drawer/navigation-drawer.component.ts b/projects/igniteui-angular/navigation-drawer/src/navigation-drawer/navigation-drawer.component.ts index 0219ceae597..1fe40a1543e 100644 --- a/projects/igniteui-angular/navigation-drawer/src/navigation-drawer/navigation-drawer.component.ts +++ b/projects/igniteui-angular/navigation-drawer/src/navigation-drawer/navigation-drawer.component.ts @@ -755,9 +755,9 @@ export class IgxNavigationDrawerComponent implements } }; - private panStart = (evt: IgxGestureEvent) => { + private panStart = (evt: IgxGestureEvent): boolean => { if (!this.enableGestures || this.pin || evt.pointerType !== 'touch') { - return; + return false; } const startPosition = this.position === 'right' ? this.getWindowWidth() - (evt.center.x + evt.distance) : evt.center.x - evt.distance; @@ -778,7 +778,13 @@ export class IgxNavigationDrawerComponent implements // slide reveals the full panel instead of just its padding/border. this.renderer.setStyle(this.drawer, 'width', `${this.getExpectedWidth(false)}px`); } + return true; } + + // The touch did not start in the edge zone (and the drawer is closed), so this + // gesture should be ignored. Returning `false` lets the touch manager stop + // tracking immediately and not interfere with normal page scrolling. + return false; }; private pan = (evt: IgxGestureEvent) => {