Skip to content

Commit 792d536

Browse files
crisbetojelbourn
authored andcommittedDec 18, 2018
feat(drag-drop): add released event (#14513)
Adds the `cdkDragReleased` event, in addition to `cdkDragEnded`. The difference between released and ended is that released will fire as soon as the user has released the item, whereas ended will fire once all animations are done. The former is useful to customize the animation on drop, based on where the item is being dropped. Fixes #14498.
1 parent c94812d commit 792d536

File tree

5 files changed

+63
-0
lines changed

5 files changed

+63
-0
lines changed
 

‎src/cdk/drag-drop/directives/drag.spec.ts

+37
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ describe('CdkDrag', () => {
354354
dragElementViaMouse(fixture, fixture.componentInstance.dragElement.nativeElement, 2, 2);
355355

356356
expect(fixture.componentInstance.startedSpy).not.toHaveBeenCalled();
357+
expect(fixture.componentInstance.releasedSpy).not.toHaveBeenCalled();
357358
expect(fixture.componentInstance.endedSpy).not.toHaveBeenCalled();
358359
expect(moveSpy).not.toHaveBeenCalled();
359360
subscription.unsubscribe();
@@ -1221,6 +1222,40 @@ describe('CdkDrag', () => {
12211222
.toBeFalsy('Expected preview to be removed from the DOM if the transition timed out');
12221223
}));
12231224

1225+
it('should emit the released event as soon as the item is released', fakeAsync(() => {
1226+
const fixture = createComponent(DraggableInDropZone);
1227+
fixture.detectChanges();
1228+
const item = fixture.componentInstance.dragItems.toArray()[1];
1229+
const endedSpy = jasmine.createSpy('ended spy');
1230+
const releasedSpy = jasmine.createSpy('released spy');
1231+
const endedSubscription = item.ended.subscribe(endedSpy);
1232+
const releasedSubscription = item.released.subscribe(releasedSpy);
1233+
1234+
startDraggingViaMouse(fixture, item.element.nativeElement);
1235+
1236+
const preview = document.querySelector('.cdk-drag-preview')! as HTMLElement;
1237+
1238+
// Add a duration since the tests won't include one.
1239+
preview.style.transitionDuration = '500ms';
1240+
1241+
// Move somewhere so the draggable doesn't exit immediately.
1242+
dispatchMouseEvent(document, 'mousemove', 50, 50);
1243+
fixture.detectChanges();
1244+
1245+
dispatchMouseEvent(document, 'mouseup');
1246+
fixture.detectChanges();
1247+
1248+
// Expected the released event to fire immediately upon release.
1249+
expect(releasedSpy).toHaveBeenCalled();
1250+
tick(1000);
1251+
1252+
// Expected the ended event to fire once the entire sequence is done.
1253+
expect(endedSpy).toHaveBeenCalled();
1254+
1255+
endedSubscription.unsubscribe();
1256+
releasedSubscription.unsubscribe();
1257+
}));
1258+
12241259
it('should reset immediately when failed drag happens after a successful one', fakeAsync(() => {
12251260
const fixture = createComponent(DraggableInDropZone);
12261261
fixture.detectChanges();
@@ -2597,6 +2632,7 @@ describe('CdkDrag', () => {
25972632
cdkDrag
25982633
[cdkDragBoundary]="boundarySelector"
25992634
(cdkDragStarted)="startedSpy($event)"
2635+
(cdkDragReleased)="releasedSpy($event)"
26002636
(cdkDragEnded)="endedSpy($event)"
26012637
#dragElement
26022638
style="width: 100px; height: 100px; background: red;"></div>
@@ -2608,6 +2644,7 @@ class StandaloneDraggable {
26082644
@ViewChild(CdkDrag) dragInstance: CdkDrag;
26092645
startedSpy = jasmine.createSpy('started spy');
26102646
endedSpy = jasmine.createSpy('ended spy');
2647+
releasedSpy = jasmine.createSpy('released spy');
26112648
boundarySelector: string;
26122649
}
26132650

‎src/cdk/drag-drop/directives/drag.ts

+9
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
CdkDragExit,
3939
CdkDragMove,
4040
CdkDragStart,
41+
CdkDragRelease,
4142
} from '../drag-events';
4243
import {CdkDragHandle} from './drag-handle';
4344
import {CdkDragPlaceholder} from './drag-placeholder';
@@ -118,6 +119,10 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
118119
/** Emits when the user starts dragging the item. */
119120
@Output('cdkDragStarted') started: EventEmitter<CdkDragStart> = new EventEmitter<CdkDragStart>();
120121

122+
/** Emits when the user has released a drag item, before any animations have started. */
123+
@Output('cdkDragReleased') released: EventEmitter<CdkDragRelease> =
124+
new EventEmitter<CdkDragRelease>();
125+
121126
/** Emits when the user stops dragging an item in the container. */
122127
@Output('cdkDragEnded') ended: EventEmitter<CdkDragEnd> = new EventEmitter<CdkDragEnd>();
123128

@@ -254,6 +259,10 @@ export class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
254259
this.started.emit({source: this});
255260
});
256261

262+
ref.released.subscribe(() => {
263+
this.released.emit({source: this});
264+
});
265+
257266
ref.ended.subscribe(() => {
258267
this.ended.emit({source: this});
259268
});

‎src/cdk/drag-drop/drag-events.ts

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export interface CdkDragStart<T = any> {
1515
source: CdkDrag<T>;
1616
}
1717

18+
/** Event emitted when the user releases an item, before any animations have started. */
19+
export interface CdkDragRelease<T = any> {
20+
/** Draggable that emitted the event. */
21+
source: CdkDrag<T>;
22+
}
23+
1824
/** Event emitted when the user stops dragging a draggable. */
1925
export interface CdkDragEnd<T = any> {
2026
/** Draggable that emitted the event. */

‎src/cdk/drag-drop/drag-ref.ts

+6
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ export class DragRef<T = any> {
213213
/** Emits when the user starts dragging the item. */
214214
started = new Subject<{source: DragRef}>();
215215

216+
/** Emits when the user has released a drag item, before any animations have started. */
217+
released = new Subject<{source: DragRef}>();
218+
216219
/** Emits when the user stops dragging an item in the container. */
217220
ended = new Subject<{source: DragRef}>();
218221

@@ -355,6 +358,7 @@ export class DragRef<T = any> {
355358
this._removeSubscriptions();
356359
this.beforeStarted.complete();
357360
this.started.complete();
361+
this.released.complete();
358362
this.ended.complete();
359363
this.entered.complete();
360364
this.exited.complete();
@@ -516,6 +520,8 @@ export class DragRef<T = any> {
516520
return;
517521
}
518522

523+
this.released.next({source: this});
524+
519525
if (!this.dropContainer) {
520526
// Convert the active transform into a passive one. This means that next time
521527
// the user starts dragging the item, its position will be calculated relatively

‎tools/public_api_guard/cdk/drag-drop.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnDestroy {
2222
exited: EventEmitter<CdkDragExit<any>>;
2323
lockAxis: 'x' | 'y';
2424
moved: Observable<CdkDragMove<T>>;
25+
released: EventEmitter<CdkDragRelease>;
2526
rootElementSelector: string;
2627
started: EventEmitter<CdkDragStart>;
2728
constructor(
@@ -92,6 +93,10 @@ export declare class CdkDragPreview<T = any> {
9293
constructor(templateRef: TemplateRef<T>);
9394
}
9495

96+
export interface CdkDragRelease<T = any> {
97+
source: CdkDrag<T>;
98+
}
99+
95100
export interface CdkDragSortEvent<T = any, I = T> {
96101
container: CdkDropList<T>;
97102
currentIndex: number;

0 commit comments

Comments
 (0)
Please sign in to comment.