Browse Source

sqxSorted

pull/297/head
Sebastian 8 years ago
parent
commit
263d05dc77
  1. 2
      src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs
  2. 3
      src/Squidex/app/app.module.ts
  3. 2
      src/Squidex/app/features/api/module.ts
  4. 2
      src/Squidex/app/features/content/module.ts
  5. 6
      src/Squidex/app/features/content/shared/references-editor.component.html
  6. 36
      src/Squidex/app/features/schemas/pages/schema/field.component.html
  7. 4
      src/Squidex/app/features/schemas/pages/schema/field.component.scss
  8. 3
      src/Squidex/app/features/schemas/pages/schema/field.component.ts
  9. 10
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.html
  10. 4
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.scss
  11. 2
      src/Squidex/app/features/settings/module.ts
  12. 4
      src/Squidex/app/features/settings/pages/languages/language.component.html
  13. 102
      src/Squidex/app/framework/angular/sorted.directive.ts
  14. 11
      src/Squidex/app/theme/_common.scss
  15. 4
      src/Squidex/app/theme/theme.scss
  16. 6
      src/Squidex/package-lock.json
  17. 3
      src/Squidex/package.json

2
src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs

@ -122,7 +122,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas
/// 404 => Schema, field or app not found.
/// </returns>
[HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/ordering/")]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")]
[ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(1)]
public async Task<IActionResult> PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request)

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

@ -12,8 +12,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { DragulaModule } from 'ng2-dragula';
import { DndModule } from 'ng2-dnd';
import { AppComponent } from './app.component';

2
src/Squidex/app/features/api/module.ts

