diff --git a/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs index c6652b884..809745d82 100644 --- a/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs +++ b/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs @@ -73,6 +73,11 @@ namespace Squidex.Read.MongoDb.Schemas return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); } + protected Task On(SchemaFieldsReordered @event, EnvelopeHeaders headers) + { + return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + } + protected Task On(SchemaUpdated @event, EnvelopeHeaders headers) { return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); diff --git a/src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs b/src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs index 55e7c1b28..432420d00 100644 --- a/src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs +++ b/src/Squidex/Controllers/Api/Schemas/SchemaFieldsController.cs @@ -72,7 +72,7 @@ namespace Squidex.Controllers.Api.Schemas /// 400 => Schema field ids do not cover the fields of the schema. /// 404 => Schema or app not found. /// - [HttpPost] + [HttpPut] [Route("apps/{app}/schemas/{name}/fields/ordering")] [ProducesResponseType(typeof(ErrorDto), 400)] public async Task PutFieldOrdering(string app, string name, [FromBody] ReorderFields request) diff --git a/src/Squidex/app/app.module.ts b/src/Squidex/app/app.module.ts index 8f00fce4f..1055e5c57 100644 --- a/src/Squidex/app/app.module.ts +++ b/src/Squidex/app/app.module.ts @@ -8,6 +8,7 @@ import { ApplicationRef, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { DndModule } from 'ng2-dnd'; import { AppComponent } from './app.component'; @@ -49,6 +50,7 @@ export function configUserReport() { imports: [ BrowserModule, BrowserAnimationsModule, + DndModule.forRoot(), SqxFrameworkModule.forRoot(), SqxSharedModule.forRoot(), SqxShellModule, diff --git a/src/Squidex/app/features/schemas/module.ts b/src/Squidex/app/features/schemas/module.ts index 5d71b6d99..78557351b 100644 --- a/src/Squidex/app/features/schemas/module.ts +++ b/src/Squidex/app/features/schemas/module.ts @@ -7,6 +7,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { DndModule } from 'ng2-dnd'; import { HelpComponent, @@ -16,6 +17,8 @@ import { SqxSharedModule } from 'shared'; +import { SortedDirective } from './utils/sorted.directive'; + import { FieldComponent, BooleanUIComponent, @@ -74,6 +77,7 @@ const routes: Routes = [ imports: [ SqxFrameworkModule, SqxSharedModule, + DndModule, RouterModule.forChild(routes) ], declarations: [ @@ -92,6 +96,7 @@ const routes: Routes = [ SchemaFormComponent, SchemaPageComponent, SchemasPageComponent, + SortedDirective, StringUIComponent, StringValidationComponent ] diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html index 39847533f..ccc722830 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/schema-page.component.html @@ -36,8 +36,8 @@
-
-
+
+
this.schemasService.putFieldOrdering(app, this.schemaName, fields.map(t => t.fieldId), this.version)).retry(2) + .subscribe(() => { + this.updateFields(ImmutableArray.of(fields)); + }, error => { + this.notifyError(error); + }); + } + public saveField(field: FieldDto, newField: FieldDto) { const request = new UpdateFieldDto(newField.properties); diff --git a/src/Squidex/app/features/schemas/utils/sorted.directive.ts b/src/Squidex/app/features/schemas/utils/sorted.directive.ts new file mode 100644 index 000000000..3ccf1c3e5 --- /dev/null +++ b/src/Squidex/app/features/schemas/utils/sorted.directive.ts @@ -0,0 +1,39 @@ + +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Sebastian Stehle. All rights reserved + */ + +import { Directive, EventEmitter, Output } from '@angular/core'; + +import { + SortableComponent, + SortableContainer, + DragDropSortableService +} from 'ng2-dnd'; + +@Directive({ + selector: '[sorted]' +}) +export class SortedDirective { + @Output() + public sorted = new EventEmitter>(); + + constructor( + sortableComponent: SortableComponent, + sortableContainer: SortableContainer, + sortableDragDropService: DragDropSortableService + ) { + const oldCallback = sortableComponent._onDropCallback.bind(sortableComponent); + + sortableComponent._onDropCallback = (event: Event) => { + oldCallback(event); + + if (sortableDragDropService.isDragged) { + this.sorted.emit(sortableContainer.sortableData); + } + }; + } +} diff --git a/src/Squidex/app/framework/utils/immutable-array.spec.ts b/src/Squidex/app/framework/utils/immutable-array.spec.ts index b9e57db0c..d447e9d8b 100644 --- a/src/Squidex/app/framework/utils/immutable-array.spec.ts +++ b/src/Squidex/app/framework/utils/immutable-array.spec.ts @@ -154,6 +154,12 @@ describe('ImmutableArray', () => { expect(array_2.values).toEqual([1, 2, 3, 4]); }); + it('should provide mutable values', () => { + const array_1 = ImmutableArray.of([3, 1, 4, 2]); + + expect(array_1.mutableValues).toBe(array_1.mutableValues); + }); + it('should iterate over array items', () => { const array_1 = ImmutableArray.of([3, 1, 4, 2]); diff --git a/src/Squidex/app/framework/utils/immutable-array.ts b/src/Squidex/app/framework/utils/immutable-array.ts index 769553803..1a94449ab 100644 --- a/src/Squidex/app/framework/utils/immutable-array.ts +++ b/src/Squidex/app/framework/utils/immutable-array.ts @@ -23,6 +23,10 @@ export class ImmutableArray implements Iterable { return [...this.items]; } + public get mutableValues(): T[] { + return this.items; + } + private constructor(items: T[]) { this.items = items; } diff --git a/src/Squidex/app/shared/services/schemas.service.spec.ts b/src/Squidex/app/shared/services/schemas.service.spec.ts index e7f9c0952..a1fc75160 100644 --- a/src/Squidex/app/shared/services/schemas.service.spec.ts +++ b/src/Squidex/app/shared/services/schemas.service.spec.ts @@ -273,6 +273,22 @@ describe('SchemasService', () => { authService.verifyAll(); }); + it('should make put request to update field ordering', () => { + const dto = [1, 2, 3] + + authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/fields/ordering', It.isAny(), version)) + .returns(() => Observable.of( + new Response( + new ResponseOptions() + ) + )) + .verifiable(Times.once()); + + schemasService.putFieldOrdering('my-app', 'my-schema', dto, version); + + authService.verifyAll(); + }); + it('should make put request to publish schema', () => { authService.setup(x => x.authPut('http://service/p/api/apps/my-app/schemas/my-schema/publish', It.isAny(), version)) .returns(() => Observable.of( diff --git a/src/Squidex/app/shared/services/schemas.service.ts b/src/Squidex/app/shared/services/schemas.service.ts index e41123bec..79a9a4f7e 100644 --- a/src/Squidex/app/shared/services/schemas.service.ts +++ b/src/Squidex/app/shared/services/schemas.service.ts @@ -327,6 +327,13 @@ export class SchemasService { .catchError('Failed to update schema. Please reload.'); } + public putFieldOrdering(appName: string, schemaName: string, dto: number[], version: Version): Observable { + const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/fields/ordering`); + + return this.authService.authPut(url, { fieldIds: dto }, version) + .catchError('Failed to reorder fields. Please reload.'); + } + public publishSchema(appName: string, schemaName: string, version: Version): Observable { const url = this.apiUrl.buildUrl(`api/apps/${appName}/schemas/${schemaName}/publish`); diff --git a/src/Squidex/app/theme/theme.scss b/src/Squidex/app/theme/theme.scss index 1eca554d7..55e4f8af4 100644 --- a/src/Squidex/app/theme/theme.scss +++ b/src/Squidex/app/theme/theme.scss @@ -6,6 +6,9 @@ // Pikaday @import './../../node_modules/pikaday/css/pikaday.css'; +// Drag and Drop +@import './../../node_modules/ng2-dnd/bundles/style.css'; + // Bootstrap Overrides @import '_bootstrap.scss'; diff --git a/src/Squidex/package.json b/src/Squidex/package.json index a93710ba9..98c663cac 100644 --- a/src/Squidex/package.json +++ b/src/Squidex/package.json @@ -29,6 +29,7 @@ "core-js": "2.4.1", "moment": "2.18.1", "mousetrap": "1.6.1", + "ng2-dnd": "^4.0.2", "oidc-client": "1.3.0", "pikaday": "1.5.1", "redoc": "1.12.1",