Browse Source

Collapsible fields in schema editor.

pull/942/head
Sebastian 4 years ago
parent
commit
3c7e7be062
  1. 2
      frontend/src/app/features/schemas/declarations.ts
  2. 6
      frontend/src/app/features/schemas/module.ts
  3. 45
      frontend/src/app/features/schemas/pages/schema/fields/field.component.html
  4. 61
      frontend/src/app/features/schemas/pages/schema/fields/field.component.scss
  5. 19
      frontend/src/app/features/schemas/pages/schema/fields/field.component.ts
  6. 28
      frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html
  7. 19
      frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.ts
  8. 2
      frontend/src/app/framework/angular/drag-helper.ts
  9. 10
      frontend/src/app/shared/state/contents.forms-helpers.ts
  10. 2
      frontend/src/app/shared/state/schemas.state.ts
  11. 1
      frontend/src/app/shared/state/settings.ts

2
frontend/src/app/features/schemas/declarations.ts

@ -7,6 +7,7 @@
export * from './pages/schema/common/schema-edit-form.component'; export * from './pages/schema/common/schema-edit-form.component';
export * from './pages/schema/export/schema-export-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-wizard.component';
export * from './pages/schema/fields/field.component'; export * from './pages/schema/fields/field.component';
export * from './pages/schema/fields/forms/field-form-common.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-validation.component';
export * from './pages/schema/fields/forms/field-form.component'; export * from './pages/schema/fields/forms/field-form.component';
export * from './pages/schema/fields/schema-fields.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/array-validation.component';
export * from './pages/schema/fields/types/assets-ui.component'; export * from './pages/schema/fields/types/assets-ui.component';
export * from './pages/schema/fields/types/assets-validation.component'; export * from './pages/schema/fields/types/assets-validation.component';

6
frontend/src/app/features/schemas/module.ts