@ -7,7 +7,6 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import {
SqxFrameworkModule,
@ -37,7 +36,6 @@ const routes: Routes = [
@NgModule({
imports: [
DndModule,
SqxFrameworkModule,
SqxSharedModule,
RouterModule.forChild(routes)

2
src/Squidex/app/features/content/module.ts

@ -7,7 +7,6 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import {
CanDeactivateGuard,
@ -82,7 +81,6 @@ const routes: Routes = [
imports: [
SqxFrameworkModule,
SqxSharedModule,
DndModule,
RouterModule.forChild(routes)
],
declarations: [

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

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

36
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" dnd-sortable-handle>
<div class="table-items-row-summary {{handleClass}}">
<div class="row">
<div class="col col-6">
<span class="field-name">
@ -102,26 +102,30 @@
</div>
<div class="nested-fields" *ngIf="field.properties.fieldType === 'Array'">
<span class="nested-field-line-v"></span>
<ng-container *ngIf="field['nested']; let nested">
<span class="nested-field-line-v"></span>
<div class="nested-field" *ngFor="let nested of field['nested']; let i = index;" dnd-sortable [sortableIndex]="i" (sqxSorted)="sortFields($event)">
<span class="nested-field-line-h"></span>
<div [sqxSortModel]="nested" (sqxSorted)="sortFields($event)" handleClass="nested">
<div class="nested-field" *ngFor="let nested of nested">
<span class="nested-field-line-h"></span>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns"></sqx-field>
</div>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns" handleClass="nested"></sqx-field>
</div>
</div>
<div class="nested-field nested-field-add">
<span class="nested-field-line-h"></span>
<div class="nested-field nested-field-add">
<span class="nested-field-line-h"></span>
<button class="btn btn-success btn-sm" (click)="addFieldDialog.show()">
<i class="icon icon-plus"></i> Add Nested Field
</button>
</div>
<button class="btn btn-success btn-sm" (click)="addFieldDialog.show()">
<i class="icon icon-plus"></i> Add Nested Field
</button>
</div>
<ng-container *sqxModalView="addFieldDialog;onRoot:true;closeAuto:false">
<sqx-field-wizard [schema]="schema" [parent]="field"
(completed)="addFieldDialog.hide()">
</sqx-field-wizard>
<ng-container *sqxModalView="addFieldDialog;onRoot:true;closeAuto:false">
<sqx-field-wizard [schema]="schema" [parent]="field"
(completed)="addFieldDialog.hide()">
</sqx-field-wizard>
</ng-container>
</ng-container>
</div>
</div>

4
src/Squidex/app/features/schemas/pages/schema/field.component.scss

@ -88,7 +88,3 @@ $padding: 1rem;
max-width: 6rem;
}
}
.dnd-sortable-drag {
border: 0;
}

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

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

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

@ -59,17 +59,15 @@
</button>
</div>
<div dnd-sortable-container [sortableData]="schema.fields">
<div class="schemas">
<div *ngFor="let field of schema.fields; let i = index;" dnd-sortable [sortableIndex]="i" (sqxSorted)="sortFields($event)">
<sqx-field [field]="field" [schema]="schema" [patterns]="patternsState.patterns | async"></sqx-field>
</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" [field]="field" [schema]="schema" [patterns]="patterns" handleClass="root"></sqx-field>
</div>
<button class="btn btn-success field-button" (click)="addFieldDialog.show()">
<i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">Add Field</div>
</button>
</div>
</ng-container>
</ng-container>
<ng-container sidebar>

4
src/Squidex/app/features/schemas/pages/schema/schema-page.component.scss

@ -59,7 +59,3 @@
font-size: .9rem;
}
}
.dnd-sortable-drag {
border: 0;
}

2
src/Squidex/app/features/settings/module.ts

@ -7,7 +7,6 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import {
HelpComponent,
@ -156,7 +155,6 @@ const routes: Routes = [
@NgModule({
imports: [
DndModule,
SqxFrameworkModule,
SqxSharedModule,
RouterModule.forChild(routes)

4
src/Squidex/app/features/settings/pages/languages/language.component.html

@ -38,8 +38,8 @@
<label class="col col-3 col-form-label fallback-label">Fallback</label>
<div class="col col-9">
<div class="fallback-languages" dnd-sortable-container [sortableData]="fallbackLanguages.mutableValues" *ngIf="fallbackLanguages.length > 0">
<div class="fallback-language" *ngFor="let language of fallbackLanguages; let i = index" dnd-sortable [sortableIndex]="i">
<div class="fallback-languages" [sqxSortModel]="fallbackLanguages.mutableValues" *ngIf="fallbackLanguages.length > 0">
<div class="fallback-language" *ngFor="let language of fallbackLanguages">
<div class="row no-gutter">
<div class="col">
{{language.englishName}}

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

@ -6,56 +6,90 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Directive, EventEmitter, Output } from '@angular/core';
import { Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, Renderer2 } from '@angular/core';
import {
DragDropSortableService,
SortableComponent,
SortableContainer
} from 'ng2-dnd';
import * as dragula from 'dragula';
@Directive({
selector: '[sqxSorted]'
selector: '[sqxSortModel]'
})
export class SortedDirective {
private oldArray: any[];
export class SortedDirective implements OnDestroy, OnInit {
private drake: dragula.Drake;
@Input('sqxSortModel')
public sortModel: any[];
@Input()
public handleClass: string | null = null;
@Output('sqxSorted')
public sorted = new EventEmitter<Array<any>>();
public sorted = new EventEmitter<any[]>();
constructor(
sortableComponent: SortableComponent,
sortableContainer: SortableContainer,
sortableDragDropService: DragDropSortableService
private readonly elementRef: ElementRef,
private readonly renderer: Renderer2
) {
const oldDragStartCallback = sortableComponent._onDragStartCallback.bind(sortableComponent);
}
if (Array.isArray(sortableContainer.sortableData)) {
sortableComponent._onDragStartCallback = () => {
oldDragStartCallback();
public ngOnDestroy() {
this.drake.destroy();
}
this.oldArray = [...<any>sortableContainer.sortableData];
};
public ngOnInit() {
const handleClass = this.handleClass;
const oldDropCallback = sortableComponent._onDropCallback.bind(sortableComponent);
this.drake = dragula([this.elementRef.nativeElement], {
ignoreInputTextSelection: true,
sortableComponent._onDropCallback = (event: Event) => {
oldDropCallback(event);
moves: (element, container, handle: HTMLElement) => {
if (!handleClass) {
return true;
}
if (sortableDragDropService.isDragged) {
const newArray: any[] = <any>sortableContainer.sortableData;
const oldArray = this.oldArray;
let current: HTMLElement | null = handle;
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;
}
}
while (current && current !== container) {
if (current.classList.contains(handleClass)) {
return true;
}
current = <any>current.parentNode;
}
};
}
return false;
}
});
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);
}
}

11
src/Squidex/app/theme/_common.scss

@ -113,7 +113,9 @@ body {
// Drop area for drag and drop features.
//
.drop-container {
position: relative;
& {
position: relative;
}
.drop-area {
& {
@ -147,6 +149,13 @@ body {
}
}
.sorting {
&,
& > * {
@include opacity(.5);
}
}
//
// Animations
//

4
src/Squidex/app/theme/theme.scss

@ -6,8 +6,8 @@
// Pikaday
@import '~pikaday/css/pikaday.css';
// Drag and Drop
@import '~ng2-dnd/bundles/style.css';
// Dragula
@import '~dragula/dist/dragula.css';
// Bootstrap Overrides
@import '_bootstrap.scss';

6
src/Squidex/package-lock.json

@ -121,6 +121,12 @@
"integrity": "sha1-mL1OSb1cXo2l2F36pYnfwmm3sGc=",
"dev": true
},
"@types/dragula": {
"version": "2.1.33",
"resolved": "https://registry.npmjs.org/@types/dragula/-/dragula-2.1.33.tgz",
"integrity": "sha512-cb5BNoOXPZ4Bohe+TC7/bbNxbFOL9T+32xjlU2h7gJfCg+9qV/5uX1mVm7dfyFutVdxAQDjZemGz2m9Dav8rMA==",
"dev": true
},
"@types/graphql": {
"version": "0.11.7",
"resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-0.11.7.tgz",

3
src/Squidex/package.json

@ -29,11 +29,11 @@
"babel-polyfill": "6.26.0",
"bootstrap": "4.0.0",
"core-js": "2.5.3",
"dragula": "^3.7.2",
"graphiql": "0.11.11",
"moment": "2.20.1",
"mousetrap": "1.6.1",
"ng2-dnd": "5.0.2",
"ng2-dragula": "^1.5.0",
"oidc-client": "1.4.1",
"pikaday": "1.7.0",
"progressbar.js": "1.0.1",
@ -48,6 +48,7 @@
"@angular/compiler-cli": "5.2.1",
"@ngtools/webpack": "1.9.5",
"@types/core-js": "0.9.45",
"@types/dragula": "^2.1.33",
"@types/jasmine": "2.5.45",
"@types/mousetrap": "1.6",
"@types/node": "8.5.2",

Loading…
Cancel
Save