Repository navigation
feat(drag-drop): cancel an in-progress drag with Escape or cancelDrag() - #17744
Open
ivanvpetrov wants to merge 16 commits into
Open
ivanvpetrov wants to merge 16 commits into
ivanvpetrov wants to merge 16 commits into
Conversation
…IgniteUI/igniteui-angular into ipetrov/drag-cancel-upon-escape Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
It changes re-entrant drag lifecycle, pointer capture, animation, and multiple grid integrations that warrant final human validation.
0 open findings
1 resolved since last review
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
ivanvpetrov
marked this pull request as ready for review
October 8, 2026 13:49
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #17735
Description
Adds a way to cancel an
igxDragdrag that is in progress, either with the newcancelDrag()method or, opt-in, withEscape.IgxDragDirectivecancelDrag(): voidends the drag without dropping:igxDropis dispatched; a drop area under the pointer only getsleave;dragEndis emitted withcancelled: trueand the current pointer position, and the element returns to where it was before the drag (the ghost is removed, or withghost = falsethe element gets back its full pre-drag inline transform);dragTolerance(the release still emitsdragClick);dragStart:dragEnd/transitionedare still emitted withcancelled: true, so everydragStartgets a matchingdragEnd;ghostCreate, including while aghostTemplateghost is created;dragMove;enter/leave.The drag stops right away, with no orphan ghost, no further movement and no further drop-area events;
ghost = false).@Input() cancelOnEscape = false(opt-in): whentrue,Escapecancels the drag.document, because focus is on<body>during a pointer drag. It is attached only while a drag is active, in the capture phase.Escapeis consumed (preventDefault+stopPropagation) only when it actually cancels a drag, so it doesn't also close a dialog or an overlay. Outside a drag it isn't touched.cancelled?: booleanonIDragBaseEventArgs, set for bothdragEndandtransitioned. Ontransitionedit reaches components that commit their changes when the movement ends, such asigx-chip/igx-chips-areamoveEnd, where it isoriginalEvent.cancelled.dragEndof a cancelled drag,originalEventis theKeyboardEvent(Escape) ornull(cancelDrag()).transitionToOrigin()in thedragEndhandler (chips withanimateOnReleasealready do). For a cancelled drag withghost = false, it animates back to the pre-drag transform.transitionToOrigin()compared the start location's Y withbaseOriginLeftinstead ofbaseOriginTop;transitionToOrigin()andtransitionTo()now emittransitionedright away when the element is already at the target or there is no ghost to animate. Previously the drag state got stuck and the next drag didn't start correctly;No behavior change for existing apps.
cancelOnEscapeis off by default, so existingigxDragusages, includingigx-chip, behave as before and no migration is needed. Apps that turn it on and move or reorder items in adragEndortransitionedhandler should checkcancelledthere and restore their state instead.Components updated to handle a cancelled drag
Escapehandling (cancelOnEscape = false).cancelDrag()on them now ends the grid state cleanly without dropping:rowDragEndand resetsrowDragging/dragRowID, including when cancelled from arowDragStarthandler;Demos (
src/app/drag-drop)cancelOnEscapeand shows how to restore the items moved during the drag when it is cancelled.Changes after review
cancelOnEscapeis now opt-in (defaultfalse) instead of changing the behavior of every existingigxDrag.cancelDrag()is safe to call from every handler that runs during a drag (see above), ignores a pending click, and reports the current pointer position.transitionToOrigin()/transitionTo()finish when there is nothing to animate.cancelDrag().ghostDestroyhandler that setscancel = truestill keeps the ghost on cancel, the same as on a normal release.Motivation / Context
Users couldn't back out of an accidental drag, and components built on
igxDraghad no way to cancel one. See #17735.Follow-ups, not part of this PR:
Escape(resolves theTODO cancel mouse draginquery-builder-drag.service.ts). Deferred until this PR and the Query Builder refactor [Query Builder] Refactor, bug fixing and test coverage improved #17706 are merged. Tracked in Query Builder: cancel a mouse drag of a condition with Escape #17749.Escapewhile dragging a grid column over the group-by area groups by that column. This also happens onmasterand is tracked in Grid: pressing Escape while dragging a column over the group-by area groups by that column #17745.Type of Change (check all that apply):
Component(s) / Area(s) Affected:
Drag and Drop (
igxDrag), Chip / Chips Area, Grid and Tree Grid (group-by area, row drag, column moving), drag-drop demoHow Has This Been Tested?
Unit tests
Manual testing
Automated e2e tests
drag-drop.spec.ts:cancelDrag()over a drop area:leaveonly, no drop,dragEnd/transitionedwithcancelled, ghost removed, later move/release ignored;dragClickstill emitted); a drag after a cancelled one doesn't reportcancelled;Escapedoesn't cancel by default; withcancelOnEscape = true,Escape/Esconbodycancels; withfalseit doesn't;Escapeis consumed only during a drag, and the listener is removed after drop, cancel and destroy;dragStart,ghostCreate(also duringghostTemplatecreation),dragMove,enterandleavehandlers: matchingdragEnd/transitioned, current pointer coordinates, no orphan ghost, no further movement or drop-area events, no crash;ghostCreatecancel combined with adragEndreturn animation, also when ghost creation itself is cancelled;ghost = false, and no ghost reference is kept after a drag ends;ghost = falsereturns to its pre-drag position and restores any inline transform (e.g.translate(...) rotate(...)), also when animating back;transitionToOrigin()/transitionTo()finish when the element is already at the target or there is no ghost;baseOriginTopfix.Grid group-by: a cancelled chip drag keeps the grouping and the expansion state; a drag released without reordering keeps the expansion state.
Tree grid:
handleMoveEndrestores the chips order on cancel.Grid row drag / column moving:
cancelDrag()during the drag and fromrowDragStartends the grid state cleanly; cancelling inghostCreatedoesn't throw; a cancelledrowDragStartdoesn't keep the ghost reference.The new tests fail without their fixes. The existing column-moving and row-drag
Escapespecs pass.test:lib:others(3229 passing);grid.groupby,column-moving,row-drag.directiveandgrid-filtering-advancedspecs (269 passing);tree-grid-grouping(23 passing).lint:libhas no errors.Test Configuration:
Screenshots / Recordings
Checklist:
feature/README.MDupdates for the feature docsREADME.MDCHANGELOG.MDupdates for newly added functionalityng updatemigrations for the breaking changes (migrations guidelines)