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. 10
      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. 96
      src/Squidex/app/framework/angular/sorted.directive.ts
  14. 9
      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. /// 404 => Schema, field or app not found.
/// </returns> /// </returns>
[HttpPut] [HttpPut]
[Route("apps/{app}/schemas/{name}/fields/{parentId:long}/ordering/")] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/ordering/")]
[ProducesResponseType(typeof(ErrorDto), 400)] [ProducesResponseType(typeof(ErrorDto), 400)]
[ApiCosts(1)] [ApiCosts(1)]
public async Task<IActionResult> PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request) 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 { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { DragulaModule } from 'ng2-dragula'; import { DndModule } from 'ng2-dnd';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';

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

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

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

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

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

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

10
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 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="row">
<div class="col col-6"> <div class="col col-6">
<span class="field-name"> <span class="field-name">
@ -102,12 +102,15 @@
</div> </div>
<div class="nested-fields" *ngIf="field.properties.fieldType === 'Array'"> <div class="nested-fields" *ngIf="field.properties.fieldType === 'Array'">
<ng-container *ngIf="field['nested']; let nested">
<span class="nested-field-line-v"></span> <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)"> <div [sqxSortModel]="nested" (sqxSorted)="sortFields($event)" handleClass="nested">
<div class="nested-field" *ngFor="let nested of nested">
<span class="nested-field-line-h"></span> <span class="nested-field-line-h"></span>
<sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns"></sqx-field> <sqx-field [field]="nested" [schema]="schema" [parent]="field" [patterns]="patterns" handleClass="nested"></sqx-field>
</div>
</div> </div>
<div class="nested-field nested-field-add"> <div class="nested-field nested-field-add">
@ -123,5 +126,6 @@
(completed)="addFieldDialog.hide()"> (completed)="addFieldDialog.hide()">
</sqx-field-wizard> </sqx-field-wizard>
</ng-container> </ng-container>
</ng-container>
</div> </div>
</div> </div>

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

@ -88,7 +88,3 @@ $padding: 1rem;
max-width: 6rem; 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() @Input()
public parent: RootFieldDto; public parent: RootFieldDto;
@Input()
public handleClass: string;
@Input() @Input()
public patterns: ImmutableArray<AppPatternDto>; public patterns: ImmutableArray<AppPatternDto>;

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

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

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

@ -59,7 +59,3 @@
font-size: .9rem; 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 { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { DndModule } from 'ng2-dnd';
import { import {
HelpComponent, HelpComponent,
@ -156,7 +155,6 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
DndModule,
SqxFrameworkModule, SqxFrameworkModule,
SqxSharedModule, SqxSharedModule,
RouterModule.forChild(routes) 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> <label class="col col-3 col-form-label fallback-label">Fallback</label>
<div class="col col-9"> <div class="col col-9">
<div class="fallback-languages" dnd-sortable-container [sortableData]="fallbackLanguages.mutableValues" *ngIf="fallbackLanguages.length > 0"> <div class="fallback-languages" [sqxSortModel]="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-language" *ngFor="let language of fallbackLanguages">
<div class="row no-gutter"> <div class="row no-gutter">
<div class="col"> <div class="col">
{{language.englishName}} {{language.englishName}}

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

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

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

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

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

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

6
src/Squidex/package-lock.json

@ -121,6 +121,12 @@
"integrity": "sha1-mL1OSb1cXo2l2F36pYnfwmm3sGc=", "integrity": "sha1-mL1OSb1cXo2l2F36pYnfwmm3sGc=",
"dev": true "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": { "@types/graphql": {
"version": "0.11.7", "version": "0.11.7",
"resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-0.11.7.tgz", "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", "babel-polyfill": "6.26.0",
"bootstrap": "4.0.0", "bootstrap": "4.0.0",
"core-js": "2.5.3", "core-js": "2.5.3",
"dragula": "^3.7.2",
"graphiql": "0.11.11", "graphiql": "0.11.11",
"moment": "2.20.1", "moment": "2.20.1",
"mousetrap": "1.6.1", "mousetrap": "1.6.1",
"ng2-dnd": "5.0.2", "ng2-dnd": "5.0.2",
"ng2-dragula": "^1.5.0",
"oidc-client": "1.4.1", "oidc-client": "1.4.1",
"pikaday": "1.7.0", "pikaday": "1.7.0",
"progressbar.js": "1.0.1", "progressbar.js": "1.0.1",
@ -48,6 +48,7 @@
"@angular/compiler-cli": "5.2.1", "@angular/compiler-cli": "5.2.1",
"@ngtools/webpack": "1.9.5", "@ngtools/webpack": "1.9.5",
"@types/core-js": "0.9.45", "@types/core-js": "0.9.45",
"@types/dragula": "^2.1.33",
"@types/jasmine": "2.5.45", "@types/jasmine": "2.5.45",
"@types/mousetrap": "1.6", "@types/mousetrap": "1.6",
"@types/node": "8.5.2", "@types/node": "8.5.2",

Loading…
Cancel
Save