Skip to content

feat(drag-drop): add support for sorting animations #12530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions src/cdk-experimental/drag-drop/drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ describe('CdkDrag', () => {
// Add a few pixels to the left offset so we get some overlap.
dispatchMouseEvent(document, 'mousemove', elementRect.left + 5, elementRect.top);
fixture.detectChanges();
expect(getElementIndex(placeholder)).toBe(i);
expect(getElementIndexByPosition(placeholder, 'left')).toBe(i);
}

dispatchMouseEvent(document, 'mouseup');
Expand Down Expand Up @@ -590,7 +590,7 @@ describe('CdkDrag', () => {
// Remove a few pixels from the right offset so we get some overlap.
dispatchMouseEvent(document, 'mousemove', elementRect.right - 5, elementRect.top);
fixture.detectChanges();
expect(getElementIndex(placeholder)).toBe(Math.min(i + 1, items.length - 1));
expect(getElementIndexByPosition(placeholder, 'left')).toBe(i);
}

dispatchMouseEvent(document, 'mouseup');
Expand Down Expand Up @@ -660,6 +660,31 @@ describe('CdkDrag', () => {
expect(placeholder.textContent!.trim()).toContain('Custom placeholder');
}));

it('should clear the `transform` value from siblings when item is dropped`', fakeAsync(() => {
const fixture = createComponent(DraggableInDropZone);
fixture.detectChanges();

const dragItems = fixture.componentInstance.dragItems;
const firstItem = dragItems.first;
const thirdItem = dragItems.toArray()[2].element.nativeElement;
const thirdItemRect = thirdItem.getBoundingClientRect();

dispatchMouseEvent(firstItem.element.nativeElement, 'mousedown');
fixture.detectChanges();

dispatchMouseEvent(document, 'mousemove', thirdItemRect.left + 1, thirdItemRect.top + 1);
fixture.detectChanges();

expect(thirdItem.style.transform).toBeTruthy();

dispatchMouseEvent(document, 'mouseup');
fixture.detectChanges();
flush();
fixture.detectChanges();

expect(thirdItem.style.transform).toBeFalsy();
}));

});

