Browse Source

Better sortable

pull/303/head
Sebastian 8 years ago
parent
commit
259eefdf26
  1. 5
      src/Squidex/app/features/content/shared/references-editor.component.html
  2. 6
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  3. 3
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  4. 6
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  5. 61
      src/Squidex/app/framework/angular/sorted-dnd.directive.ts
  6. 78
      src/Squidex/app/framework/angular/sorted.directive.ts
  7. 1
      src/Squidex/app/framework/declarations.ts
  8. 1
      src/Squidex/app/framework/internal.ts
  9. 3
      src/Squidex/app/framework/module.ts
  10. 7
      src/Squidex/package.json

5
src/Squidex/app/features/content/shared/references-editor.component.html

@ -7,8 +7,9 @@
</div>
<table class="table table-items table-fixed" [class.disabled]="isDisabled" *ngIf="schema && contentItems && contentItems.length > 0"
dnd-sortable-container [sortableData]="contentItems.mutableValues">
<tbody *ngFor="let content of contentItems; let i = index" dnd-sortable [sortableIndex]="i" (sqxSortedDnd)="sort($event)">
[sqxSortModel]="contentItems.values"
(sqxSorted)="sort($event)">
<tbody *ngFor="let content of contentItems">
<tr [sqxContent]="content"
[language]="language"
[schema]="schema"

6
src/Squidex/app/features/schemas/pages/schema/field.component.html

@ -1,5 +1,5 @@
<div class="table-items-row table-items-row-expandable field">
<div class="table-items-row-summary {{handleClass}}">
<div class="table-items-row-summary">
<div class="row">
<div class="col col-6">
<span class="field-name">
@ -105,11 +105,11 @@
<ng-container *ngIf="field['nested']; let nested">
<span class="nested-field-line-v"></span>
<div [sqxSortModel]="nested" (sqxSorted)="sortFields($event)" handleClass="nested">
<div [sqxSortModel]="nested" (sqxSorted)="sortFields($event)">
<div class="nested-field" *ngFor="let nested of nested; trackBy: trackByField">
<span class="nested-field-line-h"></span>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns" handleClass="nested"></sqx-field>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns"></sqx-field>
</div>
</div>

3
src/Squidex/app/features/schemas/pages/schema/field.component.ts

@ -41,9 +41,6 @@ export class FieldComponent implements OnInit {
@Input()
public parent: RootFieldDto;
@Input()
public handleClass: string;
@Input()
public patterns: ImmutableArray<AppPatternDto>;

6
src/Squidex/app/features/schemas/pages/schema/schema-page.component.html

@ -60,8 +60,10 @@
</div>
<ng-container *ngIf="patternsState.patterns | async; let patterns">
<div class="schemas" [sqxSortModel]="schema.fields" (sqxSorted)="sortFields($event)" handleClass="root">
<sqx-field *ngFor="let field of schema.fields; trackBy: trackByField" [field]="field" [schema]="schema" [patterns]="patterns" handleClass="root"></sqx-field>
<div class="schemas" [sqxSortModel]="schema.fields" (sqxSorted)="sortFields($event)">
<div *ngFor="let field of schema.fields; trackBy: trackByField">
<sqx-field [field]="field" [schema]="schema" [patterns]="patterns"></sqx-field>
</div>
</div>
<button class="btn btn-success field-button" (click)="addFieldDialog.show()">

61
src/Squidex/app/framework/angular/sorted-dnd.directive.ts

@ -1,61 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, EventEmitter, Output } from '@angular/core';
import {
DragDropSortableService,
SortableComponent,
SortableContainer
} from 'ng2-dnd';
@Directive({
selector: '[sqxSortedDnd]'
})
export class SortedDndDirective {
private oldArray: any[];
@Output('sqxSortedDnd')
public sorted = new EventEmitter<Array<any>>();
constructor(
sortableComponent: SortableComponent,
sortableContainer: SortableContainer,
sortableDragDropService: DragDropSortableService
) {
const oldDragStartCallback = sortableComponent._onDragStartCallback.bind(sortableComponent);
if (Array.isArray(sortableContainer.sortableData)) {
sortableComponent._onDragStartCallback = () => {
oldDragStartCallback();
this.oldArray = [...<any>sortableContainer.sortableData];
};
const oldDropCallback = sortableComponent._onDropCallback.bind(sortableComponent);
sortableComponent._onDropCallback = (event: Event) => {
oldDropCallback(event);
if (sortableDragDropService.isDragged) {
const newArray: any[] = <any>sortableContainer.sortableData;
const oldArray = this.oldArray;
if (newArray && oldArray && newArray.length === oldArray.length) {
for (let i = 0; i < oldArray.length; i++) {
if (oldArray[i] !== newArray[i]) {
this.sorted.emit(newArray);
break;
}
}
}
}
};
}
}
}

78
src/Squidex/app/framework/angular/sorted.directive.ts