@ -8,9 +8,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { HelpComponent, HistoryComponent, LoadSchemasGuard, SchemaMustExistGuard, SqxFrameworkModule, SqxSharedModule } from '@app/shared'; 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 { 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';
import { ComponentUIComponent } from './pages/schema/fields/types/component-ui.component';
import { ComponentValidationComponent } from './pages/schema/fields/types/component-validation.component';
const routes: Routes = [ const routes: Routes = [
{ {
@ -65,6 +63,7 @@ const routes: Routes = [
DateTimeUIComponent, DateTimeUIComponent,
DateTimeValidationComponent, DateTimeValidationComponent,
FieldComponent, FieldComponent,
FieldGroupComponent,
FieldFormCommonComponent, FieldFormCommonComponent,
FieldFormComponent, FieldFormComponent,
FieldFormUIComponent, FieldFormUIComponent,
@ -91,6 +90,7 @@ const routes: Routes = [
SchemaScriptNamePipe, SchemaScriptNamePipe,
SchemasPageComponent, SchemasPageComponent,
SchemaUIFormComponent, SchemaUIFormComponent,
SortableFieldListComponent,
StringUIComponent, StringUIComponent,
StringValidationComponent, StringValidationComponent,
TagsUIComponent, TagsUIComponent,

45
frontend/src/app/features/schemas/pages/schema/fields/field.component.html

@ -1,13 +1,12 @@
<div class="table-items-row table-items-row-expandable field"> <div class="table-items-row table-items-row-expandable field" [class.plain]="plain">
<div class="table-items-row-summary"> <div class="table-items-row-summary">
<span class="drag-container">
<ng-content></ng-content>
</span>
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-6"> <div class="col col-auto">
<ng-content></ng-content>
</div>
<div class="col">
<span class="field-name"> <span class="field-name">
<i class="field-icon icon-type-{{field.properties.fieldType}}"></i> <i *ngIf="!plain" class="field-icon icon-type-{{field.properties.fieldType}}"></i>
<ng-container *ngIf="field.isHidden else visible"> <ng-container *ngIf="field.isHidden else visible">
<span class="field-hidden" title="i18n:schemas.field.hiddenMarker">{{field.displayName}}</span> <span class="field-hidden" title="i18n:schemas.field.hiddenMarker">{{field.displayName}}</span>
@ -20,7 +19,7 @@
<span class="field-partitioning ms-2" *ngIf="field['isLocalizable']">{{ 'schemas.field.localizableMarker' | sqxTranslate }}</span> <span class="field-partitioning ms-2" *ngIf="field['isLocalizable']">{{ 'schemas.field.localizableMarker' | sqxTranslate }}</span>
</span> </span>
</div> </div>
<div class="col col-tags flex-nowrap"> <div class="col-4 flex-nowrap" *ngIf="!plain">
<div class="float-end"> <div class="float-end">
<span class="ms-1 badge rounded-pill badge-danger" *ngIf="field.isLocked"> <span class="ms-1 badge rounded-pill badge-danger" *ngIf="field.isLocked">
{{ 'schemas.field.lockedMarker' | sqxTranslate }} {{ 'schemas.field.lockedMarker' | sqxTranslate }}
@ -110,35 +109,31 @@
<ng-container *ngIf="field['nested']; let nested"> <ng-container *ngIf="field['nested']; let nested">
<span class="nested-field-line-v"></span> <span class="nested-field-line-v"></span>
<div <sqx-sortable-field-list
cdkDropList [fields]="nested"
[cdkDropListDisabled]="!isEditable" [fieldsEmpty]="nested.length === 0"
[cdkDropListData]="nested" [languages]="languages"
(cdkDropListDropped)="sortFields($event)"> [schema]="schema"
<div *ngFor="let nested of nested; trackBy: trackByFieldFn" class="nested-field table-drag" cdkDrag cdkDragLockAxis="y"> [settings]="settings"
[sortable]="isEditable === true"
<span class="nested-field-line-h"></span> (sorted)="sortFields($event)"
[parent]="$any(field)">
<sqx-field [field]="nested" [schema]="schema" [parent]="$any(field)" [settings]="settings" [languages]="languages"> </sqx-sortable-field-list>
<i cdkDragHandle class="icon-drag2 drag-handle"></i>
</sqx-field>
</div>
</div>
<div class="nested-field nested-field-add" *ngIf="isEditable"> <div class="nested-field nested-field-add" *ngIf="isEditable">
<span class="nested-field-line-h"></span> <span class="nested-field-line-h"></span>
<button type="button" class="btn btn-success btn-sm" (click)="addFieldDialog.show()"> <button type="button" class="btn btn-success btn-sm" (click)="fieldWizard.show()">
<i class="icon icon-plus"></i> {{ 'schemas.addNestedField' | sqxTranslate }} <i class="icon icon-plus"></i> {{ 'schemas.addNestedField' | sqxTranslate }}
</button> </button>
</div> </div>
<ng-container *sqxModal="addFieldDialog"> <ng-container *sqxModal="fieldWizard">
<sqx-field-wizard <sqx-field-wizard
[parent]="$any(field)" [parent]="$any(field)"
[schema]="schema" [schema]="schema"
[settings]="settings" [settings]="settings"
(complete)="addFieldDialog.hide()"> (complete)="fieldWizard.hide()">
</sqx-field-wizard> </sqx-field-wizard>
</ng-container> </ng-container>
</ng-container> </ng-container>

61
frontend/src/app/features/schemas/pages/schema/fields/field.component.scss

@ -14,14 +14,23 @@ $padding: 1rem;
} }
.table-items-row-summary { .table-items-row-summary {
padding-left: 3rem; padding-left: .75rem;
padding-right: 1.25rem;
position: relative;
} }
.drag-container { .plain {
@include absolute(1.6rem, auto, auto, .75rem); background: none;
line-height: 1px; 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 { .col {
@ -39,27 +48,29 @@ $padding: 1rem;
position: relative; position: relative;
} }
.nested-field { :host ::ng-deep {
position: relative; .nested-field {
&-add {
border: 2px dashed $field-line;
padding: 1rem;
position: relative; position: relative;
}
&-line-v {
@include absolute($padding, auto, 3 * $padding + .25rem, $padding);
border: 0;
border-left: 2px dashed $field-line;
width: 2px;
}
&-line-h { &-add {
@include absolute(-2px, auto, 50%, -$padding); padding-top: 1rem;
border: 0; padding-bottom: 1rem;
border-bottom: 2px dashed $field-line; position: relative;
width: $padding - .25rem; }
&-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;
}
} }
} }

19
frontend/src/app/features/schemas/pages/schema/fields/field.component.ts

@ -5,9 +5,8 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * 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 { 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({ @Component({
selector: 'sqx-field[field][languages][schema][settings]', selector: 'sqx-field[field][languages][schema][settings]',
@ -21,6 +20,9 @@ export class FieldComponent implements OnChanges {
@Input() @Input()
public schema!: SchemaDto; public schema!: SchemaDto;
@Input()
public plain = false;
@Input() @Input()
public parent?: RootFieldDto; public parent?: RootFieldDto;
@ -32,14 +34,12 @@ export class FieldComponent implements OnChanges {
public dropdown = new ModalModel(); public dropdown = new ModalModel();
public trackByFieldFn: (_index: number, field: NestedFieldDto) => any;
public isEditing = false; public isEditing = false;
public isEditable?: boolean | null; public isEditable?: boolean | null;
public editForm!: EditFieldForm; public editForm!: EditFieldForm;
public addFieldDialog = new DialogModel(); public fieldWizard = new DialogModel();
public get isLocalizable() { public get isLocalizable() {
return (this.parent && this.parent.isLocalizable) || this.field['isLocalizable']; return (this.parent && this.parent.isLocalizable) || this.field['isLocalizable'];
@ -48,7 +48,6 @@ export class FieldComponent implements OnChanges {
constructor( constructor(
private readonly schemasState: SchemasState, private readonly schemasState: SchemasState,
) { ) {
this.trackByFieldFn = this.trackByField.bind(this);
} }
public ngOnChanges(changes: SimpleChanges) { public ngOnChanges(changes: SimpleChanges) {
@ -88,8 +87,8 @@ export class FieldComponent implements OnChanges {
this.schemasState.hideField(this.schema, this.field); this.schemasState.hideField(this.schema, this.field);
} }
public sortFields(event: CdkDragDrop<ReadonlyArray<NestedFieldDto>>) { public sortFields(fields: ReadonlyArray<FieldDto>) {
this.schemasState.orderFields(this.schema, sorted(event), this.field as any).subscribe(); this.schemasState.orderFields(this.schema, fields, this.field as any);
} }
public lockField() { public lockField() {
@ -117,8 +116,4 @@ export class FieldComponent implements OnChanges {
}); });
} }
} }
public trackByField(_index: number, field: NestedFieldDto) {
return field.fieldId + this.schema.id;
}
} }

28
frontend/src/app/features/schemas/pages/schema/fields/schema-fields.component.html

@ -1,35 +1,33 @@
<div class="table-items-row table-items-row-summary table-items-row-empty" *ngIf="schema && schema.fields.length === 0"> <div class="table-items-row table-items-row-summary table-items-row-empty" *ngIf="schema && schema.fields.length === 0">
{{ 'schemas.field.empty' | sqxTranslate }} {{ 'schemas.field.empty' | sqxTranslate }}
<button type="button" class="btn btn-success btn-sm ms-2" (click)="addFieldDialog.show()" *ngIf="schema.canAddField"> <button type="button" class="btn btn-success btn-sm ms-2" (click)="fieldWizard.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus"></i> {{ 'schemas.addField' | sqxTranslate }} <i class="icon icon-plus"></i> {{ 'schemas.addField' | sqxTranslate }}
</button> </button>
</div> </div>
<ng-container *ngIf="appsState.selectedSettings| async; let settings"> <ng-container *ngIf="appsState.selectedSettings| async; let settings">
<ng-container *ngIf="languageState.isoLanguages | async; let languages"> <ng-container *ngIf="languageState.isoLanguages | async; let languages">
<div <sqx-sortable-field-list
cdkDropList [fields]="schema.fields"
[cdkDropListDisabled]="!schema.canOrderFields" [fieldsEmpty]="schema.fields.length === 0"
[cdkDropListData]="schema.fields" [languages]="languages"
(cdkDropListDropped)="sortFields($event)"> [schema]="schema"
<div *ngFor="let field of schema.fields; trackBy: trackByFieldFn" class="table-drag" cdkDrag cdkDragLockAxis="y"> [settings]="settings"
<sqx-field [field]="$any(field)" [schema]="schema" [settings]="settings" [languages]="languages"> [sortable]="schema.canOrderFields"
<i cdkDragHandle class="icon-drag2 drag-handle"></i> (sorted)="sortFields($event)">
</sqx-field> </sqx-sortable-field-list>
</div>
</div>
<button type="button" class="btn btn-success field-button" (click)="addFieldDialog.show()" *ngIf="schema.canAddField"> <button type="button" class="btn btn-success field-button" (click)="fieldWizard.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">{{ 'schemas.addFieldButton' | sqxTranslate }}</div> <i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">{{ 'schemas.addFieldButton' | sqxTranslate }}</div>
</button> </button>
</ng-container> </ng-container>
<ng-container *sqxModal="addFieldDialog"> <ng-container *sqxModal="fieldWizard">
<sqx-field-wizard <sqx-field-wizard
[settings]="settings" [settings]="settings"
[schema]="schema" [schema]="schema"
(complete)="addFieldDialog.hide()"> (complete)="fieldWizard.hide()">
</sqx-field-wizard> </sqx-field-wizard>
</ng-container> </ng-container>
</ng-container> </ng-container>

19
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. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component, Input, OnInit } from '@angular/core'; 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({ @Component({
selector: 'sqx-schema-fields[schema]', selector: 'sqx-schema-fields[schema]',
@ -15,32 +14,24 @@ import { AppsState, DialogModel, FieldDto, fieldTypes, LanguagesState, RootField
templateUrl: './schema-fields.component.html', templateUrl: './schema-fields.component.html',
}) })
export class SchemaFieldsComponent implements OnInit { export class SchemaFieldsComponent implements OnInit {
public fieldTypes = fieldTypes;
@Input() @Input()
public schema!: SchemaDto; public schema!: SchemaDto;
public addFieldDialog = new DialogModel(); public fieldTypes = fieldTypes;
public fieldWizard = new DialogModel();
public trackByFieldFn: (_index: number, field: FieldDto) => any;
constructor( constructor(
public readonly appsState: AppsState, public readonly appsState: AppsState,
public readonly schemasState: SchemasState, public readonly schemasState: SchemasState,
public readonly languageState: LanguagesState, public readonly languageState: LanguagesState,
) { ) {
this.trackByFieldFn = this.trackByField.bind(this);
} }
public ngOnInit() { public ngOnInit() {
this.languageState.load(); this.languageState.load();
} }
public sortFields(event: CdkDragDrop<ReadonlyArray<RootFieldDto>>) { public sortFields(fields: ReadonlyArray<FieldDto>) {
this.schemasState.orderFields(this.schema, sorted(event)).subscribe(); this.schemasState.orderFields(this.schema, fields).subscribe();
}
public trackByField(_index: number, field: FieldDto) {
return field.fieldId + this.schema.id;
} }
} }

2
frontend/src/app/framework/angular/drag-helper.ts

@ -8,7 +8,7 @@
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { Types } from './../utils/types'; import { Types } from './../utils/types';
export function sorted<T>(event: CdkDragDrop<ReadonlyArray<T>>): ReadonlyArray<T> { export function sorted<T>(event: CdkDragDrop<ReadonlyArray<T>>): T[] {
const items = <T[]>event.container.data; const items = <T[]>event.container.data;
moveItemInArray(items, event.previousIndex, event.currentIndex); moveItemInArray(items, event.previousIndex, event.currentIndex);

10
frontend/src/app/shared/state/contents.forms-helpers.ts

@ -99,8 +99,10 @@ export abstract class Hidden {
} }
} }
export function groupFields<T extends FieldDto>(fields: ReadonlyArray<T>): { separator?: T; fields: ReadonlyArray<T> }[] { export type FieldGroup<T = FieldDto> = { separator?: T; fields: T[] };
const result: { separator?: T; fields: ReadonlyArray<T> }[] = [];
export function groupFields<T extends FieldDto>(fields: ReadonlyArray<T>, keepEmpty = false): FieldGroup<T>[] {
const result: FieldGroup<T>[] = [];
let currentSeparator: T | undefined; let currentSeparator: T | undefined;
let currentFields: T[] = []; let currentFields: T[] = [];
@ -109,7 +111,7 @@ export function groupFields<T extends FieldDto>(fields: ReadonlyArray<T>): { sep
if (field.properties.isContentField) { if (field.properties.isContentField) {
currentFields.push(field); currentFields.push(field);
} else { } else {
if (currentFields.length > 0) { if (currentFields.length > 0 || keepEmpty) {
result.push({ separator: currentSeparator, fields: currentFields }); result.push({ separator: currentSeparator, fields: currentFields });
} }
@ -118,7 +120,7 @@ export function groupFields<T extends FieldDto>(fields: ReadonlyArray<T>): { sep
} }
} }
if (currentFields.length > 0) { if (currentFields.length > 0 || keepEmpty) {
result.push({ separator: currentSeparator, fields: currentFields }); result.push({ separator: currentSeparator, fields: currentFields });
} }

2
frontend/src/app/shared/state/schemas.state.ts

@ -266,7 +266,7 @@ export class SchemasState extends State<Snapshot> {
shareSubscribed(this.dialogs)); shareSubscribed(this.dialogs));
} }
public orderFields(schema: SchemaDto, fields: ReadonlyArray<any>, parent?: RootFieldDto): Observable<SchemaDto> { public orderFields(schema: SchemaDto, fields: ReadonlyArray<FieldDto>, parent?: RootFieldDto): Observable<SchemaDto> {
return this.schemasService.putFieldOrdering(this.appName, parent || schema, fields.map(t => t.fieldId), schema.version).pipe( return this.schemasService.putFieldOrdering(this.appName, parent || schema, fields.map(t => t.fieldId), schema.version).pipe(
tap(updated => { tap(updated => {
this.replaceSchema(updated, schema.version, 'i18n:schemas.saved'); this.replaceSchema(updated, schema.version, 'i18n:schemas.saved');

1
frontend/src/app/shared/state/settings.ts

@ -21,6 +21,7 @@ export const Settings = {
DISABLE_ONBOARDING: (key: any) => `squidex.onboarding.disable.${key}`, DISABLE_ONBOARDING: (key: any) => `squidex.onboarding.disable.${key}`,
FIELD_ALL: (schema: any, field: any) => `squidex.schemas.${schema}.fields.${field}.show-all`, 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_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', HIDE_MAP: 'hideMap',
NEWS_VERSION: 'squidex.news.version', NEWS_VERSION: 'squidex.news.version',
NOTIFICATION_VERSION: 'notifications.version', NOTIFICATION_VERSION: 'notifications.version',

Loading…
Cancel
Save