diff --git a/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs b/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs index 6275e953b..878591922 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/ContentSchemaBuilder.cs @@ -31,7 +31,8 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema ["created"] = Builder.DateTimeProperty($"The date and time when the {schemaName} content has been created.", true), ["createdBy"] = Builder.StringProperty($"The user that has created the {schemaName} content.", true), ["lastModified"] = Builder.DateTimeProperty($"The date and time when the {schemaName} content has been modified last.", true), - ["lastModifiedBy"] = Builder.StringProperty($"The user that has updated the {schemaName} content last.", true) + ["lastModifiedBy"] = Builder.StringProperty($"The user that has updated the {schemaName} content last.", true), + ["status"] = Builder.StringProperty($"The status of the content.", true) }, Type = JsonObjectType.Object }; diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs b/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs index 0cf81f181..b44bc2a0f 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs @@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Queries { var cacheKey = BuildJsonCacheKey(context.App, schema, context.IsFrontendClient); - var result = Cache.GetOrCreate(cacheKey, entry => + var result = Cache.GetOrCreate(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = CacheTime; diff --git a/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs b/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs index 38c801b15..96b9b8c47 100644 --- a/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs +++ b/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using NJsonSchema; using NodaTime; using NodaTime.Text; @@ -18,6 +19,13 @@ namespace Squidex.Infrastructure.Queries.Json { private delegate bool Parser(List errors, PropertyPath path, IJsonValue value, out T result); + private static readonly InstantPattern[] InstantPatterns = + { + InstantPattern.General, + InstantPattern.ExtendedIso, + InstantPattern.CreateWithInvariantCulture("yyyy-MM-dd") + }; + public static ClrValue Convert(JsonSchema schema, IJsonValue value, PropertyPath path, List errors) { ClrValue result = null; @@ -196,13 +204,16 @@ namespace Squidex.Infrastructure.Queries.Json if (value is JsonString jsonString) { - var parsed = InstantPattern.General.Parse(jsonString.Value); - - if (parsed.Success) + foreach (var pattern in InstantPatterns) { - result = parsed.Value; + var parsed = pattern.Parse(jsonString.Value); - return true; + if (parsed.Success) + { + result = parsed.Value; + + return true; + } } errors.Add($"Expected ISO8601 DateTime String for path '{path}', but got invalid String."); diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs index 823794c6b..be751858b 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AddWorkflowDto.cs @@ -7,7 +7,6 @@ using System.ComponentModel.DataAnnotations; using Squidex.Domain.Apps.Entities.Apps.Commands; -using Squidex.Infrastructure.Commands; namespace Squidex.Areas.Api.Controllers.Apps.Models { @@ -19,7 +18,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models [Required] public string Name { get; set; } - public ICommand ToCommand() + public AddWorkflow ToCommand() { return new AddWorkflow { Name = Name }; } diff --git a/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs b/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs index 60c645d02..a86ef6c85 100644 --- a/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Assets/Models/AnnotateAssetDto.cs @@ -29,7 +29,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models /// public HashSet Tags { get; set; } - public AssetCommand ToCommand(Guid id) + public AnnotateAsset ToCommand(Guid id) { return SimpleMapper.Map(this, new AnnotateAsset { AssetId = id }); } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs index cd753bc9e..63ba803ec 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaDto.cs @@ -68,18 +68,18 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models foreach (var rootFieldDto in dto.Fields) { - var rootProps = rootFieldDto?.Properties.ToProperties(); + var rootProps = rootFieldDto?.Properties?.ToProperties(); var rootField = new UpsertSchemaField { Properties = rootProps }; SimpleMapper.Map(rootFieldDto, rootField); - if (rootFieldDto.Nested?.Count > 0) + if (rootFieldDto?.Nested?.Count > 0) { rootField.Nested = new List(); foreach (var nestedFieldDto in rootFieldDto.Nested) { - var nestedProps = nestedFieldDto?.Properties.ToProperties(); + var nestedProps = nestedFieldDto?.Properties?.ToProperties(); var nestedField = new UpsertSchemaNestedField { Properties = nestedProps }; SimpleMapper.Map(nestedFieldDto, nestedField); diff --git a/src/Squidex/app/features/assets/pages/assets-filters-page.component.html b/src/Squidex/app/features/assets/pages/assets-filters-page.component.html index 7d8cb432f..8c18d686f 100644 --- a/src/Squidex/app/features/assets/pages/assets-filters-page.component.html +++ b/src/Squidex/app/features/assets/pages/assets-filters-page.component.html @@ -31,14 +31,14 @@
diff --git a/src/Squidex/app/features/assets/pages/assets-page.component.ts b/src/Squidex/app/features/assets/pages/assets-page.component.ts index b2971b602..b464fab13 100644 --- a/src/Squidex/app/features/assets/pages/assets-page.component.ts +++ b/src/Squidex/app/features/assets/pages/assets-page.component.ts @@ -11,9 +11,9 @@ import { FormControl } from '@angular/forms'; import { AppsState, AssetsState, - FilterState, LocalStoreService, Queries, + Query, ResourceOwner, UIState } from '@app/shared'; @@ -28,8 +28,6 @@ export class AssetsPageComponent extends ResourceOwner implements OnInit { public queries = new Queries(this.uiState, 'assets'); - public filter = new FilterState(); - public isListView: boolean; constructor( @@ -44,12 +42,6 @@ export class AssetsPageComponent extends ResourceOwner implements OnInit { } public ngOnInit() { - this.own( - this.assetsState.assetsQuery - .subscribe(query => { - this.filter.setQuery(query); - })); - this.assetsState.load(); } @@ -57,8 +49,8 @@ export class AssetsPageComponent extends ResourceOwner implements OnInit { this.assetsState.load(true); } - public search() { - this.assetsState.search(this.filter.apiFilter); + public search(query: Query) { + this.assetsState.search(query); } public selectTags(tags: string[]) { diff --git a/src/Squidex/app/features/content/declarations.ts b/src/Squidex/app/features/content/declarations.ts index a8d2ba651..0513a17f8 100644 --- a/src/Squidex/app/features/content/declarations.ts +++ b/src/Squidex/app/features/content/declarations.ts @@ -25,5 +25,4 @@ export * from './shared/contents-selector.component'; export * from './shared/due-time-selector.component'; export * from './shared/field-editor.component'; export * from './shared/preview-button.component'; -export * from './shared/references-dropdown.component'; export * from './shared/references-editor.component'; \ No newline at end of file diff --git a/src/Squidex/app/features/content/module.ts b/src/Squidex/app/features/content/module.ts index 2592ae979..a6d036c56 100644 --- a/src/Squidex/app/features/content/module.ts +++ b/src/Squidex/app/features/content/module.ts @@ -39,7 +39,6 @@ import { FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, - ReferencesDropdownComponent, ReferencesEditorComponent, SchemasPageComponent } from './declarations'; @@ -125,7 +124,6 @@ const routes: Routes = [ FieldEditorComponent, FieldLanguagesComponent, PreviewButtonComponent, - ReferencesDropdownComponent, ReferencesEditorComponent, SchemasPageComponent ] diff --git a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.html b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.html index e06a9bcea..cc0943fa2 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.html +++ b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.html @@ -4,9 +4,9 @@ - - {{query.name}} + + {{default.name}}
@@ -14,10 +14,10 @@ @@ -28,12 +28,12 @@ - + - {{query.name}} + {{saved.name}} - + diff --git a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts index 5a0ff774a..4f4726d30 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-filters-page.component.ts @@ -10,7 +10,9 @@ import { Component, OnInit } from '@angular/core'; import { ContentsState, Queries, + Query, ResourceOwner, + SavedQuery, SchemasState, UIState } from '@app/shared'; @@ -41,12 +43,12 @@ export class ContentsFiltersPageComponent extends ResourceOwner implements OnIni })); } - public search(query: string) { + public search(query: Query) { this.contentsState.search(query); } - public isSelectedQuery(query: string) { - return query === this.contentsState.snapshot.contentsQuery || (!query && !this.contentsState.snapshot.contentsQuery); + public isSelectedQuery(saved: SavedQuery) { + return this.contentsState.isQueryUsed(saved); } public trackByTag(index: number, tag: { name: string }) { diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.html b/src/Squidex/app/features/content/pages/contents/contents-page.component.html index 3a793af80..762adaa88 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.html +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.html @@ -15,12 +15,11 @@
-
@@ -43,7 +42,7 @@ - + Actions @@ -54,15 +53,19 @@ + [field]="field" + [query]="contentsState.contentsQuery | async" + (queryChange)="search($event)" + [language]="language"> + [field]="'lastModified'" + [query]="contentsState.contentsQuery | async" + (queryChange)="search($event)" + [language]="language"> diff --git a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts index 8723613fa..2a17a72a5 100644 --- a/src/Squidex/app/features/content/pages/contents/contents-page.component.ts +++ b/src/Squidex/app/features/content/pages/contents/contents-page.component.ts @@ -13,16 +13,16 @@ import { AppsState, ContentDto, ContentsState, - FilterState, ImmutableArray, LanguagesState, ModalModel, Queries, + Query, + QueryModel, + queryModelFromSchema, ResourceOwner, - RootFieldDto, SchemaDetailsDto, SchemasState, - Sorting, UIState } from '@app/shared'; @@ -35,11 +35,11 @@ import { DueTimeSelectorComponent } from './../../shared/due-time-selector.compo }) export class ContentsPageComponent extends ResourceOwner implements OnInit { public schema: SchemaDetailsDto; - public schemaQueries: Queries; public searchModal = new ModalModel(); public selectedItems: { [id: string]: boolean; } = {}; + public selectedAll = false; public selectionCount = 0; public selectionCanDelete = false; @@ -48,9 +48,8 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { public language: AppLanguageDto; public languages: ImmutableArray; - public filter = new FilterState(); - - public isAllSelected = false; + public queryModel: QueryModel; + public queries: Queries; public minWidth: string; @@ -71,23 +70,22 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { this.own( this.schemasState.selectedSchema .subscribe(schema => { - this.filter = new FilterState(); - this.filter.setLanguage(this.language); - this.resetSelection(); this.schema = schema!; - this.schemaQueries = new Queries(this.uiState, `schemas.${this.schema.name}`); this.minWidth = `${300 + (200 * this.schema.listFields.length)}px`; this.contentsState.load(); + + this.updateQueries(); + this.updateModel(); })); this.own( - this.contentsState.contentsQuery - .subscribe(query => { - this.filter.setQuery(query); + this.contentsState.statuses + .subscribe(() => { + this.updateModel(); })); this.own( @@ -102,7 +100,7 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { this.languages = languages.map(x => x.language); this.language = this.languages.at(0); - this.filter.setLanguage(this.language); + this.updateModel(); })); } @@ -152,14 +150,12 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { this.contentsState.goNext(); } - public search() { - this.contentsState.search(this.filter.apiFilter); + public search(query: Query) { + this.contentsState.search(query); } public selectLanguage(language: AppLanguageDto) { this.language = language; - - this.filter.setLanguage(language); } public isItemSelected(content: ContentDto): boolean { @@ -194,19 +190,12 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { this.updateSelectionSummary(); } - public sort(field: string | RootFieldDto, sorting: Sorting) { - this.filter.setOrderField(field, sorting); - - this.search(); - } - - public trackByContent(index: number, content: ContentDto): string { + public trackByContent(content: ContentDto): string { return content.id; } private updateSelectionSummary() { - this.isAllSelected = this.contentsState.snapshot.contents.length > 0; - + this.selectedAll = this.contentsState.snapshot.contents.length > 0; this.selectionCount = 0; this.selectionCanDelete = true; @@ -232,11 +221,23 @@ export class ContentsPageComponent extends ResourceOwner implements OnInit { this.selectionCanDelete = false; } } else { - this.isAllSelected = false; + this.selectedAll = false; } } this.nextStatuses = Object.keys(allActions); } + + private updateQueries() { + if (this.schema) { + this.queries = new Queries(this.uiState, `schemas.${this.schema.name}`); + } + } + + private updateModel() { + if (this.schema && this.languages) { + this.queryModel = queryModelFromSchema(this.schema, this.languages.values, this.contentsState.snapshot.statuses); + } + } } diff --git a/src/Squidex/app/features/content/shared/assets-editor.component.ts b/src/Squidex/app/features/content/shared/assets-editor.component.ts index 9dc11ac56..4a0406bc0 100644 --- a/src/Squidex/app/features/content/shared/assets-editor.component.ts +++ b/src/Squidex/app/features/content/shared/assets-editor.component.ts @@ -48,11 +48,11 @@ interface State { changeDetection: ChangeDetectionStrategy.OnPush }) export class AssetsEditorComponent extends StatefulControlComponent implements OnInit { - public assetsDialog = new DialogModel(); - @Input() public isCompact = false; + public assetsDialog = new DialogModel(); + constructor(changeDetector: ChangeDetectorRef, private readonly appsState: AppsState, private readonly assetsService: AssetsService, diff --git a/src/Squidex/app/features/content/shared/contents-selector.component.html b/src/Squidex/app/features/content/shared/contents-selector.component.html index 921fe4b2e..50b3a6fcf 100644 --- a/src/Squidex/app/features/content/shared/contents-selector.component.html +++ b/src/Squidex/app/features/content/shared/contents-selector.component.html @@ -11,11 +11,10 @@
- +
@@ -31,7 +30,7 @@ - + @@ -39,15 +38,20 @@ + [field]="field" + [query]="contentsState.contentsQuery | async" + (queryChange)="search($event)" + [language]="language"> + [sortable]="field.properties.isSortable" + [field]="'lastModified'" + [query]="contentsState.contentsQuery | async" + (queryChange)="search($event)" + [language]="language"> diff --git a/src/Squidex/app/features/content/shared/contents-selector.component.ts b/src/Squidex/app/features/content/shared/contents-selector.component.ts index ff3a08351..b0a631fd9 100644 --- a/src/Squidex/app/features/content/shared/contents-selector.component.ts +++ b/src/Squidex/app/features/content/shared/contents-selector.component.ts @@ -9,12 +9,13 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ContentDto, - FilterState, LanguageDto, ManualContentsState, - RootFieldDto, - SchemaDetailsDto, - Sorting + Query, + QueryModel, + queryModelFromSchema, + ResourceOwner, + SchemaDetailsDto } from '@app/shared'; @Component({ @@ -25,7 +26,7 @@ import { ManualContentsState ] }) -export class ContentsSelectorComponent implements OnInit { +export class ContentsSelectorComponent extends ResourceOwner implements OnInit { @Input() public language: LanguageDto; @@ -44,23 +45,29 @@ export class ContentsSelectorComponent implements OnInit { @Output() public select = new EventEmitter(); - public filter = new FilterState(); + public queryModel: QueryModel; public selectedItems: { [id: string]: ContentDto; } = {}; public selectionCount = 0; - - public isAllSelected = false; + public selectedAll = false; public minWidth: string; constructor( public readonly contentsState: ManualContentsState ) { + super(); } public ngOnInit() { this.minWidth = `${200 + (200 * this.schema.referenceFields.length)}px`; + this.own( + this.contentsState.statuses + .subscribe(() => { + this.updateModel(); + })); + this.contentsState.schema = this.schema; this.contentsState.load(); } @@ -69,8 +76,8 @@ export class ContentsSelectorComponent implements OnInit { this.contentsState.load(true); } - public search() { - this.contentsState.search(this.filter.apiFilter); + public search(query: Query) { + this.contentsState.search(query); } public goNext() { @@ -123,19 +130,16 @@ export class ContentsSelectorComponent implements OnInit { this.updateSelectionSummary(); } - public sort(field: string | RootFieldDto, sorting: Sorting) { - this.filter.setOrderField(field, sorting); - - this.search(); - } - private updateSelectionSummary() { this.selectionCount = Object.keys(this.selectedItems).length; + this.selectedAll = this.selectionCount === this.contentsState.snapshot.contents.length; + } - this.isAllSelected = this.selectionCount === this.contentsState.snapshot.contents.length; + private updateModel() { + this.queryModel = queryModelFromSchema(this.schema, this.languages, this.contentsState.snapshot.statuses); } - public trackByContent(index: number, content: ContentDto): string { + public trackByContent(content: ContentDto): string { return content.id; } } diff --git a/src/Squidex/app/features/content/shared/due-time-selector.component.html b/src/Squidex/app/features/content/shared/due-time-selector.component.html index cbd859267..08d41205e 100644 --- a/src/Squidex/app/features/content/shared/due-time-selector.component.html +++ b/src/Squidex/app/features/content/shared/due-time-selector.component.html @@ -1,4 +1,4 @@ - + Change content item(s) to {{dueTimeAction}} diff --git a/src/Squidex/app/features/content/shared/references-editor.component.html b/src/Squidex/app/features/content/shared/references-editor.component.html index 3995986c9..0d2ed7797 100644 --- a/src/Squidex/app/features/content/shared/references-editor.component.html +++ b/src/Squidex/app/features/content/shared/references-editor.component.html @@ -28,7 +28,7 @@ - + Name
- + The name of the field in the API response. @@ -87,6 +87,8 @@ import { FieldDto } from '@app/shared'; ` }) export class FieldFormCommonComponent implements OnInit { + public readonly standalone = { standalone: true }; + @Input() public editForm: FormGroup; diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html b/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html index 494aad805..3af647d3f 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.html @@ -8,7 +8,7 @@
- +
diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts b/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts index b25b815ae..ffa2d165b 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/schema-edit-form.component.ts @@ -20,6 +20,8 @@ import { templateUrl: './schema-edit-form.component.html' }) export class SchemaEditFormComponent implements OnInit { + public readonly standalone = { standalone: true }; + @Output() public complete = new EventEmitter(); diff --git a/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html b/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html index 8aeff4355..7a899def9 100644 --- a/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html +++ b/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html @@ -51,7 +51,7 @@
-
{{target.name}} + {{target.name}}
diff --git a/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss b/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss index f186e2097..5fa3adc10 100644 --- a/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss +++ b/src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss @@ -6,13 +6,6 @@ line-height: 2.8rem; } - .color-circle { - @include circle(12px); - border: 1px solid $color-border-dark; - background: $color-border; - display: inline-block; - } - .col-label { padding: 0 .5rem; } diff --git a/src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html b/src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html index 7eb5a295f..6a56542aa 100644 --- a/src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html +++ b/src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html @@ -4,7 +4,7 @@
-
{{transition.to}} + {{transition.to}}
diff --git a/src/Squidex/app/features/settings/pages/workflows/workflow.component.html b/src/Squidex/app/features/settings/pages/workflows/workflow.component.html index dcfac9bda..f311d85bd 100644 --- a/src/Squidex/app/features/settings/pages/workflows/workflow.component.html +++ b/src/Squidex/app/features/settings/pages/workflows/workflow.component.html @@ -6,7 +6,7 @@
DateTimeEditorComponent), multi: true }; +const NO_EMIT = { emitEvent: false }; + @Component({ selector: 'sqx-date-time-editor', styleUrls: ['./date-time-editor.component.scss'], @@ -33,7 +35,7 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string private suppressEvents = false; @Input() - public mode: string; + public mode: 'DateTime' | 'Date'; @Input() public enforceTime: boolean; @@ -104,11 +106,11 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string super.setDisabledState(isDisabled); if (isDisabled) { - this.dateControl.disable({ emitEvent: false }); - this.timeControl.disable({ emitEvent: false }); + this.dateControl.disable(NO_EMIT); + this.timeControl.disable(NO_EMIT); } else { - this.dateControl.enable({ emitEvent: false }); - this.timeControl.enable({ emitEvent: false }); + this.dateControl.enable(NO_EMIT); + this.timeControl.enable(NO_EMIT); } } @@ -147,8 +149,8 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string } public reset() { - this.timeControl.setValue(null, { emitEvent: false }); - this.dateControl.setValue(null, { emitEvent: false }); + this.timeControl.setValue(null, NO_EMIT); + this.dateControl.setValue(null, NO_EMIT); this.dateValue = null; @@ -188,20 +190,20 @@ export class DateTimeEditorComponent extends StatefulControlComponent<{}, string this.suppressEvents = true; if (this.timeValue && this.timeValue.isValid()) { - this.timeControl.setValue(this.timeValue.format('HH:mm:ss'), { emitEvent: false }); + this.timeControl.setValue(this.timeValue.format('HH:mm:ss'), NO_EMIT); } else { - this.timeControl.setValue(null, { emitEvent: false }); + this.timeControl.setValue(null, NO_EMIT); } if (this.dateValue && this.dateValue.isValid() && this.picker) { const dateString = this.dateValue.format('YYYY-MM-DD'); const dateLocal = moment(dateString); - this.dateControl.setValue(dateString, { emitEvent: false }); + this.dateControl.setValue(dateString, NO_EMIT); this.picker.setMoment(dateLocal); } else { - this.dateControl.setValue(null, { emitEvent: false }); + this.dateControl.setValue(null, NO_EMIT); } this.suppressEvents = false; diff --git a/src/Squidex/app/framework/angular/modals/modal.directive.ts b/src/Squidex/app/framework/angular/modals/modal.directive.ts index 78e57052d..87d64f126 100644 --- a/src/Squidex/app/framework/angular/modals/modal.directive.ts +++ b/src/Squidex/app/framework/angular/modals/modal.directive.ts @@ -121,6 +121,10 @@ export class ModalDirective implements OnDestroy { } private subscribeToView() { + if (Types.is(this.model, DialogModel)) { + return; + } + if (this.closeAuto) { document.addEventListener('mousedown', this.documentClickListener, true); diff --git a/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html b/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html index 04d8088f6..df9a86098 100644 --- a/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html +++ b/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.html @@ -1,4 +1,4 @@ - +
diff --git a/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts b/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts index cc1e55afa..c507d0397 100644 --- a/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts +++ b/src/Squidex/app/framework/angular/modals/onboarding-tooltip.component.ts @@ -9,8 +9,8 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy import { timer } from 'rxjs'; import { + DialogModel, fadeAnimation, - ModalModel, OnboardingService, StatefulComponent, Types @@ -38,7 +38,7 @@ export class OnboardingTooltipComponent extends StatefulComponent implements OnD @Input() public position = 'left'; - public tooltipModal = new ModalModel(); + public tooltipModal = new DialogModel(); constructor(changeDetector: ChangeDetectorRef, private readonly onboardingService: OnboardingService, diff --git a/src/Squidex/app/framework/utils/types.spec.ts b/src/Squidex/app/framework/utils/types.spec.ts index 6ce7d78d2..68cbb4613 100644 --- a/src/Squidex/app/framework/utils/types.spec.ts +++ b/src/Squidex/app/framework/utils/types.spec.ts @@ -108,6 +108,50 @@ describe('Types', () => { expect(Types.jsJsonEquals({ a: 1, b: 2 }, { b: 2, a: 1 })).toBeFalsy(); }); + + it('should not treat zero as empty', () => { + expect(Types.isEmpty(0)).toBeFalsy(); + }); + + it('should not treat empty string as empty', () => { + expect(Types.isEmpty('')).toBeFalsy(); + }); + + it('should not treat false as empty', () => { + expect(Types.isEmpty(false)).toBeFalsy(); + }); + + it('should not treat array with at least one non-empty value as empty', () => { + expect(Types.isEmpty([null, 0])).toBeFalsy(); + }); + + it('should not treat array with at least one non-empty value as empty', () => { + expect(Types.isEmpty({ a: null, b: 0 })).toBeFalsy(); + }); + + it('should treat empty object as empty', () => { + expect(Types.isEmpty({})).toBeTruthy(); + }); + + it('should treat array object as empty', () => { + expect(Types.isEmpty([])).toBeTruthy(); + }); + + it('should treat null as empty', () => { + expect(Types.isEmpty(null)).toBeTruthy(); + }); + + it('should treat undefined as empty', () => { + expect(Types.isEmpty(undefined)).toBeTruthy(); + }); + + it('should treat array of empty values as empty', () => { + expect(Types.isEmpty([])).toBeTruthy(); + }); + + it('should treat object of empty values as empty', () => { + expect(Types.isEmpty({ a: null, b: null })).toBeTruthy(); + }); }); class MyClass { diff --git a/src/Squidex/app/framework/utils/types.ts b/src/Squidex/app/framework/utils/types.ts index 3c17ee8dd..22841eb4f 100644 --- a/src/Squidex/app/framework/utils/types.ts +++ b/src/Squidex/app/framework/utils/types.ts @@ -101,4 +101,30 @@ export module Types { return true; } + + export function isEmpty(value: any): boolean { + if (Types.isArray(value)) { + for (let v of value) { + if (!isEmpty(v)) { + return false; + } + } + + return true; + } + + if (Types.isObject(value)) { + for (let key in value) { + if (value.hasOwnProperty(key)) { + if (!isEmpty(value[key])) { + return false; + } + } + } + + return true; + } + + return Types.isUndefined(value) === true || Types.isNull(value) === true; + } } \ No newline at end of file diff --git a/src/Squidex/app/shared/components/asset-uploader.component.html b/src/Squidex/app/shared/components/asset-uploader.component.html index 769d19969..6c4dab19d 100644 --- a/src/Squidex/app/shared/components/asset-uploader.component.html +++ b/src/Squidex/app/shared/components/asset-uploader.component.html @@ -8,7 +8,7 @@ {{uploads.length}} - +