describe('in a connected drop container', () => {
Expand Down Expand Up @@ -1151,9 +1176,15 @@ function dragElementViaTouch(fixture: ComponentFixture<any>,
fixture.detectChanges();
}

/** Gets the index of a DOM element inside its parent. */
function getElementIndex(element: HTMLElement) {
return element.parentElement ? Array.from(element.parentElement.children).indexOf(element) : -1;
/** Gets the index of an element among its siblings, based on their position on the page. */
function getElementIndexByPosition(element: HTMLElement, direction: 'top' | 'left') {
if (!element.parentElement) {
return -1;
}

return Array.from(element.parentElement.children)
.sort((a, b) => a.getBoundingClientRect()[direction] - b.getBoundingClientRect()[direction])
.indexOf(element);
}

/**
Expand Down Expand Up @@ -1193,7 +1224,7 @@ function assertDownwardSorting(fixture: ComponentFixture<any>, items: Element[])
// Add a few pixels to the top offset so we get some overlap.
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.top + 5);
fixture.detectChanges();
expect(getElementIndex(placeholder)).toBe(i);
expect(getElementIndexByPosition(placeholder, 'top')).toBe(i);
}

dispatchMouseEvent(document, 'mouseup');
Expand Down Expand Up @@ -1222,7 +1253,7 @@ function assertUpwardSorting(fixture: ComponentFixture<any>, items: Element[]) {
// Remove a few pixels from the bottom offset so we get some overlap.
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.bottom - 5);
fixture.detectChanges();
expect(getElementIndex(placeholder)).toBe(Math.min(i + 1, items.length - 1));
expect(getElementIndexByPosition(placeholder, 'top')).toBe(i);
}

dispatchMouseEvent(document, 'mouseup');
Expand Down
43 changes: 6 additions & 37 deletions src/cdk-experimental/drag-drop/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import {CdkDragDropRegistry} from './drag-drop-registry';
import {Subject, merge} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

// TODO: add auto-scrolling functionality.
// TODO: add an API for moving a draggable up/down the
// TODO(crisbeto): add auto-scrolling functionality.
// TODO(crisbeto): add an API for moving a draggable up/down the
// list programmatically. Useful for keyboard controls.

/** Element that can be moved inside a CdkDrop container. */
Expand Down Expand Up @@ -236,7 +236,7 @@ export class CdkDrag<T = any> implements OnDestroy {

/** Handler that is invoked when the user moves their pointer after they've initiated a drag. */
private _pointerMove = (event: MouseEvent | TouchEvent) => {
// TODO: this should start dragging after a certain threshold,
// TODO(crisbeto): this should start dragging after a certain threshold,
// otherwise we risk interfering with clicks on the element.
if (!this._dragDropRegistry.isDragging(this)) {
return;
Expand Down Expand Up @@ -279,8 +279,6 @@ export class CdkDrag<T = any> implements OnDestroy {

/** Cleans up the DOM artifacts that were added to facilitate the element being dragged. */
private _cleanupDragArtifacts() {
const currentIndex = this._getElementIndexInDom(this._placeholder);

// Restore the element's visibility and insert it at its old position in the DOM.
// It's important that we maintain the position, because moving the element around in the DOM
// can throw off `NgFor` which does smart diffing and re-creates elements only when necessary,
Expand All @@ -298,6 +296,8 @@ export class CdkDrag<T = any> implements OnDestroy {

// Re-enter the NgZone since we bound `document` events on the outside.
this._ngZone.run(() => {
const currentIndex = this.dropContainer.getItemIndex(this);

this.ended.emit({source: this});
this.dropped.emit({
item: this,
Expand Down Expand Up @@ -328,7 +328,7 @@ export class CdkDrag<T = any> implements OnDestroy {
// Notify the new container that the item has entered.
this.entered.emit({ item: this, container: newContainer });
this.dropContainer = newContainer;
this.dropContainer.enter(this);
this.dropContainer.enter(this, x, y);
});
}

Expand Down Expand Up @@ -386,37 +386,6 @@ export class CdkDrag<T = any> implements OnDestroy {
return placeholder;
}

/** Gets the index of an element, based on its index in the DOM. */
private _getElementIndexInDom(element: HTMLElement): number {
// Note: we may be able to figure this in memory while sorting, but doing so won't be very
// reliable when transferring between containers, because the new container doesn't have
// the proper indices yet. Also this will work better for the case where the consumer
// isn't using an `ngFor` to render the list.
if (!element.parentElement) {
return -1;
}

// Avoid accessing `children` and `children.length` too much since they're a "live collection".
let index = 0;
const siblings = element.parentElement.children;
const siblingsLength = siblings.length;
const draggableElements = this.dropContainer._draggables
.filter(item => item !== this)
.map(item => item.element.nativeElement);

// Loop through the sibling elements to find out the index of the
// current one, while skipping any elements that aren't draggable.
for (let i = 0; i < siblingsLength; i++) {
if (siblings[i] === element) {
return index;
} else if (draggableElements.indexOf(siblings[i] as HTMLElement) > -1) {
index++;
}
}

return -1;
}

/**
* Figures out the coordinates at which an element was picked up.
* @param referenceElement Element that initiated the dragging.
Expand Down
4 changes: 3 additions & 1 deletion src/cdk-experimental/drag-drop/drop-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export interface CdkDropContainer<T = any> {
/**
* Emits an event to indicate that the user moved an item into the container.
* @param item Item that was moved into the container.
* @param xOffset Position of the item along the X axis.
* @param yOffset Position of the item along the Y axis.
*/
enter(item: CdkDrag): void;
enter(item: CdkDrag, xOffset: number, yOffset: number): void;

/**
* Removes an item from the container after it was dragged into another container by the user.
Expand Down
Loading