From 3c7e7be062197b604b004e51e90b236e84e2b4a0 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 21 Nov 2022 21:35:18 +0100 Subject: [PATCH] Collapsible fields in schema editor. --- .../src/app/features/schemas/declarations.ts | 2 + frontend/src/app/features/schemas/module.ts | 6 +- .../pages/schema/fields/field.component.html | 45 ++++++-------- .../pages/schema/fields/field.component.scss | 61 +++++++++++-------- .../pages/schema/fields/field.component.ts | 19 +++--- .../fields/schema-fields.component.html | 28 ++++----- .../schema/fields/schema-fields.component.ts | 19 ++---- .../src/app/framework/angular/drag-helper.ts | 2 +- .../shared/state/contents.forms-helpers.ts | 10 +-- .../src/app/shared/state/schemas.state.ts | 2 +- frontend/src/app/shared/state/settings.ts | 1 + 11 files changed, 95 insertions(+), 100 deletions(-) diff --git a/frontend/src/app/features/schemas/declarations.ts b/frontend/src/app/features/schemas/declarations.ts index 29cda57a3..680ccc3d0 100644 --- a/frontend/src/app/features/schemas/declarations.ts +++ b/frontend/src/app/features/schemas/declarations.ts @@ -7,6 +7,7 @@ export * from './pages/schema/common/schema-edit-form.component'; export * from './pages/schema/export/schema-export-form.component'; +export * from './pages/schema/fields/field-group.component'; export * from './pages/schema/fields/field-wizard.component'; export * from './pages/schema/fields/field.component'; export * from './pages/schema/fields/forms/field-form-common.component'; @@ -14,6 +15,7 @@ export * from './pages/schema/fields/forms/field-form-ui.component'; export * from './pages/schema/fields/forms/field-form-validation.component'; export * from './pages/schema/fields/forms/field-form.component'; export * from './pages/schema/fields/schema-fields.component'; +export * from './pages/schema/fields/sortable-field-list.component'; export * from './pages/schema/fields/types/array-validation.component'; export * from './pages/schema/fields/types/assets-ui.component'; export * from './pages/schema/fields/types/assets-validation.component'; diff --git a/frontend/src/app/features/schemas/module.ts b/frontend/src/app/features/schemas/module.ts index 82cf69bc5..3011f4c73 100644 --- a/frontend/src/app/features/schemas/module.ts +++ b/frontend/src/app/features/schemas/module.ts @@ -8,9 +8,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HelpComponent, HistoryComponent, LoadSchemasGuard, SchemaMustExistGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; -import { ArrayValidationComponent, AssetsUIComponent, AssetsValidationComponent, BooleanUIComponent, BooleanValidationComponent, ComponentsUIComponent, ComponentsValidationComponent, DateTimeUIComponent, DateTimeValidationComponent, FieldComponent, FieldFormCommonComponent, FieldFormComponent, FieldFormUIComponent, FieldFormValidationComponent, FieldListComponent, FieldWizardComponent, GeolocationUIComponent, GeolocationValidationComponent, JsonMoreComponent, JsonUIComponent, JsonValidationComponent, NumberUIComponent, NumberValidationComponent, ReferencesUIComponent, ReferencesValidationComponent, SchemaEditFormComponent, SchemaExportFormComponent, SchemaFieldRulesFormComponent, SchemaFieldsComponent, SchemaFormComponent, SchemaPageComponent, SchemaPreviewUrlsFormComponent, SchemaScriptNamePipe, SchemaScriptsFormComponent, SchemasPageComponent, SchemaUIFormComponent, StringUIComponent, StringValidationComponent, TagsUIComponent, TagsValidationComponent } from './declarations'; -import { ComponentUIComponent } from './pages/schema/fields/types/component-ui.component'; -import { ComponentValidationComponent } from './pages/schema/fields/types/component-validation.component'; +import { ArrayValidationComponent, AssetsUIComponent, AssetsValidationComponent, BooleanUIComponent, BooleanValidationComponent, ComponentsUIComponent, ComponentsValidationComponent, ComponentUIComponent, ComponentValidationComponent, DateTimeUIComponent, DateTimeValidationComponent, FieldComponent, FieldFormCommonComponent, FieldFormComponent, FieldFormUIComponent, FieldFormValidationComponent, FieldGroupComponent, FieldListComponent, FieldWizardComponent, GeolocationUIComponent, GeolocationValidationComponent, JsonMoreComponent, JsonUIComponent, JsonValidationComponent, NumberUIComponent, NumberValidationComponent, ReferencesUIComponent, ReferencesValidationComponent, SchemaEditFormComponent, SchemaExportFormComponent, SchemaFieldRulesFormComponent, SchemaFieldsComponent, SchemaFormComponent, SchemaPageComponent, SchemaPreviewUrlsFormComponent, SchemaScriptNamePipe, SchemaScriptsFormComponent, SchemasPageComponent, SchemaUIFormComponent, SortableFieldListComponent, StringUIComponent, StringValidationComponent, TagsUIComponent, TagsValidationComponent } from './declarations'; const routes: Routes = [ { @@ -65,6 +63,7 @@ const routes: Routes = [ DateTimeUIComponent, DateTimeValidationComponent, FieldComponent, + FieldGroupComponent, FieldFormCommonComponent, FieldFormComponent, FieldFormUIComponent, @@ -91,6 +90,7 @@ const routes: Routes = [ SchemaScriptNamePipe, SchemasPageComponent, SchemaUIFormComponent, + SortableFieldListComponent, StringUIComponent, StringValidationComponent, TagsUIComponent, diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.html b/frontend/src/app/features/schemas/pages/schema/fields/field.component.html index 33890131c..509966790 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.html @@ -1,13 +1,12 @@ -
+
- - - -
-
+
+ +
+
- + {{field.displayName}} @@ -20,7 +19,7 @@ {{ 'schemas.field.localizableMarker' | sqxTranslate }}
-
+
{{ 'schemas.field.lockedMarker' | sqxTranslate }} @@ -110,35 +109,31 @@ -
-
- - - - - - -
-
+ +
-
- + + (complete)="fieldWizard.hide()"> diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.scss b/frontend/src/app/features/schemas/pages/schema/fields/field.component.scss index af2520f0c..5ee756d0e 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.scss +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.scss @@ -14,14 +14,23 @@ $padding: 1rem; } .table-items-row-summary { - padding-left: 3rem; - padding-right: 1.25rem; - position: relative; + padding-left: .75rem; } -.drag-container { - @include absolute(1.6rem, auto, auto, .75rem); - line-height: 1px; +.plain { + background: none; + border: 0; + border-radius: 0; + + .table-items-row-details { + background: $color-white; + border: 1px solid $color-border; + border-radius: $border-radius; + } + + .btn-expand.expanded::before { + border-bottom-color: $color-border !important; + } } .col { @@ -39,27 +48,29 @@ $padding: 1rem; position: relative; } -.nested-field { - position: relative; - - &-add { - border: 2px dashed $field-line; - padding: 1rem; +:host ::ng-deep { + .nested-field { position: relative; - } - - &-line-v { - @include absolute($padding, auto, 3 * $padding + .25rem, $padding); - border: 0; - border-left: 2px dashed $field-line; - width: 2px; - } - &-line-h { - @include absolute(-2px, auto, 50%, -$padding); - border: 0; - border-bottom: 2px dashed $field-line; - width: $padding - .25rem; + &-add { + padding-top: 1rem; + padding-bottom: 1rem; + position: relative; + } + + &-line-v { + @include absolute($padding, auto, 3 * $padding + .25rem, $padding); + border: 0; + border-left: 2px dashed $field-line; + width: 2px; + } + + &-line-h { + @include absolute(-2px, auto, 50%, -$padding); + border: 0; + border-bottom: 2px dashed $field-line; + width: $padding - .25rem; + } } } diff --git a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts index 06dce86e0..84d3b15b2 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/field.component.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { AppSettingsDto, createProperties, DialogModel, EditFieldForm, LanguageDto, ModalModel, NestedFieldDto, RootFieldDto, SchemaDto, SchemasState, sorted } from '@app/shared'; +import { AppSettingsDto, createProperties, DialogModel, EditFieldForm, FieldDto, LanguageDto, ModalModel, NestedFieldDto, RootFieldDto, SchemaDto, SchemasState } from '@app/shared'; @Component({ selector: 'sqx-field[field][languages][schema][settings]', @@ -21,6 +20,9 @@ export class FieldComponent implements OnChanges { @Input() public schema!: SchemaDto; + @Input() + public plain = false; + @Input() public parent?: RootFieldDto; @@ -32,14 +34,12 @@ export class FieldComponent implements OnChanges { public dropdown = new ModalModel(); - public trackByFieldFn: (_index: number, field: NestedFieldDto) => any; - public isEditing = false; public isEditable?: boolean | null; public editForm!: EditFieldForm; - public addFieldDialog = new DialogModel(); + public fieldWizard = new DialogModel(); public get isLocalizable() { return (this.parent && this.parent.isLocalizable) || this.field['isLocalizable']; @@ -48,7 +48,6 @@ export class FieldComponent implements OnChanges { constructor( private readonly schemasState: SchemasState, ) { - this.trackByFieldFn = this.trackByField.bind(this); } public ngOnChanges(changes: SimpleChanges) { @@ -88,8 +87,8 @@ export class FieldComponent implements OnChanges { this.schemasState.hideField(this.schema, this.field); } - public sortFields(event: CdkDragDrop>) { - this.schemasState.orderFields(this.schema, sorted(event), this.field as any).subscribe(); + public sortFields(fields: ReadonlyArray) { + this.schemasState.orderFields(this.schema, fields, this.field as any); } public lockField() { @@ -117,8 +116,4 @@ export class FieldComponent implements OnChanges { }); } } - - public trackByField(_index: number, field: NestedFieldDto) { - return field.fieldId + this.schema.id; - } } diff --git a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html index e3dc0c6bd..1f965ef4a 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html @@ -1,35 +1,33 @@
{{ 'schemas.field.empty' | sqxTranslate }} -
-
-
- - - -
-
+ + -
- + + (complete)="fieldWizard.hide()"> \ No newline at end of file diff --git a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts index 23ad5601f..feb060576 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts +++ b/frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { Component, Input, OnInit } from '@angular/core'; -import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, RootFieldDto, SchemaDto, SchemasState, sorted } from '@app/shared'; +import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, SchemaDto, SchemasState } from '@app/shared'; @Component({ selector: 'sqx-schema-fields[schema]', @@ -15,32 +14,24 @@ import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, RootField templateUrl: './schema-fields.component.html', }) export class SchemaFieldsComponent implements OnInit { - public fieldTypes = fieldTypes; - @Input() public schema!: SchemaDto; - public addFieldDialog = new DialogModel(); - - public trackByFieldFn: (_index: number, field: FieldDto) => any; + public fieldTypes = fieldTypes; + public fieldWizard = new DialogModel(); constructor( public readonly appsState: AppsState, public readonly schemasState: SchemasState, public readonly languageState: LanguagesState, ) { - this.trackByFieldFn = this.trackByField.bind(this); } public ngOnInit() { this.languageState.load(); } - public sortFields(event: CdkDragDrop>) { - this.schemasState.orderFields(this.schema, sorted(event)).subscribe(); - } - - public trackByField(_index: number, field: FieldDto) { - return field.fieldId + this.schema.id; + public sortFields(fields: ReadonlyArray) { + this.schemasState.orderFields(this.schema, fields).subscribe(); } } diff --git a/frontend/src/app/framework/angular/drag-helper.ts b/frontend/src/app/framework/angular/drag-helper.ts index ad54a2222..5adba9ad6 100644 --- a/frontend/src/app/framework/angular/drag-helper.ts +++ b/frontend/src/app/framework/angular/drag-helper.ts @@ -8,7 +8,7 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { Types } from './../utils/types'; -export function sorted(event: CdkDragDrop>): ReadonlyArray { +export function sorted(event: CdkDragDrop>): T[] { const items = event.container.data; moveItemInArray(items, event.previousIndex, event.currentIndex); diff --git a/frontend/src/app/shared/state/contents.forms-helpers.ts b/frontend/src/app/shared/state/contents.forms-helpers.ts index 245ed28ec..a0b3d181c 100644 --- a/frontend/src/app/shared/state/contents.forms-helpers.ts +++ b/frontend/src/app/shared/state/contents.forms-helpers.ts @@ -99,8 +99,10 @@ export abstract class Hidden { } } -export function groupFields(fields: ReadonlyArray): { separator?: T; fields: ReadonlyArray }[] { - const result: { separator?: T; fields: ReadonlyArray }[] = []; +export type FieldGroup = { separator?: T; fields: T[] }; + +export function groupFields(fields: ReadonlyArray, keepEmpty = false): FieldGroup[] { + const result: FieldGroup[] = []; let currentSeparator: T | undefined; let currentFields: T[] = []; @@ -109,7 +111,7 @@ export function groupFields(fields: ReadonlyArray): { sep if (field.properties.isContentField) { currentFields.push(field); } else { - if (currentFields.length > 0) { + if (currentFields.length > 0 || keepEmpty) { result.push({ separator: currentSeparator, fields: currentFields }); } @@ -118,7 +120,7 @@ export function groupFields(fields: ReadonlyArray): { sep } } - if (currentFields.length > 0) { + if (currentFields.length > 0 || keepEmpty) { result.push({ separator: currentSeparator, fields: currentFields }); } diff --git a/frontend/src/app/shared/state/schemas.state.ts b/frontend/src/app/shared/state/schemas.state.ts index 36b219e3b..03515172f 100644 --- a/frontend/src/app/shared/state/schemas.state.ts +++ b/frontend/src/app/shared/state/schemas.state.ts @@ -266,7 +266,7 @@ export class SchemasState extends State { shareSubscribed(this.dialogs)); } - public orderFields(schema: SchemaDto, fields: ReadonlyArray, parent?: RootFieldDto): Observable { + public orderFields(schema: SchemaDto, fields: ReadonlyArray, parent?: RootFieldDto): Observable { return this.schemasService.putFieldOrdering(this.appName, parent || schema, fields.map(t => t.fieldId), schema.version).pipe( tap(updated => { this.replaceSchema(updated, schema.version, 'i18n:schemas.saved'); diff --git a/frontend/src/app/shared/state/settings.ts b/frontend/src/app/shared/state/settings.ts index 147c249e5..1be5e8fff 100644 --- a/frontend/src/app/shared/state/settings.ts +++ b/frontend/src/app/shared/state/settings.ts @@ -21,6 +21,7 @@ export const Settings = { DISABLE_ONBOARDING: (key: any) => `squidex.onboarding.disable.${key}`, FIELD_ALL: (schema: any, field: any) => `squidex.schemas.${schema}.fields.${field}.show-all`, FIELD_COLLAPSED: (schema: any, field: any) => `squidex.schemas.${schema}.fields.${field}.closed`, + FIELD_EDITOR_COLLAPSED: (schema: any, field: any) => `squidex.schemas.${schema}.editor.fields.${field}.closed`, HIDE_MAP: 'hideMap', NEWS_VERSION: 'squidex.news.version', NOTIFICATION_VERSION: 'notifications.version',