@worktile/ngx-sortablejs
Version:
250 lines (241 loc) • 11 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, EventEmitter, Output, Input, Optional, Inject, Directive, NgModule } from '@angular/core';
import Sortable from 'sortablejs';
const GLOBALS = new InjectionToken('Global config for sortablejs');
class SortablejsBinding {
constructor(target) {
this.target = target;
}
insert(index, item) {
if (this.isFormArray) {
this.target.insert(index, item);
}
else {
this.target.splice(index, 0, item);
}
}
get(index) {
return this.isFormArray ? this.target.at(index) : this.target[index];
}
remove(index) {
let item;
if (this.isFormArray) {
item = this.target.at(index);
this.target.removeAt(index);
}
else {
item = this.target.splice(index, 1)[0];
}
return item;
}
// we need this to identify that the target is a FormArray
// we don't want to have a dependency on @angular/forms just for that
get isFormArray() {
// just checking for random FormArray methods not available on a standard array
return !!this.target.at && !!this.target.insert && !!this.target.reset;
}
}
class SortablejsBindings {
constructor(bindingTargets) {
this.bindings = bindingTargets.map(target => new SortablejsBinding(target));
}
injectIntoEvery(index, items) {
this.bindings.forEach((b, i) => b.insert(index, items[i]));
}
getFromEvery(index) {
return this.bindings.map(b => b.get(index));
}
extractFromEvery(index) {
return this.bindings.map(b => b.remove(index));
}
get provided() {
return !!this.bindings.length;
}
}
class SortablejsService {
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
const getIndexesFromEvent = (event) => {
if (event.hasOwnProperty('newDraggableIndex') && event.hasOwnProperty('oldDraggableIndex')) {
return {
new: event.newDraggableIndex,
old: event.oldDraggableIndex,
};
}
else {
return {
new: event.newIndex,
old: event.oldIndex,
};
}
};
class SortablejsDirective {
constructor(globalConfig, service, element, zone, renderer) {
this.globalConfig = globalConfig;
this.service = service;
this.element = element;
this.zone = zone;
this.renderer = renderer;
this.sortablejsInit = new EventEmitter();
}
ngOnInit() {
if (Sortable && Sortable.create) { // Sortable does not exist in angular universal (SSR)
this.create();
}
}
ngOnChanges(changes) {
const optionsChange = changes.sortablejsOptions;
if (optionsChange && !optionsChange.isFirstChange()) {
const previousOptions = optionsChange.previousValue;
const currentOptions = optionsChange.currentValue;
Object.keys(currentOptions).forEach(optionName => {
if (currentOptions[optionName] !== previousOptions[optionName]) {
// use low-level option setter
this.sortableInstance.option(optionName, this.options[optionName]);
}
});
}
}
ngOnDestroy() {
if (this.sortableInstance) {
this.sortableInstance.destroy();
}
}
create() {
const container = this.sortablejsContainer ? this.element.nativeElement.querySelector(this.sortablejsContainer) : this.element.nativeElement;
setTimeout(() => {
this.sortableInstance = Sortable.create(container, this.options);
this.sortablejsInit.emit(this.sortableInstance);
}, 0);
}
getBindings() {
if (!this.sortablejs) {
return new SortablejsBindings([]);
}
else if (this.sortablejs instanceof SortablejsBindings) {
return this.sortablejs;
}
else {
return new SortablejsBindings([this.sortablejs]);
}
}
get options() {
return { ...this.optionsWithoutEvents, ...this.overridenOptions };
}
get optionsWithoutEvents() {
return { ...(this.globalConfig || {}), ...(this.sortablejsOptions || {}) };
}
proxyEvent(eventName, ...params) {
this.zone.run(() => {
if (this.optionsWithoutEvents && this.optionsWithoutEvents[eventName]) {
this.optionsWithoutEvents[eventName](...params);
}
});
}
get isCloning() {
return this.sortableInstance.options.group.checkPull(this.sortableInstance, this.sortableInstance) === 'clone';
}
clone(item) {
// by default pass the item through, no cloning performed
return (this.sortablejsCloneFunction || (subitem => subitem))(item);
}
get overridenOptions() {
// always intercept standard events but act only in case items are set (bindingEnabled)
// allows to forget about tracking this.items changes
return {
onAdd: (event) => {
this.service.transfer = (items) => {
this.getBindings().injectIntoEvery(event.newIndex, items);
this.proxyEvent('onAdd', event);
};
this.proxyEvent('onAddOriginal', event);
},
onRemove: (event) => {
const bindings = this.getBindings();
if (bindings.provided) {
if (this.isCloning) {
this.service.transfer(bindings.getFromEvery(event.oldIndex).map(item => this.clone(item)));
// great thanks to https://github.com/tauu
// event.item is the original item from the source list which is moved to the target list
// event.clone is a clone of the original item and will be added to source list
// If bindings are provided, adding the item dom element to the target list causes artifacts
// as it interferes with the rendering performed by the angular template.
// Therefore we remove it immediately and also move the original item back to the source list.
// (event handler may be attached to the original item and not its clone, therefore keeping
// the original dom node, circumvents side effects )
this.renderer.removeChild(event.item.parentNode, event.item);
this.renderer.insertBefore(event.clone.parentNode, event.item, event.clone);
this.renderer.removeChild(event.clone.parentNode, event.clone);
}
else {
this.service.transfer(bindings.extractFromEvery(event.oldIndex));
}
this.service.transfer = null;
}
this.proxyEvent('onRemove', event);
},
onUpdate: (event) => {
const bindings = this.getBindings();
const indexes = getIndexesFromEvent(event);
bindings.injectIntoEvery(indexes.new, bindings.extractFromEvery(indexes.old));
this.proxyEvent('onUpdate', event);
},
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsDirective, deps: [{ token: GLOBALS, optional: true }, { token: SortablejsService }, { token: i0.ElementRef }, { token: i0.NgZone }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive }); }
/** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.2", type: SortablejsDirective, isStandalone: true, selector: "[sortablejs]", inputs: { sortablejs: "sortablejs", sortablejsContainer: "sortablejsContainer", sortablejsOptions: "sortablejsOptions", sortablejsCloneFunction: "sortablejsCloneFunction" }, outputs: { sortablejsInit: "sortablejsInit" }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsDirective, decorators: [{
type: Directive,
args: [{
selector: '[sortablejs]',
standalone: true,
}]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [GLOBALS]
}] }, { type: SortablejsService }, { type: i0.ElementRef }, { type: i0.NgZone }, { type: i0.Renderer2 }], propDecorators: { sortablejs: [{
type: Input
}], sortablejsContainer: [{
type: Input
}], sortablejsOptions: [{
type: Input
}], sortablejsCloneFunction: [{
type: Input
}], sortablejsInit: [{
type: Output
}] } });
class SortablejsModule {
static forRoot(globalOptions) {
return {
ngModule: SortablejsModule,
providers: [
{ provide: GLOBALS, useValue: globalOptions },
],
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.2", ngImport: i0, type: SortablejsModule, imports: [SortablejsDirective], exports: [SortablejsDirective] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.2", ngImport: i0, type: SortablejsModule, decorators: [{
type: NgModule,
args: [{
imports: [SortablejsDirective],
exports: [SortablejsDirective],
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { SortablejsDirective, SortablejsModule };
//# sourceMappingURL=worktile-ngx-sortablejs.mjs.map