Created
November 15, 2018 23:00
-
-
Save shlomiassaf/49126eed76925a90ccfe6bb6c78bfddc to your computer and use it in GitHub Desktop.
Lazy binding between `CdkDrag` and `CdkDropList` + support for non direct draggables in a drop container
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
import { take } from 'rxjs/operators'; | |
import { Input, Directive, ElementRef, QueryList, OnDestroy, Optional, AfterViewInit } from '@angular/core'; | |
import { CdkDropList, CdkDrag, CdkDragHandle, CDK_DROP_LIST_CONTAINER } from '@angular/cdk/drag-drop'; | |
@Directive({ | |
selector: '[cdkLazyDropList]', | |
exportAs: 'cdkLazyDropList', | |
providers: [ | |
{ provide: CDK_DROP_LIST_CONTAINER, useExisting: CdkLazyDropList }, | |
], | |
host: { // tslint:disable-line:use-host-property-decorator | |
'class': 'cdk-drop-list', | |
'[id]': 'id', | |
'[class.cdk-drop-list-dragging]': '_dragging' | |
} | |
}) | |
export class CdkLazyDropList<T = any> extends CdkDropList<T> { | |
/** | |
* Selector that will be used to determine the direct container element, starting from | |
* the `cdkDropList` element and going down the DOM. Passing an alternate direct container element | |
* is useful when the `cdkDropList` is not the direct parent (i.e. ancestor but not father) | |
* of the draggable elements. | |
*/ | |
// tslint:disable-next-line:no-input-rename | |
@Input('cdkDropListDirectContainerElement') directContainerElement: string; | |
_draggables: QueryList<CdkDrag>; | |
private _draggablesSet = new Set<CdkDrag>(); | |
// This is a workaround for https://github.com/angular/material2/pull/14153 | |
// Working around the missing capability for selecting a container element that is not the drop container host. | |
// The entire enter() overriding method can be removed if PR accepted, along with `directContainerElement` | |
enter(item: CdkDrag, pointerX: number, pointerY: number): void { | |
super.enter(item, pointerX, pointerY); | |
if (this.directContainerElement) { | |
const placeholder = item.getPlaceholderElement(); | |
if (this.element.nativeElement.lastChild === placeholder) { | |
const element = this.element.nativeElement.querySelector(this.directContainerElement); | |
if (element) { | |
element.appendChild(item.getPlaceholderElement()); | |
} | |
} | |
} | |
} | |
addDrag(drag: CdkDrag): void { | |
this._draggablesSet.add(drag); | |
this._draggables.reset(Array.from(this._draggablesSet.values())); | |
} | |
removeDrag(drag: CdkDrag): boolean { | |
const result = this._draggablesSet.delete(drag); | |
if (result) { | |
this._draggables.reset(Array.from(this._draggablesSet.values())); | |
} | |
return result; | |
} | |
} | |
@Directive({ | |
selector: '[cdkLazyDrag]', | |
exportAs: 'cdkLazyDrag', | |
host: { // tslint:disable-line:use-host-property-decorator | |
'class': 'cdk-drag', | |
'[class.cdk-drag-dragging]': '_hasStartedDragging && _isDragging()', | |
}, | |
}) | |
export class CdkLazyDrag<T = any, Z extends CdkLazyDropList<T> = CdkLazyDropList<T>> extends CdkDrag<T> implements AfterViewInit, OnDestroy { | |
@Input() get cdkDropList(): Z { return this.dropContainer as Z; } | |
set cdkDropList(value: Z) { | |
// TO SUPPORT `cdkDropList` via string input (ID) we need a reactive registry... | |
if (this.cdkDropList) { | |
this.cdkDropList.removeDrag(this); | |
} | |
this.dropContainer = value; | |
if (value) { | |
value.addDrag(this); | |
} | |
} | |
// This is a workaround for https://github.com/angular/material2/pull/14158 | |
// Working around the issue of drop container is not the direct parent (father) of a drag item. | |
// The entire ngAfterViewInit() overriding method can be removed if PR accepted. | |
ngAfterViewInit(): void { | |
this.started.subscribe( startedEvent => { | |
if (this.dropContainer) { | |
const element = this.getRootElement(); | |
const initialRootElementParent = element.parentNode as HTMLElement; | |
if (!element.nextSibling && initialRootElementParent !== this.dropContainer.element.nativeElement) { | |
this.ended.pipe(take(1)).subscribe( endedEvent => initialRootElementParent.appendChild(element) ); | |
} | |
} | |
}); | |
super.ngAfterViewInit(); | |
} | |
ngOnDestroy(): void { | |
if (this.cdkDropList) { | |
this.cdkDropList.removeDrag(this); | |
} | |
super.ngOnDestroy(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there any chance to get this to work with Angular 8 too?