UNPKG

@supermemo/ng2-dragula

Version:

Simple drag and drop with dragula

139 lines (125 loc) 4.54 kB
import { Directive, Input, Output, ElementRef, OnInit, OnChanges, OnDestroy, SimpleChange, EventEmitter } from '@angular/core'; import { DragulaService } from './dragula.service'; import { DrakeWithModels } from '../DrakeWithModels'; import { Subscription } from 'rxjs'; import { Group } from '../Group'; @Directive({selector: '[dragula]'}) export class DragulaDirective implements OnChanges, OnDestroy { @Input() public dragula: string; @Input() public dragulaModel: any[]; @Output() public dragulaModelChange = new EventEmitter<any[]>(); private subs: Subscription; private get container(): HTMLElement { return this.el && this.el.nativeElement; } private group: Group; public constructor(private el: ElementRef, private dragulaService: DragulaService) { } public ngOnChanges(changes: {dragula?: SimpleChange, dragulaModel?: SimpleChange}): void { if (changes && changes.dragula) { const { previousValue: prev, currentValue: current, firstChange } = changes.dragula; let hadPreviousValue = !!prev; let hasNewValue = !!current; // something -> null => teardown only // something -> something => teardown, then setup // null -> something => setup only // // null -> null (precluded by fact of change being present) if (hadPreviousValue) { this.teardown(prev); } if (hasNewValue) { this.setup(); } } else if (changes && changes.dragulaModel) { // this code only runs when you're not changing the group name // because if you're changing the group name, you'll be doing setup or teardown // it also only runs if there is a group name to attach to. const { previousValue: prev, currentValue: current, firstChange } = changes.dragulaModel; const { drake } = this.group; if (this.dragula && drake) { drake.models = drake.models || []; let prevIndex = drake.models.indexOf(prev); if (prevIndex !== -1) { // delete the previous drake.models.splice(prevIndex, 1); // maybe insert a new one at the same spot if (!!current) { drake.models.splice(prevIndex, 0, current); } } else if (!!current) { // no previous one to remove; just push this one. drake.models.push(current); } } } } // call ngOnInit 'setup' because we want to call it in ngOnChanges // and it would otherwise run twice public setup(): void { let checkModel = (group: Group) => { if (this.dragulaModel) { if (group.drake.models) { group.drake.models.push(this.dragulaModel); } else { group.drake.models = [this.dragulaModel]; } } }; // find or create a group let group = this.dragulaService.find(this.dragula); if (!group) { let options = {}; group = this.dragulaService.createGroup(this.dragula, options); } // ensure model and container element are pushed checkModel(group); group.drake.containers.push(this.container); this.subscribe(this.dragula); this.group = group; } public subscribe(name: string) { this.subs = new Subscription(); this.subs.add( this.dragulaService .dropModel(name) .subscribe(({ source, target, sourceModel, targetModel }) => { if (source === this.el.nativeElement) { this.dragulaModelChange.emit(sourceModel); } else if (target === this.el.nativeElement) { this.dragulaModelChange.emit(targetModel); } }) ); this.subs.add( this.dragulaService .removeModel(name) .subscribe(({ source, sourceModel }) => { if (source === this.el.nativeElement) { this.dragulaModelChange.emit(sourceModel); } }) ); } public teardown(groupName: string): void { if (this.subs) { this.subs.unsubscribe(); } const group = this.dragulaService.find(groupName); if (group) { const itemToRemove = group.drake.containers.indexOf(this.el.nativeElement); if (itemToRemove !== -1) { group.drake.containers.splice(itemToRemove, 1); } if (this.dragulaModel && group.drake && group.drake.models) { let modelIndex = group.drake.models.indexOf(this.dragulaModel); if (modelIndex !== -1) { group.drake.models.splice(modelIndex, 1); } } } } public ngOnDestroy(): void { this.teardown(this.dragula); } }