diff --git a/backend/i18n/frontend_en.json b/backend/i18n/frontend_en.json index a7e61cf80..e6f3e6530 100644 --- a/backend/i18n/frontend_en.json +++ b/backend/i18n/frontend_en.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Failed to load tags. Please reload.", "assets.metadata": "Metadata", "assets.metadataAdd": "Add Metadata", + "assets.move": "Move", "assets.moveFailed": "Failed to move asset. Please reload.", "assets.protected": "Protected", "assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.", @@ -535,7 +536,7 @@ "contributors.import.run": "Add Contributors", "contributors.import.run2": "Import", "contributors.importButton": "Add many contributors at once", - "contributors.importHintg": "Big team?", + "contributors.importHint": "Big team?", "contributors.importTitle": "Import contributors", "contributors.loadFailed": "Failed to load contributors. Please reload.", "contributors.planHint": "Your plan allows up to {maxContributors} contributors.", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "Failed to load permissions. Please reload.", "roles.permissions": "Permissions", "roles.permissionsDescription": "Permissions restrict the allowed operations and queries at API level and are a security feature.", - "roles.permissionsPlaceholder": "Start typing to search for permissions", "roles.properties": "Properties", "roles.properties.hideAPI": "Hide API", "roles.properties.hideAssets": "Hide Assets", diff --git a/backend/i18n/frontend_it.json b/backend/i18n/frontend_it.json index bceba87c1..7bc159889 100644 --- a/backend/i18n/frontend_it.json +++ b/backend/i18n/frontend_it.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Failed to load tags. Please reload.", "assets.metadata": "Metadati", "assets.metadataAdd": "Aggiungi un metadato", + "assets.move": "Move", "assets.moveFailed": "Non è stato possibile spostare la risorsa. Per favore ricarica.", "assets.protected": "Protetto", "assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.", @@ -535,7 +536,7 @@ "contributors.import.run": "Aggiungi Collaboratori", "contributors.import.run2": "Importa", "contributors.importButton": "Aggiungi più collaboratori contemporaneamente", - "contributors.importHintg": "Team numeroso?", + "contributors.importHint": "Team numeroso?", "contributors.importTitle": "Importa collaboratori", "contributors.loadFailed": "Non è stato possibile caricare contributors. Per favore ricarica.", "contributors.planHint": "Il tuo piano prevede un numero massimo di {maxContributors} collaboratori.", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "Non è stato possibile caricare i permessi. Per favore ricarica.", "roles.permissions": "Permessi", "roles.permissionsDescription": "I permessi limitano le operazioni consentite e le interrogazioni (query) a livello di API e sono una funzionalità per garantire la sicurezza.", - "roles.permissionsPlaceholder": "Inizia a digitare per ricercare i permessi", "roles.properties": "Proprietà", "roles.properties.hideAPI": "Nascondi le API", "roles.properties.hideAssets": "Nascondi le Risorse", diff --git a/backend/i18n/frontend_nl.json b/backend/i18n/frontend_nl.json index cc0fa6cf5..e4fc9ecdb 100644 --- a/backend/i18n/frontend_nl.json +++ b/backend/i18n/frontend_nl.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Laden van tags is mislukt. Laad opnieuw.", "assets.metadata": "Metadata", "assets.metadataAdd": "Metadata toevoegen", + "assets.move": "Move", "assets.moveFailed": "Verplaatsen van item is mislukt. Laad opnieuw.", "assets.protected": "Beschermd", "assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.", @@ -535,7 +536,7 @@ "contributors.import.run": "Bijdrager toevoegen", "contributors.import.run2": "Importeren", "contributors.importButton": "Voeg veel bijdragers tegelijk toe", - "contributors.importHintg": "Groot team?", + "contributors.importHint": "Groot team?", "contributors.importTitle": "Bijdragers importeren", "contributors.loadFailed": "Laden van bijdragers is mislukt. Laad opnieuw.", "contributors.planHint": "Uw plan staat maximaal {maxContributors} bijdragers toe.", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "Kan machtigingen niet laden. Laad opnieuw.", "roles.permissions": "Rechten", "roles.permissionsDescription": "Machtigingen beperken de toegestane bewerkingen en zoekopdrachten op API-niveau en zijn een beveiligingsfunctie.", - "roles.permissionsPlaceholder": "Begin met typen om naar rechten te zoeken", "roles.properties": "Eigenschappen", "roles.properties.hideAPI": "API verbergen", "roles.properties.hideAssets": "Assets verbergen", diff --git a/backend/i18n/frontend_pt.json b/backend/i18n/frontend_pt.json index 2b7f07b34..129858483 100644 --- a/backend/i18n/frontend_pt.json +++ b/backend/i18n/frontend_pt.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Falhou em carregar etiquetas. Por favor, recarregue.", "assets.metadata": "Metadados", "assets.metadataAdd": "Adicionar metadados", + "assets.move": "Move", "assets.moveFailed": "Falha ao mover ficheiro. Por favor, recarregue.", "assets.protected": "Protegido", "assets.protectedHint": "Os ativos são públicos por defeito. Todos com o link podem descarregar o ficheiro. Se fizer um ficheiro protegido, apenas utilizadores autenticados (normalmente um cliente) podem descarregar o ficheiro.", @@ -535,7 +536,7 @@ "contributors.import.run": "Adicionar Contribuintes", "contributors.import.run2": "Importação", "contributors.importButton": "Adicionar muitos contribuintes ao mesmo tempo", - "contributors.importHintg": "Uma grande equipa?", + "contributors.importHint": "Uma grande equipa?", "contributors.importTitle": "Contribuintes de importação", "contributors.loadFailed": "Falhou em carregar os contribuintes. Por favor, recarregue.", "contributors.planHint": "O seu plano permite até contribuintes {maxContributors} .", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "Falhou em carregar permissões. Por favor, recarregue.", "roles.permissions": "Permissões", "roles.permissionsDescription": "As permissões restringem as operações e consultas permitidas a nível API e são uma funcionalidade de segurança.", - "roles.permissionsPlaceholder": "Comece a escrever para procurar permissões", "roles.properties": "Propriedades", "roles.properties.hideAPI": "Ocultar API", "roles.properties.hideAssets": "Ocultar ficheiros", diff --git a/backend/i18n/frontend_zh.json b/backend/i18n/frontend_zh.json index 31e417bac..29b6b4bd9 100644 --- a/backend/i18n/frontend_zh.json +++ b/backend/i18n/frontend_zh.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Failed to load tags. Please reload.", "assets.metadata": "元数据", "assets.metadataAdd": "添加元数据", + "assets.move": "Move", "assets.moveFailed": "资源移动失败。请重新加载。", "assets.protected": "受保护", "assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.", @@ -535,7 +536,7 @@ "contributors.import.run": "添加贡献者", "contributors.import.run2": "导入", "contributors.importButton": "一次添加多个贡献者", - "contributors.importHintg": "大团队?", + "contributors.importHint": "大团队?", "contributors.importTitle": "导入贡献者", "contributors.loadFailed": "加载贡献者失败。请重新加载。", "contributors.planHint": "您的计划允许最多 {maxContributors} 个贡献者。", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "加载权限失败。请重新加载。", "roles.permissions": "权限", "roles.permissionsDescription": "权限在 API 级别限制允许的操作和查询,是一项安全功能。", - "roles.permissionsPlaceholder": "开始输入以搜索权限", "roles.properties": "属性", "roles.properties.hideAPI": "隐藏 API", "roles.properties.hideAssets": "隐藏资源", diff --git a/backend/i18n/source/frontend_en.json b/backend/i18n/source/frontend_en.json index a7e61cf80..e6f3e6530 100644 --- a/backend/i18n/source/frontend_en.json +++ b/backend/i18n/source/frontend_en.json @@ -98,6 +98,7 @@ "assets.loadTagsFailed": "Failed to load tags. Please reload.", "assets.metadata": "Metadata", "assets.metadataAdd": "Add Metadata", + "assets.move": "Move", "assets.moveFailed": "Failed to move asset. Please reload.", "assets.protected": "Protected", "assets.protectedHint": "Assets are public by default. Everybody with the link can download the file. If you make an asset protected, only authenticated users (usually a client) can download the asset.", @@ -535,7 +536,7 @@ "contributors.import.run": "Add Contributors", "contributors.import.run2": "Import", "contributors.importButton": "Add many contributors at once", - "contributors.importHintg": "Big team?", + "contributors.importHint": "Big team?", "contributors.importTitle": "Import contributors", "contributors.loadFailed": "Failed to load contributors. Please reload.", "contributors.planHint": "Your plan allows up to {maxContributors} contributors.", @@ -665,7 +666,6 @@ "roles.loadPermissionsFailed": "Failed to load permissions. Please reload.", "roles.permissions": "Permissions", "roles.permissionsDescription": "Permissions restrict the allowed operations and queries at API level and are a security feature.", - "roles.permissionsPlaceholder": "Start typing to search for permissions", "roles.properties": "Properties", "roles.properties.hideAPI": "Hide API", "roles.properties.hideAssets": "Hide Assets", diff --git a/backend/i18n/source/frontend_it.json b/backend/i18n/source/frontend_it.json index 298a02ee0..0547aeda0 100644 --- a/backend/i18n/source/frontend_it.json +++ b/backend/i18n/source/frontend_it.json @@ -431,7 +431,7 @@ "contributors.import.run": "Aggiungi Collaboratori", "contributors.import.run2": "Importa", "contributors.importButton": "Aggiungi più collaboratori contemporaneamente", - "contributors.importHintg": "Team numeroso?", + "contributors.importHint": "Team numeroso?", "contributors.importTitle": "Importa collaboratori", "contributors.loadFailed": "Non è stato possibile caricare contributors. Per favore ricarica.", "contributors.planHint": "Il tuo piano prevede un numero massimo di {maxContributors} collaboratori.", diff --git a/backend/i18n/source/frontend_nl.json b/backend/i18n/source/frontend_nl.json index 72cdb9eec..817adb751 100644 --- a/backend/i18n/source/frontend_nl.json +++ b/backend/i18n/source/frontend_nl.json @@ -500,7 +500,7 @@ "contributors.import.run": "Bijdrager toevoegen", "contributors.import.run2": "Importeren", "contributors.importButton": "Voeg veel bijdragers tegelijk toe", - "contributors.importHintg": "Groot team?", + "contributors.importHint": "Groot team?", "contributors.importTitle": "Bijdragers importeren", "contributors.loadFailed": "Laden van bijdragers is mislukt. Laad opnieuw.", "contributors.planHint": "Uw plan staat maximaal {maxContributors} bijdragers toe.", diff --git a/backend/i18n/source/frontend_pt.json b/backend/i18n/source/frontend_pt.json index 93256083b..954b0aee6 100644 --- a/backend/i18n/source/frontend_pt.json +++ b/backend/i18n/source/frontend_pt.json @@ -524,7 +524,7 @@ "contributors.import.run": "Adicionar Contribuintes", "contributors.import.run2": "Importação", "contributors.importButton": "Adicionar muitos contribuintes ao mesmo tempo", - "contributors.importHintg": "Uma grande equipa?", + "contributors.importHint": "Uma grande equipa?", "contributors.importTitle": "Contribuintes de importação", "contributors.loadFailed": "Falhou em carregar os contribuintes. Por favor, recarregue.", "contributors.planHint": "O seu plano permite até contribuintes {maxContributors} .", diff --git a/backend/i18n/source/frontend_zh.json b/backend/i18n/source/frontend_zh.json index 9452bd36d..ec4b89da1 100644 --- a/backend/i18n/source/frontend_zh.json +++ b/backend/i18n/source/frontend_zh.json @@ -453,7 +453,7 @@ "contributors.import.run": "添加贡献者", "contributors.import.run2": "导入", "contributors.importButton": "一次添加多个贡献者", - "contributors.importHintg": "大团队?", + "contributors.importHint": "大团队?", "contributors.importTitle": "导入贡献者", "contributors.loadFailed": "加载贡献者失败。请重新加载。", "contributors.planHint": "您的计划允许最多 {maxContributors} 个贡献者。", diff --git a/frontend/src/app/features/administration/state/event-consumers.state.spec.ts b/frontend/src/app/features/administration/state/event-consumers.state.spec.ts index 8cd472089..7ed1a04f2 100644 --- a/frontend/src/app/features/administration/state/event-consumers.state.spec.ts +++ b/frontend/src/app/features/administration/state/event-consumers.state.spec.ts @@ -5,8 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { of, throwError } from 'rxjs'; -import { onErrorResumeNext } from 'rxjs/operators'; +import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { EventConsumersService } from '@app/features/administration/internal'; import { DialogService } from '@app/framework'; @@ -50,7 +49,7 @@ describe('EventConsumersState', () => { eventConsumersService.setup(x => x.getEventConsumers()) .returns(() => throwError(() => 'Service Error')); - eventConsumersState.load().pipe(onErrorResumeNext()).subscribe(); + eventConsumersState.load().pipe(onErrorResumeNextWith()).subscribe(); expect(eventConsumersState.snapshot.isLoading).toBeFalsy(); }); @@ -70,7 +69,7 @@ describe('EventConsumersState', () => { eventConsumersService.setup(x => x.getEventConsumers()) .returns(() => throwError(() => 'Service Error')).verifiable(); - eventConsumersState.load(true, false).pipe(onErrorResumeNext()).subscribe(); + eventConsumersState.load(true, false).pipe(onErrorResumeNextWith()).subscribe(); expect().nothing(); diff --git a/frontend/src/app/features/administration/state/users.state.spec.ts b/frontend/src/app/features/administration/state/users.state.spec.ts index e8cd798d9..266d2dd05 100644 --- a/frontend/src/app/features/administration/state/users.state.spec.ts +++ b/frontend/src/app/features/administration/state/users.state.spec.ts @@ -5,8 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { firstValueFrom, of, throwError } from 'rxjs'; -import { onErrorResumeNext } from 'rxjs/operators'; +import { firstValueFrom, of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { UpsertUserDto, UsersService } from '@app/features/administration/internal'; import { DialogService } from '@app/shared'; @@ -53,7 +52,7 @@ describe('UsersState', () => { usersService.setup(x => x.getUsers(10, 0, undefined)) .returns(() => throwError(() => 'Service Error')); - usersState.load().pipe(onErrorResumeNext()).subscribe(); + usersState.load().pipe(onErrorResumeNextWith()).subscribe(); expect(usersState.snapshot.isLoading).toBeFalsy(); }); diff --git a/frontend/src/app/features/assets/pages/assets-page.component.html b/frontend/src/app/features/assets/pages/assets-page.component.html index 817c5823a..f6d961a4e 100644 --- a/frontend/src/app/features/assets/pages/assets-page.component.html +++ b/frontend/src/app/features/assets/pages/assets-page.component.html @@ -11,10 +11,11 @@
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.html b/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.html index 92b989393..0a036bab9 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/references-validation.component.html @@ -4,7 +4,8 @@
+ [itemConverter]="(schemasSource.normalConverter | async)!" + [itemsSource]="(schemasSource.normalConverter | async)?.suggestions">
diff --git a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html index 2fb8c2afd..76a52a376 100644 --- a/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html +++ b/frontend/src/app/features/schemas/pages/schema/fields/types/string-ui.component.html @@ -91,7 +91,8 @@
+ [itemConverter]="(schemasSource.normalConverter | async)!" + [itemsSource]="(schemasSource.normalConverter | async)?.suggestions">
diff --git a/frontend/src/app/features/settings/pages/clients/client.component.html b/frontend/src/app/features/settings/pages/clients/client.component.html index a5afdae21..1e243c35f 100644 --- a/frontend/src/app/features/settings/pages/clients/client.component.html +++ b/frontend/src/app/features/settings/pages/clients/client.component.html @@ -33,9 +33,9 @@
- + -
diff --git a/frontend/src/app/features/settings/pages/clients/client.component.ts b/frontend/src/app/features/settings/pages/clients/client.component.ts index b8209a9f4..7b230f85b 100644 --- a/frontend/src/app/features/settings/pages/clients/client.component.ts +++ b/frontend/src/app/features/settings/pages/clients/client.component.ts @@ -50,7 +50,7 @@ export class ClientComponent implements OnChanges { } public updateApiCallsLimit() { - this.clientsState.update(this.client, { apiCallsLimit: this.client.apiCallsLimit }); + this.clientsState.update(this.client, { apiCallsLimit: this.apiCallsLimit }); } public rename(name: string) { diff --git a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html index 9813ad6ec..86972f570 100644 --- a/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html +++ b/frontend/src/app/features/settings/pages/contributors/contributor-add-form.component.html @@ -4,7 +4,7 @@
- + @@ -29,7 +29,7 @@
- {{ 'contributors.importHintg' | sqxTranslate }} {{ 'contributors.importButton' | sqxTranslate }} + {{ 'contributors.importHint' | sqxTranslate }} {{ 'contributors.importButton' | sqxTranslate }}
diff --git a/frontend/src/app/features/settings/pages/languages/language-add-form.component.html b/frontend/src/app/features/settings/pages/languages/language-add-form.component.html index f97b03f99..a69ec5002 100644 --- a/frontend/src/app/features/settings/pages/languages/language-add-form.component.html +++ b/frontend/src/app/features/settings/pages/languages/language-add-form.component.html @@ -4,7 +4,7 @@
- + {{language.iso2Code}} ({{language.englishName}}) diff --git a/frontend/src/app/features/settings/pages/roles/role.component.html b/frontend/src/app/features/settings/pages/roles/role.component.html index fd95815cb..6eb20d4fe 100644 --- a/frontend/src/app/features/settings/pages/roles/role.component.html +++ b/frontend/src/app/features/settings/pages/roles/role.component.html @@ -56,7 +56,7 @@
- +
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html index c367c5730..4485e88a3 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow-transition.component.html @@ -22,15 +22,15 @@ {{ 'workflows.syntax.for' | sqxTranslate }}
- + [styleDashed]="true" + [styleScrollable]="true">
diff --git a/frontend/src/app/features/settings/pages/workflows/workflow.component.html b/frontend/src/app/features/settings/pages/workflows/workflow.component.html index 33ba797f3..be6df5079 100644 --- a/frontend/src/app/features/settings/pages/workflows/workflow.component.html +++ b/frontend/src/app/features/settings/pages/workflows/workflow.component.html @@ -4,10 +4,12 @@ {{workflow.displayName}}
- + [styleBlank]="true" + [styleScrollable]="true"
@@ -77,11 +79,12 @@
- + [itemConverter]="(schemasSource.converter | async)!" + [itemsSource]="(schemasSource.normalConverter | async)?.suggestions" + [ngModel]="workflow.schemaIds" + (ngModelChange)="changeSchemaIds($event)"> diff --git a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html index 91133f4bb..800b733ff 100644 --- a/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html +++ b/frontend/src/app/features/teams/pages/contributors/contributor-add-form.component.html @@ -4,7 +4,7 @@
- + diff --git a/frontend/src/app/features/teams/state/team-contributors.state.spec.ts b/frontend/src/app/features/teams/state/team-contributors.state.spec.ts index 41d5a1af3..3b9d91c1a 100644 --- a/frontend/src/app/features/teams/state/team-contributors.state.spec.ts +++ b/frontend/src/app/features/teams/state/team-contributors.state.spec.ts @@ -5,8 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { EMPTY, of, throwError } from 'rxjs'; -import { catchError, onErrorResumeNext } from 'rxjs/operators'; +import { EMPTY, of, onErrorResumeNextWith, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; import { IMock, It, Mock, Times } from 'typemoq'; import { TeamContributorsService, TeamContributorsState } from '@app/features/teams/internal'; import { ContributorDto, ContributorsPayload, DialogService, ErrorDto, versioned } from '@app/shared'; @@ -65,7 +65,7 @@ describe('TeamContributorsState', () => { contributorsService.setup(x => x.getContributors(team)) .returns(() => throwError(() => 'Service Error')); - contributorsState.load().pipe(onErrorResumeNext()).subscribe(); + contributorsState.load().pipe(onErrorResumeNextWith()).subscribe(); expect(contributorsState.snapshot.isLoading).toBeFalsy(); }); diff --git a/frontend/src/app/features/teams/state/team-plans.state.spec.ts b/frontend/src/app/features/teams/state/team-plans.state.spec.ts index fde27e2ea..f2574e29a 100644 --- a/frontend/src/app/features/teams/state/team-plans.state.spec.ts +++ b/frontend/src/app/features/teams/state/team-plans.state.spec.ts @@ -5,8 +5,7 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { of, throwError } from 'rxjs'; -import { onErrorResumeNext } from 'rxjs/operators'; +import { of, onErrorResumeNextWith, throwError } from 'rxjs'; import { IMock, It, Mock, Times } from 'typemoq'; import { TeamPlansService, TeamPlansState } from '@app/features/teams/internal'; import { DialogService, PlanDto, PlanLockedReason, versioned } from '@app/shared'; @@ -84,7 +83,7 @@ describe('TeamPlansState', () => { plansService.setup(x => x.getPlans(team)) .returns(() => throwError(() => 'Service Error')); - plansState.load().pipe(onErrorResumeNext()).subscribe(); + plansState.load().pipe(onErrorResumeNextWith()).subscribe(); expect(plansState.snapshot.isLoading).toBeFalsy(); }); @@ -119,7 +118,7 @@ describe('TeamPlansState', () => { plansService.setup(x => x.putPlan(team, It.isAny(), version)) .returns(() => of(versioned(newVersion, result))); - plansState.change('free').pipe(onErrorResumeNext()).subscribe(); + plansState.change('free').pipe(onErrorResumeNextWith()).subscribe(); expect(plansState.snapshot.plans).toEqual([ { isSelected: true, isYearlySelected: false, plan: oldPlans.plans[0] }, @@ -133,7 +132,7 @@ describe('TeamPlansState', () => { plansService.setup(x => x.putPlan(team, It.isAny(), version)) .returns(() => of(versioned(newVersion, { redirectUri: '' }))); - plansState.change('id2_yearly').pipe(onErrorResumeNext()).subscribe(); + plansState.change('id2_yearly').pipe(onErrorResumeNextWith()).subscribe(); expect(plansState.snapshot.plans).toEqual([ { isSelected: false, isYearlySelected: false, plan: oldPlans.plans[0] }, diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html index c454fc03b..10ea1bed3 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html @@ -1,15 +1,15 @@
- + [class.form-underlined]="inputStyle === 'underlined'" + [formControl]="queryInput" + [placeholder]="placeholder">
diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts index 621f444d8..88af4ebb3 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.ts @@ -36,7 +36,7 @@ interface State { const NO_EMIT = { emitEvent: false }; @Component({ - selector: 'sqx-autocomplete', + selector: 'sqx-autocomplete[itemsSource]', styleUrls: ['./autocomplete.component.scss'], templateUrl: './autocomplete.component.html', providers: [ @@ -49,10 +49,7 @@ export class AutocompleteComponent extends StatefulControlComponent { - if (!this.source) { + if (!this.itemsSource) { return of([]); } else { this.setLoading(true); - return this.source.find(query).pipe( + return this.itemsSource.find(query).pipe( finalize(() => { this.setLoading(false); }), diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts index 8fdff4ee0..305ddfd3d 100644 --- a/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.stories.ts @@ -50,7 +50,7 @@ const Template: Story = (args: Autocompl [disabled]="disabled" [icon]="icon" [inputStyle]="inputStyle" - [source]="source"> + [itemsSource]="itemsSource"> `, @@ -71,7 +71,7 @@ class Source implements AutocompleteSource { export const Default = Template.bind({}); Default.args = { - source: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing']), + itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing']), }; export const Disabled = Template.bind({}); @@ -101,6 +101,6 @@ StyleUnderlined.args = { export const IconLoading = Template.bind({}); IconLoading.args = { - source: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing'], 4000), + itemsSource: new Source(['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing'], 4000), icon: 'user', }; \ No newline at end of file diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html index 2fb7b108d..f9c07c0e7 100644 --- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html +++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html @@ -29,9 +29,9 @@
{{item}} diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html index e7a80a28b..1ec34342a 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html @@ -1,35 +1,33 @@
- - {{item}} + [class.focus]="snapshot.hasFocus" + [class.multiline]="!styleScrollable" + [class.readonly]="readonly" + [class.singleline]="styleScrollable" + [class.suggested]="itemsSorted.length > 0"> + + {{tag}}
-
+
- + -
{{item}}
- +
-
+
-
- +
+ - {{suggestionsEmptyText | sqxTranslate}} + {{itemsSourceEmptyText | sqxTranslate}}
diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts index b01547c56..f491cdcab 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts @@ -19,13 +19,13 @@ interface State { hasFocus: boolean; // The suggested item. - suggestedItems: ReadonlyArray; + itemsList: ReadonlyArray; // The index of the selected suggested items. - suggestedIndex: number; + itemsIndex: number; // All available tag values. - items: ReadonlyArray; + tags: ReadonlyArray; } @Component({ @@ -58,7 +58,7 @@ export class TagEditorComponent extends StatefulControlComponent | undefined | null) { - this.suggestionsSorted = getTagValues(value); + public set itemsSource(value: ReadonlyArray | undefined | null) { + this.itemsSorted = getTagValues(value); if (this.addInput.value) { const query = this.addInput.value; - const items = this.suggestionsSorted.filter(s => s.lowerCaseName.includes(query) && !this.snapshot.items.find(x => x.id === s.id)); + const items = this.itemsSorted.filter(s => s.lowerCaseName.includes(query) && !this.snapshot.tags.find(x => x.id === s.id)); this.next({ - suggestedIndex: -1, - suggestedItems: items || [], + itemsIndex: -1, + itemsList: items || [], }); } } - public suggestionsSorted: ReadonlyArray = []; - public suggestionsModal = new ModalModel(); + public itemsSorted: ReadonlyArray = []; + public itemsModal = new ModalModel(); public addInput = new UntypedFormControl(); constructor(changeDetector: ChangeDetectorRef) { super(changeDetector, { hasFocus: false, - suggestedItems: [], - suggestedIndex: 0, - items: [], + itemsList: [], + itemsIndex: 0, + tags: [], }); this.textMeasurer = new TextMeasurer(() => this.inputElement); @@ -175,16 +172,16 @@ export class TagEditorComponent extends StatefulControlComponent { if (!query) { return []; - } else if (Types.isArray(this.suggestionsSorted)) { - return this.suggestionsSorted.filter(s => s.lowerCaseName.includes(query) && !this.snapshot.items.find(x => x.id === s.id)); + } else if (Types.isArray(this.itemsSorted)) { + return this.itemsSorted.filter(s => s.lowerCaseName.includes(query) && !this.snapshot.tags.find(x => x.id === s.id)); } else { return []; } })) .subscribe(suggestedItems => { this.next({ - suggestedIndex: -1, - suggestedItems, + itemsIndex: -1, + itemsList: suggestedItems, }); })); } @@ -199,12 +196,12 @@ export class TagEditorComponent extends StatefulControlComponent i !== index), true); + this.updateItems(this.snapshot.tags.filter((_, i) => i !== index), true); } public resetSize() { @@ -261,7 +258,7 @@ export class TagEditorComponent extends StatefulControlComponent { this.formElement.nativeElement.scrollLeft = this.formElement.nativeElement.scrollWidth; }, 0); @@ -275,11 +272,11 @@ export class TagEditorComponent extends StatefulControlComponent= 0) { - if (this.selectValue(this.snapshot.suggestedItems[this.snapshot.suggestedIndex])) { + if (this.snapshot.itemsIndex >= 0) { + if (this.selectValue(this.snapshot.itemsList[this.snapshot.itemsIndex])) { return false; } } else if (this.acceptEnter) { @@ -311,14 +308,14 @@ export class TagEditorComponent extends StatefulControlComponent x.id !== tagValue.id), true); + this.updateItems(this.snapshot.tags.filter(x => x.id !== tagValue.id), true); } } public selectPrevIndex() { - this.selectIndex(this.snapshot.suggestedIndex - 1); + this.selectIndex(this.snapshot.itemsIndex - 1); } public selectNextIndex() { - this.selectIndex(this.snapshot.suggestedIndex + 1); + this.selectIndex(this.snapshot.itemsIndex + 1); } public selectIndex(suggestedIndex: number) { @@ -350,11 +347,11 @@ export class TagEditorComponent extends StatefulControlComponent= this.snapshot.suggestedItems.length) { - suggestedIndex = this.snapshot.suggestedItems.length - 1; + if (suggestedIndex >= this.snapshot.itemsList.length) { + suggestedIndex = this.snapshot.itemsList.length - 1; } - this.next({ suggestedIndex }); + this.next({ itemsIndex: suggestedIndex }); } public resetFocus(): any { @@ -362,7 +359,7 @@ export class TagEditorComponent extends StatefulControlComponent x.id === tagValue.id); + return this.snapshot.tags.find(x => x.id === tagValue.id); } public closeModal() { - if (this.suggestionsModal.isOpen) { + if (this.itemsModal.isOpen) { this.close.emit(); - this.suggestionsModal.hide(); + this.itemsModal.hide(); } } public openModal() { - if (!this.suggestionsModal.isOpen) { + if (!this.itemsModal.isOpen) { this.open.emit(); - this.suggestionsModal.show(); + this.itemsModal.show(); } } @@ -412,7 +409,7 @@ export class TagEditorComponent extends StatefulControlComponent x.name).join(',')); + event.clipboardData.setData('text/plain', this.snapshot.tags.map(x => x.name).join(',')); } event.preventDefault(); @@ -426,10 +423,10 @@ export class TagEditorComponent extends StatefulControlComponent, touched: boolean) { - this.next({ items }); + this.next({ tags: items }); if (items.length === 0 && this.undefinedWhenEmpty) { this.callChange(undefined); diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts index 8510eb9c8..dc594236a 100644 --- a/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts +++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.stories.ts @@ -23,24 +23,24 @@ const TRANSLATIONS = { `, }) class TestComponent { - public suggestions: string[] = []; - public suggestionsLoading = false; + public itemsSource: string[] = []; + public itemsSourceLoading = false; public load() { - this.suggestions = []; - this.suggestionsLoading = true; + this.itemsSource = []; + this.itemsSourceLoading = true; setTimeout(() => { - this.suggestions = ['A', 'B']; - this.suggestionsLoading = false; + this.itemsSource = ['A', 'B']; + this.itemsSourceLoading = false; }, 1000); } } @@ -80,12 +80,12 @@ const Template: Story = (args: TagEditorC + [styleDashed]="styleDashed"> `, @@ -103,28 +103,28 @@ export const Default = Template.bind({}); export const Suggestions = Template.bind({}); Suggestions.args = { - suggestions: ['A', 'B', 'C'], + itemsSource: ['A', 'B', 'C'], allowOpen: true, }; export const SuggestionsEmpty = Template.bind({}); SuggestionsEmpty.args = { - suggestions: [], + itemsSource: [], allowOpen: true, }; export const SuggestionsLoading = Template.bind({}); SuggestionsLoading.args = { - suggestionsLoading: true, + itemsSourceLoading: true, allowOpen: true, }; export const Values = Template.bind({}); Values.args = { - suggestions: [], + itemsSource: [], ngModel: ['A', 'A', 'B'], }; @@ -159,14 +159,14 @@ StyleBlankValues.args = { export const Multiline = Template.bind({}); Multiline.args = { - singleLine: false, + styleScrollable: false, ngModel: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua'], }; export const SingleLine = Template.bind({}); SingleLine.args = { - singleLine: true, + styleScrollable: true, ngModel: ['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua'], }; diff --git a/frontend/src/app/framework/angular/modals/modal.directive.ts b/frontend/src/app/framework/angular/modals/modal.directive.ts index da8a47f29..1e02ff492 100644 --- a/frontend/src/app/framework/angular/modals/modal.directive.ts +++ b/frontend/src/app/framework/angular/modals/modal.directive.ts @@ -43,6 +43,9 @@ export class ModalDirective implements OnDestroy { @Input('sqxModalCloseAlways') public closeAlways = false; + @Input('sqxModalIsDialog') + public isDialog = false; + constructor( private readonly changeDetector: ChangeDetectorRef, private readonly renderer: Renderer2, @@ -115,7 +118,7 @@ export class ModalDirective implements OnDestroy { } private subscribeToView() { - if (Types.is(this.currentModel, DialogModel)) { + if (Types.is(this.currentModel, DialogModel) || this.isDialog) { return; } diff --git a/frontend/src/app/framework/utils/rxjs-extensions.ts b/frontend/src/app/framework/utils/rxjs-extensions.ts index 05a485c29..35c54d88d 100644 --- a/frontend/src/app/framework/utils/rxjs-extensions.ts +++ b/frontend/src/app/framework/utils/rxjs-extensions.ts @@ -5,9 +5,8 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { EMPTY, Observable, ReplaySubject, throwError } from 'rxjs'; -import { catchError, debounceTime, distinctUntilChanged, filter, map, share, switchMap } from 'rxjs/operators'; -import { onErrorResumeNext } from 'rxjs/operators'; +import { EMPTY, Observable, of, onErrorResumeNextWith, ReplaySubject, throwError } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged, filter, map, share, switchMap, tap } from 'rxjs/operators'; import { DialogService } from './../services/dialog.service'; import { Version, versioned, Versioned } from './version'; @@ -55,9 +54,9 @@ export function shareMapSubscribed(dialogs: DialogService, project: (v export function debounceTimeSafe(duration: number) { return function mapOperation(source: Observable) { if (duration > 0) { - return source.pipe(debounceTime(duration), onErrorResumeNext()); + return source.pipe(debounceTime(duration), onErrorResumeNextWith()); } else { - return source.pipe(onErrorResumeNext()); + return source.pipe(onErrorResumeNextWith()); } }; } @@ -69,22 +68,27 @@ export function defined() { } export function switchSafe(project: (source: T) => Observable) { - return function mapOperation(source: Observable) { - return source.pipe( - switchMap(x => { - try { - return project(x).pipe(catchError(_ => EMPTY)); - } catch { - return EMPTY; - } - })); - }; + return switchMap>(x => { + try { + return project(x).pipe(catchError(_ => EMPTY)); + } catch { + return EMPTY; + } + }); } -export function ofForever(...values: ReadonlyArray) { - return new Observable(s => { - for (const value of values) { - s.next(value); +export function switchMapCached(project: (source: string) => Observable) { + const cache: { [key: string]: R } = {}; + + return switchMap>(x => { + const cached = cache[x]; + + if (cached) { + return of(cached); } + + return project(x).pipe(tap(result => { + cache[x] = result; + })); }); -} +} \ No newline at end of file diff --git a/frontend/src/app/shared/components/assets/asset-dialog.component.html b/frontend/src/app/shared/components/assets/asset-dialog.component.html index 5c1d0901f..59414df3c 100644 --- a/frontend/src/app/shared/components/assets/asset-dialog.component.html +++ b/frontend/src/app/shared/components/assets/asset-dialog.component.html @@ -77,7 +77,35 @@
- + + +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+ +
+ +
+ +
+
+
@@ -125,12 +153,12 @@
-
+
- +
diff --git a/frontend/src/app/shared/components/assets/asset-dialog.component.scss b/frontend/src/app/shared/components/assets/asset-dialog.component.scss index 850aeb393..797bfbb4f 100644 --- a/frontend/src/app/shared/components/assets/asset-dialog.component.scss +++ b/frontend/src/app/shared/components/assets/asset-dialog.component.scss @@ -41,10 +41,6 @@ } } -.path { - min-height: 2.5rem; -} - .slug { padding-right: 6rem; } diff --git a/frontend/src/app/shared/components/assets/asset-dialog.component.ts b/frontend/src/app/shared/components/assets/asset-dialog.component.ts index fd4eb9858..7fe5f367a 100644 --- a/frontend/src/app/shared/components/assets/asset-dialog.component.ts +++ b/frontend/src/app/shared/components/assets/asset-dialog.component.ts @@ -5,33 +5,33 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, QueryList, ViewChildren } from '@angular/core'; -import { Observable } from 'rxjs'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AnnotateAssetDto, AnnotateAssetForm, AppsState, AssetDto, AssetsState, AssetUploaderState, AuthService, DialogService, Types, UploadCanceled } from '@app/shared/internal'; -import { AssetsService } from '@app/shared/services/assets.service'; +import { AnnotateAssetDto, AnnotateAssetForm, AppsState, AssetDto, AssetsState, AssetUploaderState, AuthService, DialogService, MoveAssetForm, switchMapCached, Types, UploadCanceled } from '@app/shared/internal'; +import { AssetsService, MoveAssetItemDto } from '@app/shared/services/assets.service'; import { AssetPathItem, ROOT_ITEM } from '@app/shared/state/assets.state'; import { AssetTextEditorComponent } from './asset-text-editor.component'; import { ImageCropperComponent } from './image-cropper.component'; import { ImageFocusPointComponent } from './image-focus-point.component'; @Component({ - selector: 'sqx-asset-dialog[allTags][asset]', + selector: 'sqx-asset-dialog[asset]', styleUrls: ['./asset-dialog.component.scss'], templateUrl: './asset-dialog.component.html', }) -export class AssetDialogComponent implements OnChanges { +export class AssetDialogComponent implements OnInit { @Output() public complete = new EventEmitter(); @Output() - public changed = new EventEmitter(); + public assetReplaced = new EventEmitter(); - @Input() - public asset!: AssetDto; + @Output() + public assetUpdated = new EventEmitter(); @Input() - public allTags!: ReadonlyArray; + public asset!: AssetDto; @ViewChildren(ImageCropperComponent) public imageCropper!: QueryList; @@ -42,15 +42,21 @@ export class AssetDialogComponent implements OnChanges { @ViewChildren(AssetTextEditorComponent) public textEditor!: QueryList; - public path!: Observable>; + public pathSource = new BehaviorSubject(''); + public pathItems!: Observable>; + + public progress = 0; public selectedTab = 0; public isEditable = false; public isEditableAny = false; public isUploadable = false; + public isMoving = false; + public isMoveable = false; - public progress = 0; + public moveForm = new MoveAssetForm(); + public annotateTags!: Observable; public annotateForm = new AnnotateAssetForm(); public get isImage() { @@ -76,32 +82,52 @@ export class AssetDialogComponent implements OnChanges { ) { } - public ngOnChanges() { + public ngOnInit() { + this.annotateTags = + this.assetsService.getTags(this.appsState.appName).pipe( + map(tags => Object.keys(tags))); + + this.pathItems = + this.pathSource.pipe( + switchMapCached(x => this.assetsService.getAssetFolders(this.appsState.appName, x, 'Path')), map(({ path }) => [ROOT_ITEM, ...path])); + this.selectTab(0); - this.isEditable = this.asset.canUpdate; - this.isUploadable = this.asset.canUpload; + this.assetchanged(this.asset); + } + + private assetchanged(asset: AssetDto) { + this.pathSource.next(asset.parentId); + + this.isEditable = asset.canUpdate; + this.isUploadable = asset.canUpload; + this.isMoveable = asset.canMove; - this.annotateForm.load(this.asset); + this.annotateForm.load(asset); this.annotateForm.setEnabled(this.isEditable); - this.path = - this.assetsService.getAssetFolders(this.appsState.appName, this.asset.parentId, 'Path').pipe( - map(folders => [ROOT_ITEM, ...folders.path])); - } + this.moveForm.load(asset); + this.moveForm.setEnabled(this.isMoveable); - public navigate(id: string) { - this.assetsState.navigate(id); + this.asset = asset; } public selectTab(tab: number) { this.selectedTab = tab; } + public navigate(id: string) { + this.assetsState.navigate(id); + } + public generateSlug() { this.annotateForm.generateSlug(this.asset); } + public startMoving() { + this.isMoving = true; + } + public emitComplete() { this.complete.emit(); } @@ -133,7 +159,8 @@ export class AssetDialogComponent implements OnChanges { if (Types.isNumber(dto)) { this.setProgress(dto); } else { - this.changed.emit(dto); + this.assetReplaced.emit(dto); + this.assetchanged(dto); this.dialogs.notifyInfo('i18n:assets.updated'); } @@ -144,7 +171,7 @@ export class AssetDialogComponent implements OnChanges { } }, complete: () => { - this.setProgress(0); + this.setProgress(0); }, }); } else { @@ -158,7 +185,7 @@ export class AssetDialogComponent implements OnChanges { return; } - this.annoateAssetInternal(this.imageFocus.first.submit(this.asset)); + this.annotateInternal(this.imageFocus.first.submit(this.asset)); } public annotateAsset() { @@ -166,25 +193,59 @@ export class AssetDialogComponent implements OnChanges { return; } - this.annoateAssetInternal(this.annotateForm.submit(this.asset)); + this.annotateInternal(this.annotateForm.submit(this.asset)); } - private annoateAssetInternal(value: AnnotateAssetDto | null) { - if (value) { - this.assetsState.updateAsset(this.asset, value) - .subscribe({ - next: () => { - this.annotateForm.submitCompleted({ noReset: true }); + public moveAsset() { + if (!this.isMoveable) { + return; + } - this.dialogs.notifyInfo('i18n:assets.updated'); - }, - error: error => { - this.annotateForm.submitFailed(error); - }, - }); - } else { + this.moveInternal(this.moveForm.submit()); + } + + private annotateInternal(value: AnnotateAssetDto | null) { + if (!value) { this.dialogs.notifyInfo('i18n:common.nothingChanged'); + return; + } + + this.assetsState.updateAsset(this.asset, value) + .subscribe({ + next: dto => { + this.assetUpdated.emit(dto); + this.assetchanged(dto); + + this.annotateForm.submitCompleted({ noReset: true }); + + this.dialogs.notifyInfo('i18n:assets.updated'); + }, + error: error => { + this.annotateForm.submitFailed(error); + }, + }); + } + + private moveInternal(values: MoveAssetItemDto | null) { + if (!values) { + this.isMoving = false; + return; } + + this.assetsState.moveAsset(this.asset, values.parentId) + .subscribe({ + next: (dto) => { + this.assetUpdated.emit(dto); + this.assetchanged(dto); + + this.annotateForm.submitCompleted({ noReset: true }); + + this.dialogs.notifyInfo('i18n:assets.moved'); + }, + complete: () => { + this.isMoving = false; + }, + }); } public setProgress(progress: number) { diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html index 78c36c422..501beee13 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.html @@ -1,26 +1,25 @@ -
- - +
+ -
- {{node.item.folderName | sqxTranslate}} + {{nodeModel.item.folderName | sqxTranslate}}
-
- + diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.scss b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.scss index 90b8469b8..60634049f 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.scss +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.scss @@ -1,6 +1,10 @@ @import 'mixins'; @import 'vars'; +.name { + cursor: default; +} + .loader { font-size: 60%; flex-grow: 0; diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts index 29d7a0e11..122bbaeb6 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown-item.component.ts @@ -5,12 +5,12 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; import { AssetsService } from '@app/shared/internal'; import { AssetFolderDropdowNode } from './asset-folder-dropdown.state'; @Component({ - selector: 'sqx-asset-folder-dropdown-item[appName][node]', + selector: 'sqx-asset-folder-dropdown-item[appName][nodeModel]', styleUrls: ['./asset-folder-dropdown-item.component.scss'], templateUrl: './asset-folder-dropdown-item.component.html', }) @@ -19,7 +19,7 @@ export class AssetFolderDropdownItemComponent { public appName!: string; @Input() - public node!: AssetFolderDropdowNode; + public nodeModel!: AssetFolderDropdowNode; @Input() public nodeLevel = 0; @@ -33,11 +33,12 @@ export class AssetFolderDropdownItemComponent { constructor( private readonly assetsService: AssetsService, + private readonly changeDetector: ChangeDetectorRef, ) { } public toggle() { - if (this.node.isExpanded && this.node.isLoaded) { + if (this.nodeModel.isExpanded && this.nodeModel.isLoaded) { this.collapse(); } else { this.expand(); @@ -45,27 +46,27 @@ export class AssetFolderDropdownItemComponent { } public collapse() { - this.node.isExpanded = false; + this.nodeModel.isExpanded = false; } public expand() { - this.node.isExpanded = true; + this.nodeModel.isExpanded = true; this.loadChildren(); } public loadChildren() { - if (this.node.isLoading || this.node.isLoaded) { + if (this.nodeModel.isLoading || this.nodeModel.isLoaded) { return; } - this.node.isLoading = true; + this.nodeModel.isLoading = true; - this.assetsService.getAssetFolders(this.appName, this.node.item.id, 'Items') + this.assetsService.getAssetFolders(this.appName, this.nodeModel.item.id, 'Items') .subscribe({ next: dto => { if (dto.items.length > 0) { - const parent = this.node; + const parent = this.nodeModel; for (const item of dto.items) { if (!parent.children.find(x => x.item.id === item.id)) { @@ -76,11 +77,13 @@ export class AssetFolderDropdownItemComponent { parent.children.sortByString(x => x.item.folderName); } - this.node.isLoaded = true; + this.nodeModel.isLoaded = true; + this.changeDetector.detectChanges(); }, complete: () => { setTimeout(() => { - this.node.isLoading = false; + this.nodeModel.isLoading = false; + this.changeDetector.detectChanges(); }, 250); }, }); diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.html b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.html index 307dcb6e3..b09ef2145 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.html +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.html @@ -14,7 +14,7 @@
diff --git a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts index 61994cd63..d255c001a 100644 --- a/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder-dropdown.component.ts @@ -113,6 +113,8 @@ export class AssetFolderDropdownComponent extends StatefulControlComponent
- - - + diff --git a/frontend/src/app/shared/components/assets/asset-folder.component.ts b/frontend/src/app/shared/components/assets/asset-folder.component.ts index 7fa1f08b0..ce784acbc 100644 --- a/frontend/src/app/shared/components/assets/asset-folder.component.ts +++ b/frontend/src/app/shared/components/assets/asset-folder.component.ts @@ -22,10 +22,12 @@ export class AssetFolderComponent { public delete = new EventEmitter(); @Input() - public assetPathItem!: AssetPathItem; + public isDisabled?: boolean | null; - public dropdown = new ModalModel(); + @Input() + public assetPathItem!: AssetPathItem; + public editDropdown = new ModalModel(); public editDialog = new DialogModel(); public get assetFolder(): AssetFolderDto { @@ -47,10 +49,18 @@ export class AssetFolderComponent { } public emitDelete() { + if (this.isDisabled) { + return; + } + this.delete.emit(this.assetFolder); } public emitNavigate() { + if (this.isDisabled) { + return; + } + this.navigate.emit(this.assetPathItem); } } diff --git a/frontend/src/app/shared/components/assets/asset-selector.component.html b/frontend/src/app/shared/components/assets/asset-selector.component.html index 4c6c71620..56933247e 100644 --- a/frontend/src/app/shared/components/assets/asset-selector.component.html +++ b/frontend/src/app/shared/components/assets/asset-selector.component.html @@ -1,4 +1,4 @@ - + {{ 'assets.selectMany' | sqxTranslate }} @@ -13,10 +13,11 @@