@ -1,4 +1,3 @@
/*
* Squidex Headless CMS
*
@ -6,95 +5,48 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core';
import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import * as dragula from 'dragula';
import Sortable = require('sortablejs');
@Directive({
selector: '[sqxSortModel]'
})
export class SortedDirective implements OnDestroy, OnInit {
private drake: dragula.Drake;
private sortable: Sortable;
@Input('sqxSortModel')
public sortModel: any[];
@Input()
public handleClass: string | null = null;
@Input()
public mirrorToSelf: boolean;
@Output('sqxSorted')
public sorted = new EventEmitter<any[]>();
constructor(
private readonly elementRef: ElementRef,
private readonly renderer: Renderer2
private readonly elementRef: ElementRef
) {
}
public ngOnDestroy() {
this.drake.destroy();
this.sortable.destroy();
}
public ngOnInit() {
const handleClass = this.handleClass;
this.sortable = Sortable.create(this.elementRef.nativeElement, {
sort: true,
animation: 150,
this.drake = dragula([this.elementRef.nativeElement], {
ignoreInputTextSelection: true,
onSort: (event: { oldIndex: number, newIndex: number }) => {
if (this.sortModel && event.newIndex !== event.oldIndex) {
const newModel = [...this.sortModel];
moves: (element, container, handle: HTMLElement) => {
if (!handleClass) {
return true;
}
let current: HTMLElement | null = handle;
const item = this.sortModel[event.oldIndex];
while (current && current !== container) {
if (current.classList.contains(handleClass)) {
return true;
}
newModel.splice(event.oldIndex, 1);
newModel.splice(event.newIndex, 0, item);
current = <any>current.parentNode;
this.sorted.emit(newModel);
}
return false;
},
mirrorContainer: this.mirrorToSelf ? this.elementRef.nativeElement : document.body
});
let dragIndex: number;
let dropIndex: number;
this.drake.on('dragend', (element: any, container: any) => {
this.renderer.removeClass(element, 'sorting');
});
this.drake.on('drag', (element: any, container: any) => {
this.renderer.addClass(element, 'sorting');
dragIndex = this.domIndexOf(container, element);
});
this.drake.on('drop', (element: any, container: any) => {
dropIndex = this.domIndexOf(container, element);
if (this.sortModel && dragIndex !== dropIndex) {
const newModel = [...this.sortModel];
const item = this.sortModel[dragIndex];
newModel.splice(dragIndex, 1);
newModel.splice(dropIndex, 0, item);
this.sorted.emit(newModel);
}
});
}
private domIndexOf(parent: any, child: any): any {
return Array.prototype.indexOf.call(parent.children, child);
}
}

1
src/Squidex/app/framework/declarations.ts

@ -55,7 +55,6 @@ export * from './angular/popup-link.directive';
export * from './angular/scroll-active.directive';
export * from './angular/shortcut.component';
export * from './angular/sorted.directive';
export * from './angular/sorted-dnd.directive';
export * from './angular/template-wrapper.directive';
export * from './angular/title.component';
export * from './angular/user-report.component';

1
src/Squidex/app/framework/internal.ts

@ -11,6 +11,7 @@ export * from './angular/animations';
export * from './services/analytics.service';
export * from './services/clipboard.service';
export * from './services/dialog.service';
export * from './services/dnd.service';
export * from './services/local-store.service';
export * from './services/message-bus.service';
export * from './services/onboarding.service';

3
src/Squidex/app/framework/module.ts

@ -65,7 +65,6 @@ import {
ShortTimePipe,
SliderComponent,
SortedDirective,
SortedDndDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapperDirective,
@ -130,7 +129,6 @@ import {
ShortTimePipe,
SliderComponent,
SortedDirective,
SortedDndDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapperDirective,
@ -190,7 +188,6 @@ import {
ShortTimePipe,
SliderComponent,
SortedDirective,
SortedDndDirective,
StarsComponent,
TagEditorComponent,
TemplateWrapperDirective,

7
src/Squidex/package.json

@ -29,9 +29,8 @@
"babel-polyfill": "6.26.0",
"bootstrap": "4.1.1",
"core-js": "2.5.7",
"dragula": "3.7.2",
"graphql": "0.13.2",
"graphiql": "0.11.11",
"graphql": "0.13.2",
"moment": "2.22.2",
"mousetrap": "1.6.2",
"ng2-dnd": "5.0.2",
@ -42,6 +41,7 @@
"react-dom": "16.4.0",
"rxjs": "6.2.0",
"slugify": "1.3.0",
"sortablejs": "1.7.0",
"zone.js": "0.8.26"
},
"devDependencies": {
@ -55,6 +55,7 @@
"@types/node": "10.1.2",
"@types/react": "16.3.16",
"@types/react-dom": "16.0.6",
"@types/sortablejs": "1.3.32",
"angular-router-loader": "0.8.5",
"angular2-template-loader": "0.6.2",
"awesome-typescript-loader": "5.0.0",
@ -93,7 +94,7 @@
"tslint-loader": "3.6.0",
"typemoq": "2.1.0",
"typescript": "2.7.2",
"uglifyjs-webpack-plugin": "^1.2.5",
"uglifyjs-webpack-plugin": "1.2.5",
"underscore": "1.9.1",
"webpack": "4.11.1",
"webpack-cli": "3.0.3",

Loading…
Cancel
Save