From 95316690519d63d1fc627d5a173f26f839fd6cec Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 26 Feb 2024 20:18:53 +0100 Subject: [PATCH 001/285] add section user permission condition --- .../src/packages/core/manifests.ts | 2 + .../section-user-permission.condition.ts | 41 +++++++++++++++++++ .../src/packages/core/section/manifests.ts | 11 +++++ 3 files changed, 54 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts index a72acdc198..460ad72c22 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts @@ -7,6 +7,7 @@ import { manifests as localizationManifests } from './localization/manifests.js' import { manifests as modalManifests } from './modal/common/manifests.js'; import { manifests as propertyActionManifests } from './property-action/manifests.js'; import { manifests as propertyEditorManifests } from './property-editor/manifests.js'; +import { manifests as sectionManifests } from './section/manifests.js'; import { manifests as settingsManifests } from './settings/manifests.js'; import { manifests as themeManifests } from './themes/manifests.js'; import { manifests as treeManifests } from './tree/manifests.js'; @@ -24,6 +25,7 @@ export const manifests: Array = [ ...modalManifests, ...propertyActionManifests, ...propertyEditorManifests, + ...sectionManifests, ...settingsManifests, ...themeManifests, ...treeManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts new file mode 100644 index 0000000000..abcade2eea --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts @@ -0,0 +1,41 @@ +import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; +import { UmbBaseController } from '@umbraco-cms/backoffice/class-api'; +import type { + UmbConditionConfigBase, + UmbConditionControllerArguments, + UmbExtensionCondition, +} from '@umbraco-cms/backoffice/extension-api'; + +export class UmbSectionUserPermissionCondition extends UmbBaseController implements UmbExtensionCondition { + config: UmbSectionUserPermissionConditionConfig; + permitted = false; + #onChange: () => void; + + constructor(args: UmbConditionControllerArguments) { + super(args.host); + this.config = args.config; + this.#onChange = args.onChange; + + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (context) => { + this.observe( + context.currentUser, + (currentUser) => { + const sectionAccess = currentUser?.sections || []; + this.permitted = sectionAccess.includes(this.config.match); + this.#onChange(); + }, + 'umbSectionUserPermissionConditionObserver', + ); + }); + } +} + +export type UmbSectionUserPermissionConditionConfig = UmbConditionConfigBase<'Umb.Condition.SectionUserPermission'> & { + /** + * + * + * @example + * "Umb.Section.Content" + */ + match: string; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts new file mode 100644 index 0000000000..7850364753 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts @@ -0,0 +1,11 @@ +import { UmbSectionUserPermissionCondition } from './conditions/section-user-permission.condition.js'; +import type { ManifestCondition } from '@umbraco-cms/backoffice/extension-api'; + +export const manifests: Array = [ + { + type: 'condition', + name: 'Section User Permission Condition', + alias: 'Umb.Condition.SectionUserPermission', + api: UmbSectionUserPermissionCondition, + }, +]; From de57cb73eae7ec9f7c80cea82371101151e0122b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 26 Feb 2024 20:19:00 +0100 Subject: [PATCH 002/285] Create index.ts --- .../src/packages/core/section/conditions/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/index.ts new file mode 100644 index 0000000000..3256287dd5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/index.ts @@ -0,0 +1 @@ +export * from './section-user-permission.condition.js'; From 8fd2e69cb4e82875fb202fd87e191685dec8f363 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 26 Feb 2024 20:19:25 +0100 Subject: [PATCH 003/285] add sections to current user model --- .../current-user/repository/current-user.server.data-source.ts | 1 + .../src/packages/user/current-user/types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 5038b01a3e..c4cfedf4bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -41,6 +41,7 @@ export class UmbCurrentUserServerDataSource { languages: data.languages, hasAccessToAllLanguages: data.hasAccessToAllLanguages, permissions: data.permissions, + sections: data.sections, }; return { data: user }; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts index 21a3296d5a..79ba8bb335 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/types.ts @@ -10,4 +10,5 @@ export interface UmbCurrentUserModel { languages: Array; hasAccessToAllLanguages: boolean; permissions: Array; + sections: Array; } From 3d072e072d9aefd498ce6be4908fb1091d5071ab Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 26 Feb 2024 20:20:01 +0100 Subject: [PATCH 004/285] add section condition to all sections --- .../src/packages/core/settings/manifests.ts | 6 ++++++ .../src/packages/dictionary/section/manifests.ts | 6 ++++++ .../src/packages/documents/section.manifests.ts | 8 +++++++- .../src/packages/media/section.manifests.ts | 9 +++++++-- .../src/packages/members/member-section/manifests.ts | 6 ++++++ .../src/packages/packages/package-section/manifests.ts | 6 ++++++ .../src/packages/user/user-section/manifests.ts | 6 ++++++ 7 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts index 7ba20a23dd..0f84871955 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts @@ -12,6 +12,12 @@ export const manifests = [ label: 'Settings', pathname: 'settings', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: UMB_SETTINGS_SECTION_ALIAS, + }, + ], }, { type: 'menu', diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts index 2189235fb3..2f89aa04c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts @@ -13,6 +13,12 @@ const section: ManifestSection = { label: 'Dictionary', pathname: 'dictionary', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: UMB_DICTIONARY_SECTION_ALIAS, + }, + ], }; const menuSectionSidebarApp: ManifestSectionSidebarApp = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts index 9f2c28a7e9..6d73c5fcab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts @@ -8,13 +8,19 @@ const sectionAlias = 'Umb.Section.Content'; const section: ManifestSection = { type: 'section', - alias: sectionAlias, + alias: 'Umb.Section.Content', name: 'Content Section', weight: 600, meta: { label: 'Content', pathname: 'content', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: sectionAlias, + }, + ], }; const menuSectionSidebarApp: ManifestSectionSidebarAppMenuWithEntityActionsKind = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts index acc9e2cd6a..cc2984ff1e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts @@ -5,14 +5,19 @@ const sectionAlias = 'Umb.Section.Media'; const section: ManifestSection = { type: 'section', - alias: sectionAlias, + alias: 'Umb.Section.Media', name: 'Media Section', weight: 500, meta: { label: 'Media', pathname: 'media-management', }, - conditions: [], + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: sectionAlias, + }, + ], }; const menuSectionSidebarApp: ManifestSectionSidebarApp = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-section/manifests.ts index 6db6a7edfa..9b9ffe225e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-section/manifests.ts @@ -9,6 +9,12 @@ const section: ManifestSection = { label: 'Members', pathname: 'member-management', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: 'Umb.Section.Members', + }, + ], }; export const manifests = [section]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts index 24a35bcf07..7b59347c0c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/packages/package-section/manifests.ts @@ -11,6 +11,12 @@ const section: ManifestSection = { label: 'Packages', pathname: 'packages', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: sectionAlias, + }, + ], }; const sectionsViews: Array = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts index 81fd811d65..9adfdafdd1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts @@ -11,6 +11,12 @@ const section: ManifestSection = { label: 'Users', pathname: 'user-management', }, + conditions: [ + { + alias: 'Umb.Condition.SectionUserPermission', + match: UMB_USER_MANAGEMENT_SECTION_ALIAS, + }, + ], }; export const manifests = [section]; From 5ff24a38c58b7c8b33f67ae375a88f37e4d00735 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 28 Feb 2024 22:41:52 +0100 Subject: [PATCH 005/285] update current user model --- .../section/conditions/section-user-permission.condition.ts | 2 +- .../current-user/repository/current-user.server.data-source.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts index abcade2eea..e7ef8e68a9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts @@ -20,7 +20,7 @@ export class UmbSectionUserPermissionCondition extends UmbBaseController impleme this.observe( context.currentUser, (currentUser) => { - const sectionAccess = currentUser?.sections || []; + const sectionAccess = currentUser?.allowedSections || []; this.permitted = sectionAccess.includes(this.config.match); this.#onChange(); }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts index 43e6297345..d0eaf67d99 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.server.data-source.ts @@ -42,7 +42,7 @@ export class UmbCurrentUserServerDataSource { hasAccessToAllLanguages: data.hasAccessToAllLanguages, fallbackPermissions: data.fallbackPermissions, permissions: data.permissions, - sections: data.sections, + allowedSections: data.allowedSections, }; return { data: user }; } From be6e6c09e805dba645fa5cda3b1c19e0ace891d7 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 29 Feb 2024 09:34:10 +0100 Subject: [PATCH 006/285] wire up types condition types correctly --- .../src/packages/core/extension-registry/conditions/types.ts | 2 ++ .../packages/core/extension-registry/models/section.model.ts | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts index 8b4e8db7fa..f6747e7ed9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/conditions/types.ts @@ -1,5 +1,6 @@ import type { CollectionAliasConditionConfig } from '../../collection/collection-alias.manifest.js'; import type { CollectionBulkActionPermissionConditionConfig } from '../../collection/collection-bulk-action-permission.manifest.js'; +import type { UmbSectionUserPermissionConditionConfig } from '../../section/conditions/index.js'; import type { SectionAliasConditionConfig } from './section-alias.condition.js'; import type { SwitchConditionConfig } from './switch.condition.js'; import type { UserPermissionConditionConfig } from '@umbraco-cms/backoffice/user-permission'; @@ -23,4 +24,5 @@ export type ConditionTypes = | WorkspaceEntityTypeConditionConfig | SwitchConditionConfig | UserPermissionConditionConfig + | UmbSectionUserPermissionConditionConfig | UmbConditionConfigBase; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/section.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/section.model.ts index a1a66a5269..95b6661c05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/section.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/section.model.ts @@ -1,7 +1,10 @@ +import type { ConditionTypes } from '../conditions/types.js'; import type { UmbSectionElement } from '../interfaces/index.js'; import type { ManifestElement, ManifestWithDynamicConditions } from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestSection extends ManifestElement, ManifestWithDynamicConditions { +export interface ManifestSection + extends ManifestElement, + ManifestWithDynamicConditions { type: 'section'; meta: MetaSection; } From da8529a827bfaf9a7e4b1a437789eff95ac8114b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 29 Feb 2024 09:34:50 +0100 Subject: [PATCH 007/285] update aliases to match server --- .../src/packages/dictionary/section/manifests.ts | 2 +- .../src/packages/documents/section.manifests.ts | 2 +- .../src/packages/user/user-section/manifests.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts index 2f89aa04c1..9baccac979 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/section/manifests.ts @@ -2,7 +2,7 @@ import { UMB_DICTIONARY_ROOT_ENTITY_TYPE } from '../entity.js'; import { UMB_DICTIONARY_MENU_ALIAS } from '../menu/manifests.js'; import type { ManifestSection, ManifestSectionSidebarApp } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_DICTIONARY_SECTION_ALIAS = 'Umb.Section.Dictionary'; +export const UMB_DICTIONARY_SECTION_ALIAS = 'Umb.Section.Translation'; const section: ManifestSection = { type: 'section', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts index 6d73c5fcab..81a84ad864 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/section.manifests.ts @@ -8,7 +8,7 @@ const sectionAlias = 'Umb.Section.Content'; const section: ManifestSection = { type: 'section', - alias: 'Umb.Section.Content', + alias: sectionAlias, name: 'Content Section', weight: 600, meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts index 9adfdafdd1..72f444e0b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts @@ -1,6 +1,6 @@ import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_USER_MANAGEMENT_SECTION_ALIAS = 'Umb.Section.UserManagement'; +export const UMB_USER_MANAGEMENT_SECTION_ALIAS = 'Umb.Section.Users'; const section: ManifestSection = { type: 'section', From fd90d22a9a924a713281dd3b55f67dc30df0a20c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 29 Feb 2024 09:35:03 +0100 Subject: [PATCH 008/285] use allowed sections field --- .../section/conditions/section-user-permission.condition.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts index e7ef8e68a9..330e47bae0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts @@ -20,8 +20,8 @@ export class UmbSectionUserPermissionCondition extends UmbBaseController impleme this.observe( context.currentUser, (currentUser) => { - const sectionAccess = currentUser?.allowedSections || []; - this.permitted = sectionAccess.includes(this.config.match); + const allowedSections = currentUser?.allowedSections || []; + this.permitted = allowedSections.includes(this.config.match); this.#onChange(); }, 'umbSectionUserPermissionConditionObserver', From 7392762d8f1795dd94e05434afe96ba9c0472fbb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:57:25 +0100 Subject: [PATCH 009/285] create a new function to enable to assign to a frozen object --- .../observable-api/utils/assign-to-frozen-object.function.ts | 3 +++ .../src/libs/observable-api/utils/index.ts | 1 + 2 files changed, 4 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/assign-to-frozen-object.function.ts diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/assign-to-frozen-object.function.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/assign-to-frozen-object.function.ts new file mode 100644 index 0000000000..f0f2665bbb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/assign-to-frozen-object.function.ts @@ -0,0 +1,3 @@ +export function assignToFrozenObject(target: T, source: Partial): T { + return Object.assign(Object.create(Object.getPrototypeOf(target)), target, source); +} diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts index 0c77207194..f207030636 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts @@ -1,4 +1,5 @@ export * from './append-to-frozen-array.function.js'; +export * from './assign-to-frozen-object.function.js'; export * from './create-observable-part.function.js'; export * from './deep-freeze.function.js'; export * from './default-memoization.function.js'; From 6914a7c938d74de726c1ebf9ec4bbe6304c96cdc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:57:52 +0100 Subject: [PATCH 010/285] organize imports and ensure the image cropper can assign to a frozen object --- .../input-image-cropper.element.ts | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts index f4bc06e0a8..f87d1663d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts @@ -1,14 +1,17 @@ import type { UmbImageCropperPropertyEditorValue } from './types.js'; +import type { UmbInputImageCropperFieldElement } from './image-cropper-field.element.js'; import { html, customElement, property, query, state } from '@umbraco-cms/backoffice/external/lit'; -import './image-cropper.element.js'; -import './image-cropper-focus-setter.element.js'; -import './image-cropper-preview.element.js'; -import './image-cropper-field.element.js'; import type { UUIFileDropzoneElement, UUIFileDropzoneEvent } from '@umbraco-cms/backoffice/external/uui'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { type TemporaryFileQueueItem, UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; +import { assignToFrozenObject } from '@umbraco-cms/backoffice/observable-api'; + +import './image-cropper.element.js'; +import './image-cropper-focus-setter.element.js'; +import './image-cropper-preview.element.js'; +import './image-cropper-field.element.js'; @customElement('umb-input-image-cropper') export class UmbInputImageCropperElement extends UmbLitElement { @@ -53,7 +56,8 @@ export class UmbInputImageCropperElement extends UmbLitElement { this.file = file; this.fileUnique = unique; - this.value.src = unique; + + assignToFrozenObject(this.value, { src: unique }); this.#manager?.uploadOne(unique, file, 'waiting'); @@ -66,7 +70,7 @@ export class UmbInputImageCropperElement extends UmbLitElement { } #onRemove = () => { - this.value = { ...this.value, src: '' }; + assignToFrozenObject(this.value, { src: '' }); if (!this.fileUnique) return; this.#manager?.removeOne(this.fileUnique); this.fileUnique = undefined; @@ -91,9 +95,16 @@ export class UmbInputImageCropperElement extends UmbLitElement { `; } - #onChange(e: any) { - this.value = e.target.value; + #onChange(e: CustomEvent) { + const value = (e.target as UmbInputImageCropperFieldElement).value; + if (!value) { + this.value = { src: '', crops: [], focalPoint: { left: 0.5, top: 0.5 } }; + this.dispatchEvent(new UmbChangeEvent()); + return; + } + + this.value = value; this.dispatchEvent(new UmbChangeEvent()); } From 1dc64cd8ae6d2ed9742b096340c139bc4882c191 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 6 Mar 2024 13:37:08 +0100 Subject: [PATCH 011/285] Update section-user-permission.condition.ts --- .../section/conditions/section-user-permission.condition.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts index 330e47bae0..75e490fdf7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts @@ -1,12 +1,12 @@ import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; -import { UmbBaseController } from '@umbraco-cms/backoffice/class-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbConditionConfigBase, UmbConditionControllerArguments, UmbExtensionCondition, } from '@umbraco-cms/backoffice/extension-api'; -export class UmbSectionUserPermissionCondition extends UmbBaseController implements UmbExtensionCondition { +export class UmbSectionUserPermissionCondition extends UmbControllerBase implements UmbExtensionCondition { config: UmbSectionUserPermissionConditionConfig; permitted = false; #onChange: () => void; From 5f2a096198069db4b4b6c3f060828f5d17d42744 Mon Sep 17 00:00:00 2001 From: JesmoDev Date: Wed, 6 Mar 2024 14:45:51 +0100 Subject: [PATCH 012/285] fix picker --- .../input-media/input-media.context.ts | 3 +- .../input-media/input-media.element.ts | 2 +- ...property-editor-ui-media-picker.element.ts | 34 ++++++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts index 44e59a35cf..4d8ba6ece6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.context.ts @@ -1,4 +1,5 @@ import type { UmbMediaItemModel } from '../../repository/item/types.js'; +import { UMB_MEDIA_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js'; import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_MEDIA_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; @@ -7,6 +8,6 @@ export class UmbMediaPickerContext extends UmbPickerInputContext = { getUniqueOfElement: (element) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts index 2443efbfa6..26d22b9a6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts @@ -1,10 +1,8 @@ import type { UmbInputMediaElement } from '../../components/input-media/input-media.element.js'; import '../../components/input-media/input-media.element.js'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { - UmbPropertyValueChangeEvent, - type UmbPropertyEditorConfigCollection, -} from '@umbraco-cms/backoffice/property-editor'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -14,7 +12,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @customElement('umb-property-editor-ui-media-picker') export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property() - public value?: string; + public value: string = ''; @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { @@ -30,21 +28,41 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement impleme return undefined; } + @state() + _items: Array = []; + @state() private _limitMin: number = 0; @state() private _limitMax: number = Infinity; + protected updated(_changedProperties: PropertyValueMap | Map): void { + super.updated(_changedProperties); + if (_changedProperties.has('value')) { + if (typeof this.value !== 'string') { + //TODO: Temp fix for when the value is an array, this should be fixed elsewhere. + this.value = ''; + } + this._items = this.value ? this.value.split(',') : []; + } + } + private _onChange(event: CustomEvent) { - this.value = (event.target as UmbInputMediaElement).selectedIds.join(','); - this.dispatchEvent(new UmbPropertyValueChangeEvent()); + const selectedIds = (event.target as UmbInputMediaElement).selectedIds; + //TODO: This is a hack, something changed so now we need to convert the array to a comma separated string to make it work with the server. + const toCommaSeparatedString = selectedIds.join(','); + // this.value = (event.target as UmbInputMediaElement).selectedIds; + + this.value = toCommaSeparatedString; + console.log('property-value-change', this.value); + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { return html` Add From 24520ce0011937e4b361a889c0ea29ec2059a24d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 6 Mar 2024 15:37:25 +0100 Subject: [PATCH 013/285] remove rename of const --- .../src/packages/data-type/repository/move/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/move/index.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/move/index.ts index 01792b911d..dfc6dcb884 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/move/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/move/index.ts @@ -1,2 +1,2 @@ export { UmbMoveDataTypeRepository } from './data-type-move.repository.js'; -export { UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS as MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js'; +export { UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './manifests.js'; From bb21c7bd767f24b824e3033502e17379a77ee34b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 6 Mar 2024 15:44:03 +0100 Subject: [PATCH 014/285] Update index.ts --- .../src/packages/data-type/repository/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts index dc76f38eaf..56c390dd50 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/index.ts @@ -5,6 +5,6 @@ export { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT, } from './detail/index.js'; export { UmbDataTypeItemRepository, UMB_DATA_TYPE_ITEM_REPOSITORY_ALIAS } from './item/index.js'; -export { UmbMoveDataTypeRepository, MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './move/index.js'; +export { UmbMoveDataTypeRepository, UMB_MOVE_DATA_TYPE_REPOSITORY_ALIAS } from './move/index.js'; export type { UmbDataTypeItemModel } from './item/index.js'; From d90e498c7c27ab806a199c01d97d075168c89f41 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:27:24 +0000 Subject: [PATCH 015/285] bump version to beta002 --- src/Umbraco.Web.UI.Client/package-lock.json | 4 ++-- src/Umbraco.Web.UI.Client/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index c3ae26382c..f16c285049 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1,12 +1,12 @@ { "name": "@umbraco-cms/backoffice", - "version": "14.0.0-beta001", + "version": "14.0.0-beta002", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@umbraco-cms/backoffice", - "version": "14.0.0-beta001", + "version": "14.0.0-beta002", "license": "MIT", "dependencies": { "@openid/appauth": "^1.3.1", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 52dbc03297..83a2ff5f43 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -1,7 +1,7 @@ { "name": "@umbraco-cms/backoffice", "license": "MIT", - "version": "14.0.0-beta001", + "version": "14.0.0-beta002", "type": "module", "exports": { ".": null, From 62ccab0309af138acc32458e9d5719175d93fbb0 Mon Sep 17 00:00:00 2001 From: JesmoDev Date: Wed, 6 Mar 2024 18:12:56 +0100 Subject: [PATCH 016/285] use correct value model --- ...property-editor-ui-media-picker.element.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts index 26d22b9a6e..4edb989e9f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts @@ -5,14 +5,15 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/ex import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbId } from '@umbraco-cms/backoffice/id'; /** * @element umb-property-editor-ui-media-picker */ @customElement('umb-property-editor-ui-media-picker') export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - @property() - public value: string = ''; + @property({ attribute: false }) + public value: Array<{ key: string; mediaKey: string; mediaTypeAlias: string }> = []; @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { @@ -39,22 +40,22 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement impleme protected updated(_changedProperties: PropertyValueMap | Map): void { super.updated(_changedProperties); if (_changedProperties.has('value')) { - if (typeof this.value !== 'string') { - //TODO: Temp fix for when the value is an array, this should be fixed elsewhere. - this.value = ''; - } - this._items = this.value ? this.value.split(',') : []; + this._items = this.value ? this.value.map((x) => x.mediaKey) : []; } } private _onChange(event: CustomEvent) { const selectedIds = (event.target as UmbInputMediaElement).selectedIds; - //TODO: This is a hack, something changed so now we need to convert the array to a comma separated string to make it work with the server. - const toCommaSeparatedString = selectedIds.join(','); - // this.value = (event.target as UmbInputMediaElement).selectedIds; - this.value = toCommaSeparatedString; - console.log('property-value-change', this.value); + const result = selectedIds.map((mediaKey) => { + return { + key: UmbId.new(), + mediaKey, + mediaTypeAlias: 'Image', + }; + }); + + this.value = result; this.dispatchEvent(new CustomEvent('property-value-change')); } From a7bcc7949edcd7a679844441f7ed407b35486c80 Mon Sep 17 00:00:00 2001 From: JesmoDev Date: Wed, 6 Mar 2024 18:38:43 +0100 Subject: [PATCH 017/285] you can now remove media items --- .../property-editor-ui-media-picker.element.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts index 4edb989e9f..1636c5fe24 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts @@ -14,6 +14,8 @@ import { UmbId } from '@umbraco-cms/backoffice/id'; export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) public value: Array<{ key: string; mediaKey: string; mediaTypeAlias: string }> = []; + //TODO: Add support for document specific crops. The server side already supports this. + //TODO: Add crops and focalpoint to value. @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { @@ -37,11 +39,10 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement impleme @state() private _limitMax: number = Infinity; - protected updated(_changedProperties: PropertyValueMap | Map): void { - super.updated(_changedProperties); - if (_changedProperties.has('value')) { - this._items = this.value ? this.value.map((x) => x.mediaKey) : []; - } + protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { + super.firstUpdated(_changedProperties); + + this._items = this.value ? this.value.map((x) => x.mediaKey) : []; } private _onChange(event: CustomEvent) { @@ -56,6 +57,7 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement impleme }); this.value = result; + this._items = this.value ? this.value.map((x) => x.mediaKey) : []; this.dispatchEvent(new CustomEvent('property-value-change')); } From 1c769a4e1294f7cbf3b8b92a7fa692f4aa2c1351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:21:31 +0100 Subject: [PATCH 018/285] move to repository folder --- .../src/packages/core/content-type/index.ts | 4 ++-- .../content-type-structure-data-source.interface.ts | 0 .../content-type-structure-repository-base.ts | 0 .../content-type-structure-repository.interface.ts | 0 .../content-type-structure-server-data-source-base.ts | 0 .../core/content-type/{structure => repository}/index.ts | 0 6 files changed, 2 insertions(+), 2 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{structure => repository}/content-type-structure-data-source.interface.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{structure => repository}/content-type-structure-repository-base.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{structure => repository}/content-type-structure-repository.interface.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{structure => repository}/content-type-structure-server-data-source-base.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{structure => repository}/index.ts (100%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts index f2eddad99c..944a60f9d3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts @@ -1,6 +1,6 @@ +export * from './components/index.js'; export * from './content-type-container-structure-helper.class.js'; export * from './content-type-property-structure-helper.class.js'; export * from './content-type-structure-manager.class.js'; +export * from './repository/index.js'; export * from './types.js'; -export * from './components/index.js'; -export * from './structure/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-data-source.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-data-source.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-data-source.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-data-source.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository-base.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-repository-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository-base.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-repository.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-server-data-source-base.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-server-data-source-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-server-data-source-base.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts From 03b534a5e1d74e1fb1de26782c7adf870e1a09a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:25:59 +0100 Subject: [PATCH 019/285] move helpers into structure folder --- .../src/packages/core/content-type/index.ts | 4 +--- .../content-type-container-structure-helper.class.ts | 2 +- .../content-type-property-structure-helper.class.ts | 2 +- .../{ => structure}/content-type-structure-manager.class.ts | 2 +- .../src/packages/core/content-type/structure/index.ts | 3 +++ 5 files changed, 7 insertions(+), 6 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{ => structure}/content-type-container-structure-helper.class.ts (99%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{ => structure}/content-type-property-structure-helper.class.ts (99%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/{ => structure}/content-type-structure-manager.class.ts (99%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts index 944a60f9d3..d33d503388 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts @@ -1,6 +1,4 @@ export * from './components/index.js'; -export * from './content-type-container-structure-helper.class.js'; -export * from './content-type-property-structure-helper.class.js'; -export * from './content-type-structure-manager.class.js'; export * from './repository/index.js'; +export * from './structure/index.js'; export * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts similarity index 99% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-container-structure-helper.class.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index b6ddbd5ac1..22553ca391 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -1,5 +1,5 @@ +import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '../types.js'; import type { UmbContentTypePropertyStructureManager } from './content-type-structure-manager.class.js'; -import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from './types.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbBooleanState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts similarity index 99% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-property-structure-helper.class.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 4f5f31441f..4760ba1831 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -1,5 +1,5 @@ +import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeModel } from '../types.js'; import type { UmbContentTypePropertyStructureManager } from './content-type-structure-manager.class.js'; -import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeModel } from './types.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts similarity index 99% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-structure-manager.class.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 4bbc6e7abb..67cabd5837 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -4,7 +4,7 @@ import type { UmbPropertyTypeContainerModel, UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel, -} from './types.js'; +} from '../types.js'; import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts new file mode 100644 index 0000000000..c587218a3d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/index.ts @@ -0,0 +1,3 @@ +export * from './content-type-container-structure-helper.class.js'; +export * from './content-type-property-structure-helper.class.js'; +export * from './content-type-structure-manager.class.js'; From 11390ef491d50abc92473f12e128588f052f31e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:37:36 +0100 Subject: [PATCH 020/285] allow null to be parsed for this.observe --- .../src/libs/class-api/class.interface.ts | 2 +- .../src/libs/class-api/class.mixin.ts | 2 +- .../src/libs/element-api/element.mixin.ts | 2 +- .../content-type-structure-manager.class.ts | 39 ++++++++++--------- ...-workspace-view-edit-properties.element.ts | 10 +++-- ...ocument-workspace-view-edit-tab.element.ts | 22 +++++++---- .../document-workspace-view-edit.element.ts | 16 +++++--- 7 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts index 7235a74eb9..8b46eb957f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts @@ -31,7 +31,7 @@ export interface UmbClassInterface extends UmbControllerHost { // This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL] source: ObservableType, callback: ObserverCallback, - controllerAlias?: UmbControllerAlias, + controllerAlias?: UmbControllerAlias | null, ): SpecificR; /** diff --git a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts index ce5c4c6f5a..90a83a1c27 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts @@ -54,7 +54,7 @@ export const UmbClassMixin = >(superClas // This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL] source: ObservableType, callback: ObserverCallback, - controllerAlias?: UmbControllerAlias, + controllerAlias?: UmbControllerAlias | null, ): SpecificR { // Fallback to use a hash of the provided method, but only if the alias is undefined. controllerAlias ??= controllerAlias === undefined ? simpleHashCode(callback.toString()) : undefined; diff --git a/src/Umbraco.Web.UI.Client/src/libs/element-api/element.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/element-api/element.mixin.ts index aea4582c81..aebd9bd789 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/element-api/element.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/element-api/element.mixin.ts @@ -27,7 +27,7 @@ export const UmbElementMixin = (superClass: T) // This type dance checks if the Observable given could be undefined, if it potentially could be undefined it means that this potentially could return undefined and then call the callback with undefined. [NL] source: ObservableType, callback: ObserverCallback, - controllerAlias?: UmbControllerAlias, + controllerAlias?: UmbControllerAlias | null, ): SpecificR { // Fallback to use a hash of the provided method, but only if the alias is undefined. controllerAlias ??= controllerAlias === undefined ? simpleHashCode(callback.toString()) : undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 67cabd5837..3a1a02c303 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -22,10 +22,11 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; export class UmbContentTypePropertyStructureManager extends UmbControllerBase { #init!: Promise; - #contentTypeRepository: UmbDetailRepository; + #repository: UmbDetailRepository; #ownerContentTypeUnique?: string; #contentTypeObservers = new Array(); + #contentTypes = new UmbArrayState([], (x) => x.unique); readonly contentTypes = this.#contentTypes.asObservable(); readonly ownerContentType = this.#contentTypes.asObservablePart((x) => @@ -42,7 +43,7 @@ export class UmbContentTypePropertyStructureManager) { super(host); - this.#contentTypeRepository = typeRepository; + this.#repository = typeRepository; this.observe(this.contentTypes, (contentTypes) => { contentTypes.forEach((contentType) => { @@ -55,7 +56,7 @@ export class UmbContentTypePropertyStructureManager { + this._ensureType(composition.contentType.unique); + }); + } + private async _ensureType(unique?: string) { if (!unique) return; if (this.#contentTypes.getValue().find((x) => x.unique === unique)) return; @@ -129,7 +136,7 @@ export class UmbContentTypePropertyStructureManager { if (docType) { // TODO: Handle if there was changes made to the owner document type in this context. [NL] @@ -163,12 +170,6 @@ export class UmbContentTypePropertyStructureManager { - this._ensureType(composition.contentType.unique); - }); - } - /** Public methods for consuming structure: */ ownerContentTypePart(mappingFunction: MappingFunction) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts index 39e9bd1f54..1c985a5601 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts @@ -34,9 +34,13 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { this._propertyStructureHelper.setStructureManager(workspaceContext.structure); }); - this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { - this._propertyStructure = propertyStructure; - }); + this.observe( + this._propertyStructureHelper.propertyStructure, + (propertyStructure) => { + this._propertyStructure = propertyStructure; + }, + null, + ); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts index 796f9274ef..ecce9f47f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts @@ -55,12 +55,20 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { this._groupStructureHelper.setStructureManager(workspaceContext.structure); }); - this.observe(this._groupStructureHelper.containers, (groups) => { - this._groups = groups; - }); - this.observe(this._groupStructureHelper.hasProperties, (hasProperties) => { - this._hasProperties = hasProperties; - }); + this.observe( + this._groupStructureHelper.containers, + (groups) => { + this._groups = groups; + }, + null, + ); + this.observe( + this._groupStructureHelper.hasProperties, + (hasProperties) => { + this._hasProperties = hasProperties; + }, + null, + ); } render() { @@ -73,7 +81,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : ''} ${repeat( this._groups, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts index 65b544bc01..ce6f2b4482 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts @@ -38,10 +38,14 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement implement this._tabsStructureHelper.setIsRoot(true); this._tabsStructureHelper.setContainerChildType('Tab'); - this.observe(this._tabsStructureHelper.containers, (tabs) => { - this._tabs = tabs; - this._createRoutes(); - }); + this.observe( + this._tabsStructureHelper.containers, + (tabs) => { + this._tabs = tabs; + this._createRoutes(); + }, + null, + ); // _hasRootProperties can be gotten via _tabsStructureHelper.hasProperties. But we do not support root properties currently. @@ -121,7 +125,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement implement href=${this._routerPath + '/'} >Content - ` + ` : ''} ${repeat( this._tabs, @@ -133,7 +137,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement implement >`; }, )} - ` + ` : ''} Date: Wed, 6 Mar 2024 19:42:20 +0100 Subject: [PATCH 021/285] make helpers controllers --- ...t-type-container-structure-helper.class.ts | 23 ++++++++----------- ...nt-type-property-structure-helper.class.ts | 14 +++++------ 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 22553ca391..b259e727fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -1,10 +1,10 @@ import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeContainerModel } from '../types.js'; import type { UmbContentTypePropertyStructureManager } from './content-type-structure-manager.class.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbArrayState, UmbBooleanState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; -export class UmbContentTypeContainerStructureHelper { - #host: UmbControllerHost; +export class UmbContentTypeContainerStructureHelper extends UmbControllerBase { #init; #initResolver?: (value: unknown) => void; @@ -34,7 +34,7 @@ export class UmbContentTypeContainerStructureHelper { this.#initResolver = resolve; }); @@ -101,8 +101,7 @@ export class UmbContentTypeContainerStructureHelper { this._ownerContainers = ownerContainers || []; @@ -110,8 +109,7 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); @@ -131,8 +129,7 @@ export class UmbContentTypeContainerStructureHelper { - new UmbObserverController( - this.#host, + this.observe( this.#structure!.hasPropertyStructuresOf(container.id!), (hasProperties) => { this.#hasProperties.setValue(hasProperties); @@ -146,8 +143,7 @@ export class UmbContentTypeContainerStructureHelper { - new UmbObserverController( - this.#host, + this.observe( this.#structure!.containersOfParentKey(container.id, this._childType!), this._insertGroupContainers, '_observeGroupsOf_' + container.id, @@ -158,8 +154,7 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 4760ba1831..901c5cdb1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -1,10 +1,10 @@ import type { UmbContentTypeModel, UmbPropertyContainerTypes, UmbPropertyTypeModel } from '../types.js'; import type { UmbContentTypePropertyStructureManager } from './content-type-structure-manager.class.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; -export class UmbContentTypePropertyStructureHelper { - #host: UmbControllerHost; +export class UmbContentTypePropertyStructureHelper extends UmbControllerBase { #init; #initResolver?: (value: unknown) => void; @@ -18,7 +18,7 @@ export class UmbContentTypePropertyStructureHelper { this.#initResolver = resolve; }); @@ -72,8 +72,7 @@ export class UmbContentTypePropertyStructureHelper { groupContainers.forEach((group) => this._observePropertyStructureOf(group.id)); @@ -86,8 +85,7 @@ export class UmbContentTypePropertyStructureHelper { // If this need to be able to remove properties, we need to clean out the ones of this group.id before inserting them: From 6202c005b1fd8a8ef7a6608894b095c30a0cf962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:46:36 +0100 Subject: [PATCH 022/285] getAllControllers method --- .../src/libs/controller-api/controller-host.interface.ts | 1 + .../src/libs/controller-api/controller-host.mixin.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts index b9a8d36586..a6ed0604f7 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts @@ -3,6 +3,7 @@ import type { UmbController } from './controller.interface.js'; export interface UmbControllerHost { hasController(controller: UmbController): boolean; getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[]; + getAllControllers(): UmbController[]; addController(controller: UmbController): void; removeControllerByAlias(unique: UmbController['controllerAlias']): void; removeController(controller: UmbController): void; diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts index 0407aa9bed..1b6d599808 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts @@ -41,6 +41,13 @@ export const UmbControllerHostMixin = (superClass: T return this.#controllers.filter(filterMethod); } + /** + * Retrieve all controllers of this element. + */ + getAllControllers(): UmbController[] { + return this.#controllers; + } + /** * Append a controller to this element. * @param {UmbController} ctrl From 4b2cc5fe49547764a3e1864e8b94278f75991055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:47:24 +0100 Subject: [PATCH 023/285] remove getAll method --- .../src/libs/controller-api/controller-host.interface.ts | 1 - .../src/libs/controller-api/controller-host.mixin.ts | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts index a6ed0604f7..b9a8d36586 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.interface.ts @@ -3,7 +3,6 @@ import type { UmbController } from './controller.interface.js'; export interface UmbControllerHost { hasController(controller: UmbController): boolean; getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[]; - getAllControllers(): UmbController[]; addController(controller: UmbController): void; removeControllerByAlias(unique: UmbController['controllerAlias']): void; removeController(controller: UmbController): void; diff --git a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts index 1b6d599808..0407aa9bed 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/controller-api/controller-host.mixin.ts @@ -41,13 +41,6 @@ export const UmbControllerHostMixin = (superClass: T return this.#controllers.filter(filterMethod); } - /** - * Retrieve all controllers of this element. - */ - getAllControllers(): UmbController[] { - return this.#controllers; - } - /** * Append a controller to this element. * @param {UmbController} ctrl From bb8dbb0db28955dd23c1c6957bdb9f917dfa277e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 19:49:06 +0100 Subject: [PATCH 024/285] error for awareness --- .../content-type-property-structure-helper.class.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 901c5cdb1b..ac0288ea49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -33,6 +33,12 @@ export class UmbContentTypePropertyStructureHelper) { + if (this.#structure === structure) return; + if (this.#structure) { + throw new Error( + 'Structure manager is already set, the helpers are not designed to be re-setup with new managers', + ); + } this.#structure = structure; this.#initResolver?.(undefined); this.#initResolver = undefined; From 4e7dda3dde653276cf331b72beeee1fdd9ab7151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 20:08:38 +0100 Subject: [PATCH 025/285] move sorter config outside of class --- ...-workspace-view-edit-properties.element.ts | 44 ++++++++++--------- ...nt-type-workspace-view-edit-tab.element.ts | 21 +++++---- ...ocument-workspace-view-edit-tab.element.ts | 2 +- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts index 8498263bd9..a3207792c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts @@ -7,27 +7,31 @@ import { css, html, customElement, property, state, repeat, ifDefined } from '@u import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +const SORTER_CONFIG: UmbSorterConfig = { + getUniqueOfElement: (element) => { + return element.getAttribute('data-umb-property-id'); + }, + getUniqueOfModel: (modelEntry) => { + return modelEntry.id; + }, + identifier: 'document-type-property-sorter', + itemSelector: 'umb-document-type-workspace-view-edit-property', + //disabledItemSelector: '[inherited]', + //TODO: Set the property list (sorter wrapper) to inherited, if its inherited + // This is because we don't want to move local properties into an inherited group container. + // Or maybe we do, but we still need to check if the group exists locally, if not, then it needs to be created before we move a property into it. + // TODO: Fix bug where a local property turn into an inherited when moved to a new group container. + containerSelector: '#property-list', +}; + @customElement('umb-document-type-workspace-view-edit-properties') export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitElement { #sorter = new UmbSorterController(this, { - getUniqueOfElement: (element) => { - return element.getAttribute('data-umb-property-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.id; - }, - identifier: 'document-type-property-sorter', - itemSelector: 'umb-document-type-workspace-view-edit-property', - //disabledItemSelector: '[inherited]', - //TODO: Set the property list (sorter wrapper) to inherited, if its inherited - // This is because we don't want to move local properties into an inherited group container. - // Or maybe we do, but we still need to check if the group exists locally, if not, then it needs to be created before we move a property into it. - // TODO: Fix bug where a local property turn into an inherited when moved to a new group container. - containerSelector: '#property-list', + ...SORTER_CONFIG, onChange: ({ model }) => { this._propertyStructure = model; }, @@ -158,8 +162,8 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) .addAdditionalPath('new-property') .onSetup(async () => { - const documentTypeId = this._ownerDocumentTypes?.find( - (types) => types.containers?.find((containers) => containers.id === this.containerId), + const documentTypeId = this._ownerDocumentTypes?.find((types) => + types.containers?.find((containers) => containers.id === this.containerId), )?.unique; if (documentTypeId === undefined) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); @@ -189,8 +193,8 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle (property) => '' + property.container?.id + property.id + '' + property.sortOrder, (property) => { // Note: This piece might be moved into the property component - const inheritedFromDocument = this._ownerDocumentTypes?.find( - (types) => types.containers?.find((containers) => containers.id === property.container?.id), + const inheritedFromDocument = this._ownerDocumentTypes?.find((types) => + types.containers?.find((containers) => containers.id === property.container?.id), ); return html` @@ -220,7 +224,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle look="placeholder" href=${ifDefined(this._modalRouteNewProperty)}> Add property - ` + ` : ''} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index c6c6516ea9..b6eff02a8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -12,19 +12,24 @@ import { import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import './document-type-workspace-view-edit-properties.element.js'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; +import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; + +const SORTER_CONFIG: UmbSorterConfig = + { + getUniqueOfElement: (element) => + element.querySelector('umb-document-type-workspace-view-edit-properties')!.getAttribute('container-id'), + getUniqueOfModel: (modelEntry) => modelEntry.id, + identifier: 'document-type-container-sorter', + itemSelector: '.container-handle', + containerSelector: '.container-list', + }; @customElement('umb-document-type-workspace-view-edit-tab') export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { #sorter = new UmbSorterController( this, { - getUniqueOfElement: (element) => - element.querySelector('umb-document-type-workspace-view-edit-properties')!.getAttribute('container-id'), - getUniqueOfModel: (modelEntry) => modelEntry.id, - identifier: 'document-type-container-sorter', - itemSelector: '.container-handle', - containerSelector: '.container-list', + ...SORTER_CONFIG, onChange: ({ model }) => { this._groups = model; }, @@ -173,7 +178,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : '' }
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts index ecce9f47f1..9ca72f1cec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts @@ -85,7 +85,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { : ''} ${repeat( this._groups, - (group) => group.name, + (group) => group.id, (group) => html` Date: Wed, 6 Mar 2024 20:25:53 +0100 Subject: [PATCH 026/285] general clean up --- ...nt-type-workspace-view-edit-properties.element.ts | 4 ++-- ...ment-type-workspace-view-edit-property.element.ts | 12 ++++++------ .../document-type-workspace-view-edit-tab.element.ts | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts index a3207792c4..f0c55345dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts @@ -190,7 +190,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle
${repeat( this._propertyStructure, - (property) => '' + property.container?.id + property.id + '' + property.sortOrder, + (property) => property.id, (property) => { // Note: This piece might be moved into the property component const inheritedFromDocument = this._ownerDocumentTypes?.find((types) => @@ -209,7 +209,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); }} @property-delete=${() => { - this._propertyStructureHelper.removeProperty(property.id!); + this._propertyStructureHelper.removeProperty(property.id); }}> `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts index 975e0a9137..158401542e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts @@ -178,7 +178,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { ?readonly=${this.inherited} label="sort order" @change=${(e: UUIInputEvent) => - this._partialUpdate({ sortOrder: parseInt(e.target.value as string) || 0 } as UmbPropertyTypeModel)} + this._partialUpdate({ sortOrder: parseInt(e.target.value as string) ?? 0 } as UmbPropertyTypeModel)} .value=${this.property.sortOrder ?? 0}> `; } @@ -272,7 +272,7 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement {
''} id="alias-lock" slot="prepend">
- ` + ` : ''; } @@ -283,19 +283,19 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { ${this.property.variesByCulture ? html` ${this.localize.term('contentTypeEditor_cultureVariantLabel')} - ` + ` : nothing} ${this.property.appearance?.labelOnTop == true ? html` ${this.localize.term('contentTypeEditor_displaySettingsLabelOnTop')} - ` + ` : nothing} ${this.property.validation.mandatory === true ? html` * ${this.localize.term('general_mandatory')} - ` + ` : nothing} -
` +
` : nothing; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index b6eff02a8c..8371faefe5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -201,7 +201,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { } #renderHeader(group: UmbPropertyTypeContainerModel) { - const inherited = !this.#groupStructureHelper.isOwnerChildContainer(group.id!); + const inherited = !this.#groupStructureHelper.isOwnerChildContainer(group.id); if (this._sortModeActive) { return html`
@@ -213,7 +213,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { type="number" label=${this.localize.term('sort_sortOrder')} @change=${(e: UUIInputEvent) => - this.#groupStructureHelper.partialUpdateContainer(group.id!, { + this.#groupStructureHelper.partialUpdateContainer(group.id, { sortOrder: parseInt(e.target.value as string) || 0, })} .value=${group.sortOrder || 0} @@ -233,7 +233,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { .value=${group.name} @change=${(e: InputEvent) => { const newName = (e.target as HTMLInputElement).value; - this.#groupStructureHelper.updateContainerName(group.id!, group.parent?.id ?? null, newName); + this.#groupStructureHelper.updateContainerName(group.id, group.parent?.id ?? null, newName); }}>`; } From f1c571118a52c6c8a0bbfaf2a5472611721c0ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 20:27:41 +0100 Subject: [PATCH 027/285] use ?? --- .../document-type-workspace-view-edit-property.element.ts | 4 ++-- .../design/document-type-workspace-view-edit-tab.element.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts index 158401542e..59357d630a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts @@ -135,9 +135,9 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { await umbConfirmModal(this, { headline: `${this.localize.term('actions_delete')} property`, content: html` - Are you sure you want to delete the property ${this.property.name || this.property.id} + Are you sure you want to delete the property ${this.property.name ?? this.property.id}
`, confirmLabel: this.localize.term('actions_delete'), diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index 8371faefe5..654e4dcba9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -176,7 +176,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { + container-name=${this.tabName ?? ''}> ` : '' @@ -191,7 +191,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { + container-name=${group.name ?? ''}> `, )} @@ -216,7 +216,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { this.#groupStructureHelper.partialUpdateContainer(group.id, { sortOrder: parseInt(e.target.value as string) || 0, })} - .value=${group.sortOrder || 0} + .value=${group.sortOrder ?? 0} ?disabled=${inherited}> `; } else { From 99a41d1b184e5e857d2bb719ddb9dd6b6fdd6903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 20:33:29 +0100 Subject: [PATCH 028/285] rename to contentTypes --- .../content-type-property-structure-helper.class.ts | 2 +- ...nt-type-workspace-view-edit-properties.element.ts | 3 +-- ...ia-type-workspace-view-edit-properties.element.ts | 12 ++++++------ ...er-type-workspace-view-edit-properties.element.ts | 12 ++++++------ 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index ac0288ea49..d3617f16d3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -26,7 +26,7 @@ export class UmbContentTypePropertyStructureHelper ((a as any).sortOrder ?? 0) - ((b as any).sortOrder ?? 0)); } - async ownerDocumentTypes() { + async contentTypes() { await this.#init; if (!this.#structure) return; return this.#structure.contentTypes; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts index f0c55345dd..9789ad8a84 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts @@ -143,8 +143,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle }, '_observeIsSorting', ); - const docTypesObservable = await this._propertyStructureHelper.ownerDocumentTypes(); - if (!docTypesObservable) return; + const docTypesObservable = await this._propertyStructureHelper.contentTypes(); this.observe( docTypesObservable, (documents) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts index e5ff01a8d5..6e8f42ed2e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts @@ -115,7 +115,7 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen '_observeIsSorting', ); - const mediaTypesObservable = await this._propertyStructureHelper.ownerDocumentTypes(); + const mediaTypesObservable = await this._propertyStructureHelper.contentTypes(); if (!mediaTypesObservable) return; this.observe( mediaTypesObservable, @@ -133,8 +133,8 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) .addAdditionalPath('new-property') .onSetup(async () => { - const mediaTypeId = this._ownerMediaTypes?.find( - (types) => types.containers?.find((containers) => containers.id === this.containerId), + const mediaTypeId = this._ownerMediaTypes?.find((types) => + types.containers?.find((containers) => containers.id === this.containerId), )?.unique; if (mediaTypeId === undefined) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); @@ -174,8 +174,8 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen (property) => property.id ?? '' + property.container?.id ?? '' + property.sortOrder ?? '', (property) => { // Note: This piece might be moved into the property component - const inheritedFromMedia = this._ownerMediaTypes?.find( - (types) => types.containers?.find((containers) => containers.id === property.container?.id), + const inheritedFromMedia = this._ownerMediaTypes?.find((types) => + types.containers?.find((containers) => containers.id === property.container?.id), ); return html` Add property - ` + ` : ''} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts index aefd7d051b..9a78190e27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts @@ -137,7 +137,7 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme }, '_observeIsSorting', ); - const docTypesObservable = await this._propertyStructureHelper.ownerDocumentTypes(); + const docTypesObservable = await this._propertyStructureHelper.contentTypes(); if (!docTypesObservable) return; this.observe( docTypesObservable, @@ -160,8 +160,8 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) .addAdditionalPath('new-property') .onSetup(async () => { - const memberTypeId = this._ownerMemberTypes?.find( - (types) => types.containers?.find((containers) => containers.id === this.containerId), + const memberTypeId = this._ownerMemberTypes?.find((types) => + types.containers?.find((containers) => containers.id === this.containerId), )?.unique; if (memberTypeId === undefined) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); @@ -194,8 +194,8 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme (property) => '' + property.container?.id + property.id + '' + property.sortOrder, (property) => { // Note: This piece might be moved into the property component - const inheritedFromMember = this._ownerMemberTypes?.find( - (types) => types.containers?.find((containers) => containers.id === property.container?.id), + const inheritedFromMember = this._ownerMemberTypes?.find((types) => + types.containers?.find((containers) => containers.id === property.container?.id), ); return html` @@ -225,7 +225,7 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme look="placeholder" href=${ifDefined(this._modalRouteNewProperty)}> Add property - ` + ` : ''} `; } From 2fccde8d63b5de0f500c286c3761f9aa3a06dac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 6 Mar 2024 20:45:13 +0100 Subject: [PATCH 029/285] rename to owner to parent for container helper --- .../block-workspace-view-edit-tab.element.ts | 4 +- ...t-type-container-structure-helper.class.ts | 64 +++++++++---------- ...nt-type-workspace-view-edit-tab.element.ts | 2 +- ...ocument-workspace-view-edit-tab.element.ts | 2 +- ...ia-type-workspace-view-edit-tab.element.ts | 10 +-- .../media-workspace-view-edit-tab.element.ts | 4 +- ...er-type-workspace-view-edit-tab.element.ts | 4 +- ...mber-workspace-view-content-tab.element.ts | 4 +- 8 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts index c788c7ec28..30e87d653f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts @@ -53,7 +53,7 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { public set ownerTabId(value: string | null | undefined) { if (value === this._ownerTabId) return; this._ownerTabId = value; - this.#groupStructureHelper.setOwnerId(value); + this.#groupStructureHelper.setParentId(value); } /** @@ -121,7 +121,7 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { class="properties" container-type="Group" container-name=${groupName || ''}>`; + >`; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index b259e727fb..57bcaaddbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -10,19 +10,19 @@ export class UmbContentTypeContainerStructureHelper; - private _ownerType?: UmbPropertyContainerTypes = 'Tab'; + private _parentType?: UmbPropertyContainerTypes = 'Tab'; private _childType?: UmbPropertyContainerTypes = 'Group'; private _isRoot = false; /** * The owner id is the owning container (The container that is begin presented, the container is the parent of the child containers) * If set to null, this helper class will provide containers of the root. */ - private _ownerId?: string | null; - private _ownerName?: string; + private _parentId?: string | null; + private _parentName?: string; // Containers defined in data might be more than actual containers to display as we merge them by name. // Direct containers are the containers defining the total of this container(Multiple containers with the same name and type) - private _ownerAlikeContainers: UmbPropertyTypeContainerModel[] = []; + private _parentAlikeContainers: UmbPropertyTypeContainerModel[] = []; // Owner containers are containers owned by the owner Content Type (The specific one up for editing) private _ownerContainers: UmbPropertyTypeContainerModel[] = []; @@ -46,55 +46,55 @@ export class UmbContentTypeContainerStructureHelper { this._ownerContainers = ownerContainers || []; }, '_observeOwnerContainers', ); - } else if (this._ownerName) { + } else if (this._parentName) { this.observe( - this.#structure.containersByNameAndType(this._ownerName, this._ownerType), + this.#structure.containersByNameAndType(this._parentName, this._parentType), (ownerALikeContainers) => { this.#containers.setValue([]); - this._ownerContainers = ownerALikeContainers.filter((x) => x.id === this._ownerId) || []; - this._ownerAlikeContainers = ownerALikeContainers || []; - if (this._ownerAlikeContainers.length > 0) { + this._ownerContainers = ownerALikeContainers.filter((x) => x.id === this._parentId) || []; + this._parentAlikeContainers = ownerALikeContainers || []; + if (this._parentAlikeContainers.length > 0) { this._observeChildContainerProperties(); this._observeChildContainers(); } @@ -128,7 +128,7 @@ export class UmbContentTypeContainerStructureHelper { + this._parentAlikeContainers.forEach((container) => { this.observe( this.#structure!.hasPropertyStructuresOf(container.id!), (hasProperties) => { @@ -140,9 +140,9 @@ export class UmbContentTypeContainerStructureHelper { + this._parentAlikeContainers.forEach((container) => { this.observe( this.#structure!.containersOfParentKey(container.id, this._childType!), this._insertGroupContainers, @@ -192,7 +192,7 @@ export class UmbContentTypeContainerStructureHelper (x.id === containerId && this._ownerId ? x.parent?.id === this._ownerId : x.parent === null)) !== + .find((x) => (x.id === containerId && this._parentId ? x.parent?.id === this._parentId : x.parent === null)) !== undefined ); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index 654e4dcba9..10d73dfc60 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -83,7 +83,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { if (value === this._ownerTabId) return; const oldValue = this._ownerTabId; this._ownerTabId = value; - this.#groupStructureHelper.setOwnerId(value); + this.#groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts index 9ca72f1cec..8c3f3789dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts @@ -38,7 +38,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { public set ownerTabId(value: string | null | undefined) { if (value === this._ownerTabId) return; this._ownerTabId = value; - this._groupStructureHelper.setOwnerId(value); + this._groupStructureHelper.setParentId(value); } _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts index 818c6604a3..a69a8dffd4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts @@ -65,7 +65,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { if (value === this._ownerTabId) return; const oldValue = this._ownerTabId; this._ownerTabId = value; - this._groupStructureHelper.setOwnerId(value); + this._groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } @@ -151,7 +151,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : ''}
${repeat( @@ -184,7 +184,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { ? html`` : ''}
- ` + ` : html`
${group.name ?? ''} (Inherited)
${!this._sortModeActive @@ -194,7 +194,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { label="sort order" .value=${group.sortOrder ?? 0}>` : ''} -
` + ` } - ` + ` : ''} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts index 79b53148c2..2abbfcca57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts @@ -38,7 +38,7 @@ export class UmbMediaWorkspaceViewEditTabElement extends UmbLitElement { public set ownerTabId(value: string | null | undefined) { if (value === this._ownerTabId) return; this._ownerTabId = value; - this._groupStructureHelper.setOwnerId(value); + this._groupStructureHelper.setParentId(value); } _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @@ -73,7 +73,7 @@ export class UmbMediaWorkspaceViewEditTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : ''} ${repeat( this._groups, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts index 31280fc50a..996f357133 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts @@ -80,7 +80,7 @@ export class UmbMemberTypeWorkspaceViewEditTabElement extends UmbLitElement { if (value === this._ownerTabId) return; const oldValue = this._ownerTabId; this._ownerTabId = value; - this._groupStructureHelper.setOwnerId(value); + this._groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } @@ -180,7 +180,7 @@ export class UmbMemberTypeWorkspaceViewEditTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : '' }
diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts index 0612fad852..467a56aa27 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts @@ -38,7 +38,7 @@ export class UmbMemberWorkspaceViewContentTabElement extends UmbLitElement { public set ownerTabId(value: string | null | undefined) { if (value === this._ownerTabId) return; this._ownerTabId = value; - this._groupStructureHelper.setOwnerId(value); + this._groupStructureHelper.setParentId(value); } _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @@ -73,7 +73,7 @@ export class UmbMemberWorkspaceViewContentTabElement extends UmbLitElement { container-type="Tab" container-name=${this.tabName || ''}> - ` + ` : ''} ${repeat( this._groups, From 6d9db14e6652327b97effa5900b7f798d007dc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 09:43:17 +0100 Subject: [PATCH 030/285] use isOwnerChildContainer --- ...ace-view-edit-content-no-router.element.ts | 8 +-- .../edit/block-workspace-view-edit.element.ts | 11 ++- ...t-type-container-structure-helper.class.ts | 71 ++++++++++++------- .../content-type-structure-manager.class.ts | 2 + ...nt-type-workspace-view-edit-tab.element.ts | 12 +++- ...cument-type-workspace-view-edit.element.ts | 7 +- .../document-workspace-view-edit.element.ts | 2 +- .../media-type-workspace-view-edit.element.ts | 6 +- .../edit/media-workspace-view-edit.element.ts | 11 ++- ...member-type-workspace-view-edit.element.ts | 6 +- .../member-workspace-view-content.element.ts | 6 +- 11 files changed, 83 insertions(+), 59 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts index b3d5dce3ed..4e67d8888e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-content-no-router.element.ts @@ -98,7 +98,7 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme @click=${() => this.#setTabName(null, null)} >Content - ` + ` : ''} ${repeat( this._tabs, @@ -112,18 +112,18 @@ export class UmbBlockWorkspaceViewEditContentNoRouterElement extends UmbLitEleme >`; }, )} - ` + ` : ''} ${this._activeTabId !== undefined ? html` - ` + ` : ''} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts index 54edca4e75..35d18e315c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts @@ -98,11 +98,8 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U (component as UmbBlockWorkspaceViewEditTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. // Instead have the structure manager looking at wether one of the OwnerALikecontainers is in the owner document. - (component as UmbBlockWorkspaceViewEditTabElement).ownerTabId = this.#tabsStructureHelper.isOwnerContainer( - tab.id!, - ) - ? tab.id - : undefined; + (component as UmbBlockWorkspaceViewEditTabElement).ownerTabId = + this.#tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, }); }); @@ -144,7 +141,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U href=${this._routerPath + '/'} >Content - ` + ` : ''} ${repeat( this._tabs, @@ -156,7 +153,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U >`; }, )} - ` + ` : ''} ; - private _parentType?: UmbPropertyContainerTypes = 'Tab'; + private _parentType?: UmbPropertyContainerTypes; private _childType?: UmbPropertyContainerTypes = 'Group'; private _isRoot = false; /** @@ -22,9 +22,9 @@ export class UmbContentTypeContainerStructureHelper([], (x) => x.id); @@ -43,18 +43,24 @@ export class UmbContentTypeContainerStructureHelper) { + if (this.#structure === structure) return; + if (this.#structure) { + throw new Error( + 'Structure manager is already set, the helpers are not designed to be re-setup with new managers', + ); + } this.#structure = structure; this.#initResolver?.(undefined); this.#initResolver = undefined; this._observeParentAlikeContainers(); } - public setType(value?: UmbPropertyContainerTypes) { + public setParentType(value?: UmbPropertyContainerTypes) { if (this._parentType === value) return; this._parentType = value; this._observeParentAlikeContainers(); } - public getType() { + public getParentType() { return this._parentType; } @@ -94,41 +100,52 @@ export class UmbContentTypeContainerStructureHelper { - this._ownerContainers = ownerContainers || []; + (containers) => { + this._parentContainers = containers ?? []; + console.log('root parent containers', this._parentContainers); + if (this._parentContainers.length !== 1) { + console.log( + '!!! We did not just get one parentContainer, I would have expected this, so I have to re-evaluate my understanding. !!!', + ); + } + this._parentAlikeContainers = []; }, '_observeOwnerContainers', - ); - } else if (this._parentName) { + );*/ + } else if (this._parentName && this._parentType) { this.observe( this.#structure.containersByNameAndType(this._parentName, this._parentType), - (ownerALikeContainers) => { + (parentContainers) => { this.#containers.setValue([]); - this._ownerContainers = ownerALikeContainers.filter((x) => x.id === this._parentId) || []; - this._parentAlikeContainers = ownerALikeContainers || []; - if (this._parentAlikeContainers.length > 0) { - this._observeChildContainerProperties(); + this._parentOwnerContainers = parentContainers.filter((x) => x.id === this._parentId) || []; + this._parentMatchingContainers = parentContainers ?? []; + console.log('owner containers', this._parentOwnerContainers); + console.log('matching containers', this._parentMatchingContainers); + if (this._parentMatchingContainers.length > 0) { + this._observeChildProperties(); this._observeChildContainers(); } }, '_observeOwnerContainers', ); + } else { + throw new Error('Container Structure Helper is not properly configured, missing required properties!!!!!!!!!'); } } - private _observeChildContainerProperties() { + private _observeChildProperties() { if (!this.#structure) return; - this._parentAlikeContainers.forEach((container) => { + this._parentMatchingContainers.forEach((container) => { this.observe( this.#structure!.hasPropertyStructuresOf(container.id!), (hasProperties) => { @@ -142,10 +159,10 @@ export class UmbContentTypeContainerStructureHelper { + this._parentMatchingContainers.forEach((container) => { this.observe( this.#structure!.containersOfParentKey(container.id, this._childType!), - this._insertGroupContainers, + this._insertChildContainers, '_observeGroupsOf_' + container.id, ); }); @@ -158,14 +175,14 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); - this._insertGroupContainers(rootContainers); + this._insertChildContainers(rootContainers); }, '_observeRootContainers', ); } - private _insertGroupContainers = (groupContainers: UmbPropertyTypeContainerModel[]) => { - groupContainers.forEach((group) => { + private _insertChildContainers = (childContainers: UmbPropertyTypeContainerModel[]) => { + childContainers.forEach((group) => { if (group.name !== null && group.name !== undefined) { if (!this.#containers.getValue().find((x) => x.name === group.name)) { this.#containers.appendOne(group); @@ -177,11 +194,13 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId) !== undefined; + return this._parentOwnerContainers.find((x) => x.id === containerId) !== undefined; } + */ /** * Returns true if the container is an owner container. @@ -189,6 +208,8 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); } + */ containersOfParentKey(parentId: string, containerType: UmbPropertyContainerTypes) { return this.#containers.asObservablePart((data) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index 10d73dfc60..91a55f568d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -83,6 +83,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { if (value === this._ownerTabId) return; const oldValue = this._ownerTabId; this._ownerTabId = value; + console.log('tab owner id', value); this.#groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } @@ -127,6 +128,9 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); + this.#groupStructureHelper.setParentType('Tab'); + + // TODO: Use a structured/? workspace context token... this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { this.#groupStructureHelper.setStructureManager((context as UmbDocumentTypeWorkspaceContext).structure); this.observe( @@ -168,7 +172,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { look="placeholder">` : '' } - + ${this.ownerTabId} ${ !this._noTabName ? html` @@ -182,12 +186,13 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { : '' }
+
${JSON.stringify(this._groups)}
${repeat( this._groups, (group) => group.id, (group) => html` - ${this.#renderHeader(group)} + ${this.#renderContainerHeader(group)} ${inherited ? html`` : this.#renderInputGroupName(group)} + (${group.parent?.id}/${group.id})
`; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts index d018e4d57a..58951d2334 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts @@ -179,7 +179,8 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple setup: (component) => { (component as UmbDocumentTypeWorkspaceViewEditTabElement).tabName = tabName; (component as UmbDocumentTypeWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerContainer(tab.id!) ? tab.id : undefined; + //tab.parent ? tab.parent.id === null + this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, }); }); @@ -233,7 +234,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple #remove(tabId?: string) { if (!tabId) return; this._workspaceContext?.structure.removeContainer(null, tabId); - this._tabsStructureHelper?.isOwnerContainer(tabId) + this._tabsStructureHelper?.isOwnerChildContainer(tabId) ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) : ''; } @@ -387,7 +388,7 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple renderTab(tab: PropertyTypeContainerModelBaseModel) { const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerContainer(tab.id!); + const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); return html` { (component as UmbMediaTypeWorkspaceViewEditTabElement).tabName = tabName; (component as UmbMediaTypeWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerContainer(tab.id!) ? tab.id : undefined; + this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, }); }); @@ -205,7 +205,7 @@ export class UmbMediaTypeWorkspaceViewEditElement extends UmbLitElement implemen #remove(tabId?: string) { if (!tabId) return; this._workspaceContext?.structure.removeContainer(null, tabId); - this._tabsStructureHelper?.isOwnerContainer(tabId) + this._tabsStructureHelper?.isOwnerChildContainer(tabId) ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) : ''; } @@ -338,7 +338,7 @@ export class UmbMediaTypeWorkspaceViewEditElement extends UmbLitElement implemen renderTab(tab: PropertyTypeContainerModelBaseModel) { const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerContainer(tab.id!); + const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); return html` { (component as UmbMediaWorkspaceViewEditTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. - (component as UmbMediaWorkspaceViewEditTabElement).ownerTabId = this._tabsStructureHelper.isOwnerContainer( - tab.id!, - ) - ? tab.id - : undefined; + (component as UmbMediaWorkspaceViewEditTabElement).ownerTabId = + this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, }); }); @@ -124,7 +121,7 @@ export class UmbMediaWorkspaceViewEditElement extends UmbLitElement implements U href=${this._routerPath + '/'} >Content - ` + ` : ''} ${repeat( this._tabs, @@ -136,7 +133,7 @@ export class UmbMediaWorkspaceViewEditElement extends UmbLitElement implements U >`; }, )} - ` + ` : ''} { (component as UmbMemberTypeWorkspaceViewEditTabElement).tabName = tabName; (component as UmbMemberTypeWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerContainer(tab.id!) ? tab.id : undefined; + this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, }); }); @@ -245,7 +245,7 @@ export class UmbMemberTypeWorkspaceViewEditElement extends UmbLitElement impleme #remove(tabId?: string) { if (!tabId) return; this._workspaceContext?.structure.removeContainer(null, tabId); - this._tabsStructureHelper?.isOwnerContainer(tabId) + this._tabsStructureHelper?.isOwnerChildContainer(tabId) ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) : ''; } @@ -399,7 +399,7 @@ export class UmbMemberTypeWorkspaceViewEditElement extends UmbLitElement impleme renderTab(tab: PropertyTypeContainerModelBaseModel) { const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerContainer(tab.id!); + const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); return html`Content - ` + ` : ''} ${repeat( this._tabs, @@ -133,7 +133,7 @@ export class UmbMemberWorkspaceViewEditElement extends UmbLitElement implements >`; }, )} - ` + ` : ''} Date: Thu, 7 Mar 2024 09:58:07 +0100 Subject: [PATCH 031/285] correct sorter --- .../data/document-type/document-type.data.ts | 16 ++++++++-------- ...ntent-type-property-structure-helper.class.ts | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts index 182e04ef9c..a7bf60f1ce 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts @@ -1081,7 +1081,7 @@ export const data: Array = [ dataType: { id: '0cc0eba1-9960-42c9-bf9b-60e150b429ae' }, variesByCulture: false, variesBySegment: false, - sortOrder: 0, + sortOrder: 1, validation: { mandatory: false, mandatoryMessage: null, @@ -1101,7 +1101,7 @@ export const data: Array = [ dataType: { id: '0cc0eba1-9960-42c9-bf9b-60e150b429ae' }, variesByCulture: false, variesBySegment: false, - sortOrder: 0, + sortOrder: 2, validation: { mandatory: false, mandatoryMessage: null, @@ -1126,7 +1126,7 @@ export const data: Array = [ parent: null, name: 'Alchemy', type: 'Group', - sortOrder: 0, + sortOrder: 1, }, ], allowedDocumentTypes: [], @@ -1389,7 +1389,7 @@ export const data: Array = [ dataType: { id: 'dt-textBox' }, variesByCulture: false, variesBySegment: false, - sortOrder: 10, + sortOrder: 0, validation: { mandatory: true, mandatoryMessage: null, @@ -1409,7 +1409,7 @@ export const data: Array = [ dataType: { id: 'dt-integer' }, variesByCulture: false, variesBySegment: false, - sortOrder: 10, + sortOrder: 1, validation: { mandatory: true, mandatoryMessage: null, @@ -1463,7 +1463,7 @@ export const data: Array = [ dataType: { id: 'dt-textBox' }, variesByCulture: false, variesBySegment: false, - sortOrder: 10, + sortOrder: 0, validation: { mandatory: true, mandatoryMessage: null, @@ -1517,7 +1517,7 @@ export const data: Array = [ dataType: { id: 'dt-mediaPicker' }, variesByCulture: false, variesBySegment: false, - sortOrder: 10, + sortOrder: 0, validation: { mandatory: true, mandatoryMessage: null, @@ -1571,7 +1571,7 @@ export const data: Array = [ dataType: { id: 'dt-richTextEditor' }, variesByCulture: false, variesBySegment: false, - sortOrder: 10, + sortOrder: 0, validation: { mandatory: true, mandatoryMessage: null, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index d3617f16d3..23840b9a4b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -22,8 +22,7 @@ export class UmbContentTypePropertyStructureHelper { this.#initResolver = resolve; }); - // TODO: Remove as any when sortOrder is implemented: - this.#propertyStructure.sortBy((a, b) => ((a as any).sortOrder ?? 0) - ((b as any).sortOrder ?? 0)); + this.#propertyStructure.sortBy((a, b) => a.sortOrder - b.sortOrder); } async contentTypes() { From be2965203cf48bef90302334bad589b791a9813b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 09:58:28 +0100 Subject: [PATCH 032/285] explicit about ParentType --- .../edit/block-workspace-view-edit-tab.element.ts | 2 ++ ...ontent-type-container-structure-helper.class.ts | 14 ++++---------- .../content-type-structure-manager.class.ts | 2 -- ...ocument-type-workspace-view-edit-tab.element.ts | 1 - .../media-type-workspace-view-edit-tab.element.ts | 2 ++ .../edit/media-workspace-view-edit-tab.element.ts | 2 ++ .../member-type-workspace-view-edit-tab.element.ts | 2 ++ .../member-workspace-view-content-tab.element.ts | 2 ++ 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts index 30e87d653f..15737c0b84 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts @@ -72,6 +72,8 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); + this.#groupStructureHelper.setParentType('Tab'); + this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (workspaceContext) => { this.#blockWorkspace = workspaceContext; this.#setStructureManager(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index b2f3b815ed..b0052226b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -100,7 +100,7 @@ export class UmbContentTypeContainerStructureHelper x.id === this._parentId) || []; this._parentMatchingContainers = parentContainers ?? []; - console.log('owner containers', this._parentOwnerContainers); - console.log('matching containers', this._parentMatchingContainers); if (this._parentMatchingContainers.length > 0) { this._observeChildProperties(); this._observeChildContainers(); } }, - '_observeOwnerContainers', + '_observeParentContainers', ); - } else { - throw new Error('Container Structure Helper is not properly configured, missing required properties!!!!!!!!!'); } } @@ -151,7 +147,7 @@ export class UmbContentTypeContainerStructureHelper { this.#hasProperties.setValue(hasProperties); }, - '_observeOwnerHasProperties_' + container.id, + '_observeParentHasProperties_' + container.id, ); }); } @@ -208,8 +204,6 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); } - */ containersOfParentKey(parentId: string, containerType: UmbPropertyContainerTypes) { return this.#containers.asObservablePart((data) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index 91a55f568d..b5682e3491 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -83,7 +83,6 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { if (value === this._ownerTabId) return; const oldValue = this._ownerTabId; this._ownerTabId = value; - console.log('tab owner id', value); this.#groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts index a69a8dffd4..cc14a89647 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts @@ -109,6 +109,8 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); + this._groupStructureHelper.setParentType('Tab'); + this.sorter = new UmbSorterController(this, this.config); this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts index 2abbfcca57..afcf72ebb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts @@ -52,6 +52,8 @@ export class UmbMediaWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); + this._groupStructureHelper.setParentType('Tab'); + this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (workspaceContext) => { this._groupStructureHelper.setStructureManager(workspaceContext.structure); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts index 996f357133..810452ff82 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts @@ -124,6 +124,8 @@ export class UmbMemberTypeWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); + this._groupStructureHelper.setParentType('Tab'); + this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { this._groupStructureHelper.setStructureManager((context as UmbMemberTypeWorkspaceContext).structure); this.observe( diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts index 467a56aa27..bcd9425a75 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts @@ -52,6 +52,8 @@ export class UmbMemberWorkspaceViewContentTabElement extends UmbLitElement { constructor() { super(); + this._groupStructureHelper.setParentType('Tab'); + this.consumeContext(UMB_MEMBER_WORKSPACE_CONTEXT, (workspaceContext) => { this._groupStructureHelper.setStructureManager(workspaceContext.structure); }); From ec57137f7d43d9a6817165c71bfa0b02473b52fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 10:42:47 +0100 Subject: [PATCH 033/285] note on group element --- .../design/document-type-workspace-view-edit-tab.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts index b5682e3491..6266481764 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts @@ -191,6 +191,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { (group) => group.id, (group) => html` + ${this.#renderContainerHeader(group)} Date: Thu, 7 Mar 2024 11:03:32 +0100 Subject: [PATCH 034/285] refactors --- ...-workspace-view-edit-properties.element.ts | 2 +- ...pe-workspace-view-edit-property.element.ts | 144 +++++++++--------- ...-workspace-view-edit-properties.element.ts | 2 +- ...pe-workspace-view-edit-property.element.ts | 14 +- ...-workspace-view-edit-properties.element.ts | 2 +- ...pe-workspace-view-edit-property.element.ts | 14 +- 6 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts index 9789ad8a84..fef8a6550f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts @@ -204,7 +204,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle ?inherited=${property.container?.id !== this.containerId} ?sort-mode-active=${this._sortModeActive} .property=${property} - @partial-property-update=${(event: CustomEvent) => { + @umb:partial-property-update=${(event: CustomEvent) => { this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); }} @property-delete=${() => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts index 59357d630a..2d09aa82bb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts @@ -20,7 +20,12 @@ import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbrac */ @customElement('umb-document-type-workspace-view-edit-property') export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { - private _property?: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined; + // + #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); + + #settingsModal; + #workspaceModal; + /** * Property, the data object for the property. * @type {UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined} @@ -34,10 +39,12 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { public set property(value: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined) { const oldValue = this._property; this._property = value; - this.#modalRegistration.setUniquePathValue('propertyId', value?.id?.toString()); + this.#settingsModal.setUniquePathValue('propertyId', value?.id?.toString()); + this.#workspaceModal.setUniquePathValue('propertyId', value?.id?.toString()); this.setDataType(this._property?.dataType?.unique); this.requestUpdate('property', oldValue); } + private _property?: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined; /** * Inherited, Determines if the property is part of the main document type thats being edited. @@ -52,38 +59,27 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { @property({ type: Boolean, reflect: true, attribute: 'sort-mode-active' }) public sortModeActive = false; - #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); - - #modalRegistration; - - @state() - protected _modalRoute?: string; - - @state() - protected _editDocumentTypePath?: string; - - @property() - public get modalRoute() { - return this._modalRoute; - } - @property({ type: String, attribute: 'owner-document-type-id' }) public ownerDocumentTypeId?: string; @property({ type: String, attribute: 'owner-document-type-name' }) public ownerDocumentTypeName?: string; + @state() + protected _modalRoute?: string; + + @state() + protected _editDocumentTypePath?: string; + @state() private _dataTypeName?: string; - async setDataType(dataTypeId: string | undefined) { - if (!dataTypeId) return; - this.#dataTypeDetailRepository.requestByUnique(dataTypeId).then((x) => (this._dataTypeName = x?.data?.name)); - } + @state() + private _aliasLocked = true; constructor() { super(); - this.#modalRegistration = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) + this.#settingsModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) .addUniquePaths(['propertyId']) .onSetup(() => { const documentTypeId = this.ownerDocumentTypeId; @@ -99,7 +95,8 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { this._modalRoute = routeBuilder(null); }); - new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) + this.#workspaceModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) + .addUniquePaths(['propertyId']) .addAdditionalPath('document-type') .onSetup(() => { return { data: { entityType: 'document-type', preset: {} } }; @@ -110,23 +107,25 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { } _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { const partialObject = {} as any; partialObject[propertyName] = value; - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } - @state() - private _aliasLocked = true; - #onToggleAliasLock() { this._aliasLocked = !this._aliasLocked; } + async setDataType(dataTypeId: string | undefined) { + if (!dataTypeId) return; + this.#dataTypeDetailRepository.requestByUnique(dataTypeId).then((x) => (this._dataTypeName = x?.data?.name)); + } + async #requestRemove(e: Event) { e.preventDefault(); e.stopImmediatePropagation(); @@ -166,21 +165,38 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { } } } - renderSortableProperty() { + + render() { + // TODO: Only show alias on label if user has access to DocumentType within settings: [NL] + return this.inherited ? this.renderInheritedProperty() : this.renderEditableProperty(); + } + + renderInheritedProperty() { if (!this.property) return; - return html` -
- - ${this.property.name} (${this.property.alias}) -
- - this._partialUpdate({ sortOrder: parseInt(e.target.value as string) ?? 0 } as UmbPropertyTypeModel)} - .value=${this.property.sortOrder ?? 0}> - `; + + if (this.sortModeActive) { + return this.renderSortableProperty(); + } else { + return html` + +
+ ${this.renderPropertyTags()} + + + ${this.localize.term('contentTypeEditor_inheritedFrom')} + + ${this.ownerDocumentTypeName ?? '??'} + + + +
+ `; + } } renderEditableProperty() { @@ -227,32 +243,21 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { } } - renderInheritedProperty() { + renderSortableProperty() { if (!this.property) return; - - if (this.sortModeActive) { - return this.renderSortableProperty(); - } else { - return html` - -
- ${this.renderPropertyTags()} - - - ${this.localize.term('contentTypeEditor_inheritedFrom')} - - ${this.ownerDocumentTypeName ?? '??'} - - - -
- `; - } + return html` +
+ + ${this.property.name} (${this.property.alias}) +
+ + this._partialUpdate({ sortOrder: parseInt(e.target.value as string) ?? 0 } as UmbPropertyTypeModel)} + .value=${this.property.sortOrder ?? 0}> + `; } renderPropertyAlias() { @@ -299,11 +304,6 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { : nothing; } - render() { - // TODO: Only show alias on label if user has access to DocumentType within settings: - return this.inherited ? this.renderInheritedProperty() : this.renderEditableProperty(); - } - static styles = [ UmbTextStyles, css` diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts index 6e8f42ed2e..5089fb33fa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts @@ -185,7 +185,7 @@ export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElemen ?inherited=${property.container?.id !== this.containerId} ?sort-mode-active=${this._sortModeActive} .property=${property} - @partial-property-update=${(event: CustomEvent) => { + @umb:partial-property-update=${(event: CustomEvent) => { this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); }} @property-delete=${() => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts index 4c42684238..ea69a2991a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts @@ -111,14 +111,14 @@ export class UmbMediaTypeWorkspacePropertyElement extends UmbLitElement { } _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { const partialObject = {} as any; partialObject[propertyName] = value; - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } @state() @@ -274,7 +274,7 @@ export class UmbMediaTypeWorkspacePropertyElement extends UmbLitElement {
''} id="alias-lock" slot="prepend">
- ` + ` : ''; } @@ -285,19 +285,19 @@ export class UmbMediaTypeWorkspacePropertyElement extends UmbLitElement { ${this.property.variesByCulture ? html` ${this.localize.term('contentTypeEditor_cultureVariantLabel')} - ` + ` : nothing} ${this.property.appearance?.labelOnTop == true ? html` ${this.localize.term('contentTypeEditor_displaySettingsLabelOnTop')} - ` + ` : nothing} ${this.property.validation.mandatory === true ? html` * ${this.localize.term('general_mandatory')} - ` + ` : nothing} -
` + ` : nothing; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts index 9a78190e27..f2d0125c15 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts @@ -206,7 +206,7 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme ?inherited=${property.container?.id !== this.containerId} ?sort-mode-active=${this._sortModeActive} .property=${property} - @partial-property-update=${(event: CustomEvent) => { + @umb:partial-property-update=${(event: CustomEvent) => { this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); }} @property-delete=${() => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts index 478ec3c53f..1e84cc6723 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts @@ -121,14 +121,14 @@ export class UmbMemberTypeWorkspacePropertyElement extends UmbLitElement { } _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { const partialObject = {} as any; partialObject[propertyName] = value; - this.dispatchEvent(new CustomEvent('partial-property-update', { detail: partialObject })); + this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); } @state() @@ -283,7 +283,7 @@ export class UmbMemberTypeWorkspacePropertyElement extends UmbLitElement {
''} id="alias-lock" slot="prepend">
- ` + ` : ''; } @@ -294,19 +294,19 @@ export class UmbMemberTypeWorkspacePropertyElement extends UmbLitElement { ${this.property.variesByCulture ? html` ${this.localize.term('contentTypeEditor_cultureVariantLabel')} - ` + ` : nothing} ${this.property.appearance?.labelOnTop == true ? html` ${this.localize.term('contentTypeEditor_displaySettingsLabelOnTop')} - ` + ` : nothing} ${this.property.validation.mandatory === true ? html` * ${this.localize.term('general_mandatory')} - ` + ` : nothing} - ` + ` : nothing; } From 702ffd1b4a9c59eef700ebcc2bfe086d56b45e44 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 11:34:09 +0100 Subject: [PATCH 035/285] setup repo for section items --- .../packages/core/section/repository/index.ts | 1 + .../core/section/repository/item/index.ts | 4 ++ .../core/section/repository/item/manifests.ts | 12 ++++ .../item/section-item.repository.ts | 55 +++++++++++++++++++ .../core/section/repository/item/types.ts | 5 ++ .../core/section/repository/manifests.ts | 3 + 6 files changed, 80 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/manifests.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/types.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/repository/manifests.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/index.ts new file mode 100644 index 0000000000..656744074c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/index.ts @@ -0,0 +1 @@ +export * from './item/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/index.ts new file mode 100644 index 0000000000..63ffd0d105 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/index.ts @@ -0,0 +1,4 @@ +export { UmbSectionItemRepository } from './section-item.repository.js'; +export { UMB_SECTION_ITEM_REPOSITORY_ALIAS } from './manifests.js'; + +export type { UmbSectionItemModel } from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/manifests.ts new file mode 100644 index 0000000000..f06e755135 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/manifests.ts @@ -0,0 +1,12 @@ +import type { ManifestRepository } from '@umbraco-cms/backoffice/extension-registry'; + +export const UMB_SECTION_ITEM_REPOSITORY_ALIAS = 'Umb.Repository.Section.Item'; + +const itemRepository: ManifestRepository = { + type: 'repository', + alias: UMB_SECTION_ITEM_REPOSITORY_ALIAS, + name: 'Section Item Repository', + api: () => import('./section-item.repository.js'), +}; + +export const manifests = [itemRepository]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts new file mode 100644 index 0000000000..3f112df553 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts @@ -0,0 +1,55 @@ +import type { UmbSectionItemModel } from './types.js'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; +import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { map } from '@umbraco-cms/backoffice/external/rxjs'; + +export class UmbSectionItemRepository extends UmbRepositoryBase implements UmbItemRepository { + constructor(host: UmbControllerHost) { + super(host); + } + + /** + * Requests the items for the given uniques + * @param {Array} uniques + * @return {*} + * @memberof UmbItemRepositoryBase + */ + async requestItems(uniques: Array) { + if (!uniques) throw new Error('Uniques are missing'); + + const sectionManifests = umbExtensionsRegistry.getAllExtensions().filter((x) => x.type === 'section'); + const sectionItems: Array = sectionManifests.map((manifest) => itemMapper(manifest)); + + const sectionItemsObservable = umbExtensionsRegistry + .byType('section') + .pipe(map((manifests) => manifests.map((manifest) => itemMapper(manifest)))); + + return { data: sectionItems, asObservable: () => sectionItemsObservable }; + } + + /** + * Returns a promise with an observable of the items for the given uniques + * @param {Array} uniques + * @return {*} + * @memberof UmbItemRepositoryBase + */ + async items(uniques: Array) { + return umbExtensionsRegistry + .getAllExtensions() + .filter((x) => x.type === 'section') + .map((manifest) => itemMapper(manifest)) + .filter((x) => uniques.includes(x.unique)); + } +} + +const itemMapper = (manifest: ManifestSection): UmbSectionItemModel => { + return { + ...manifest, + unique: manifest.alias, + }; +}; + +export default UmbSectionItemRepository; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/types.ts new file mode 100644 index 0000000000..8da7a576e9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/types.ts @@ -0,0 +1,5 @@ +import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; + +export interface UmbSectionItemModel extends ManifestSection { + unique: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/manifests.ts new file mode 100644 index 0000000000..34d6cf7788 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/manifests.ts @@ -0,0 +1,3 @@ +import { manifests as itemManifests } from './item/manifests.js'; + +export const manifests = [...itemManifests]; From 4e03763e3a4bdf3cf72b91d8172c0171db539b6f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 11:34:34 +0100 Subject: [PATCH 036/285] reuse generic picker logic for input-section --- .../src/packages/core/section/index.ts | 2 +- .../input-section/input-section.context.ts | 11 ++ .../input-section/input-section.element.ts | 182 +++++++++++------- .../src/packages/core/section/manifests.ts | 5 +- .../section-picker-modal.element.ts | 12 +- .../section-picker-modal.token.ts | 0 .../section-picker.test.ts | 0 7 files changed, 142 insertions(+), 70 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts rename src/Umbraco.Web.UI.Client/src/packages/core/section/{section-picker => section-picker-modal}/section-picker-modal.element.ts (89%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{section-picker => section-picker-modal}/section-picker-modal.token.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{section-picker => section-picker-modal}/section-picker.test.ts (100%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts index bd007f31f0..d60293a578 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts @@ -6,4 +6,4 @@ export * from './section-sidebar-menu-with-entity-actions/index.js'; export * from './section-default.element.js'; export * from './section.context.js'; export * from './input-section/index.js'; -export * from './section-picker/section-picker-modal.token.js'; +export * from './section-picker-modal/section-picker-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts new file mode 100644 index 0000000000..5a0a3be146 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts @@ -0,0 +1,11 @@ +import type { UmbSectionItemModel } from '../repository/index.js'; +import { UMB_SECTION_ITEM_REPOSITORY_ALIAS } from '../repository/index.js'; +import { UMB_SECTION_PICKER_MODAL } from '../section-picker-modal/section-picker-modal.token.js'; +import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbSectionPickerContext extends UmbPickerInputContext { + constructor(host: UmbControllerHost) { + super(host, UMB_SECTION_ITEM_REPOSITORY_ALIAS, UMB_SECTION_PICKER_MODAL); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts index 99ac8aa0d0..c756c38f03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts @@ -1,89 +1,137 @@ -import { UmbInputListBaseElement } from '../../components/input-list-base/input-list-base.js'; -import { UMB_SECTION_PICKER_MODAL } from '../section-picker/section-picker-modal.token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import type { UmbSectionItemModel } from '../repository/index.js'; +import { UmbSectionPickerContext } from './input-section.context.js'; +import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; @customElement('umb-input-section') -export class UmbInputSectionElement extends UmbInputListBaseElement { +export class UmbInputSectionElement extends FormControlMixin(UmbLitElement) { + /** + * This is a minimum amount of selected items in this input. + * @type {number} + * @attr + * @default 0 + */ + @property({ type: Number }) + public get min(): number { + return this.#pickerContext.min; + } + public set min(value: number) { + this.#pickerContext.min = value; + } + + /** + * Min validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + minMessage = 'This field need more items'; + + /** + * This is a maximum amount of selected items in this input. + * @type {number} + * @attr + * @default Infinity + */ + @property({ type: Number }) + public get max(): number { + return this.#pickerContext.max; + } + public set max(value: number) { + this.#pickerContext.max = value; + } + + /** + * Max validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + maxMessage = 'This field exceeds the allowed amount of items'; + + public get selection(): Array { + return this.#pickerContext.getSelection(); + } + public set selection(uniques: Array) { + this.#pickerContext.setSelection(uniques); + } + + @property() + public set value(selectionString: string) { + // Its with full purpose we don't call super.value, as thats being handled by the observation of the context selection. + if (typeof selectionString !== 'string') return; + if (selectionString === this.value) return; + this.selection = splitStringToArray(selectionString); + } + @state() - private _sections: Array = []; + private _items?: Array; - connectedCallback(): void { - super.connectedCallback(); - this.pickerToken = UMB_SECTION_PICKER_MODAL; - this._observeSections(); + #pickerContext = new UmbSectionPickerContext(this); + + constructor() { + super(); + + this.addValidator( + 'rangeUnderflow', + () => this.minMessage, + () => !!this.min && this.#pickerContext.getSelection().length < this.min, + ); + + this.addValidator( + 'rangeOverflow', + () => this.maxMessage, + () => !!this.max && this.#pickerContext.getSelection().length > this.max, + ); + + this.observe(this.#pickerContext.selection, (selection) => (super.value = selection.join(','))); + this.observe(this.#pickerContext.selectedItems, (selectedItems) => (this._items = selectedItems)); } - private _observeSections() { - if (this.value.length > 0) { - this.observe(umbExtensionsRegistry.byType('section'), (sections: Array) => { - this._sections = sections.filter((section) => this.value.includes(section.alias)); - }); - } else { - this._sections = []; - } + protected getFormElement() { + return undefined; } - selectionUpdated() { - this._observeSections(); - // TODO: Use proper event class: - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); - } - - renderContent() { - if (this._sections.length === 0) return html`${nothing}`; - + render() { return html` -
- ${this._sections.map( - (section) => html` -
-
- ${section.meta.label} -
- this.removeFromSelection(section.alias)} - label="remove" - color="danger"> -
- `, - )} -
+ ${this._items?.map((item) => this._renderItem(item))} + this.#pickerContext.openPicker()} label="open" + >Add `; } + private _renderItem(item: UmbSectionItemModel) { + if (!item.unique) return; + return html`${item.unique} + `; + } + static styles = [ - UmbTextStyles, css` - :host { - display: flex; - flex-direction: column; - gap: var(--uui-size-space-4); - } - #user-group-list { - display: flex; - flex-direction: column; - gap: var(--uui-size-space-4); - } - .user-group { - display: flex; - align-items: center; - gap: var(--uui-size-space-2); - } - .user-group div { - display: flex; - align-items: center; - gap: var(--uui-size-4); - } - .user-group uui-button { - margin-left: auto; + #add-button { + width: 100%; } `, ]; } +export default UmbInputSectionElement; + declare global { interface HTMLElementTagNameMap { 'umb-input-section': UmbInputSectionElement; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts index 90eff35d58..718e97f9eb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts @@ -1,8 +1,11 @@ +import { manifests as repositoryManifests } from './repository/manifests.js'; + export const manifests = [ { type: 'modal', alias: 'Umb.Modal.SectionPicker', name: 'Section Picker Modal', - js: () => import('./section-picker/section-picker-modal.element.js'), + js: () => import('./section-picker-modal/section-picker-modal.element.js'), }, + ...repositoryManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts index 5a4061c4e1..6cc044f303 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts @@ -13,14 +13,24 @@ export class UmbSectionPickerModalElement extends UmbModalBaseElement< @state() private _sections: Array = []; + @state() + private _selectable = false; + #selectionManager = new UmbSelectionManager(this); + constructor() { + super(); + + this.observe(this.#selectionManager.selectable, (selectable) => (this._selectable = selectable)); + } + connectedCallback(): void { super.connectedCallback(); // TODO: in theory this config could change during the lifetime of the modal, so we could observe it this.#selectionManager.setMultiple(this.data?.multiple ?? false); this.#selectionManager.setSelection(this.data?.selection ?? []); + this.#selectionManager.setSelectable(true); this.observe( umbExtensionsRegistry.byType('section'), @@ -42,7 +52,7 @@ export class UmbSectionPickerModalElement extends UmbModalBaseElement< (item) => html` this.#selectionManager.select(item.alias)} @deselected=${() => this.#selectionManager.deselect(item.alias)}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.token.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker-modal.token.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.token.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker/section-picker.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker.test.ts From 6a73babfb32e883b22d758133979f7bf442a5453 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 12:57:32 +0100 Subject: [PATCH 037/285] add ref-section element --- .../core/picker-input/picker-input.context.ts | 1 - .../packages/core/section/components/index.ts | 1 + .../ref-section/ref-section.element.ts | 35 +++++++++++++++++++ .../src/packages/core/section/index.ts | 1 + .../input-section/input-section.element.ts | 19 ++++------ 5 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/components/ref-section/ref-section.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts index 7032f9bbfe..6c07396806 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts @@ -84,7 +84,6 @@ export class UmbPickerInputContext this.#getUnique(item) === unique); if (!item) throw new Error('Could not find item with unique: ' + unique); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts new file mode 100644 index 0000000000..4d5d09d52d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts @@ -0,0 +1 @@ +export * from './ref-section/ref-section.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/ref-section/ref-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/ref-section/ref-section.element.ts new file mode 100644 index 0000000000..8b41537035 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/ref-section/ref-section.element.ts @@ -0,0 +1,35 @@ +import type { UmbSectionItemModel } from '../../repository/index.js'; +import { UUIRefElement } from '@umbraco-cms/backoffice/external/uui'; +import { html, customElement, css, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; + +@customElement('umb-ref-section') +export class UmbRefSectionElement extends UmbElementMixin(UUIRefElement) { + @property({ type: Object, attribute: false }) + item?: UmbSectionItemModel; + + public render() { + return html` +
+
${this.item?.meta.label}
+
+ + + `; + } + + static styles = [ + ...UUIRefElement.styles, + css` + #name { + font-weight: 700; + } + `, + ]; +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-ref-section': UmbRefSectionElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts index d60293a578..ec281fe08d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts @@ -7,3 +7,4 @@ export * from './section-default.element.js'; export * from './section.context.js'; export * from './input-section/index.js'; export * from './section-picker-modal/section-picker-modal.token.js'; +export * from './components/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts index c756c38f03..adaa7c2ba0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts @@ -107,18 +107,13 @@ export class UmbInputSectionElement extends FormControlMixin(UmbLitElement) { private _renderItem(item: UmbSectionItemModel) { if (!item.unique) return; - return html`${item.unique} - `; + return html` + + this.#pickerContext.requestRemoveItem(item.unique)} label="Remove ${item.name}" + >Remove + + `; } static styles = [ From 66a3d4a17b9a2b904d0e4f92a7b84f9829d23f21 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 13:02:17 +0100 Subject: [PATCH 038/285] move input section into the components folder --- .../packages/core/section/components/index.ts | 1 + .../{ => components}/input-section/index.ts | 0 .../input-section/input-section.context.ts | 6 +++--- .../input-section/input-section.element.ts | 2 +- .../input-section/input-section.stories.ts | 0 .../input-section/input-section.test.ts | 0 .../src/packages/core/section/index.ts | 17 ++++++++--------- 7 files changed, 13 insertions(+), 13 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{ => components}/input-section/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{ => components}/input-section/input-section.context.ts (59%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{ => components}/input-section/input-section.element.ts (98%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{ => components}/input-section/input-section.stories.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/section/{ => components}/input-section/input-section.test.ts (100%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts index 4d5d09d52d..404c625c49 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/index.ts @@ -1 +1,2 @@ +export * from './input-section/index.js'; export * from './ref-section/ref-section.element.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.context.ts similarity index 59% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.context.ts index 5a0a3be146..09a30dd082 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.context.ts @@ -1,6 +1,6 @@ -import type { UmbSectionItemModel } from '../repository/index.js'; -import { UMB_SECTION_ITEM_REPOSITORY_ALIAS } from '../repository/index.js'; -import { UMB_SECTION_PICKER_MODAL } from '../section-picker-modal/section-picker-modal.token.js'; +import type { UmbSectionItemModel } from '../../repository/index.js'; +import { UMB_SECTION_ITEM_REPOSITORY_ALIAS } from '../../repository/index.js'; +import { UMB_SECTION_PICKER_MODAL } from '../../section-picker-modal/section-picker-modal.token.js'; import { UmbPickerInputContext } from '@umbraco-cms/backoffice/picker-input'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts similarity index 98% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts index adaa7c2ba0..ae1f7eaf8d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.element.ts @@ -1,4 +1,4 @@ -import type { UmbSectionItemModel } from '../repository/index.js'; +import type { UmbSectionItemModel } from '../../repository/index.js'; import { UmbSectionPickerContext } from './input-section.context.js'; import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.stories.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/section/input-section/input-section.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts index ec281fe08d..b46d9acc87 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/index.ts @@ -1,10 +1,9 @@ -export * from './section-main/index.js'; -export * from './section-sidebar/index.js'; -export * from './section-sidebar-context-menu/index.js'; -export * from './section-sidebar-menu/index.js'; -export * from './section-sidebar-menu-with-entity-actions/index.js'; -export * from './section-default.element.js'; -export * from './section.context.js'; -export * from './input-section/index.js'; -export * from './section-picker-modal/section-picker-modal.token.js'; export * from './components/index.js'; +export * from './section-default.element.js'; +export * from './section-main/index.js'; +export * from './section-picker-modal/section-picker-modal.token.js'; +export * from './section-sidebar-context-menu/index.js'; +export * from './section-sidebar-menu-with-entity-actions/index.js'; +export * from './section-sidebar-menu/index.js'; +export * from './section-sidebar/index.js'; +export * from './section.context.js'; From 0c02918bde287f81c431eec3392cbbeb755aaee6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 13:34:16 +0100 Subject: [PATCH 039/285] filter observable by uniques --- .../item/section-item.repository.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts index 3f112df553..40d26f4313 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts @@ -4,7 +4,7 @@ import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { map } from '@umbraco-cms/backoffice/external/rxjs'; +import { createObservablePart } from '@umbraco-cms/backoffice/observable-api'; export class UmbSectionItemRepository extends UmbRepositoryBase implements UmbItemRepository { constructor(host: UmbControllerHost) { @@ -20,14 +20,14 @@ export class UmbSectionItemRepository extends UmbRepositoryBase implements UmbIt async requestItems(uniques: Array) { if (!uniques) throw new Error('Uniques are missing'); - const sectionManifests = umbExtensionsRegistry.getAllExtensions().filter((x) => x.type === 'section'); + const sectionManifests = umbExtensionsRegistry + .getAllExtensions() + .filter((manifest) => manifest.type === 'section') + .filter((manifest) => uniques.includes(manifest.alias)); + const sectionItems: Array = sectionManifests.map((manifest) => itemMapper(manifest)); - const sectionItemsObservable = umbExtensionsRegistry - .byType('section') - .pipe(map((manifests) => manifests.map((manifest) => itemMapper(manifest)))); - - return { data: sectionItems, asObservable: () => sectionItemsObservable }; + return { data: sectionItems, asObservable: () => sectionItemsByUniquesObservable(uniques) }; } /** @@ -37,14 +37,17 @@ export class UmbSectionItemRepository extends UmbRepositoryBase implements UmbIt * @memberof UmbItemRepositoryBase */ async items(uniques: Array) { - return umbExtensionsRegistry - .getAllExtensions() - .filter((x) => x.type === 'section') - .map((manifest) => itemMapper(manifest)) - .filter((x) => uniques.includes(x.unique)); + return sectionItemsByUniquesObservable(uniques); } } +const sectionItemsObservable = createObservablePart(umbExtensionsRegistry.byType('section'), (manifests) => + manifests.map((manifest) => itemMapper(manifest)), +); + +const sectionItemsByUniquesObservable = (uniques: Array) => + createObservablePart(sectionItemsObservable, (items) => items.filter((x) => uniques.includes(x.unique))); + const itemMapper = (manifest: ManifestSection): UmbSectionItemModel => { return { ...manifest, From e72f29d84ddc8a86a191189baba14b8d026095a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 13:57:24 +0100 Subject: [PATCH 040/285] remove unused import --- .../examples/workspace-context-counter/counter-workspace-view.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-view.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-view.ts index 2b86fdcead..581a9fab53 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-view.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/counter-workspace-view.ts @@ -1,5 +1,4 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; import { css, html, customElement, state, LitElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; import { EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context'; From bcd3ed00d7345812c86e8aa50f51ff30b5436382 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 14:14:08 +0100 Subject: [PATCH 041/285] set selection --- .../workspace/user-group-workspace-editor.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts index 1ebbfd9a87..c229018da0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace-editor.element.ts @@ -33,7 +33,7 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { #onSectionsChange(event: UmbChangeEvent) { event.stopPropagation(); const target = event.target as UmbInputSectionElement; - this.#workspaceContext?.updateProperty('sections', target.value); + this.#workspaceContext?.updateProperty('sections', target.selection); } #onDocumentStartNodeChange(event: CustomEvent) { @@ -98,7 +98,7 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { description=${this.localize.term('user_sectionsHelp')}> Date: Thu, 7 Mar 2024 14:48:42 +0100 Subject: [PATCH 042/285] set value --- .../input-image-cropper/input-image-cropper.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts index f87d1663d4..1172d1863e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts @@ -57,7 +57,7 @@ export class UmbInputImageCropperElement extends UmbLitElement { this.file = file; this.fileUnique = unique; - assignToFrozenObject(this.value, { src: unique }); + this.value = assignToFrozenObject(this.value, { src: unique }); this.#manager?.uploadOne(unique, file, 'waiting'); @@ -70,7 +70,7 @@ export class UmbInputImageCropperElement extends UmbLitElement { } #onRemove = () => { - assignToFrozenObject(this.value, { src: '' }); + this.value = assignToFrozenObject(this.value, { src: '' }); if (!this.fileUnique) return; this.#manager?.removeOne(this.fileUnique); this.fileUnique = undefined; From c9ce4622433ed7026cc73f2fbb5eb04e32f5a161 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 14:55:01 +0100 Subject: [PATCH 043/285] use correct section alias --- .../src/packages/user/user-section/manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts index 81fd811d65..9fb70447c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-section/manifests.ts @@ -1,6 +1,6 @@ import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_USER_MANAGEMENT_SECTION_ALIAS = 'Umb.Section.UserManagement'; +export const UMB_USER_MANAGEMENT_SECTION_ALIAS = 'Umb.Section.Users'; const section: ManifestSection = { type: 'section', From 80acb6dea789a5a38f4675b865c8eacb7593da03 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 15:06:03 +0100 Subject: [PATCH 044/285] make it more explicit that we get the value and not an observable --- .../src/packages/core/repository/repository-items.manager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts index 9c0f969933..3d7b074924 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-items.manager.ts @@ -65,7 +65,7 @@ export class UmbRepositoryItemsManager exte } getUniques(): Array { - return this.#uniques.value; + return this.#uniques.getValue(); } setUniques(uniques: string[]): void { @@ -73,7 +73,7 @@ export class UmbRepositoryItemsManager exte } getItems(): Array { - return this.#items.value; + return this.#items.getValue(); } async #requestItems(): Promise { From d052bc00097dc4c568c401686c337065f24e4ba4 Mon Sep 17 00:00:00 2001 From: JesmoDev Date: Thu, 7 Mar 2024 15:09:31 +0100 Subject: [PATCH 045/285] use config --- ...roperty-editor-ui-image-cropper.element.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts index 90b5cbb01e..c2aa61f1b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts @@ -3,6 +3,7 @@ import { html, customElement, property, nothing } from '@umbraco-cms/backoffice/ import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import '../../components/input-image-cropper/input-image-cropper.element.js'; +import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; /** * @element umb-property-editor-ui-image-cropper @@ -29,21 +30,20 @@ export class UmbPropertyEditorUIImageCropperElement extends UmbLitElement implem } } - // #crops = []; + @property({ attribute: false }) + public set config(config: UmbPropertyEditorConfigCollection | undefined) { + const crops = config?.getValueByAlias('crops') ?? []; - // @property({ attribute: false }) - // public set config(config: UmbPropertyEditorConfigCollection | undefined) { - // this.#crops = config?.getValueByAlias('crops') ?? []; - - // if (!this.value) { - // //TODO: How should we combine the crops from the value with the configuration? - // this.value = { - // crops: this.#crops, - // focalPoint: { left: 0.5, top: 0.5 }, - // src: 'https://picsum.photos/seed/picsum/1920/1080', - // }; - // } - // } + if (!this.value) { + this.value = { + src: '', + crops: crops, + focalPoint: { left: 0.5, top: 0.5 }, + }; + } else { + this.value.crops = crops; + } + } #onChange(e: Event) { this.value = (e.target as any).value; From 7b1789e85b698bffaaf55bd22c12cbe1d8394873 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 16:16:22 +0100 Subject: [PATCH 046/285] Update section-picker-modal.element.ts --- .../section-picker-modal/section-picker-modal.element.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts index 6cc044f303..2c9ce0cdc1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/section-picker-modal/section-picker-modal.element.ts @@ -1,4 +1,5 @@ import type { UmbSectionPickerModalData, UmbSectionPickerModalValue } from './section-picker-modal.token.js'; +import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import type { ManifestSection } from '@umbraco-cms/backoffice/extension-registry'; @@ -20,17 +21,14 @@ export class UmbSectionPickerModalElement extends UmbModalBaseElement< constructor() { super(); - + this.#selectionManager.setSelectable(true); this.observe(this.#selectionManager.selectable, (selectable) => (this._selectable = selectable)); } connectedCallback(): void { super.connectedCallback(); - - // TODO: in theory this config could change during the lifetime of the modal, so we could observe it this.#selectionManager.setMultiple(this.data?.multiple ?? false); - this.#selectionManager.setSelection(this.data?.selection ?? []); - this.#selectionManager.setSelectable(true); + this.#selectionManager.setSelection(this.value?.selection ?? []); this.observe( umbExtensionsRegistry.byType('section'), From 938459f8ceb00ed2abfedfa802ce4c42297e0bf8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 16:23:07 +0100 Subject: [PATCH 047/285] remove story --- .../input-section/input-section.stories.ts | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts deleted file mode 100644 index 67668efe91..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/components/input-section/input-section.stories.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/web-components'; -import './input-section.element.js'; -import type { UmbInputSectionElement } from './input-section.element.js'; - -const meta: Meta = { - title: 'Components/Inputs/Section', - component: 'umb-input-section', - argTypes: { - modalType: { - control: 'inline-radio', - options: ['dialog', 'sidebar'], - defaultValue: 'sidebar', - description: 'The type of modal to use when selecting sections', - }, - modalSize: { - control: 'select', - options: ['small', 'medium', 'large', 'full'], - defaultValue: 'small', - description: 'The size of the modal to use when selecting sections, only applicable to sidebar not dialog', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Overview: Story = { - args: { - modalType: 'sidebar', - }, -}; - -export const WithDialog: Story = { - args: { - modalType: 'dialog', - }, -}; - -export const WithFullSidebar: Story = { - args: { - modalType: 'sidebar', - modalSize: 'full', - }, -}; From 75fac62c7098253e255e1ee4e81764c398721d4e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Mar 2024 16:23:16 +0100 Subject: [PATCH 048/285] cast --- .../core/section/repository/item/section-item.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts index 40d26f4313..af6146ec20 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/repository/item/section-item.repository.ts @@ -23,7 +23,7 @@ export class UmbSectionItemRepository extends UmbRepositoryBase implements UmbIt const sectionManifests = umbExtensionsRegistry .getAllExtensions() .filter((manifest) => manifest.type === 'section') - .filter((manifest) => uniques.includes(manifest.alias)); + .filter((manifest) => uniques.includes(manifest.alias)) as Array; const sectionItems: Array = sectionManifests.map((manifest) => itemMapper(manifest)); From eab377e9b78ad91e21ae21ddd12532aec231a743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 20:14:05 +0100 Subject: [PATCH 049/285] mega refactor --- .../src/packages/core/content-type/index.ts | 2 + .../packages/core/content-type/manifests.ts | 4 ++ .../composition-picker-modal.element.ts | 9 ++-- .../composition-picker-modal.token.ts | 0 .../modals/composition-picker/index.ts | 0 .../core/content-type/modals/index.ts | 1 + .../content-type}/modals/manifests.ts | 2 +- .../core/content-type/repository/index.ts | 5 +- ...nt-type-structure-data-source.interface.ts | 0 .../content-type-structure-repository-base.ts | 0 ...ent-type-structure-repository.interface.ts | 0 ...-type-structure-server-data-source-base.ts | 2 +- .../repository/structure/index.ts | 4 ++ .../src/packages/core/content-type/types.ts | 2 +- ...ontent-type-workspace-context.interface.ts | 29 +++++++++++ .../content-type-workspace.context-token.ts | 11 ++++ .../core/content-type/workspace/index.ts | 2 + .../core/content-type/workspace/manifests.ts | 3 ++ ...workspace-view-edit-properties.element.ts} | 46 ++++++++--------- ...e-workspace-view-edit-property.element.ts} | 12 ++--- ...t-type-workspace-view-edit-tab.element.ts} | 39 +++++++------- ...ntent-type-workspace-view-edit.element.ts} | 51 ++++++++++--------- .../workspace/views/design/manifest.ts | 19 +++++++ .../core/extension-registry/models/index.ts | 11 ++-- .../models/workspace-view.model.ts | 16 +++++- .../src/packages/core/manifests.ts | 2 + .../documents/document-types/manifests.ts | 3 -- .../documents/document-types/modals/index.ts | 1 - ...document-type-detail.server.data-source.ts | 2 +- .../document-type-workspace.context.ts | 17 ++++--- .../document-types/workspace/manifests.ts | 15 +++--- .../media-type-detail.server.data-source.ts | 2 +- .../member-type-detail.server.data-source.ts | 2 +- .../member-type-workspace.context.ts | 2 +- 34 files changed, 203 insertions(+), 113 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types => core/content-type}/modals/composition-picker/composition-picker-modal.element.ts (96%) rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types => core/content-type}/modals/composition-picker/composition-picker-modal.token.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types => core/content-type}/modals/composition-picker/index.ts (100%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types => core/content-type}/modals/manifests.ts (86%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/{ => structure}/content-type-structure-data-source.interface.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/{ => structure}/content-type-structure-repository-base.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/{ => structure}/content-type-structure-repository.interface.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/{ => structure}/content-type-structure-server-data-source-base.ts (96%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace.context-token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/manifests.ts rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts => core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts} (81%) rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts => core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts} (96%) rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts => core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts} (84%) rename src/Umbraco.Web.UI.Client/src/packages/{documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts => core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts} (88%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts index d33d503388..6f9d7d26e5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/index.ts @@ -1,4 +1,6 @@ export * from './components/index.js'; +export * from './modals/index.js'; export * from './repository/index.js'; export * from './structure/index.js'; export * from './types.js'; +export * from './workspace/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/manifests.ts new file mode 100644 index 0000000000..824f90f7ad --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/manifests.ts @@ -0,0 +1,4 @@ +import { manifests as workspaceManifests } from './workspace/manifests.js'; +import { manifests as modalManifests } from './modals/manifests.js'; + +export const manifests = [...workspaceManifests, ...modalManifests]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/composition-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.element.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/composition-picker-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.element.ts index 832ee49263..9b9d6dfc46 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/composition-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.element.ts @@ -1,13 +1,13 @@ -import { UmbDocumentTypeCompositionRepository } from '../../repository/index.js'; import type { UmbCompositionPickerModalData, UmbCompositionPickerModalValue, } from './composition-picker-modal.token.js'; import { css, html, customElement, state, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { - UmbDocumentTypeCompositionCompatibleModel, - UmbDocumentTypeCompositionReferenceModel, +import { + UmbDocumentTypeCompositionRepository, + type UmbDocumentTypeCompositionCompatibleModel, + type UmbDocumentTypeCompositionReferenceModel, } from '@umbraco-cms/backoffice/document-type'; interface CompatibleCompositions { @@ -20,6 +20,7 @@ export class UmbCompositionPickerModalElement extends UmbModalBaseElement< UmbCompositionPickerModalData, UmbCompositionPickerModalValue > { + // TODO: Loosen this from begin specific to Document Types: #compositionRepository = new UmbDocumentTypeCompositionRepository(this); #unique?: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/composition-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/composition-picker-modal.token.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/composition-picker/index.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts new file mode 100644 index 0000000000..4cdc48a7a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts @@ -0,0 +1 @@ +export * from './composition-picker/composition-picker-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts similarity index 86% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/manifests.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts index 9d3ad5e18f..538bbfb0c3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts @@ -4,7 +4,7 @@ const modals: Array = [ { type: 'modal', alias: 'Umb.Modal.CompositionPicker', - name: 'Block Catalogue Modal', + name: 'ContentType Composition Picker Modal', js: () => import('./composition-picker/composition-picker-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts index d1a5aa8c6b..c590b6690d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/index.ts @@ -1,4 +1 @@ -export * from './content-type-structure-repository-base.js'; -export * from './content-type-structure-repository.interface.js'; -export * from './content-type-structure-server-data-source-base.js'; -export * from './content-type-structure-data-source.interface.js'; +export * from './structure/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-data-source.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-data-source.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-data-source.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-data-source.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-repository-base.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-repository-base.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-repository.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-repository.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-repository.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-server-data-source-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-server-data-source-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts index a9e8896cce..262330e804 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/content-type-structure-server-data-source-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/content-type-structure-server-data-source-base.ts @@ -1,4 +1,4 @@ -import type { UmbPagedModel } from '../../repository/types.js'; +import type { UmbPagedModel } from '../../../repository/types.js'; import type { UmbContentTypeStructureDataSource } from './content-type-structure-data-source.interface.js'; import type { AllowedContentTypeModel, ItemResponseModelBaseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/index.ts new file mode 100644 index 0000000000..d1a5aa8c6b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/repository/structure/index.ts @@ -0,0 +1,4 @@ +export * from './content-type-structure-repository-base.js'; +export * from './content-type-structure-repository.interface.js'; +export * from './content-type-structure-server-data-source-base.js'; +export * from './content-type-structure-data-source.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/types.ts index 9ef9820ce7..22f3f1c9f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/types.ts @@ -14,7 +14,7 @@ export interface UmbContentTypeModel { unique: string; name: string; alias: string; - description: string | null; + description: string; icon: string; allowedAtRoot: boolean; variesByCulture: boolean; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts new file mode 100644 index 0000000000..949fb29eac --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts @@ -0,0 +1,29 @@ +import type { UmbContentTypeCompositionModel, UmbContentTypeModel, UmbContentTypeSortModel } from '../types.js'; +import type { UmbContentTypePropertyStructureManager } from '../structure/index.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; + +export interface UmbContentTypeWorkspaceContext + extends UmbSaveableWorkspaceContextInterface { + readonly name: Observable; + readonly alias: Observable; + readonly description: Observable; + readonly icon: Observable; + + readonly allowedAtRoot: Observable; + readonly variesByCulture: Observable; + readonly variesBySegment: Observable; + readonly isElement: Observable; + readonly allowedContentTypes: Observable; + readonly compositions: Observable; + + readonly structure: UmbContentTypePropertyStructureManager; + readonly isSorting: Observable; + + getIsSorting(): boolean; + setIsSorting(isSorting: boolean): void; + + setAlias(alias: string): void; + + setCompositions(compositions: Array): void; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace.context-token.ts new file mode 100644 index 0000000000..ce5f5a9ebe --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace.context-token.ts @@ -0,0 +1,11 @@ +import type { UmbContentTypeWorkspaceContext } from './content-type-workspace-context.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_CONTENT_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< + UmbContentTypeWorkspaceContext, + UmbContentTypeWorkspaceContext +>( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbContentTypeWorkspaceContext => (context as any).IS_CONTENT_TYPE_WORKSPACE_CONTEXT, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/index.ts new file mode 100644 index 0000000000..1a9af36bce --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/index.ts @@ -0,0 +1,2 @@ +export type * from './content-type-workspace-context.interface.js'; +export * from './content-type-workspace.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/manifests.ts new file mode 100644 index 0000000000..5ae405e6ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/manifests.ts @@ -0,0 +1,3 @@ +import { contentTypeDesignEditorManifest } from './views/design/manifest.js'; + +export const manifests = [contentTypeDesignEditorManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts similarity index 81% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index fef8a6550f..363d7f2b96 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -1,17 +1,19 @@ -import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; -import './document-type-workspace-view-edit-property.element.js'; -import type { UmbDocumentTypeDetailModel } from '../../../types.js'; -import type { UmbDocumentTypeWorkspacePropertyElement } from './document-type-workspace-view-edit-property.element.js'; +import './content-type-workspace-view-edit-property.element.js'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; +import type { UmbContentTypeWorkspacePropertyElement } from './content-type-workspace-view-edit-property.element.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; +import type { + UmbContentTypeModel, + UmbPropertyContainerTypes, + UmbPropertyTypeModel, +} from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; -const SORTER_CONFIG: UmbSorterConfig = { +const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { return element.getAttribute('data-umb-property-id'); }, @@ -19,7 +21,7 @@ const SORTER_CONFIG: UmbSorterConfig(this, { +@customElement('umb-content-type-workspace-view-edit-properties') +export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElement { + #sorter = new UmbSorterController(this, { ...SORTER_CONFIG, onChange: ({ model }) => { this._propertyStructure = model; @@ -108,13 +110,13 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle this._propertyStructureHelper.setContainerType(value); } - _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); + _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); @state() _propertyStructure: Array = []; @state() - _ownerDocumentTypes?: UmbDocumentTypeDetailModel[]; + _ownerDocumentTypes?: UmbContentTypeModel[]; @state() protected _modalRouteNewProperty?: string; @@ -127,12 +129,10 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle this.#sorter.disable(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, async (workspaceContext) => { - this._propertyStructureHelper.setStructureManager( - (workspaceContext as UmbDocumentTypeWorkspaceContext).structure, - ); + this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, async (workspaceContext) => { + this._propertyStructureHelper.setStructureManager(workspaceContext.structure); this.observe( - (workspaceContext as UmbDocumentTypeWorkspaceContext).isSorting, + workspaceContext.isSorting, (isSorting) => { this._sortModeActive = isSorting; if (isSorting) { @@ -197,7 +197,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle ); return html` - { this._propertyStructureHelper.removeProperty(property.id); }}> - + `; }, )} @@ -235,7 +235,7 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle width: 100%; } - #property-list[sort-mode-active]:not(:has(umb-document-type-workspace-view-edit-property)) { + #property-list[sort-mode-active]:not(:has(umb-content-type-workspace-view-edit-property)) { /* Some height so that the sorter can target the area if the group is empty*/ min-height: var(--uui-size-layout-1); } @@ -243,10 +243,10 @@ export class UmbDocumentTypeWorkspaceViewEditPropertiesElement extends UmbLitEle ]; } -export default UmbDocumentTypeWorkspaceViewEditPropertiesElement; +export default UmbContentTypeWorkspaceViewEditPropertiesElement; declare global { interface HTMLElementTagNameMap { - 'umb-document-type-workspace-view-edit-properties': UmbDocumentTypeWorkspaceViewEditPropertiesElement; + 'umb-content-type-workspace-view-edit-properties': UmbContentTypeWorkspaceViewEditPropertiesElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts similarity index 96% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts index 2d09aa82bb..5c9f4be5b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts @@ -14,12 +14,12 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type'; /** - * @element umb-document-type-workspace-view-edit-property + * @element umb-content-type-workspace-view-edit-property * @description - Element for displaying a property in an workspace. * @slot editor - Slot for rendering the Property Editor */ -@customElement('umb-document-type-workspace-view-edit-property') -export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { +@customElement('umb-content-type-workspace-view-edit-property') +export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { // #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); @@ -47,8 +47,8 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { private _property?: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined; /** - * Inherited, Determines if the property is part of the main document type thats being edited. - * If true, then the property is inherited from another document type, not a part of the main document type. + * Inherited, Determines if the property is part of the main content type thats being edited. + * If true, then the property is inherited from another content type, not a part of the main content type. * @type {boolean} * @attr * @default undefined @@ -470,6 +470,6 @@ export class UmbDocumentTypeWorkspacePropertyElement extends UmbLitElement { declare global { interface HTMLElementTagNameMap { - 'umb-document-type-workspace-view-edit-property': UmbDocumentTypeWorkspacePropertyElement; + 'umb-content-type-workspace-view-edit-property': UmbContentTypeWorkspacePropertyElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 6266481764..baf0532c9e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -1,32 +1,31 @@ -import type { UmbDocumentTypeDetailModel } from '../../../types.js'; -import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; -import type { UmbDocumentTypeWorkspaceViewEditPropertiesElement } from './document-type-workspace-view-edit-properties.element.js'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; +import type { UmbContentTypeWorkspaceViewEditPropertiesElement } from './content-type-workspace-view-edit-properties.element.js'; import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbContentTypeContainerStructureHelper, + type UmbContentTypeModel, type UmbPropertyTypeContainerModel, } from '@umbraco-cms/backoffice/content-type'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import './document-type-workspace-view-edit-properties.element.js'; +import './content-type-workspace-view-edit-properties.element.js'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -const SORTER_CONFIG: UmbSorterConfig = +const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => - element.querySelector('umb-document-type-workspace-view-edit-properties')!.getAttribute('container-id'), + element.querySelector('umb-content-type-workspace-view-edit-properties')!.getAttribute('container-id'), getUniqueOfModel: (modelEntry) => modelEntry.id, identifier: 'document-type-container-sorter', itemSelector: '.container-handle', containerSelector: '.container-list', }; -@customElement('umb-document-type-workspace-view-edit-tab') -export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { - #sorter = new UmbSorterController( +@customElement('umb-content-type-workspace-view-edit-tab') +export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { + #sorter = new UmbSorterController( this, { ...SORTER_CONFIG, @@ -122,7 +121,7 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { @state() _sortModeActive?: boolean; - #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); + #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); constructor() { super(); @@ -130,10 +129,10 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { this.#groupStructureHelper.setParentType('Tab'); // TODO: Use a structured/? workspace context token... - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { - this.#groupStructureHelper.setStructureManager((context as UmbDocumentTypeWorkspaceContext).structure); + this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (context) => { + this.#groupStructureHelper.setStructureManager(context.structure); this.observe( - (context as UmbDocumentTypeWorkspaceContext).isSorting, + context.isSorting, (isSorting) => { this._sortModeActive = isSorting; if (isSorting) { @@ -176,10 +175,10 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { !this._noTabName ? html` - + container-name=${this.tabName ?? ''}> ` : '' @@ -193,10 +192,10 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { html` ${this.#renderContainerHeader(group)} - + container-name=${group.name ?? ''}> `, )} @@ -316,10 +315,10 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { ]; } -export default UmbDocumentTypeWorkspaceViewEditTabElement; +export default UmbContentTypeWorkspaceViewEditTabElement; declare global { interface HTMLElementTagNameMap { - 'umb-document-type-workspace-view-edit-tab': UmbDocumentTypeWorkspaceViewEditTabElement; + 'umb-content-type-workspace-view-edit-tab': UmbContentTypeWorkspaceViewEditTabElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts similarity index 88% rename from src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 58951d2334..26a5e3cfa1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/design/document-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -1,17 +1,18 @@ -import { UMB_COMPOSITION_PICKER_MODAL, type UmbCompositionPickerModalData } from '../../../modals/index.js'; -import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; -import type { UmbDocumentTypeDetailModel } from '../../../types.js'; -import type { UmbDocumentTypeWorkspaceViewEditTabElement } from './document-type-workspace-view-edit-tab.element.js'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; +import type { UmbContentTypeWorkspaceViewEditTabElement } from './content-type-workspace-view-edit-tab.element.js'; import { css, html, customElement, state, repeat, nothing, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UUIInputElement, UUIInputEvent, UUITabElement } from '@umbraco-cms/backoffice/external/uui'; -import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; +import { + UMB_COMPOSITION_PICKER_MODAL, + UmbContentTypeContainerStructureHelper, + type UmbContentTypeModel, +} from '@umbraco-cms/backoffice/content-type'; import { encodeFolderName } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { CompositionTypeModel, type PropertyTypeContainerModelBaseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbRoute, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; @@ -19,8 +20,8 @@ import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoff import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -@customElement('umb-document-type-workspace-view-edit') -export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { +@customElement('umb-content-type-workspace-view-edit') +export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => element.getAttribute('data-umb-tabs-id'), getUniqueOfModel: (tab) => tab.id, @@ -93,12 +94,12 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple @state() private _buttonDisabled: boolean = false; - private _workspaceContext?: UmbDocumentTypeWorkspaceContext; + private _workspaceContext?: (typeof UMB_CONTENT_TYPE_WORKSPACE_CONTEXT)['TYPE']; - private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); + private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); @state() - private _compositionConfiguration?: UmbCompositionPickerModalData; + private _compositionConfiguration?: (typeof UMB_COMPOSITION_PICKER_MODAL)['DATA']; constructor() { super(); @@ -117,11 +118,11 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple // _hasRootProperties can be gotten via _tabsStructureHelper.hasProperties. But we do not support root properties currently. - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._workspaceContext = workspaceContext as UmbDocumentTypeWorkspaceContext; - this._tabsStructureHelper.setStructureManager((workspaceContext as UmbDocumentTypeWorkspaceContext).structure); + this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => { + this._workspaceContext = workspaceContext; + this._tabsStructureHelper.setStructureManager(workspaceContext.structure); this.observe( - this._workspaceContext.isSorting, + workspaceContext.isSorting, (isSorting) => { this._sortModeActive = isSorting; if (isSorting) { @@ -133,10 +134,10 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple '_observeIsSorting', ); - const unique = this._workspaceContext.getUnique(); + const unique = workspaceContext.getUnique(); //TODO Figure out the correct data that needs to be sent to the compositions modal. Do we really have to send isElement, currentPropertyAliases - isn't unique enough? - this.observe(this._workspaceContext.structure.contentTypes, (contentTypes) => { + this.observe(workspaceContext.structure.contentTypes, (contentTypes) => { this._compositionConfiguration = { unique: unique ?? '', selection: contentTypes.map((contentType) => contentType.unique).filter((id) => id !== unique), @@ -175,10 +176,10 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple const tabName = tab.name ?? ''; routes.push({ path: `tab/${encodeFolderName(tabName).toString()}`, - component: () => import('./document-type-workspace-view-edit-tab.element.js'), + component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { - (component as UmbDocumentTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbDocumentTypeWorkspaceViewEditTabElement).ownerTabId = + (component as UmbContentTypeWorkspaceViewEditTabElement).tabName = tabName; + (component as UmbContentTypeWorkspaceViewEditTabElement).ownerTabId = //tab.parent ? tab.parent.id === null this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, @@ -188,10 +189,10 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple routes.push({ path: 'root', - component: () => import('./document-type-workspace-view-edit-tab.element.js'), + component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { - (component as UmbDocumentTypeWorkspaceViewEditTabElement).noTabName = true; - (component as UmbDocumentTypeWorkspaceViewEditTabElement).ownerTabId = null; + (component as UmbContentTypeWorkspaceViewEditTabElement).noTabName = true; + (component as UmbContentTypeWorkspaceViewEditTabElement).ownerTabId = null; }, }); @@ -553,10 +554,10 @@ export class UmbDocumentTypeWorkspaceViewEditElement extends UmbLitElement imple ]; } -export default UmbDocumentTypeWorkspaceViewEditElement; +export default UmbContentTypeWorkspaceViewEditElement; declare global { interface HTMLElementTagNameMap { - 'umb-document-type-workspace-view-edit': UmbDocumentTypeWorkspaceViewEditElement; + 'umb-content-type-workspace-view-edit': UmbContentTypeWorkspaceViewEditElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts new file mode 100644 index 0000000000..e099cf7c32 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts @@ -0,0 +1,19 @@ +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const contentTypeDesignEditorManifest: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.WorkspaceView.ContentTypeDesignEditor', + matchKind: 'contentTypeDesign', + matchType: 'workspaceView', + manifest: { + type: 'workspaceView', + kind: 'contentTypeDesign', + element: () => import('./content-type-workspace-view-edit.element.js'), + weight: 1000, + meta: { + label: 'Design', + pathname: 'design', + icon: 'icon-document-dashed-line', + }, + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts index 0f931013a6..fe55f13d4a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/index.ts @@ -44,7 +44,10 @@ import type { ManifestWorkspaceAction, ManifestWorkspaceActionDefaultKind } from import type { ManifestWorkspaceActionMenuItem } from './workspace-action-menu-item.model.js'; import type { ManifestWorkspaceContext } from './workspace-context.model.js'; import type { ManifestWorkspaceFooterApp } from './workspace-footer-app.model.js'; -import type { ManifestWorkspaceView } from './workspace-view.model.js'; +import type { + ManifestWorkspaceView, + ManifestWorkspaceViewContentTypeDesignEditorKind, +} from './workspace-view.model.js'; import type { ManifestEntityUserPermission } from './entity-user-permission.model.js'; import type { ManifestGranularUserPermission } from './user-granular-permission.model.js'; import type { ManifestCollectionAction } from './collection-action.model.js'; @@ -107,9 +110,11 @@ export type ManifestEntityActions = | ManifestEntityActionDeleteFolderKind | ManifestEntityActionTrashKind; +export type ManifestPropertyActions = ManifestPropertyAction | ManifestPropertyActionDefaultKind; + export type ManifestWorkspaceActions = ManifestWorkspaceAction | ManifestWorkspaceActionDefaultKind; -export type ManifestPropertyActions = ManifestPropertyAction | ManifestPropertyActionDefaultKind; +export type ManifestWorkspaceViews = ManifestWorkspaceView | ManifestWorkspaceViewContentTypeDesignEditorKind; export type ManifestTypes = | ManifestBundle @@ -157,7 +162,7 @@ export type ManifestTypes = | ManifestWorkspaceActionMenuItem | ManifestWorkspaceContext | ManifestWorkspaceFooterApp - | ManifestWorkspaceView + | ManifestWorkspaceViews | ManifestEntityUserPermission | ManifestGranularUserPermission | ManifestBase; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts index 3f3c20468f..7a2ce47a34 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts @@ -1,9 +1,21 @@ import type { ConditionTypes } from '../conditions/types.js'; import type { UmbWorkspaceViewElement } from '../interfaces/workspace-view-element.interface.js'; -import type { ManifestWithDynamicConditions, ManifestWithView } from '@umbraco-cms/backoffice/extension-api'; +import type { + ManifestWithDynamicConditions, + ManifestWithView, + MetaManifestWithView, +} from '@umbraco-cms/backoffice/extension-api'; -export interface ManifestWorkspaceView +export interface ManifestWorkspaceView extends ManifestWithView, ManifestWithDynamicConditions { type: 'workspaceView'; + meta: MetaType; +} + +export interface MetaWorkspaceView extends MetaManifestWithView {} + +export interface ManifestWorkspaceViewContentTypeDesignEditorKind extends ManifestWorkspaceView { + type: 'workspaceView'; + kind: 'contentTypeDesign'; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts index 460ad72c22..1a8e2bc6ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/manifests.ts @@ -1,5 +1,6 @@ import { manifests as collectionManifests } from './collection/manifests.js'; import { manifests as cultureManifests } from './culture/manifests.js'; +import { manifests as contentTypeManifests } from './content-type/manifests.js'; import { manifests as debugManifests } from './debug/manifests.js'; import { manifests as entityActionManifests } from './entity-action/manifests.js'; import { manifests as extensionManifests } from './extension-registry/manifests.js'; @@ -18,6 +19,7 @@ import type { ManifestTypes, UmbBackofficeManifestKind } from './extension-regis export const manifests: Array = [ ...collectionManifests, ...cultureManifests, + ...contentTypeManifests, ...debugManifests, ...entityActionManifests, ...extensionManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/manifests.ts index 6da5e3f738..f1a81fd7d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/manifests.ts @@ -5,8 +5,6 @@ import { manifests as repositoryManifests } from './repository/manifests.js'; import { manifests as treeManifests } from './tree/manifests.js'; import { manifests as workspaceManifests } from './workspace/manifests.js'; -import { manifests as modalManifests } from './modals/manifests.js'; - export const manifests = [ ...entityActionsManifests, ...menuItemManifests, @@ -14,5 +12,4 @@ export const manifests = [ ...repositoryManifests, ...treeManifests, ...workspaceManifests, - ...modalManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/index.ts index cb78b5f545..2bb340714e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/modals/index.ts @@ -1,2 +1 @@ -export * from './composition-picker/index.js'; export * from './document-type-picker-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts index ab696cdcda..1d6e7f12a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/repository/detail/document-type-detail.server.data-source.ts @@ -89,7 +89,7 @@ export class UmbDocumentTypeDetailServerDataSource implements UmbDetailDataSourc unique: data.id, name: data.name, alias: data.alias, - description: data.description || null, + description: data.description ?? '', icon: data.icon, allowedAtRoot: data.allowedAsRoot, variesByCulture: data.variesByCulture, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index d2951889a3..75ee6ebe22 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -4,10 +4,13 @@ import type { UmbDocumentTypeDetailModel } from '../types.js'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; import { UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; -import type { UmbContentTypeCompositionModel, UmbContentTypeSortModel } from '@umbraco-cms/backoffice/content-type'; +import type { + UmbContentTypeCompositionModel, + UmbContentTypeSortModel, + UmbContentTypeWorkspaceContext, +} from '@umbraco-cms/backoffice/content-type'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; @@ -15,7 +18,7 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbDocumentTypeDetailModel; export class UmbDocumentTypeWorkspaceContext extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface + implements UmbContentTypeWorkspaceContext { // readonly repository = new UmbDocumentTypeDetailRepository(this); @@ -25,7 +28,7 @@ export class UmbDocumentTypeWorkspaceContext #persistedData = new UmbObjectState(undefined); // General for content types: - readonly data; + //readonly data; readonly name; readonly alias; readonly description; @@ -46,14 +49,14 @@ export class UmbDocumentTypeWorkspaceContext readonly structure = new UmbContentTypePropertyStructureManager(this, this.repository); - #isSorting = new UmbBooleanState(undefined); + #isSorting = new UmbBooleanState(false); isSorting = this.#isSorting.asObservable(); constructor(host: UmbControllerHost) { super(host, 'Umb.Workspace.DocumentType'); // General for content types: - this.data = this.structure.ownerContentType; + //this.data = this.structure.ownerContentType; this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name); this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias); this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description); @@ -75,7 +78,7 @@ export class UmbDocumentTypeWorkspaceContext protected resetState(): void { super.resetState(); this.#persistedData.setValue(undefined); - this.#isSorting.setValue(undefined); + this.#isSorting.setValue(false); } getIsSorting() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts index 6468da3c54..f960265d14 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts @@ -2,7 +2,7 @@ import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; import type { ManifestWorkspace, ManifestWorkspaceActions, - ManifestWorkspaceView, + ManifestWorkspaceViews, } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_DOCUMENT_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.DocumentType'; @@ -11,19 +11,18 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.DocumentType', name: 'Document Type Workspace', - js: () => import('./document-type-workspace.element.js'), + element: () => import('./document-type-workspace.element.js'), meta: { entityType: 'document-type', }, }; -const workspaceViews: Array = [ +const workspaceViews: Array = [ { type: 'workspaceView', + kind: 'contentTypeDesign', alias: 'Umb.WorkspaceView.DocumentType.Design', name: 'Document Type Workspace Design View', - js: () => import('./views/design/document-type-workspace-view-edit.element.js'), - weight: 1000, meta: { label: 'Design', pathname: 'design', @@ -40,7 +39,7 @@ const workspaceViews: Array = [ type: 'workspaceView', alias: 'Umb.WorkspaceView.DocumentType.Structure', name: 'Document Type Workspace Structure View', - js: () => import('./views/structure/document-type-workspace-view-structure.element.js'), + element: () => import('./views/structure/document-type-workspace-view-structure.element.js'), weight: 800, meta: { label: 'Structure', @@ -58,7 +57,7 @@ const workspaceViews: Array = [ type: 'workspaceView', alias: 'Umb.WorkspaceView.DocumentType.Settings', name: 'Document Type Workspace Settings View', - js: () => import('./views/settings/document-type-workspace-view-settings.element.js'), + element: () => import('./views/settings/document-type-workspace-view-settings.element.js'), weight: 600, meta: { label: 'Settings', @@ -76,7 +75,7 @@ const workspaceViews: Array = [ type: 'workspaceView', alias: 'Umb.WorkspaceView.DocumentType.Templates', name: 'Document Type Workspace Templates View', - js: () => import('./views/templates/document-type-workspace-view-templates.element.js'), + element: () => import('./views/templates/document-type-workspace-view-templates.element.js'), weight: 400, meta: { label: 'Templates', diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts index 9837b7d73c..c44c3003df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts @@ -79,7 +79,7 @@ export class UmbMediaTypeServerDataSource implements UmbDetailDataSource Date: Thu, 7 Mar 2024 20:16:47 +0100 Subject: [PATCH 050/285] fix token --- .../workspace/content-type-workspace-context.interface.ts | 2 ++ .../document-types/workspace/document-type-workspace.context.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts index 949fb29eac..ab72d6db64 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts @@ -5,6 +5,8 @@ import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffi export interface UmbContentTypeWorkspaceContext extends UmbSaveableWorkspaceContextInterface { + readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT: true; + readonly name: Observable; readonly alias: Observable; readonly description: Observable; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index 75ee6ebe22..26dd72bfe4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -20,6 +20,7 @@ export class UmbDocumentTypeWorkspaceContext extends UmbEditableWorkspaceContextBase implements UmbContentTypeWorkspaceContext { + readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true; // readonly repository = new UmbDocumentTypeDetailRepository(this); // Data/Draft is located in structure manager From 244a942dc39cd5bb53b3fd79b9af0c01b87b7096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 20:47:46 +0100 Subject: [PATCH 051/285] remove debugging --- .../design/content-type-workspace-view-edit-tab.element.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index baf0532c9e..f3665f68f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -170,7 +170,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { look="placeholder">` : '' } - ${this.ownerTabId} ${ !this._noTabName ? html` @@ -184,7 +183,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { : '' }
-
${JSON.stringify(this._groups)}
${repeat( this._groups, (group) => group.id, @@ -226,7 +224,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { } else { return html`
${inherited ? html`` : this.#renderInputGroupName(group)} - (${group.parent?.id}/${group.id})
`; } } From b9423debb8ada98f50db1c086823e8cab7a4e33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 21:05:56 +0100 Subject: [PATCH 052/285] ue insert property --- .../content-type-property-structure-helper.class.ts | 4 ++-- ...tent-type-workspace-view-edit-properties.element.ts | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 23840b9a4b..fd1a13f8f4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -119,11 +119,11 @@ export class UmbContentTypePropertyStructureHelper { - this.#addProperty(value as UmbPropertyTypeModel); + // TODO: The model requires a data-type to be set, we cheat currently. But this should be re-though when we implement validation(As we most likely will have to com up with partial models for the runtime model.) [NL] + this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); }) .observeRouteBuilder((routeBuilder) => { this._modalRouteNewProperty = routeBuilder(null); }); } - async #addProperty(propertyData: UmbPropertyTypeModel) { - const propertyPlaceholder = await this._propertyStructureHelper.addProperty(this._containerId); - if (!propertyPlaceholder) return; - - this._propertyStructureHelper.partialUpdateProperty(propertyPlaceholder.id, propertyData); - } - render() { return html`
From 6b2afbbb63e281481f16ef8497406560a183fdbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 7 Mar 2024 21:57:59 +0100 Subject: [PATCH 053/285] group element --- ...-type-workspace-view-edit-group.element.ts | 178 +++++++++++++++++ ...nt-type-workspace-view-edit-tab.element.ts | 186 ++++++------------ .../packages/core/sorter/sorter.controller.ts | 31 +-- 3 files changed, 253 insertions(+), 142 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts new file mode 100644 index 0000000000..ecc5a7260c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -0,0 +1,178 @@ +import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import type { + UmbContentTypeContainerStructureHelper, + UmbContentTypeModel, + UmbPropertyTypeContainerModel, +} from '@umbraco-cms/backoffice/content-type'; + +import './content-type-workspace-view-edit-properties.element.js'; + +@customElement('umb-content-type-workspace-view-edit-group') +export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { + private _ownerGroupId?: string | null; + + /* + @property({ type: String }) + public get ownerGroupId(): string | null | undefined { + return this._ownerGroupId; + } + public set ownerGroupId(value: string | null | undefined) { + if (value === this._ownerGroupId) return; + const oldValue = this._ownerGroupId; + this._ownerGroupId = value; + this.requestUpdate('ownerGroupId', oldValue); + } + private _groupName?: string | undefined; + + @property({ type: String }) + public get groupName(): string | undefined { + return this._groupName; + } + public set groupName(value: string | undefined) { + if (value === this._groupName) return; + const oldValue = this._groupName; + this._groupName = value; + this.requestUpdate('groupName', oldValue); + } + */ + + @property({ attribute: false }) + public set group(value: UmbPropertyTypeContainerModel | undefined) { + if (value === this._group) return; + this._group = value; + this.#checkInherited(); + } + public get group(): UmbPropertyTypeContainerModel | undefined { + return this._group; + } + private _group?: UmbPropertyTypeContainerModel | undefined; + + @property({ attribute: false }) + public set groupStructureHelper(value: UmbContentTypeContainerStructureHelper | undefined) { + if (value === this._groupStructureHelper) return; + this._groupStructureHelper = value; + this.#checkInherited(); + } + public get groupStructureHelper(): UmbContentTypeContainerStructureHelper | undefined { + return this._groupStructureHelper; + } + private _groupStructureHelper?: UmbContentTypeContainerStructureHelper | undefined; + + @property({ type: Boolean, attribute: 'sort-mode-active', reflect: true }) + sortModeActive = false; + + @state() + _inherited?: boolean; + + constructor() { + super(); + } + + #checkInherited() { + if (this.groupStructureHelper && this.group) { + this._inherited = !this.groupStructureHelper.isOwnerChildContainer(this.group.id); + } + } + + /* + _partialUpdate(partialObject: Partial) { + this.dispatchEvent(new CustomEvent('umb:partial-group-update', { detail: partialObject })); + } + */ + + _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { + const partialObject = {} as any; + partialObject[propertyName] = value; + + this.dispatchEvent(new CustomEvent('umb:partial-group-update', { detail: partialObject })); + } + + render() { + return this._inherited !== undefined + ? html` + + ${this.#renderContainerHeader()} + + + ` + : ''; + } + + #renderContainerHeader() { + if (this.sortModeActive) { + return html`
+
+ + ${this.#renderInputGroupName()} +
+ this._singleValueUpdate('sortOrder', parseInt(e.target.value as string) || 0)} + .value=${this.group!.sortOrder ?? 0} + ?disabled=${this._inherited}> +
`; + } else { + return html`
+ ${this._inherited ? html`` : this.#renderInputGroupName()} +
`; + } + } + + #renderInputGroupName() { + return html` { + const newName = (e.target as HTMLInputElement).value; + this._singleValueUpdate('name', newName); + }}>`; + } + + static styles = [ + css` + :host([drag-placeholder]) { + opacity: 0.5; + } + + :host([drag-placeholder]) > * { + visibility: hidden; + } + + div[slot='header'] { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + } + + div[slot='header'] > div { + display: flex; + align-items: center; + gap: var(--uui-size-3); + } + + uui-input[type='number'] { + max-width: 75px; + } + + :host([sort-mode-active]) div[slot='header'] { + cursor: grab; + } + `, + ]; +} + +export default UmbContentTypeWorkspaceViewEditGroupElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-content-type-workspace-view-edit-group': UmbContentTypeWorkspaceViewEditGroupElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index f3665f68f5..a522973ea2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -1,9 +1,7 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; -import type { UmbContentTypeWorkspaceViewEditPropertiesElement } from './content-type-workspace-view-edit-properties.element.js'; -import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-workspace-view-edit-group.element.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbContentTypeContainerStructureHelper, type UmbContentTypeModel, @@ -11,65 +9,61 @@ import { } from '@umbraco-cms/backoffice/content-type'; import './content-type-workspace-view-edit-properties.element.js'; +import './content-type-workspace-view-edit-group.element.js'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -const SORTER_CONFIG: UmbSorterConfig = - { - getUniqueOfElement: (element) => - element.querySelector('umb-content-type-workspace-view-edit-properties')!.getAttribute('container-id'), - getUniqueOfModel: (modelEntry) => modelEntry.id, - identifier: 'document-type-container-sorter', - itemSelector: '.container-handle', - containerSelector: '.container-list', - }; +const SORTER_CONFIG: UmbSorterConfig = { + getUniqueOfElement: (element) => element.group?.id, + getUniqueOfModel: (modelEntry) => modelEntry.id, + identifier: 'document-type-container-sorter', + itemSelector: '.container-handle', + containerSelector: '.container-list', +}; @customElement('umb-content-type-workspace-view-edit-tab') export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { - #sorter = new UmbSorterController( - this, - { - ...SORTER_CONFIG, - onChange: ({ model }) => { - this._groups = model; - }, - onEnd: ({ item }) => { - /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. - * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update - * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... - */ - const model = this._groups; - const newIndex = model.findIndex((entry) => entry.id === item.id); - - // Doesn't exist in model - if (newIndex === -1) return; - - // First in list - if (newIndex === 0 && model.length > 1) { - this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: model[1].sortOrder - 1 }); - return; - } - - // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; - - let weight = 1; - this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: prevItemSortOrder + weight }); - - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this.#groupStructureHelper.partialUpdateContainer(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; - }); - } - }, + #sorter = new UmbSorterController(this, { + ...SORTER_CONFIG, + onChange: ({ model }) => { + this._groups = model; }, - ); + onEnd: ({ item }) => { + /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. + * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update + * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... + */ + const model = this._groups; + const newIndex = model.findIndex((entry) => entry.id === item.id); + + // Doesn't exist in model + if (newIndex === -1) return; + + // First in list + if (newIndex === 0 && model.length > 1) { + this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: model[1].sortOrder - 1 }); + return; + } + + // Not first in list + if (newIndex > 0 && model.length > 1) { + const prevItemSortOrder = model[newIndex - 1].sortOrder; + + let weight = 1; + this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: prevItemSortOrder + weight }); + + // Check for overlaps + model.some((entry, index) => { + if (index <= newIndex) return; + if (entry.sortOrder === prevItemSortOrder + weight) { + weight++; + this.#groupStructureHelper.partialUpdateContainer(entry.id, { sortOrder: prevItemSortOrder + weight }); + } + // Break the loop + return true; + }); + } + }, + }); private _ownerTabId?: string | null; @@ -187,14 +181,12 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this._groups, (group) => group.id, (group) => - html` - - ${this.#renderContainerHeader(group)} - - `, + html` + `, )}
${this.#renderAddGroupButton()} @@ -202,43 +194,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { `; } - #renderContainerHeader(group: UmbPropertyTypeContainerModel) { - const inherited = !this.#groupStructureHelper.isOwnerChildContainer(group.id); - - if (this._sortModeActive) { - return html`
-
- - ${this.#renderInputGroupName(group)} -
- - this.#groupStructureHelper.partialUpdateContainer(group.id, { - sortOrder: parseInt(e.target.value as string) || 0, - })} - .value=${group.sortOrder ?? 0} - ?disabled=${inherited}> -
`; - } else { - return html`
- ${inherited ? html`` : this.#renderInputGroupName(group)} -
`; - } - } - - #renderInputGroupName(group: UmbPropertyTypeContainerModel) { - return html` { - const newName = (e.target as HTMLInputElement).value; - this.#groupStructureHelper.updateContainerName(group.id, group.parent?.id ?? null, newName); - }}>`; - } - #renderAddGroupButton() { if (this._sortModeActive) return; return html` div { - display: flex; - align-items: center; - gap: var(--uui-size-3); - } - - uui-input[type='number'] { - max-width: 75px; - } - - [sort-mode-active] div[slot='header'] { - cursor: grab; - } - .container-list { display: grid; gap: 10px; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts index b17249cc83..67a0918cfd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts @@ -73,8 +73,11 @@ export type resolvePlacementArgs = { }; type INTERNAL_UmbSorterConfig = { - getUniqueOfElement: (element: ElementType) => string | null | symbol | number; - getUniqueOfModel: (modeEntry: T) => string | null | symbol | number; + /** + * Define how to retrive the unique identifier of an element. If this method returns undefined, the move will be cancelled. + */ + getUniqueOfElement: (element: ElementType) => string | null | symbol | number | undefined; + getUniqueOfModel: (modeEntry: T) => string | null | symbol | number | undefined; /** * Optionally define a unique identifier for each sorter experience, all Sorters that uses the same identifier to connect with other sorters. */ @@ -266,18 +269,21 @@ export class UmbSorterController): void { if (this.#model) { - // TODO: Some updates might need to be done, as the modal is about to changed? Do make the changes after setting the model?.. + // TODO: Some updates might need to be done, as the model is about to change? Do make the changes after setting the model?.. [NL] + this.#model = model; } - this.#model = model; } hasItem(unique: string) { return this.#model.find((x) => this.#config.getUniqueOfModel(x) === unique) !== undefined; } + /* getItem(unique: string) { + if (!unique) return undefined; return this.#model.find((x) => this.#config.getUniqueOfModel(x) === unique); } + */ hostConnected() { this.#isConnected = true; @@ -381,10 +387,9 @@ export class UmbSorterController Date: Fri, 8 Mar 2024 10:22:34 +0100 Subject: [PATCH 054/285] Sorter-Setup: only if we have an activeItem --- .../src/packages/core/sorter/sorter.controller.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts index 67a0918cfd..a46c95f36e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts @@ -387,11 +387,13 @@ export class UmbSorterController Date: Fri, 8 Mar 2024 11:16:37 +0100 Subject: [PATCH 055/285] new property sortOrder --- ...nt-type-property-structure-helper.class.ts | 13 ++++++---- .../content-type-structure-manager.class.ts | 7 +++--- ...-workspace-view-edit-properties.element.ts | 25 ++++++++++++++----- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index fd1a13f8f4..73bc77d012 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -112,13 +112,16 @@ export class UmbContentTypePropertyStructureHelper = [ ...(this.#contentTypes.getValue().find((x) => x.unique === contentTypeUnique)?.properties ?? []), diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index 2d2c851e08..05fce2bae0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -11,7 +11,12 @@ import type { } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { + UMB_PROPERTY_SETTINGS_MODAL, + UmbModalRouteBuilder, + UmbModalRouteRegistrationController, +} from '@umbraco-cms/backoffice/modal'; +import { Params } from '@umbraco-cms/backoffice/router'; const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { @@ -119,7 +124,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem _ownerDocumentTypes?: UmbContentTypeModel[]; @state() - protected _modalRouteNewProperty?: string; + protected _modalRouteBuilderNewProperty?: UmbModalRouteBuilder; @state() _sortModeActive?: boolean; @@ -159,14 +164,22 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem // Note: Route for adding a new property new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addAdditionalPath('new-property') - .onSetup(async () => { + .addAdditionalPath('add-property/:sortOrder') + .onSetup(async (params) => { const documentTypeId = this._ownerDocumentTypes?.find((types) => types.containers?.find((containers) => containers.id === this.containerId), )?.unique; if (documentTypeId === undefined) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); if (propertyData === undefined) return false; + if (params.sortOrder !== undefined) { + let sortOrderInt = parseInt(params.sortOrder, 10); + if (sortOrderInt === -1) { + // Find the highest sortOrder and add 1 to it: + sortOrderInt = Math.max(...this._propertyStructure.map((x) => x.sortOrder), -1); + } + propertyData.sortOrder = sortOrderInt + 1; + } return { data: { documentTypeId }, value: propertyData }; }) .onSubmit((value) => { @@ -174,7 +187,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); }) .observeRouteBuilder((routeBuilder) => { - this._modalRouteNewProperty = routeBuilder(null); + this._modalRouteBuilderNewProperty = routeBuilder; }); } @@ -215,7 +228,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem label=${this.localize.term('contentTypeEditor_addProperty')} id="add" look="placeholder" - href=${ifDefined(this._modalRouteNewProperty)}> + href=${ifDefined(this._modalRouteBuilderNewProperty?.({ sortOrder: -1 }))}> Add property ` : ''} From 2a3ed74402bee1247fca09f735f5b3cc4f047c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 11:43:30 +0100 Subject: [PATCH 056/285] level up methods --- .../src/packages/core/router/route.context.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts index 70a2198629..c72a5b1bd4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts @@ -31,6 +31,19 @@ export class UmbRouteContext extends UmbControllerBase { }); } + public navigateLevelUp() { + const path = this.getLevelUpPath(); + if (path) { + window.history.pushState({}, '', path); + } + } + public getLevelUpPath() { + if (this.#routerBasePath) { + return this.#routerBasePath.endsWith('/') ? this.#routerBasePath : this.#routerBasePath + '/'; + } + return; + } + public registerModal(registration: UmbModalRouteRegistration) { this.#modalRegistrations.push(registration); this.#createNewUrlBuilder(registration); From 89fca12fa5a296ca127a937b3f85b0819442d267 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 8 Mar 2024 11:49:55 +0100 Subject: [PATCH 057/285] remove duplicate load field --- src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts index fc8a42b402..8442b3fd05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/manifests.ts @@ -8,7 +8,6 @@ export const manifests: Array = [ alias: 'Umb.Modal.SectionPicker', name: 'Section Picker Modal', element: () => import('./section-picker-modal/section-picker-modal.element.js'), - js: () => import('./section-picker-modal/section-picker-modal.element.js'), }, { type: 'condition', From 34ff4a6dcea46c53e5f64ed0d9f7e1d6d3a175d9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 8 Mar 2024 11:52:03 +0100 Subject: [PATCH 058/285] Update section.manifests.ts --- .../src/packages/media/section.manifests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts index 3d322b376b..e06de6f026 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/section.manifests.ts @@ -5,7 +5,7 @@ const sectionAlias = 'Umb.Section.Media'; const section: ManifestSection = { type: 'section', - alias: 'Umb.Section.Media', + alias: sectionAlias, name: 'Media Section', weight: 500, meta: { From fead0f4a2269b2e7c763f7b0cfed7290d5d35c57 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 8 Mar 2024 12:47:44 +0100 Subject: [PATCH 059/285] accept host as the first parameter --- .../section/conditions/section-user-permission.condition.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts index 75e490fdf7..f57d742553 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/section/conditions/section-user-permission.condition.ts @@ -5,14 +5,15 @@ import type { UmbConditionControllerArguments, UmbExtensionCondition, } from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbSectionUserPermissionCondition extends UmbControllerBase implements UmbExtensionCondition { config: UmbSectionUserPermissionConditionConfig; permitted = false; #onChange: () => void; - constructor(args: UmbConditionControllerArguments) { - super(args.host); + constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { + super(host); this.config = args.config; this.#onChange = args.onChange; From 219bc28f8a1942aeeedc434a44cec7f4a59993d4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 8 Mar 2024 12:47:59 +0100 Subject: [PATCH 060/285] add section to mock data --- .../src/mocks/data/user-group/user-group.data.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.data.ts index 81a46cb651..10f4fcdda9 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.data.ts @@ -32,7 +32,15 @@ export const data: Array = [ document: { id: 'simple-document-id' }, }, ], - sections: [], + sections: [ + 'Umb.Section.Content', + 'Umb.Section.Media', + 'Umb.Section.Settings', + 'Umb.Section.Members', + 'Umb.Section.Packages', + 'Umb.Section.Translation', + 'Umb.Section.Users', + ], languages: [], hasAccessToAllLanguages: true, documentRootAccess: true, @@ -60,7 +68,7 @@ export const data: Array = [ 'Umb.Document.Rollback', ], permissions: [], - sections: [], + sections: ['Umb.Section.Content', 'Umb.Section.Media'], languages: [], hasAccessToAllLanguages: true, documentRootAccess: true, @@ -88,7 +96,7 @@ export const data: Array = [ documentStartNode: { id: 'all-property-editors-document-id' }, fallbackPermissions: ['Umb.Document.Read', 'Umb.Document.Update'], permissions: [], - sections: [], + sections: ['Umb.Section.Translation'], languages: [], hasAccessToAllLanguages: true, documentRootAccess: true, @@ -107,7 +115,7 @@ export const data: Array = [ 'Umb.Document.Notifications', ], permissions: [], - sections: [], + sections: ['Umb.Section.Content'], languages: [], hasAccessToAllLanguages: true, documentRootAccess: true, From cca4f1efc49bb2a23f18c6fa6a627b49b1d35cca Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 8 Mar 2024 12:50:01 +0100 Subject: [PATCH 061/285] collect section permissions for the current user --- .../src/mocks/data/user-group/user-group.db.ts | 10 ++++++++++ .../src/mocks/data/user/user.db.ts | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.db.ts index ad7daef15d..29d72987dc 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/user-group/user-group.db.ts @@ -38,6 +38,16 @@ export class UmbUserGroupMockDB extends UmbEntityMockDbBase JSON.stringify(e)))).map((e) => JSON.parse(e)); return uniqueArray; } + + getAllowedSections(userGroupIds: string[]): string[] { + const sections = this.data + .filter((userGroup) => userGroupIds.includes(userGroup.id)) + .map((userGroup) => (userGroup.sections?.length ? userGroup.sections : [])) + .flat(); + + // Remove duplicates + return Array.from(new Set(sections)); + } } const itemMapper = (item: UmbMockUserGroupModel): UserGroupItemResponseModel => { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts index 0493571cf9..502d924d0b 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/user/user.db.ts @@ -52,6 +52,9 @@ class UmbUserMockDB extends UmbEntityMockDbBase { getCurrentUser(): CurrentUserResponseModel { const firstUser = this.data[0]; const permissions = firstUser.userGroupIds?.length ? umbUserGroupMockDb.getPermissions(firstUser.userGroupIds) : []; + const allowedSections = firstUser.userGroupIds?.length + ? umbUserGroupMockDb.getAllowedSections(firstUser.userGroupIds) + : []; return { id: firstUser.id, @@ -66,7 +69,7 @@ class UmbUserMockDB extends UmbEntityMockDbBase { mediaStartNodeIds: firstUser.mediaStartNodeIds, fallbackPermissions: [], permissions, - allowedSections: [], + allowedSections, }; } From 9724a4479650686cc0a51de65a9c02e95aa8952b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 13:21:48 +0100 Subject: [PATCH 062/285] _constructLocalRouterPath --- .../src/packages/core/router/route.context.ts | 14 -------------- .../packages/core/router/router-slot.element.ts | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts index c72a5b1bd4..d7f41278b7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/route.context.ts @@ -31,19 +31,6 @@ export class UmbRouteContext extends UmbControllerBase { }); } - public navigateLevelUp() { - const path = this.getLevelUpPath(); - if (path) { - window.history.pushState({}, '', path); - } - } - public getLevelUpPath() { - if (this.#routerBasePath) { - return this.#routerBasePath.endsWith('/') ? this.#routerBasePath : this.#routerBasePath + '/'; - } - return; - } - public registerModal(registration: UmbModalRouteRegistration) { this.#modalRegistrations.push(registration); this.#createNewUrlBuilder(registration); @@ -162,7 +149,6 @@ export class UmbRouteContext extends UmbControllerBase { : this.#routerActiveLocalPath + '/' : ''; const localPath = routeBasePath + routeActiveLocalPath + modalRegistration.generateModalPath(); - const urlBuilder = createRoutePathBuilder(localPath); modalRegistration._internal_setRouteBuilder(urlBuilder); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot.element.ts index 50e54d418f..7ec328256a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/router-slot.element.ts @@ -70,6 +70,10 @@ export class UmbRouterSlotElement extends UmbLitElement { return this.#router.constructAbsolutePath('') || ''; } + protected _constructLocalRouterPath() { + return this.#router.match?.fragments.consumed ?? ''; + } + connectedCallback() { super.connectedCallback(); // Currently we have to set this every time as RouteSlot looks for its parent every-time it is connected. Aka it has not way to explicitly set the parent. @@ -101,7 +105,7 @@ export class UmbRouterSlotElement extends UmbLitElement { this.#routeContext._internal_routerGotBasePath(this._routerPath); this.dispatchEvent(new UmbRouterSlotInitEvent()); - const newActiveLocalPath = this.#router.match?.route.path; + const newActiveLocalPath = this._constructLocalRouterPath(); if (this._activeLocalPath !== newActiveLocalPath) { this._activeLocalPath = newActiveLocalPath; this.#routeContext._internal_routerGotActiveLocalPath(this._activeLocalPath); @@ -112,11 +116,14 @@ export class UmbRouterSlotElement extends UmbLitElement { private _onNavigationChanged = (event?: any) => { if (event.detail.slot === this.#router) { - this._activeLocalPath = event.detail.match.route.path; - this.#routeContext._internal_routerGotActiveLocalPath(this._activeLocalPath); - this.dispatchEvent(new UmbRouterSlotChangeEvent()); + const newActiveLocalPath = this._constructLocalRouterPath(); + if (this._activeLocalPath !== newActiveLocalPath) { + this._activeLocalPath = newActiveLocalPath; + this.#routeContext._internal_routerGotActiveLocalPath(newActiveLocalPath); + this.dispatchEvent(new UmbRouterSlotChangeEvent()); + } } else if (event.detail.slot === this.#modalRouter) { - const newActiveModalLocalPath = event.detail.match.route.path; + const newActiveModalLocalPath = this.#modalRouter.match?.fragments.consumed ?? ''; this.#routeContext._internal_modalRouterChanged(newActiveModalLocalPath); } }; From 0667704f8c71362f501be95635f0710786ec6588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 20:31:21 +0100 Subject: [PATCH 063/285] move property settings modal --- .../src/packages/core/content-type/modals/index.ts | 1 + .../src/packages/core/content-type/modals/manifests.ts | 6 ++++++ .../property-settings-modal.context.ts | 0 .../property-settings-modal.element.ts | 0 .../property-settings-modal.token.ts | 2 +- .../content-type-property-structure-helper.class.ts | 5 ++++- ...tent-type-workspace-view-edit-properties.element.ts | 10 ++++------ ...ontent-type-workspace-view-edit-property.element.ts | 7 +++++-- .../src/packages/core/modal/common/manifests.ts | 6 ------ .../src/packages/core/modal/token/index.ts | 1 - ...edia-type-workspace-view-edit-properties.element.ts | 7 +++++-- .../media-type-workspace-view-edit-property.element.ts | 3 +-- ...mber-type-workspace-view-edit-properties.element.ts | 7 +++++-- ...member-type-workspace-view-edit-property.element.ts | 9 +++++---- 14 files changed, 37 insertions(+), 27 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/{modal/common => content-type/modals}/property-settings/property-settings-modal.context.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/{modal/common => content-type/modals}/property-settings/property-settings-modal.element.ts (100%) rename src/Umbraco.Web.UI.Client/src/packages/core/{modal/token => content-type/modals/property-settings}/property-settings-modal.token.ts (89%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts index 4cdc48a7a1..e6a2919e14 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts @@ -1 +1,2 @@ export * from './composition-picker/composition-picker-modal.token.js'; +export * from './property-settings/property-settings-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts index 538bbfb0c3..c5ceaaa20d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts @@ -7,6 +7,12 @@ const modals: Array = [ name: 'ContentType Composition Picker Modal', js: () => import('./composition-picker/composition-picker-modal.element.js'), }, + { + type: 'modal', + alias: 'Umb.Modal.PropertySettings', + name: 'Property Settings Modal', + js: () => import('.//property-settings/property-settings-modal.element.js'), + }, ]; export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.context.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.context.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/modal/common/property-settings/property-settings-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-settings-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-settings-modal.token.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts index b7636bba7c..b4d609c1d6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/property-settings-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts @@ -1,4 +1,4 @@ -import { UmbModalToken } from './modal-token.js'; +import { UmbModalToken } from '../../../modal/token/modal-token.js'; import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type'; export type UmbPropertySettingsModalData = { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 73bc77d012..d70c27131b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -129,11 +129,14 @@ export class UmbContentTypePropertyStructureHelper = { getUniqueOfElement: (element) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts index 5c9f4be5b9..58b20f267f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts @@ -3,7 +3,6 @@ import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit'; import { - UMB_PROPERTY_SETTINGS_MODAL, UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController, umbConfirmModal, @@ -11,7 +10,11 @@ import { import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type'; +import { + UMB_PROPERTY_SETTINGS_MODAL, + type UmbPropertyTypeModel, + type UmbPropertyTypeScaffoldModel, +} from '@umbraco-cms/backoffice/content-type'; /** * @element umb-content-type-workspace-view-edit-property diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts index 6635895244..2e40d60cab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/manifests.ts @@ -19,12 +19,6 @@ const modals: Array = [ name: 'Link Picker Modal', js: () => import('./link-picker/link-picker-modal.element.js'), }, - { - type: 'modal', - alias: 'Umb.Modal.PropertySettings', - name: 'Property Settings Modal', - js: () => import('./property-settings/property-settings-modal.element.js'), - }, { type: 'modal', alias: 'Umb.Modal.CodeEditor', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts index ad9c8dc47e..d6a3fb90d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/token/index.ts @@ -11,5 +11,4 @@ export * from './media-tree-picker-modal.token.js'; export * from './media-type-picker-modal.token.js'; export * from './modal-token.js'; export * from './property-editor-ui-picker-modal.token.js'; -export * from './property-settings-modal.token.js'; export * from './workspace-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts index 5089fb33fa..7864a7df61 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts @@ -4,12 +4,15 @@ import type { UmbMediaTypeDetailModel } from '../../../types.js'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; +import { + UmbContentTypePropertyStructureHelper, + UMB_PROPERTY_SETTINGS_MODAL, +} from '@umbraco-cms/backoffice/content-type'; import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts index ea69a2991a..3313594dca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts @@ -4,7 +4,6 @@ import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit'; import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; import { - UMB_PROPERTY_SETTINGS_MODAL, UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController, umbConfirmModal, @@ -12,7 +11,7 @@ import { import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; +import { type UmbPropertyTypeModel, UMB_PROPERTY_SETTINGS_MODAL } from '@umbraco-cms/backoffice/content-type'; /** * @element umb-media-type-workspace-view-edit-property diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts index f2d0125c15..22a8782520 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts @@ -6,10 +6,13 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; +import { + UmbContentTypePropertyStructureHelper, + UMB_PROPERTY_SETTINGS_MODAL, +} from '@umbraco-cms/backoffice/content-type'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import { UMB_PROPERTY_SETTINGS_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; @customElement('umb-member-type-workspace-view-edit-properties') export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts index 1e84cc6723..a4545f4940 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts @@ -2,11 +2,8 @@ import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; import { - UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, - UMB_PROPERTY_SETTINGS_MODAL, UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController, umbConfirmModal, @@ -14,7 +11,11 @@ import { import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type'; +import { + UMB_PROPERTY_SETTINGS_MODAL, + type UmbPropertyTypeModel, + type UmbPropertyTypeScaffoldModel, +} from '@umbraco-cms/backoffice/content-type'; /** * @element umb-member-type-workspace-view-edit-property From 999f051d6a6fe3061d132939c5fda25733bca561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 20:52:00 +0100 Subject: [PATCH 064/285] correct re-setting sort orders on move end --- ...-workspace-view-edit-properties.element.ts | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index b425dcbdd3..c2c356f630 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -41,6 +41,10 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this._propertyStructure = model; }, onEnd: ({ item }) => { + if (this._containerId === undefined) { + throw new Error('ContainerId is not set, we have not duplicated this container.'); + return; + } /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... @@ -51,35 +55,30 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem // Doesn't exist in model if (newIndex === -1) return; - // First in list - if (newIndex === 0 && model.length > 1) { - this._propertyStructureHelper.partialUpdateProperty(item.id, { - sortOrder: model[1].sortOrder - 1, - container: this._containerId ? { id: this._containerId } : null, - }); - return; - } + // As origin we set prev sort order to -1, so if no other then our item will become 0 + let prevSortOrder = -1; // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; + if (newIndex > 0 && model.length > 0) { + prevSortOrder = model[newIndex - 1].sortOrder; + } - let weight = 1; - this._propertyStructureHelper.partialUpdateProperty(item.id, { - sortOrder: prevItemSortOrder + weight, - container: this._containerId ? { id: this._containerId } : null, + // increase the prevSortOrder and use it for the moved item, + this._propertyStructureHelper.partialUpdateProperty(item.id, { + sortOrder: ++prevSortOrder, + }); + + // Adjust everyone right after, meaning until there is a gap between the sortOrders: + let i = newIndex + 1; + let entry: UmbPropertyTypeModel | undefined; + // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: + while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { + // Increase the prevSortOrder and use it for the item: + this._propertyStructureHelper.partialUpdateProperty(entry.id, { + sortOrder: ++prevSortOrder, }); - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this._propertyStructureHelper.partialUpdateProperty(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; - }); + i++; } }, }); From fe7448631297484f7fb61b663a6668c7bb4681ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 21:01:29 +0100 Subject: [PATCH 065/285] more sortOrder changes --- ...-workspace-view-edit-properties.element.ts | 3 +- ...nt-type-workspace-view-edit-tab.element.ts | 42 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index c2c356f630..73e65fffee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -42,9 +42,10 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem }, onEnd: ({ item }) => { if (this._containerId === undefined) { - throw new Error('ContainerId is not set, we have not duplicated this container.'); + throw new Error('ContainerId is not set, we have not made a local duplicated of this container.'); return; } + console.log('containerId', this._containerId); /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index a522973ea2..7289ed6626 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -28,6 +28,11 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this._groups = model; }, onEnd: ({ item }) => { + if (this._ownerTabId === undefined) { + throw new Error('OwnerTabId is not set, we have not made a local duplicated of this container.'); + return; + } + console.log('_ownerTabId', this._ownerTabId); /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... @@ -38,29 +43,30 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { // Doesn't exist in model if (newIndex === -1) return; - // First in list - if (newIndex === 0 && model.length > 1) { - this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: model[1].sortOrder - 1 }); - return; - } + // As origin we set prev sort order to -1, so if no other then our item will become 0 + let prevSortOrder = -1; // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; + if (newIndex > 0 && model.length > 0) { + prevSortOrder = model[newIndex - 1].sortOrder; + } - let weight = 1; - this.#groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: prevItemSortOrder + weight }); + // increase the prevSortOrder and use it for the moved item, + this.#groupStructureHelper.partialUpdateContainer(item.id, { + sortOrder: ++prevSortOrder, + }); - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this.#groupStructureHelper.partialUpdateContainer(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; + // Adjust everyone right after, meaning until there is a gap between the sortOrders: + let i = newIndex + 1; + let entry: UmbPropertyTypeContainerModel | undefined; + // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: + while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { + // Increase the prevSortOrder and use it for the item: + this.#groupStructureHelper.partialUpdateContainer(entry.id, { + sortOrder: ++prevSortOrder, }); + + i++; } }, }); From 5b478d9e2045910b23d18210ae124df0894f4956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 21:05:20 +0100 Subject: [PATCH 066/285] sortOrder for new groups --- .../design/content-type-workspace-view-edit-tab.element.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 7289ed6626..8a9a97dfa6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -157,7 +157,9 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { #onAddGroup = () => { // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? - this.#groupStructureHelper.addContainer(this._ownerTabId); + const len = this._groups.length; + const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; + this.#groupStructureHelper.addContainer(this._ownerTabId, sortOrder); }; render() { From 3c1cfe3b1e559d6449309ac783c05d3af6f77d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 8 Mar 2024 21:18:51 +0100 Subject: [PATCH 067/285] container modal specifications --- ...pe-workspace-view-edit-properties.element.ts | 6 +++++- ...tent-type-workspace-view-edit-tab.element.ts | 16 +++++++++++----- .../content-type-workspace-view-edit.element.ts | 17 ++++++++++++----- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index 73e65fffee..11b8c1dfc2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -94,6 +94,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem if (value === this._containerId) return; const oldValue = this._containerId; this._containerId = value; + this.#addPropertyModal.setUniquePathValue('container-id', value); this.requestUpdate('containerId', oldValue); } @@ -113,6 +114,8 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this._propertyStructureHelper.setContainerType(value); } + #addPropertyModal: UmbModalRouteRegistrationController; + _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); @state() @@ -161,7 +164,8 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem }); // Note: Route for adding a new property - new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) + this.#addPropertyModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) + .addUniquePaths(['container-id']) .addAdditionalPath('add-property/:sortOrder') .onSetup(async (params) => { const documentTypeId = this._ownerDocumentTypes?.find((types) => diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 8a9a97dfa6..c8d82f15b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -159,7 +159,8 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? const len = this._groups.length; const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; - this.#groupStructureHelper.addContainer(this._ownerTabId, sortOrder); + const container = this.#groupStructureHelper.addContainer(this._ownerTabId, sortOrder); + console.log('container', container); }; render() { @@ -188,13 +189,18 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { ${repeat( this._groups, (group) => group.id, - (group) => - html` html` + ${group.id} - ${group.name} + - `, + .groupStructureHelper=${this.#groupStructureHelper} + @umb:partial-group-update=${(event: CustomEvent) => { + this.#groupStructureHelper.partialUpdateContainer(group.id, event.detail); + }}> + + `, )}
${this.#renderAddGroupButton()} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 26a5e3cfa1..fa083b16b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -212,6 +212,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } async #requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) { + // TODO: Localize this: const modalData: UmbConfirmModalData = { headline: 'Delete tab', content: html` @@ -235,20 +236,23 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem #remove(tabId?: string) { if (!tabId) return; this._workspaceContext?.structure.removeContainer(null, tabId); + // TODO: We should only navigate away if it was the last tab and if it was the active one... this._tabsStructureHelper?.isOwnerChildContainer(tabId) ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) : ''; } async #addTab() { - if ( - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement) && - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement).value === '' - ) { + const inputEl = this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement; + if (inputEl?.value === '') { this.#focusInput(); return; } - const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab'); + if (!this._tabs) return; + + const len = this._tabs.length; + const sortOrder = len === 0 ? 0 : this._tabs[len - 1].sortOrder + 1; + const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab', sortOrder); if (tab) { const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); window.history.replaceState(null, '', path); @@ -267,6 +271,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem let newName = (event.target as HTMLInputElement).value; if (newName === '') { + // TODO: Localize this: newName = 'Unnamed'; (event.target as HTMLInputElement).value = 'Unnamed'; } @@ -330,6 +335,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } renderAddButton() { + // TODO: Localize this: if (this._sortModeActive) return; return html` @@ -401,6 +407,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } renderTabInner(tab: PropertyTypeContainerModelBaseModel, tabActive: boolean, tabInherited: boolean) { + // TODO: Localize this: if (this._sortModeActive) { return html`
${tabInherited From 085fd3163750cdbe5c7a990b9619920eec96aac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 09:32:34 +0100 Subject: [PATCH 068/285] maintain owner groups --- ...ntent-type-container-structure-helper.class.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index b0052226b0..d28cfe8e00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -100,7 +100,7 @@ export class UmbContentTypeContainerStructureHelper { @@ -165,13 +165,14 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); this._insertChildContainers(rootContainers); + this._parentOwnerContainers = this.#structure!.getOwnerContainers(this._parentType!) ?? []; }, '_observeRootContainers', ); @@ -204,12 +205,20 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); + } + return false; + /* return ( this.#containers .getValue() .find((x) => (x.id === containerId && this._parentId ? x.parent?.id === this._parentId : x.parent === null)) !== undefined ); + */ } /** Manipulate methods: */ From c9d4d02c36a81e44bfd2a939cc04dfcd9c7c2ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 10:57:07 +0100 Subject: [PATCH 069/285] inheritance of groups --- ...t-type-container-structure-helper.class.ts | 4 ++ ...-type-workspace-view-edit-group.element.ts | 38 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index d28cfe8e00..ec05f6f093 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -55,6 +55,10 @@ export class UmbContentTypeContainerStructureHelper { + const amountOfContainers = containers.length; + + const isAOwnerContainer = this.groupStructureHelper!.isOwnerChildContainer(this.group!.id); + + const pureOwnerContainer = isAOwnerContainer && amountOfContainers === 1; + + this._hasOwnerContainer = isAOwnerContainer; + this._inherited = !pureOwnerContainer; + console.log('this._inherited', this._inherited, 'this._hasOwnerContainer', this._hasOwnerContainer); + }, + 'observeGroupContainers', + ); + } else { + // We use name match to determine inheritance, but a no name should not be inherited. + this._inherited = false; + } } } @@ -115,11 +135,14 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { label=${this.localize.term('sort_sortOrder')} @change=${(e: UUIInputEvent) => this._singleValueUpdate('sortOrder', parseInt(e.target.value as string) || 0)} .value=${this.group!.sortOrder ?? 0} - ?disabled=${this._inherited}> + ?disabled=${!this._hasOwnerContainer}>
`; } else { return html`
- ${this._inherited ? html`` : this.#renderInputGroupName()} +
+ + ${this.#renderInputGroupName()} +
`; } } @@ -129,6 +152,7 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { label=${this.localize.term('contentTypeEditor_group')} placeholder=${this.localize.term('placeholders_entername')} .value=${this.group!.name} + ?disabled=${!this._hasOwnerContainer} @change=${(e: InputEvent) => { const newName = (e.target as HTMLInputElement).value; this._singleValueUpdate('name', newName); From c997527ca60f16b690a2012350ac9fcb1bd4ab01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 13:10:06 +0100 Subject: [PATCH 070/285] eliminate partial event, now that component knows about the helper --- .../design/content-type-workspace-view-edit-group.element.ts | 5 ++++- .../design/content-type-workspace-view-edit-tab.element.ts | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts index 18fc111b92..12bf2f57dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -103,10 +103,13 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { */ _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { + if (!this._groupStructureHelper || !this.group) return; + const partialObject = {} as any; partialObject[propertyName] = value; - this.dispatchEvent(new CustomEvent('umb:partial-group-update', { detail: partialObject })); + //this.dispatchEvent(new CustomEvent('umb:partial-group-update', { detail: partialObject })); + this._groupStructureHelper.partialUpdateContainer(this.group.id, partialObject); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index c8d82f15b3..f4aab531ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -195,10 +195,7 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { class="container-handle" ?sort-mode-active=${this._sortModeActive} .group=${group} - .groupStructureHelper=${this.#groupStructureHelper} - @umb:partial-group-update=${(event: CustomEvent) => { - this.#groupStructureHelper.partialUpdateContainer(group.id, event.detail); - }}> + .groupStructureHelper=${this.#groupStructureHelper}> `, )} From ca888a9d473eb40c87a3aa50c9e33bbfaaab483f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 13:11:36 +0100 Subject: [PATCH 071/285] legacy method --- .../content-type-container-structure-helper.class.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index ec05f6f093..921e32710a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -255,13 +255,19 @@ export class UmbContentTypeContainerStructureHelper Date: Mon, 11 Mar 2024 13:15:24 +0100 Subject: [PATCH 072/285] add icon to scaffold --- .../repository/detail/media-type-detail.server.data-source.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts index 9837b7d73c..28c704c689 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/repository/detail/media-type-detail.server.data-source.ts @@ -42,7 +42,7 @@ export class UmbMediaTypeServerDataSource implements UmbDetailDataSource Date: Mon, 11 Mar 2024 13:15:36 +0100 Subject: [PATCH 073/285] remove empty space in button to allow label to be shown --- .../views/design/media-type-workspace-view-edit-tab.element.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts index 818c6604a3..b1dcf8b275 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts @@ -209,8 +209,7 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { label=${this.localize.term('contentTypeEditor_addGroup')} id="add" look="placeholder" - @click=${this.#onAddGroup}> -
` + @click=${this.#onAddGroup}>` : ''} `; } From 5e46ade62b42288aadec55072aba32d06bd615f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 13:30:22 +0100 Subject: [PATCH 074/285] JSDocs --- .../content-type-container-structure-helper.class.ts | 4 ++++ .../content-type-property-structure-helper.class.ts | 4 ++++ .../structure/content-type-structure-manager.class.ts | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 921e32710a..ff8d94d045 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -4,6 +4,10 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; +/** + * This class is a helper class for managing the structure of containers in a content type. + * This requires a structure manager {@link UmbContentTypePropertyStructureManager} to manage the structure. + */ export class UmbContentTypeContainerStructureHelper extends UmbControllerBase { #init; #initResolver?: (value: unknown) => void; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index d70c27131b..05b9cbe151 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -4,6 +4,10 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +/** + * This class is a helper class for managing the structure of containers in a content type. + * This requires a structure manager {@link UmbContentTypePropertyStructureManager} to manage the structure. + */ export class UmbContentTypePropertyStructureHelper extends UmbControllerBase { #init; #initResolver?: (value: unknown) => void; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 796082c90a..9679c0151d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -19,6 +19,13 @@ import { import { incrementString } from '@umbraco-cms/backoffice/utils'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +/** + * Manages a structure of a Content Type and its properties and containers. + * This loads and merges the structures of the Content Type and its inherited and composed Content Types. + * To help manage the data, there is two helper classes: + * - {@link UmbContentTypePropertyStructureHelper} for managing the structure of properties, optional of another container or root. + * - {@link UmbContentTypeContainerStructureHelper} for managing the structure of containers, optional of another container or root. + */ export class UmbContentTypePropertyStructureManager extends UmbControllerBase { #init!: Promise; From 6c9619a1e569c4c2877b49bf20f1e8fdf9ef5a2d Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:45:46 +0100 Subject: [PATCH 075/285] Feature: LogViewer pagination --- .../log-viewer/workspace/logviewer.context.ts | 6 +-- .../logviewer/logviewer-workspace.element.ts | 6 ++- ...ewer-message-templates-overview.element.ts | 53 ++++++++++++------- ...-viewer-saved-searches-overview.element.ts | 30 +++++++++-- .../log-viewer-messages-list.element.ts | 8 ++- .../views/search/log-search-view.element.ts | 4 ++ 6 files changed, 79 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts index 6dbbe79933..39fc6efa54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts @@ -67,7 +67,7 @@ export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements U }; #savedSearches = new UmbObjectState(undefined); - savedSearches = this.#savedSearches.asObservablePart((data) => data?.items); + savedSearches = this.#savedSearches.asObservablePart((data) => data); #logCount = new UmbObjectState(null); logCount = this.#logCount.asObservable(); @@ -168,8 +168,8 @@ export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements U return this.#dateRange.getValue(); } - async getSavedSearches() { - const { data } = await this.#repository.getSavedSearches({ skip: 0, take: 100 }); + async getSavedSearches({ skip = 0, take = 15 }: { skip?: number; take?: number } = {}) { + const { data } = await this.#repository.getSavedSearches({ skip, take }); if (data) { this.#savedSearches.setValue(data); } else { diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts index c0aed6bc80..aea79e21e9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer/logviewer-workspace.element.ts @@ -33,7 +33,11 @@ export class UmbLogViewerWorkspaceElement extends UmbLitElement { } render() { - return html` `; + return html` + + `; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts index 56e67450cd..b4b8c53919 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts @@ -1,25 +1,31 @@ import type { UmbLogViewerWorkspaceContext } from '../../../logviewer.context.js'; import { UMB_APP_LOG_VIEWER_CONTEXT } from '../../../logviewer.context.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { - PagedLogTemplateResponseModel, + LogTemplateResponseModel, SavedLogSearchResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; -//TODO: fix pagination bug when API is fixed @customElement('umb-log-viewer-message-templates-overview') export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { + #itemsPerPage = 10; + #currentPage = 1; + @state() - private _messageTemplates: PagedLogTemplateResponseModel | null = null; + private _total = 0; + + @state() + private _messageTemplates: Array = []; #logViewerContext?: UmbLogViewerWorkspaceContext; constructor() { super(); this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT, (instance) => { this.#logViewerContext = instance; - this.#logViewerContext?.getMessageTemplates(0, 10); + this.#logViewerContext?.getMessageTemplates(0, this.#itemsPerPage); this.#observeStuff(); }); } @@ -27,13 +33,20 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { #observeStuff() { if (!this.#logViewerContext) return; this.observe(this.#logViewerContext.messageTemplates, (templates) => { - this._messageTemplates = templates ?? null; + this._messageTemplates = templates?.items ?? []; + this._total = templates?.total ?? 0; }); } #getMessageTemplates() { - const take = this._messageTemplates?.items?.length ?? 0; - this.#logViewerContext?.getMessageTemplates(0, take + 10); + //const take = this._messageTemplates?.length ?? 0; + const skip = this.#currentPage * this.#itemsPerPage - this.#itemsPerPage; + this.#logViewerContext?.getMessageTemplates(skip, this.#itemsPerPage); + } + + #onChangePage(event: UUIPaginationEvent) { + this.#currentPage = event.target.current; + this.#getMessageTemplates(); } #renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => { @@ -54,11 +67,11 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { render() { return html` -

Total Unique Message types: ${this._messageTemplates?.total}

+

Total Unique Message types: ${this._total}

${this._messageTemplates - ? this._messageTemplates.items.map( + ? this._messageTemplates.map( (template) => html` @@ -70,17 +83,15 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { `, - ) + ) : ''} - - - Show more - + ${this._total > this.#itemsPerPage + ? html`` + : nothing}
`; } @@ -88,6 +99,10 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { static styles = [ UmbTextStyles, css` + uui-pagination { + margin-top: var(--uui-size-layout-1); + } + #show-more-templates-btn { margin-top: var(--uui-size-space-5); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts index 517a46d848..ca34b34fcd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts @@ -1,23 +1,30 @@ import type { UmbLogViewerWorkspaceContext } from '../../../logviewer.context.js'; import { UMB_APP_LOG_VIEWER_CONTEXT } from '../../../logviewer.context.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, state, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { SavedLogSearchResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; +import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; //TODO: implement the saved searches pagination when the API total bug is fixed @customElement('umb-log-viewer-saved-searches-overview') export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement { + #itemsPerPage = 15; + #currentPage = 1; + @state() private _savedSearches: SavedLogSearchResponseModel[] = []; + @state() + private _total = 0; + #logViewerContext?: UmbLogViewerWorkspaceContext; constructor() { super(); this.consumeContext(UMB_APP_LOG_VIEWER_CONTEXT, (instance) => { this.#logViewerContext = instance; - this.#logViewerContext?.getSavedSearches(); + this.#logViewerContext?.getSavedSearches({ skip: 0, take: this.#itemsPerPage }); this.#observeStuff(); }); } @@ -25,10 +32,21 @@ export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement { #observeStuff() { if (!this.#logViewerContext) return; this.observe(this.#logViewerContext.savedSearches, (savedSearches) => { - this._savedSearches = savedSearches ?? []; + this._savedSearches = savedSearches?.items ?? []; + this._total = savedSearches?.total ?? 0; }); } + #getSavedSearches() { + const skip = this.#currentPage * this.#itemsPerPage - this.#itemsPerPage; + this.#logViewerContext?.getSavedSearches({ skip, take: this.#itemsPerPage }); + } + + #onChangePage(event: UUIPaginationEvent) { + this.#currentPage = event.target.current; + this.#getSavedSearches(); + } + #renderSearchItem = (searchListItem: SavedLogSearchResponseModel) => { return html`
  • ${this.#renderSearchItem({ name: 'All logs', query: '' })} ${this._savedSearches.map(this.#renderSearchItem)} + ${this._total > this.#itemsPerPage + ? html`` + : nothing} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-messages-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-messages-list.element.ts index ec98da6550..88a8fa48a1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-messages-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-messages-list.element.ts @@ -89,12 +89,12 @@ export class UmbLogViewerMessagesListElement extends UmbLitElement { .properties=${log.properties ?? []} .exception=${log.exception ?? ''} .messageTemplate=${log.messageTemplate ?? ''}>`, - )}` + )}` : html` Sorry, we cannot find what you are looking for. - `}`; + `}`; } render() { @@ -124,6 +124,10 @@ export class UmbLogViewerMessagesListElement extends UmbLitElement { static styles = [ css` + uui-pagination { + display: block; + margin-bottom: var(--uui-size-layout-1); + } uui-box { --uui-box-default-padding: 0; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/log-search-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/log-search-view.element.ts index 9443eb507a..7ca2dd25cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/log-search-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/log-search-view.element.ts @@ -57,6 +57,10 @@ export class UmbLogViewerSearchViewElement extends UmbLitElement { static styles = [ UmbTextStyles, css` + :host { + margin-bottom: var(--uui-size-space-2); + } + uui-box { --uui-box-default-padding: 0; } From d7d1c7925e6f1f5c9f5e4f02301cef87e981a2b5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:13:46 +0100 Subject: [PATCH 076/285] add support for having paramater-like segments of a url that shouldn't be replaced + tests --- ...nerate-route-path-builder.function.test.ts | 39 +++++++++++++++++++ .../generate-route-path-builder.function.ts | 11 +++--- 2 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts new file mode 100644 index 0000000000..efacc1ea44 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts @@ -0,0 +1,39 @@ +import { expect } from '@open-wc/testing'; +import { createRoutePathBuilder } from './generate-route-path-builder.function.js'; + +describe('createRoutePathBuilder', () => { + it('should return a function that builds a route path without parameters', () => { + const buildPath = createRoutePathBuilder('test/path'); + expect(buildPath(null)).to.eq('/test/path/'); + }); + + it('should return a function that builds a route path with parameters', () => { + const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); + expect(buildPath({ param1: 'value1', param2: 'value2' })).to.eq('/test/value1/path/value2/'); + }); + + it('should convert number parameters to strings', () => { + const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); + expect(buildPath({ param1: 123, param2: 456 })).to.eq('/test/123/path/456/'); + }); + + it('should not consider route segments that resembles parameters as parameters', () => { + const buildPath = createRoutePathBuilder('test/uc:store/path'); + expect(buildPath({ someOtherParam: 'test' })).to.eq('/test/uc:store/path/'); + }); + + it('should support multiple parameters with the same name', () => { + const buildPath = createRoutePathBuilder('test/:param1/path/:param1'); + expect(buildPath({ param1: 'value1' })).to.eq('/test/value1/path/value1/'); + }); + + it('should support complex objects as parameters with a custom toString method', () => { + const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); + const obj = { + toString() { + return 'value1'; + }, + }; + expect(buildPath({ param1: obj, param2: 'value2' })).to.eq('/test/value1/path/value2/'); + }); +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts index b2fbf53067..162f8514dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts @@ -1,17 +1,18 @@ /* eslint-disable */ import { stripSlash } from '@umbraco-cms/backoffice/external/router-slot'; // This must only include the util to avoid side effects of registering the route element. -const PARAM_IDENTIFIER = /:([^\\/]+)/g; +const PARAM_IDENTIFIER = /\/:([^\/]+)/g; export function createRoutePathBuilder(path: string) { - return (params: { [key: string]: string | number } | null) => { + return (params: { [key: string]: string | number | { toString: () => string } } | null) => { return ( '/' + stripSlash( params - ? path.replace(PARAM_IDENTIFIER, (substring: string, ...args: string[]) => { - return params[args[0]].toString(); - }) + ? path.replace(PARAM_IDENTIFIER, (_substring: string, ...args: string[]) => { + // Replace the parameter with the value from the params object or the parameter name if it doesn't exist (args[0] is the parameter name without the colon) + return '/' + (typeof params[args[0]] !== 'undefined' ? params[args[0]].toString() : `:${args[0]}`); + }) : path, ) + '/' From 638c1139f97388ad3f5b704e39bd284aadd1841f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:12:55 +0100 Subject: [PATCH 077/285] add support for URLs without a leading slash --- .../router/generate-route-path-builder.function.test.ts | 6 ++++-- .../core/router/generate-route-path-builder.function.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts index efacc1ea44..6efbf1845f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts @@ -8,8 +8,10 @@ describe('createRoutePathBuilder', () => { }); it('should return a function that builds a route path with parameters', () => { - const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); - expect(buildPath({ param1: 'value1', param2: 'value2' })).to.eq('/test/value1/path/value2/'); + const buildPath = createRoutePathBuilder(':param0/test/:param1/path/:param2'); + expect(buildPath({ param0: 'value0', param1: 'value1', param2: 'value2' })).to.eq( + '/value0/test/value1/path/value2/', + ); }); it('should convert number parameters to strings', () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts index 162f8514dc..115a6c3f91 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { stripSlash } from '@umbraco-cms/backoffice/external/router-slot'; // This must only include the util to avoid side effects of registering the route element. -const PARAM_IDENTIFIER = /\/:([^\/]+)/g; +const PARAM_IDENTIFIER = /:([^\/]+)/g; export function createRoutePathBuilder(path: string) { return (params: { [key: string]: string | number | { toString: () => string } } | null) => { @@ -11,7 +11,7 @@ export function createRoutePathBuilder(path: string) { params ? path.replace(PARAM_IDENTIFIER, (_substring: string, ...args: string[]) => { // Replace the parameter with the value from the params object or the parameter name if it doesn't exist (args[0] is the parameter name without the colon) - return '/' + (typeof params[args[0]] !== 'undefined' ? params[args[0]].toString() : `:${args[0]}`); + return typeof params[args[0]] !== 'undefined' ? params[args[0]].toString() : `:${args[0]}`; }) : path, ) + From 586758dd4e7fdb0cc98ab431c8267bbb4690cced Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:14:19 +0100 Subject: [PATCH 078/285] test if only known parameters are replaced --- .../core/router/generate-route-path-builder.function.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts index 6efbf1845f..f6de7d9f86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/generate-route-path-builder.function.test.ts @@ -29,6 +29,11 @@ describe('createRoutePathBuilder', () => { expect(buildPath({ param1: 'value1' })).to.eq('/test/value1/path/value1/'); }); + it('should not consider parameters that are not in the params object', () => { + const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); + expect(buildPath({ param1: 'value1' })).to.eq('/test/value1/path/:param2/'); + }); + it('should support complex objects as parameters with a custom toString method', () => { const buildPath = createRoutePathBuilder('test/:param1/path/:param2'); const obj = { From b6897771e24bafc029dafbf2af8f81730c62aa12 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 11 Mar 2024 15:20:38 +0100 Subject: [PATCH 079/285] don't allow reload of data type children --- .../data-type/tree/reload-tree-item-children/manifests.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/reload-tree-item-children/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/reload-tree-item-children/manifests.ts index e5f14fc8cb..cf276f7dc6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/reload-tree-item-children/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/reload-tree-item-children/manifests.ts @@ -1,8 +1,4 @@ -import { - UMB_DATA_TYPE_ENTITY_TYPE, - UMB_DATA_TYPE_FOLDER_ENTITY_TYPE, - UMB_DATA_TYPE_ROOT_ENTITY_TYPE, -} from '../../entity.js'; +import { UMB_DATA_TYPE_FOLDER_ENTITY_TYPE, UMB_DATA_TYPE_ROOT_ENTITY_TYPE } from '../../entity.js'; import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ @@ -11,7 +7,7 @@ export const manifests: Array = [ kind: 'reloadTreeItemChildren', alias: 'Umb.EntityAction.DataType.Tree.ReloadChildrenOf', name: 'Reload Data Type Tree Item Children Entity Action', - forEntityTypes: [UMB_DATA_TYPE_ENTITY_TYPE, UMB_DATA_TYPE_ROOT_ENTITY_TYPE, UMB_DATA_TYPE_FOLDER_ENTITY_TYPE], + forEntityTypes: [UMB_DATA_TYPE_ROOT_ENTITY_TYPE, UMB_DATA_TYPE_FOLDER_ENTITY_TYPE], meta: {}, }, ]; From b2ddc17d5e716408d605b664bec428ec1cd721f4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:15:38 +0100 Subject: [PATCH 080/285] create individual modal for publish --- .../documents/documents/modals/manifests.ts | 7 + .../document-publish-modal.element.ts | 110 ++++++++++++ .../document-publish-modal.stories.ts | 161 ++++++++++++++++++ .../document-publish-modal.token.ts | 18 ++ .../documents/modals/publish-modal/index.ts | 1 + ...ocument-variant-language-picker.element.ts | 93 ++++++++++ 6 files changed, 390 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts index 758fbc4314..6799fb055d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts @@ -1,6 +1,7 @@ import type { ManifestModal } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS = 'Umb.Modal.DocumentVariantPicker'; +export const UMB_DOCUMENT_PUBLISH_MODAL_ALIAS = 'Umb.Modal.DocumentPublish'; const modals: Array = [ { @@ -9,6 +10,12 @@ const modals: Array = [ name: 'Document Variant Picker Modal', js: () => import('./variant-picker/document-variant-picker-modal.element.js'), }, + { + type: 'modal', + alias: UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, + name: 'Document Publish Modal', + js: () => import('./variant-picker/document-variant-picker-modal.element.js'), + }, ]; export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts new file mode 100644 index 0000000000..d94df2c45b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -0,0 +1,110 @@ +import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; +import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; + +import '../variant-picker/document-variant-language-picker.element.js'; + +@customElement('umb-document-variant-picker-modal') +export class UmbDocumentPublishModalElement extends UmbModalBaseElement< + UmbDocumentPublishModalData, + UmbDocumentPublishModalValue +> { + #selectionManager = new UmbSelectionManager(this); + + @state() + _selection: Array = []; + + constructor() { + super(); + this.observe(this.#selectionManager.selection, (selection) => { + this._selection = selection; + }); + + this.#configureSelectionManager(); + } + + async #configureSelectionManager() { + let selected = this.value?.selection ?? []; + + // Filter selection based on options: + selected = selected.filter((s) => this.data?.options.some((o) => o.unique === s)); + + if (selected.length === 0) { + const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + const appCulture = context.getAppCulture(); + // If the app language is one of the options, select it by default: + if (appCulture && this.data?.options.some((o) => o.language.unique === appCulture)) { + selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); + } + } + + this.#selectionManager.setMultiple(true); + this.#selectionManager.setSelectable(true); + this.#selectionManager.setSelection(selected); + + this.data?.options.forEach((variant) => { + if (variant.language?.isMandatory) { + this.#selectionManager.select(variant.unique); + } + }); + } + + #submit() { + this.value = { selection: this.#selectionManager.getSelection() }; + this.modalContext?.submit(); + } + + #close() { + this.modalContext?.reject(); + } + + render() { + return html` +

    + ${this.localize.term( + this.data?.allowScheduledPublish ? 'content_languagesToSchedule' : 'content_variantsToPublish', + )} +

    + + +

    ${this.localize.term('content_variantsWillBeSaved')}

    + +
    + + +
    +
    `; + } + + static styles = [ + UmbTextStyles, + css` + :host { + display: block; + width: 400px; + max-width: 90vw; + } + `, + ]; +} + +export default UmbDocumentPublishModalElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-publish-modal': UmbDocumentPublishModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts new file mode 100644 index 0000000000..1e555f69a9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -0,0 +1,161 @@ +import '../../../../core/components/body-layout/body-layout.element.js'; +import './document-publish-modal.element.js'; + +import type { Meta, StoryObj } from '@storybook/web-components'; +import { UmbDocumentVariantState } from '../../types.js'; +import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; +import type { UmbDocumentPublishModalElement } from './document-publish-modal.element.js'; +import { html } from '@umbraco-cms/backoffice/external/lit'; + +const modalData: UmbDocumentPublishModalData = { + type: 'save', + options: [ + { + unique: 'en-us', + culture: 'en-us', + segment: null, + variant: { + name: 'English variant name', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'English', + unique: 'en-us', + isDefault: true, + isMandatory: true, + fallbackIsoCode: null, + }, + }, + { + unique: 'en-us-gtm', + culture: 'en-us', + segment: 'GTM', + variant: { + name: 'English', + culture: 'en-us', + segment: 'GTM', + state: UmbDocumentVariantState.DRAFT, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + }, + language: { + entityType: 'language', + name: 'English', + unique: 'en-us', + isDefault: true, + isMandatory: true, + fallbackIsoCode: null, + }, + }, + { + unique: 'da-dk', + culture: 'da-dk', + segment: null, + variant: { + name: 'Danish variant name', + culture: 'da-dk', + state: UmbDocumentVariantState.NOT_CREATED, + createDate: null, + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'Danish', + unique: 'da-dk', + isDefault: false, + isMandatory: false, + fallbackIsoCode: null, + }, + }, + ], +}; + +const modalValue: UmbDocumentPublishModalValue = { + selection: ['en-us'], +}; + +const meta: Meta = { + title: 'Workspaces/Document/Modals/Publish', + component: 'umb-document-publish-modal', + id: 'umb-document-publish-modal', + args: { + data: modalData, + value: modalValue, + }, + decorators: [(Story) => html`
    ${Story()}
    `], + parameters: { + layout: 'centered', + docs: { + source: { + code: ` +import { UMB_DOCUMENT_PUBLISH_MODAL, UmbDocumentVariantState } from '@umbraco-cms/backoffice/document'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => { + modalManager.open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options: [ + { + name: 'English', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: '2021-08-25T14:00:00Z', + updateDate: null, + segment: null, + }, + { + name: 'English', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: '2021-08-25T14:00:00Z', + updateDate: null, + segment: 'GTM', + }, + { + name: 'Danish', + culture: 'da-dk', + state: UmbDocumentVariantState.NOT_CREATED, + createDate: null, + publishDate: null, + updateDate: null, + segment: null, + }, + ], + } + } +}); + `, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Overview: Story = {}; +export const Schedule: Story = { + args: { + data: { ...modalData, allowScheduledPublish: true }, + }, + parameters: { + docs: { + source: { + code: ` +modalManager.open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options: [], allowScheduledPublish: true } }); + `, + }, + }, + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts new file mode 100644 index 0000000000..226b199ef4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts @@ -0,0 +1,18 @@ +import type { UmbDocumentVariantPickerModalData, UmbDocumentVariantPickerModalValue } from '../variant-picker/index.js'; +import { UMB_DOCUMENT_PUBLISH_MODAL_ALIAS } from '../manifests.js'; +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; + +export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerModalData { + allowScheduledPublish?: boolean; +} + +export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerModalValue {} + +export const UMB_DOCUMENT_PUBLISH_MODAL = new UmbModalToken( + UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, + { + modal: { + type: 'dialog', + }, + }, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/index.ts new file mode 100644 index 0000000000..e2baa59079 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/index.ts @@ -0,0 +1 @@ +export * from './document-publish-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts new file mode 100644 index 0000000000..7c3c54c3fd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts @@ -0,0 +1,93 @@ +import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; +import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; + +@customElement('umb-document-variant-language-picker') +export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { + @property({ type: Array, attribute: false }) + variantLanguageOptions: Array = []; + + @property({ attribute: false }) + selectionManager!: UmbSelectionManager; + + @state() + _selection: Array = []; + + constructor() { + super(); + this.observe(this.selectionManager.selection, (selection) => { + this._selection = selection; + }); + } + + render() { + return repeat( + this.variantLanguageOptions, + (option) => option.unique, + (option) => html` + this.selectionManager.select(option.unique)} + @deselected=${() => this.selectionManager.deselect(option.unique)} + ?selected=${this._selection.includes(option.language.unique)}> + + ${this.#renderLabel(option)} + + `, + ); + } + + #renderLabel(option: UmbDocumentVariantOptionModel) { + return html`
    + + ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name} + +
    ${this.#renderVariantStatus(option)}
    + ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED + ? html`
    + Mandatory language +
    ` + : ''} +
    `; + } + + #renderVariantStatus(option: UmbDocumentVariantOptionModel) { + switch (option.variant?.state) { + case UmbDocumentVariantState.PUBLISHED: + return this.localize.term('content_published'); + case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES: + return this.localize.term('content_publishedPendingChanges'); + case UmbDocumentVariantState.DRAFT: + return this.localize.term('content_unpublished'); + case UmbDocumentVariantState.NOT_CREATED: + default: + return this.localize.term('content_notCreated'); + } + } + + static styles = [ + UmbTextStyles, + css` + #subtitle { + margin-top: 0; + } + .label { + padding: 0.5rem 0; + } + .label-status { + font-size: 0.8rem; + } + `, + ]; +} + +export default UmbDocumentVariantLanguagePickerElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-variant-language-picker': UmbDocumentVariantLanguagePickerElement; + } +} From ef8c19ac7f7a55812f055f9d07bdfe01920b25a1 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:16:57 +0100 Subject: [PATCH 081/285] add conditional for scheduled publishing --- .../publish-modal/document-publish-modal.element.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index d94df2c45b..9694a10372 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -1,5 +1,5 @@ import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; -import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; @@ -74,6 +74,12 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< .selectionManager=${this.#selectionManager} .variantLanguageOptions=${this.data?.options ?? []}> + ${when( + this.data?.allowScheduledPublish, + () => html`This is a scheduled publish`, + () => nothing, + )} +

    ${this.localize.term('content_variantsWillBeSaved')}

    From 0adec22aaacb6a58267dfc5e861d791e5cd6b510 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 11 Mar 2024 18:43:33 +0000 Subject: [PATCH 082/285] Adds Workspace Collection Context interface and token --- .../core/workspace/workspace-context/index.ts | 2 ++ .../workspace-collection-context.interface.ts | 10 ++++++++++ .../workspace-collection-context.token.ts | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.interface.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.token.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts index f467da08d3..269d85792a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/index.ts @@ -1,6 +1,8 @@ export * from './property-structure-workspace-context.interface.js'; export * from './publishable-workspace-context.interface.js'; export * from './saveable-workspace-context.interface.js'; +export * from './workspace-collection-context.interface.js'; +export * from './workspace-collection-context.token.js'; export * from './variant-workspace-context.token.js'; export * from './workspace-context.interface.js'; export * from './editable-workspace-context-base.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.interface.ts new file mode 100644 index 0000000000..aaadac60de --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.interface.ts @@ -0,0 +1,10 @@ +import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbContentTypeModel, UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; + +export interface UmbWorkspaceCollectionContextInterface + extends UmbWorkspaceContextInterface { + contentTypeHasCollection: Observable; + getCollectionAlias(): string; + structure: UmbContentTypePropertyStructureManager; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.token.ts new file mode 100644 index 0000000000..4cc4ba1460 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-collection-context.token.ts @@ -0,0 +1,16 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; +import type { + UmbWorkspaceContextInterface, + UmbWorkspaceCollectionContextInterface, +} from '@umbraco-cms/backoffice/workspace'; + +export const UMB_WORKSPACE_COLLECTION_CONTEXT = new UmbContextToken< + UmbWorkspaceContextInterface, + UmbWorkspaceCollectionContextInterface +>( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbWorkspaceCollectionContextInterface => + (context as UmbWorkspaceCollectionContextInterface).contentTypeHasCollection !== undefined, +); From d38d3e42fb61e59cbc8665acfe821384fa4af20a Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 11 Mar 2024 18:45:57 +0000 Subject: [PATCH 083/285] Implements the Workspace Collection Context interface on Documents and Media --- .../workspace/document-workspace.context.ts | 21 +++++++++++++------ .../workspace/media-workspace.context.ts | 17 +++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index dd8f95e9b7..3b754584e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -14,11 +14,11 @@ import { UmbUnpublishDocumentEntityAction } from '../entity-actions/unpublish.ac import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js'; import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; -import { - UmbEditableWorkspaceContextBase, - UmbWorkspaceSplitViewManager, - type UmbVariantableWorkspaceContextInterface, - type UmbPublishableWorkspaceContextInterface, +import { UmbEditableWorkspaceContextBase, UmbWorkspaceSplitViewManager } from '@umbraco-cms/backoffice/workspace'; +import type { + UmbWorkspaceCollectionContextInterface, + UmbVariantableWorkspaceContextInterface, + UmbPublishableWorkspaceContextInterface, } from '@umbraco-cms/backoffice/workspace'; import { appendToFrozenArray, @@ -33,11 +33,15 @@ import { type Observable, firstValueFrom } from '@umbraco-cms/backoffice/externa import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; +import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/document-type'; type EntityType = UmbDocumentDetailModel; export class UmbDocumentWorkspaceContext extends UmbEditableWorkspaceContextBase - implements UmbVariantableWorkspaceContextInterface, UmbPublishableWorkspaceContextInterface + implements + UmbVariantableWorkspaceContextInterface, + UmbPublishableWorkspaceContextInterface, + UmbWorkspaceCollectionContextInterface { // public readonly repository = new UmbDocumentDetailRepository(this); @@ -64,6 +68,7 @@ export class UmbDocumentWorkspaceContext readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.documentType.unique); readonly contentTypeHasCollection = this.#currentData.asObservablePart((data) => !!data?.documentType.collection); + readonly variants = this.#currentData.asObservablePart((data) => data?.variants ?? []); readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []); @@ -163,6 +168,10 @@ export class UmbDocumentWorkspaceContext return data; } + getCollectionAlias() { + return 'Umb.Collection.Document'; + } + getData() { return this.#currentData.getValue(); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts index f36ba974b9..bf13bd9675 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context.ts @@ -5,11 +5,11 @@ import { UmbMediaDetailRepository } from '../repository/index.js'; import type { UmbMediaDetailModel, UmbMediaVariantModel, UmbMediaVariantOptionModel } from '../types.js'; import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; -import { - UmbEditableWorkspaceContextBase, - UmbWorkspaceSplitViewManager, - type UmbVariantableWorkspaceContextInterface, +import type { + UmbWorkspaceCollectionContextInterface, + UmbVariantableWorkspaceContextInterface, } from '@umbraco-cms/backoffice/workspace'; +import { UmbEditableWorkspaceContextBase, UmbWorkspaceSplitViewManager } from '@umbraco-cms/backoffice/workspace'; import { appendToFrozenArray, jsonStringComparison, @@ -23,11 +23,12 @@ import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@u import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; +import type { UmbMediaTypeDetailModel } from '@umbraco-cms/backoffice/media-type'; type EntityType = UmbMediaDetailModel; export class UmbMediaWorkspaceContext extends UmbEditableWorkspaceContextBase - implements UmbVariantableWorkspaceContextInterface + implements UmbVariantableWorkspaceContextInterface, UmbWorkspaceCollectionContextInterface { // public readonly repository = new UmbMediaDetailRepository(this); @@ -51,7 +52,7 @@ export class UmbMediaWorkspaceContext readonly unique = this.#currentData.asObservablePart((data) => data?.unique); readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.mediaType.unique); - readonly contentTypeCollection = this.#currentData.asObservablePart((data) => data?.mediaType.collection); + readonly contentTypeHasCollection = this.#currentData.asObservablePart((data) => !!data?.mediaType.collection); readonly variants = this.#currentData.asObservablePart((data) => data?.variants || []); @@ -146,6 +147,10 @@ export class UmbMediaWorkspaceContext return data; } + getCollectionAlias() { + return 'Umb.Collection.Media'; + } + getData() { return this.#currentData.getValue(); } From 62453f59d0936719e8646674e742ad36c69c51ab Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 11 Mar 2024 18:56:01 +0000 Subject: [PATCH 084/285] Combined the Document/Media "Workspace Has Collection" condition --- .../src/packages/core/workspace/manifests.ts | 2 + .../workspace-has-collection.condition.ts | 44 +++++++++++++++++++ ...ment-workspace-has-collection.condition.ts | 44 ------------------- .../documents/documents/conditions/index.ts | 1 - .../documents/conditions/manifests.ts | 3 -- .../src/packages/documents/documents/index.ts | 1 - .../packages/documents/documents/manifests.ts | 2 - .../documents/workspace/manifests.ts | 7 ++- .../packages/media/media/conditions/index.ts | 1 - .../media/media/conditions/manifests.ts | 3 -- ...edia-workspace-has-collection.condition.ts | 44 ------------------- .../src/packages/media/media/index.ts | 1 - .../src/packages/media/media/manifests.ts | 2 - .../media/media/workspace/manifests.ts | 7 ++- 14 files changed, 56 insertions(+), 106 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-has-collection.condition.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/document-workspace-has-collection.condition.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/index.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/manifests.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/index.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/manifests.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/media-workspace-has-collection.condition.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/manifests.ts index fdcf26b4c7..d7a4ef36ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/manifests.ts @@ -2,10 +2,12 @@ import { manifests as componentManifests } from './components/manifests.js'; import { manifests as workspaceModals } from './workspace-modal/manifests.js'; import { manifest as workspaceAliasCondition } from './workspace-alias.condition.js'; import { manifest as workspaceEntityTypeCondition } from './workspace-entity-type.condition.js'; +import { manifest as workspaceHasCollectionCondition } from './workspace-has-collection.condition.js'; export const manifests = [ ...componentManifests, ...workspaceModals, workspaceAliasCondition, workspaceEntityTypeCondition, + workspaceHasCollectionCondition, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-has-collection.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-has-collection.condition.ts new file mode 100644 index 0000000000..03555f0019 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-has-collection.condition.ts @@ -0,0 +1,44 @@ +import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; +import type { + ManifestCondition, + UmbConditionConfigBase, + UmbConditionControllerArguments, + UmbExtensionCondition, +} from '@umbraco-cms/backoffice/extension-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UMB_WORKSPACE_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/workspace'; + +export class UmbWorkspaceHasCollectionCondition + extends UmbConditionBase + implements UmbExtensionCondition +{ + constructor( + host: UmbControllerHost, + args: UmbConditionControllerArguments, + ) { + super(host, args); + + this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (context) => { + this.observe( + context.contentTypeHasCollection, + (hasCollection) => { + this.permitted = hasCollection; + }, + 'observeHasCollection', + ); + }); + } +} + +export type WorkspaceHasCollectionConditionConfig = UmbConditionConfigBase< + typeof UMB_WORKSPACE_HAS_COLLECTION_CONDITION +>; + +export const UMB_WORKSPACE_HAS_COLLECTION_CONDITION = 'Umb.Condition.WorkspaceHasCollection'; + +export const manifest: ManifestCondition = { + type: 'condition', + name: 'Workspace Has Collection Condition', + alias: UMB_WORKSPACE_HAS_COLLECTION_CONDITION, + api: UmbWorkspaceHasCollectionCondition, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/document-workspace-has-collection.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/document-workspace-has-collection.condition.ts deleted file mode 100644 index 8c7670b5d3..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/document-workspace-has-collection.condition.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../workspace/document-workspace.context-token.js'; -import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; -import type { - ManifestCondition, - UmbConditionConfigBase, - UmbConditionControllerArguments, - UmbExtensionCondition, -} from '@umbraco-cms/backoffice/extension-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; - -export class UmbDocumentWorkspaceHasCollectionCondition - extends UmbConditionBase - implements UmbExtensionCondition -{ - constructor( - host: UmbControllerHost, - args: UmbConditionControllerArguments, - ) { - super(host, args); - - this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (context) => { - this.observe( - context.contentTypeHasCollection, - (hasCollection) => { - this.permitted = hasCollection; - }, - 'observeCollection', - ); - }); - } -} - -export type DocumentWorkspaceHasCollectionConditionConfig = UmbConditionConfigBase< - typeof UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION ->; - -export const UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION = 'Umb.Condition.DocumentWorkspaceHasCollection'; - -export const manifest: ManifestCondition = { - type: 'condition', - name: 'Document Workspace Has Collection Condition', - alias: UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION, - api: UmbDocumentWorkspaceHasCollectionCondition, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/index.ts deleted file mode 100644 index 2e4679e22a..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION } from './document-workspace-has-collection.condition.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/manifests.ts deleted file mode 100644 index 2935035044..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/conditions/manifests.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { manifest as docummentWorkspaceHasCollectionCondition } from './document-workspace-has-collection.condition.js'; - -export const manifests = [docummentWorkspaceHasCollectionCondition]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts index f8e2d03e2d..1367c634c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts @@ -8,7 +8,6 @@ export * from './user-permissions/index.js'; export * from './components/index.js'; export * from './entity.js'; export * from './entity-actions/index.js'; -export * from './conditions/index.js'; export * from './modals/index.js'; export * from './tree/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/manifests.ts index 4b900ea615..52b65d695d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/manifests.ts @@ -1,6 +1,5 @@ import { manifests as breadcrumbManifests } from './breadcrumb/manifests.js'; import { manifests as collectionManifests } from './collection/manifests.js'; -import { manifests as conditionManifests } from './conditions/manifests.js'; import { manifests as entityActionManifests } from './entity-actions/manifests.js'; import { manifests as entityBulkActionManifests } from './entity-bulk-actions/manifests.js'; import { manifests as menuItemManifests } from './menu-item/manifests.js'; @@ -16,7 +15,6 @@ import { manifests as workspaceManifests } from './workspace/manifests.js'; export const manifests = [ ...breadcrumbManifests, ...collectionManifests, - ...conditionManifests, ...entityActionManifests, ...entityBulkActionManifests, ...menuItemManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 9ab6259089..6d91660a03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -1,5 +1,4 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; -import { UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION } from '../conditions/document-workspace-has-collection.condition.js'; //import { UmbUnpublishDocumentEntityAction } from '../entity-actions/unpublish.action.js'; //import { UmbPublishDocumentEntityAction } from '../entity-actions/publish.action.js'; import { UmbDocumentSaveAndPublishWorkspaceAction } from './actions/save-and-publish.action.js'; @@ -40,7 +39,11 @@ const workspaceViews: Array = [ }, conditions: [ { - alias: UMB_DOCUMENT_WORKSPACE_HAS_COLLECTION_CONDITION, + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + { + alias: 'Umb.Condition.WorkspaceHasCollection', }, ], }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/index.ts deleted file mode 100644 index a6d19b94f4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION } from './media-workspace-has-collection.condition.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/manifests.ts deleted file mode 100644 index 839a888d34..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/manifests.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { manifest as mediaWorkspaceHasCollectionCondition } from './media-workspace-has-collection.condition.js'; - -export const manifests = [mediaWorkspaceHasCollectionCondition]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/media-workspace-has-collection.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/media-workspace-has-collection.condition.ts deleted file mode 100644 index 9641679f12..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/conditions/media-workspace-has-collection.condition.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UMB_MEDIA_WORKSPACE_CONTEXT } from '../workspace/media-workspace.context-token.js'; -import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; -import type { - ManifestCondition, - UmbConditionConfigBase, - UmbConditionControllerArguments, - UmbExtensionCondition, -} from '@umbraco-cms/backoffice/extension-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; - -export class UmbMediaWorkspaceHasCollectionCondition - extends UmbConditionBase - implements UmbExtensionCondition -{ - constructor( - host: UmbControllerHost, - args: UmbConditionControllerArguments, - ) { - super(host, args); - - this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (context) => { - this.observe( - context.contentTypeCollection, - (collection) => { - this.permitted = !!collection?.unique; - }, - 'observeCollection', - ); - }); - } -} - -export type MediaWorkspaceHasCollectionConditionConfig = UmbConditionConfigBase< - typeof UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION ->; - -export const UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION = 'Umb.Condition.MediaWorkspaceHasCollection'; - -export const manifest: ManifestCondition = { - type: 'condition', - name: 'Media Workspace Has Collection Condition', - alias: UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION, - api: UmbMediaWorkspaceHasCollectionCondition, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts index 00c3d17dda..2629a02d48 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/index.ts @@ -6,7 +6,6 @@ export * from './tracked-reference/index.js'; export * from './components/index.js'; export * from './entity.js'; export * from './utils/index.js'; -export * from './conditions/index.js'; export { UMB_MEDIA_TREE_ALIAS } from './tree/index.js'; export { UMB_MEDIA_COLLECTION_ALIAS } from './collection/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts index 222c4f8347..fcff2b6510 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/manifests.ts @@ -1,5 +1,4 @@ import { manifests as collectionManifests } from './collection/manifests.js'; -import { manifests as conditionManifests } from './conditions/manifests.js'; import { manifests as entityActionsManifests } from './entity-actions/manifests.js'; import { manifests as entityBulkActionsManifests } from './entity-bulk-actions/manifests.js'; import { manifests as menuItemManifests } from './menu-item/manifests.js'; @@ -11,7 +10,6 @@ import { manifests as workspaceManifests } from './workspace/manifests.js'; export const manifests = [ ...collectionManifests, - ...conditionManifests, ...entityActionsManifests, ...entityBulkActionsManifests, ...menuItemManifests, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts index e702e90afc..a51cd4223b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/manifests.ts @@ -1,4 +1,3 @@ -import { UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION } from '../conditions/media-workspace-has-collection.condition.js'; import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; import type { ManifestWorkspace, @@ -31,7 +30,11 @@ const workspaceViews: Array = [ }, conditions: [ { - alias: UMB_MEDIA_WORKSPACE_HAS_COLLECTION_CONDITION, + alias: 'Umb.Condition.WorkspaceAlias', + match: workspace.alias, + }, + { + alias: 'Umb.Condition.WorkspaceHasCollection', }, ], }, From 490379868cfa88fc22d93de852b6ea2bee0ca71b Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 11 Mar 2024 18:56:48 +0000 Subject: [PATCH 085/285] Refactored Collection property-editor UI to use the Workspace Collection Context. --- ...perty-editor-ui-collection-view.element.ts | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts index d7f4dc65d5..98877f738f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts @@ -8,7 +8,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_DOCUMENT_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/document'; import { UMB_MEDIA_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/media'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { UMB_WORKSPACE_CONTEXT, UMB_WORKSPACE_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbDocumentWorkspaceContext } from '@umbraco-cms/backoffice/document'; import type { UmbMediaWorkspaceContext } from '@umbraco-cms/backoffice/media'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; @@ -35,28 +35,16 @@ export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement impl constructor() { super(); - // Gets the Data Type ID for the current property. - this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - // TODO: [LK:2024-02-22] We need a solution that will allow the Collection property-editor - // to work in any workspace (that supports `unique` and `structure.getPropertyStructureByAlias`). - const entityType = workspaceContext.getEntityType(); - const contentWorkspaceContext = - entityType === 'media' - ? (workspaceContext as UmbMediaWorkspaceContext) - : (workspaceContext as UmbDocumentWorkspaceContext); + this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (workspaceContext) => { + this._collectionAlias = workspaceContext.getCollectionAlias(); - this._collectionAlias = entityType === 'media' ? UMB_MEDIA_COLLECTION_ALIAS : UMB_DOCUMENT_COLLECTION_ALIAS; - - this.observe(contentWorkspaceContext.unique, (unique) => { - if (this._config) { - this._config.unique = unique; - } - }); + this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { this.observe(propertyContext.alias, async (propertyAlias) => { if (propertyAlias) { - const property = await contentWorkspaceContext.structure.getPropertyStructureByAlias(propertyAlias); + // Gets the Data Type ID for the current property. + const property = await workspaceContext.structure.getPropertyStructureByAlias(propertyAlias); if (property && this._config) { + this._config.unique = workspaceContext.getUnique(); this._config.dataTypeId = property.dataType.unique; this.requestUpdate('_config'); } From d434fa19450552a259c5f95838372936fa2d533e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 20:21:34 +0100 Subject: [PATCH 086/285] further work on cloning groups --- .../content-type-container-structure-helper.class.ts | 4 ---- .../structure/content-type-structure-manager.class.ts | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index ff8d94d045..3082302234 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -259,8 +259,6 @@ export class UmbContentTypeContainerStructureHelper x.unique === contentTypeUnique)?.containers ?? []; + const ownerContainer = frozenContainers.find((x) => x.id === containerId); + if (!ownerContainer) { + console.error( + 'We do not have this container on the requested id, we should clone the container and append the change to it. [NL]', + ); + } + const containers = partialUpdateFrozenArray(frozenContainers, partialUpdate, (x) => x.id === containerId); // eslint-disable-next-line @typescript-eslint/ban-ts-comment From a14630bacad05baf4bff1b7b2378c4fcaa99e6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 11 Mar 2024 21:27:13 +0100 Subject: [PATCH 087/285] notes for furhter refactor --- .../property-settings-modal.element.ts | 2 +- ...nt-type-property-structure-helper.class.ts | 4 ++ ...-workspace-view-edit-properties.element.ts | 62 +++++++++++++++---- ...nt-type-workspace-view-edit-tab.element.ts | 30 +++++---- ...ontent-type-workspace-view-edit.element.ts | 4 +- 5 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts index 6e6e313c9c..3e92ffdfef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts @@ -2,12 +2,12 @@ import { UMB_PROPERTY_TYPE_WORKSPACE_ALIAS, UmbPropertyTypeWorkspaceContext, } from './property-settings-modal.context.js'; +import type { UmbPropertySettingsModalData, UmbPropertySettingsModalValue } from './property-settings-modal.token.js'; import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/document-type'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIBooleanInputEvent, UUIInputEvent, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertySettingsModalValue, UmbPropertySettingsModalData } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; // TODO: Could base take a token to get its types?. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 05b9cbe151..6bfddc917e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -48,6 +48,10 @@ export class UmbContentTypePropertyStructureHelper = []; @state() - _ownerDocumentTypes?: UmbContentTypeModel[]; + _ownerDocumentType?: UmbContentTypeModel; @state() protected _modalRouteBuilderNewProperty?: UmbModalRouteBuilder; @@ -137,6 +138,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, async (workspaceContext) => { this._propertyStructureHelper.setStructureManager(workspaceContext.structure); + this.observe( workspaceContext.isSorting, (isSorting) => { @@ -149,13 +151,13 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem }, '_observeIsSorting', ); - const docTypesObservable = await this._propertyStructureHelper.contentTypes(); + const docTypeObservable = workspaceContext.structure.ownerContentType; this.observe( - docTypesObservable, - (documents) => { - this._ownerDocumentTypes = documents; + docTypeObservable, + (document) => { + this._ownerDocumentType = document; }, - 'observeOwnerDocumentTypes', + 'observeOwnerDocumentType', ); }); this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { @@ -165,14 +167,18 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem // Note: Route for adding a new property this.#addPropertyModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addUniquePaths(['container-id']) + .addUniquePaths(['container-type', 'container-name']) .addAdditionalPath('add-property/:sortOrder') .onSetup(async (params) => { + if (!this._ownerDocumentType) return false; + /* const documentTypeId = this._ownerDocumentTypes?.find((types) => types.containers?.find((containers) => containers.id === this.containerId), )?.unique; if (documentTypeId === undefined) return false; - const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); + */ + + const propertyData = await this._propertyStructureHelper.createPropertyScaffold(); if (propertyData === undefined) return false; if (params.sortOrder !== undefined) { let sortOrderInt = parseInt(params.sortOrder, 10); @@ -182,11 +188,45 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } propertyData.sortOrder = sortOrderInt + 1; } - return { data: { documentTypeId }, value: propertyData }; + return { data: { documentTypeId: this._ownerDocumentType.unique }, value: propertyData }; }) - .onSubmit((value) => { + .onSubmit(async (value) => { + if (!this._ownerDocumentType) return false; + // I would like to create the missing container at this point: + + // TODO: Missing params in this case. but maybe its not a problem since I do know the containerType and containerName? + let containerId: string | undefined; + containerId = this._ownerDocumentType.containers.find( + (containers) => containers.type === this.containerType && containers.name === this.containerName, + )?.id; + if (!containerId) { + // We did not have this container in the owner document: + // TODO: Find the existing container to clone from: + + // TODO: Missing method to recursively create containers for a documentType: [NL] + // TODO: Such method should take an existing container id as 'inspiration'. + const containerData = await this._propertyStructureHelper + .getStructureManager() + ?.createContainer(this._ownerDocumentType.unique, parentID!!, params.containerType, sortOrder); + //TODO: inherit the name + + containerId = containerData?.id; + } + if (!containerId) { + throw new Error('Could not get or create a container to insert property into'); + return false; + } + + // Unless we are at root? + if (value.container) { + value.container.id = containerId; + } else { + value.container = { id: containerId }; + } + // TODO: The model requires a data-type to be set, we cheat currently. But this should be re-though when we implement validation(As we most likely will have to com up with partial models for the runtime model.) [NL] this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); + return true; }) .observeRouteBuilder((routeBuilder) => { this._modalRouteBuilderNewProperty = routeBuilder; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index f4aab531ef..39f21d0031 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -28,11 +28,11 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this._groups = model; }, onEnd: ({ item }) => { - if (this._ownerTabId === undefined) { + if (this._inherited === undefined) { throw new Error('OwnerTabId is not set, we have not made a local duplicated of this container.'); return; } - console.log('_ownerTabId', this._ownerTabId); + console.log('_ownerTabId', this._inherited); /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... @@ -71,17 +71,19 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { }, }); - private _ownerTabId?: string | null; + private _containerId?: string | null; + private _inherited?: boolean; - // TODO: get rid of this: @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; + public get containerId(): string | null | undefined { + return this._containerId; } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - const oldValue = this._ownerTabId; - this._ownerTabId = value; + public set containerId(value: string | null | undefined) { + if (value === this._inherited) return; + const oldValue = this._inherited; + this._containerId = value; + // TODO: Needs ways to check if the container is inherited or not, that works for root containers, maybe null === not inherited is okay: + this._inherited = value === null ? false : !this.#groupStructureHelper.isOwnerChildContainer(value); this.#groupStructureHelper.setParentId(value); this.requestUpdate('ownerTabId', oldValue); } @@ -159,7 +161,11 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? const len = this._groups.length; const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; - const container = this.#groupStructureHelper.addContainer(this._ownerTabId, sortOrder); + if (this._inherited) { + //TODO: recursively create containers for this docType. + // Then container ID should update to the new container ID. + } + const container = this.#groupStructureHelper.addContainer(this._containerId, sortOrder); console.log('container', container); }; @@ -178,7 +184,7 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { ? html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index fa083b16b9..c9df2b9898 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -179,7 +179,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbContentTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbContentTypeWorkspaceViewEditTabElement).ownerTabId = + (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = //tab.parent ? tab.parent.id === null this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; }, @@ -192,7 +192,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbContentTypeWorkspaceViewEditTabElement).noTabName = true; - (component as UmbContentTypeWorkspaceViewEditTabElement).ownerTabId = null; + (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = null; }, }); From 320e2511d593e60f8c98304d630a2d104daef8e7 Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Tue, 12 Mar 2024 09:09:50 +0100 Subject: [PATCH 088/285] items --- .../search/components/log-viewer-search-input.element.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-search-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-search-input.element.ts index aeaa9df8f1..d07c85ef72 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-search-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/search/components/log-viewer-search-input.element.ts @@ -71,7 +71,7 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement { #observeStuff() { if (!this.#logViewerContext) return; this.observe(this.#logViewerContext.savedSearches, (savedSearches) => { - this._savedSearches = savedSearches ?? []; + this._savedSearches = savedSearches?.items ?? []; this._isQuerySaved = this._savedSearches.some((search) => search.query === this._inputQuery); }); @@ -156,13 +156,13 @@ export class UmbLogViewerSearchInputElement extends UmbLitElement { ${this._showLoader ? html`
    -
    ` +
    ` : ''} ${this._inputQuery ? html`${!this._isQuerySaved ? html`` + >` : ''}` From 16ebae756aea21d287b9af0e07409ce842660697 Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Tue, 12 Mar 2024 09:15:56 +0100 Subject: [PATCH 089/285] remove todo --- .../overview/components/log-viewer-log-level-overview.element.ts | 1 - .../components/log-viewer-saved-searches-overview.element.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-log-level-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-log-level-overview.element.ts index 21c65ce697..24dc5c6022 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-log-level-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-log-level-overview.element.ts @@ -4,7 +4,6 @@ import { html, nothing, customElement, property, state } from '@umbraco-cms/back import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { LoggerResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -//TODO: implement the saved searches pagination when the API total bug is fixed @customElement('umb-log-viewer-log-level-overview') export class UmbLogViewerLogLevelOverviewElement extends UmbLitElement { #logViewerContext?: UmbLogViewerWorkspaceContext; diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts index ca34b34fcd..9ecc2ef98f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts @@ -6,7 +6,6 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { SavedLogSearchResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; -//TODO: implement the saved searches pagination when the API total bug is fixed @customElement('umb-log-viewer-saved-searches-overview') export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement { #itemsPerPage = 15; From 6d0016da82409093e169efd9e4cb1b71001113a4 Mon Sep 17 00:00:00 2001 From: Jason Elkin Date: Mon, 11 Mar 2024 15:57:08 +0000 Subject: [PATCH 090/285] Make icon search case insensitive --- .../modal/common/icon-picker/icon-picker-modal.element.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/icon-picker/icon-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/icon-picker/icon-picker-modal.element.ts index 464a42f7e0..9e28e70b4e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/icon-picker/icon-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/icon-picker/icon-picker-modal.element.ts @@ -26,7 +26,7 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement icon.name.includes(e.target.value)); + this._iconListFiltered = this._iconList.filter((icon) => + icon.name.toLowerCase().includes(e.target.value.toLowerCase()), + ); } else { this._iconListFiltered = this._iconList; } From ed1a0b6af4a8c611cd3beb2b73ed0338bb4e6714 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:27:50 +0100 Subject: [PATCH 091/285] fix mock data so it works with pagination --- src/Umbraco.Web.UI.Client/src/mocks/data/log-viewer.data.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/log-viewer.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/log-viewer.data.ts index 33f66fb115..fa179f05e7 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/log-viewer.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/log-viewer.data.ts @@ -14,7 +14,7 @@ class UmbLogViewerSearchesData extends UmbMockDBBase { - return this.data.slice(skip, take); + return this.data.slice(skip, take + skip); } getByName(name: string) { @@ -29,7 +29,7 @@ class UmbLogViewerTemplatesData extends UmbMockDBBase // skip can be number or null getTemplates(skip = 0, take = this.data.length): Array { - return this.data.slice(skip, take); + return this.data.slice(skip, take + skip); } } From 531c770af8db09a191f173d504a89c8e22fa1321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 10:32:32 +0100 Subject: [PATCH 092/285] Ensure local containers --- .../content-type-structure-manager.class.ts | 94 +++++++++++++- ...-type-workspace-view-edit-group.element.ts | 1 + ...-workspace-view-edit-properties.element.ts | 115 ++++++------------ ...nt-type-workspace-view-edit-tab.element.ts | 5 - ...ia-type-workspace-view-edit-tab.element.ts | 4 +- ...er-type-workspace-view-edit-tab.element.ts | 3 + 6 files changed, 136 insertions(+), 86 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 64ee771187..a9e3f4e6cd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -191,7 +191,70 @@ export class UmbContentTypePropertyStructureManager + */ + async ensureContainerOf( + containerId: string, + contentTypeUnique?: string, + ): Promise { + await this.#init; + const containers = this.#contentTypes.getValue().find((x) => x.unique === contentTypeUnique)?.containers; + const container = containers?.find((x) => x.id === containerId); + if (!container) { + return this.cloneContainerTo(containerId, contentTypeUnique); + } + return container; + } + + /** + * Clone a container to a specific Content Type. + * @param containerId - The container to clone, assuming it does not already exist on the given Content Type. + * @param toContentTypeUnique - The content type to clone to. + * @returns Promise + */ + async cloneContainerTo( + containerId: string, + toContentTypeUnique?: string, + ): Promise { + await this.#init; + toContentTypeUnique = toContentTypeUnique ?? this.#ownerContentTypeUnique!; + + // Find container. + const container = this.#containers.getValue().find((x) => x.id === containerId); + if (!container) throw new Error('Container to clone was not found'); + + const clonedContainer: UmbPropertyTypeContainerModel = { + ...container, + id: UmbId.new(), + }; + if (container.parent) { + // Investigate parent container. (See if we have one that matches if not, then clone it.) + const parentContainer = await this.ensureContainerOf(container.parent.id, toContentTypeUnique); + if (!parentContainer) { + throw new Error('Parent container for cloning could not be found or created'); + } + // Clone container. + clonedContainer.parent = { id: parentContainer.id }; + } + // Spread containers, so we can append to it, and then update the specific content-type with the new set of containers: [NL] + const containers = [ + ...(this.#contentTypes.getValue().find((x) => x.unique === toContentTypeUnique)?.containers ?? []), + ]; + containers.push(clonedContainer); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // TODO: fix TS partial complaint [NL] + this.#contentTypes.updateOne(toContentTypeUnique, { containers }); + + return container; + } async createContainer( contentTypeUnique: string | null, @@ -227,12 +290,20 @@ export class UmbContentTypePropertyStructureManager x.unique === contentTypeUnique)?.containers ?? []; const containers = appendToFrozenArray(frozenContainers, container, (x) => x.id === container.id); - console.log(frozenContainers, containers); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // TODO: fix TS partial complaint @@ -325,6 +396,16 @@ export class UmbContentTypePropertyStructureManager x.unique === contentTypeUnique)?.properties ?? []; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts index 12bf2f57dd..568276b1bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -92,6 +92,7 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { } else { // We use name match to determine inheritance, but a no name should not be inherited. this._inherited = false; + this.removeControllerByAlias('observeGroupContainers'); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index 2d26cc34a5..858fca6e29 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -171,12 +171,6 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem .addAdditionalPath('add-property/:sortOrder') .onSetup(async (params) => { if (!this._ownerDocumentType) return false; - /* - const documentTypeId = this._ownerDocumentTypes?.find((types) => - types.containers?.find((containers) => containers.id === this.containerId), - )?.unique; - if (documentTypeId === undefined) return false; - */ const propertyData = await this._propertyStructureHelper.createPropertyScaffold(); if (propertyData === undefined) return false; @@ -192,38 +186,6 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem }) .onSubmit(async (value) => { if (!this._ownerDocumentType) return false; - // I would like to create the missing container at this point: - - // TODO: Missing params in this case. but maybe its not a problem since I do know the containerType and containerName? - let containerId: string | undefined; - containerId = this._ownerDocumentType.containers.find( - (containers) => containers.type === this.containerType && containers.name === this.containerName, - )?.id; - if (!containerId) { - // We did not have this container in the owner document: - // TODO: Find the existing container to clone from: - - // TODO: Missing method to recursively create containers for a documentType: [NL] - // TODO: Such method should take an existing container id as 'inspiration'. - const containerData = await this._propertyStructureHelper - .getStructureManager() - ?.createContainer(this._ownerDocumentType.unique, parentID!!, params.containerType, sortOrder); - //TODO: inherit the name - - containerId = containerData?.id; - } - if (!containerId) { - throw new Error('Could not get or create a container to insert property into'); - return false; - } - - // Unless we are at root? - if (value.container) { - value.container.id = containerId; - } else { - value.container = { id: containerId }; - } - // TODO: The model requires a data-type to be set, we cheat currently. But this should be re-though when we implement validation(As we most likely will have to com up with partial models for the runtime model.) [NL] this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); return true; @@ -234,47 +196,44 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } render() { - return html` -
    - ${repeat( - this._propertyStructure, - (property) => property.id, - (property) => { - // Note: This piece might be moved into the property component - const inheritedFromDocument = this._ownerDocumentTypes?.find((types) => - types.containers?.find((containers) => containers.id === property.container?.id), - ); + return this._ownerDocumentType + ? html` +
    + ${repeat( + this._propertyStructure, + (property) => property.id, + (property) => { + return html` + { + this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); + }} + @property-delete=${() => { + this._propertyStructureHelper.removeProperty(property.id); + }}> + + `; + }, + )} +
    - return html` - { - this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); - }} - @property-delete=${() => { - this._propertyStructureHelper.removeProperty(property.id); - }}> - - `; - }, - )} -
    - - ${!this._sortModeActive - ? html` - Add property - ` - : ''} - `; + ${!this._sortModeActive + ? html` + Add property + ` + : ''} + ` + : ''; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 39f21d0031..5ae6aa9692 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -130,7 +130,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this.#groupStructureHelper.setParentType('Tab'); - // TODO: Use a structured/? workspace context token... this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (context) => { this.#groupStructureHelper.setStructureManager(context.structure); this.observe( @@ -161,10 +160,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? const len = this._groups.length; const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; - if (this._inherited) { - //TODO: recursively create containers for this docType. - // Then container ID should update to the new container ID. - } const container = this.#groupStructureHelper.addContainer(this._containerId, sortOrder); console.log('container', container); }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts index cc14a89647..d3dd37bf51 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts @@ -173,12 +173,14 @@ export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { placeholder="Enter a group name" value=${group.name ?? ''} @change=${(e: InputEvent) => { - const newName = (e.target as HTMLInputElement).value; + throw new Error('Not implemented'); + /*const newName = (e.target as HTMLInputElement).value; this._groupStructureHelper.updateContainerName( group.id!, group.parent?.id ?? null, newName, ); + */ }}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts index 810452ff82..ac0ac5f400 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts @@ -236,8 +236,11 @@ export class UmbMemberTypeWorkspaceViewEditTabElement extends UmbLitElement { placeholder=${this.localize.term('placeholders_entername')} .value=${group.name} @change=${(e: InputEvent) => { + throw new Error('Not implemented'); + /* const newName = (e.target as HTMLInputElement).value; this._groupStructureHelper.updateContainerName(group.id!, group.parent?.id ?? null, newName); + */ }}>`; } From 586c90862e33c6677e7fa564544651cf292e5999 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:33:56 +0100 Subject: [PATCH 093/285] fix mock data so it works with pagination --- .../src/mocks/handlers/log-viewer.handlers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts b/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts index c1aabd575f..acf457650e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/handlers/log-viewer.handlers.ts @@ -14,7 +14,7 @@ export const handlers = [ const items = umbLogViewerData.searches.getSavedSearches(skipNumber, takeNumber); const response = { - total: items.length, + total: umbLogViewerData.searches.total, items, }; @@ -57,7 +57,7 @@ export const handlers = [ return res(ctx.delay(), ctx.status(200), ctx.json(response)); }), //#endregion - + //#region Logs rest.get(umbracoPath('/log-viewer/level'), (req, res, ctx) => { return res(ctx.delay(), ctx.status(200), ctx.json(umbLogViewerData.logLevels)); From a2a10fc3b4a3b42735fe25b8e682a96d6c075e8c Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:22:44 +0100 Subject: [PATCH 094/285] image cropper use config for crops --- ...roperty-editor-ui-image-cropper.element.ts | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts index c2aa61f1b2..3162b622a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts @@ -1,9 +1,12 @@ import type { UmbImageCropperPropertyEditorValue } from '../../components/index.js'; -import { html, customElement, property, nothing } from '@umbraco-cms/backoffice/external/lit'; +import { html, customElement, property, nothing, state, PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import '../../components/input-image-cropper/input-image-cropper.element.js'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; /** * @element umb-property-editor-ui-image-cropper @@ -17,6 +20,9 @@ export class UmbPropertyEditorUIImageCropperElement extends UmbLitElement implem focalPoint: { left: 0.5, top: 0.5 }, }; + @state() + crops: UmbImageCropperPropertyEditorValue['crops'] = []; + updated(changedProperties: Map) { super.updated(changedProperties); if (changedProperties.has('value')) { @@ -32,22 +38,28 @@ export class UmbPropertyEditorUIImageCropperElement extends UmbLitElement implem @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { - const crops = config?.getValueByAlias('crops') ?? []; + this.crops = config?.getValueByAlias('crops') ?? []; - if (!this.value) { - this.value = { - src: '', - crops: crops, - focalPoint: { left: 0.5, top: 0.5 }, + // Replace crops from the value with the crops from the config while keeping the coordinates from the value if they exist. + const filteredCrops = this.crops.map((crop) => { + const cropFromValue = this.value.crops.find((valueCrop) => valueCrop.alias === crop.alias); + const result = { + ...crop, + coordinates: cropFromValue?.coordinates ?? undefined, }; - } else { - this.value.crops = crops; - } + + return result; + }); + + this.value = { + ...this.value, + crops: filteredCrops, + }; } #onChange(e: Event) { this.value = (e.target as any).value; - this.dispatchEvent(new CustomEvent('property-value-change')); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { From ee4cf4f0d4546374807f740d53c799609adf1b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 11:34:12 +0100 Subject: [PATCH 095/285] fix group inheritance determination --- ...t-type-container-structure-helper.class.ts | 22 ++++--------------- .../content-type-structure-manager.class.ts | 5 +++-- ...-type-workspace-view-edit-group.element.ts | 8 +++---- ...-workspace-view-edit-properties.element.ts | 6 ++--- ...nt-type-workspace-view-edit-tab.element.ts | 2 +- ...ontent-type-workspace-view-edit.element.ts | 4 +--- 6 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 3082302234..fa628b8904 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -173,14 +173,14 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); this._insertChildContainers(rootContainers); - this._parentOwnerContainers = this.#structure!.getOwnerContainers(this._parentType!) ?? []; + this._parentOwnerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; }, '_observeRootContainers', ); @@ -212,21 +212,7 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); - } - return false; - /* - return ( - this.#containers - .getValue() - .find((x) => (x.id === containerId && this._parentId ? x.parent?.id === this._parentId : x.parent === null)) !== - undefined - ); - */ + return this._parentOwnerContainers.some((x) => x.id === containerId); } /** Manipulate methods: */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index a9e3f4e6cd..05de94a5f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -315,7 +315,7 @@ export class UmbContentTypePropertyStructureManager x.containers?.filter((x) => x.type === containerType) ?? []); } - getOwnerContainers(containerType: UmbPropertyContainerTypes, parentId: string | null = null) { + getOwnerContainers(containerType: UmbPropertyContainerTypes, parentId: string | null) { + console.log('containers of owner:', this.getOwnerContentType()?.containers); return this.getOwnerContentType()?.containers?.filter((x) => parentId ? x.parent?.id === parentId : x.parent === null && x.type === containerType, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts index 568276b1bc..087850513f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -71,9 +71,9 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { #checkInherited() { if (this.groupStructureHelper && this.group) { - console.log('checkInherited!!!!'); - // TODO: Check for any inherited containers? + // Check is this container matches with any other group. If so it is inherited aka. merged with others. [NL] if (this.group.name) { + // We can first match with something if we have a name [NL] this.observe( this.groupStructureHelper.getStructureManager()!.containersByNameAndType(this.group.name, 'Group'), (containers) => { @@ -85,13 +85,13 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { this._hasOwnerContainer = isAOwnerContainer; this._inherited = !pureOwnerContainer; - console.log('this._inherited', this._inherited, 'this._hasOwnerContainer', this._hasOwnerContainer); }, 'observeGroupContainers', ); } else { - // We use name match to determine inheritance, but a no name should not be inherited. + // We use name match to determine inheritance, so no name cannot inherit. this._inherited = false; + this._hasOwnerContainer = true; this.removeControllerByAlias('observeGroupContainers'); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index 858fca6e29..d5b21f0e79 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -84,13 +84,13 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem }, }); - private _containerId: string | undefined; + private _containerId: string | null | undefined; @property({ type: String, attribute: 'container-id', reflect: false }) - public get containerId(): string | undefined { + public get containerId(): string | null | undefined { return this._containerId; } - public set containerId(value: string | undefined) { + public set containerId(value: string | null | undefined) { if (value === this._containerId) return; const oldValue = this._containerId; this._containerId = value; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 5ae6aa9692..f610127db2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -179,7 +179,7 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { ? html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index c9df2b9898..82f2ea197f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -179,9 +179,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbContentTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = - //tab.parent ? tab.parent.id === null - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; + (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = tab.id; }, }); }); From f2b012aa1710aa18aa66e88c5d113cdc8923cc5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 12:19:19 +0100 Subject: [PATCH 096/285] use container id for settings modal --- .../property-settings/property-settings-modal.element.ts | 2 +- .../content-type-workspace-view-edit-group.element.ts | 2 +- ...ontent-type-workspace-view-edit-properties.element.ts | 9 ++++----- .../data-type-flow-input/data-type-flow-input.element.ts | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts index 3e92ffdfef..1142f3cb8b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts @@ -260,7 +260,7 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< .value=${this.value.description}>
    diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts index 087850513f..a579554872 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -121,7 +121,7 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { + container-name=${this.group?.name ?? ''}> ` : ''; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index d5b21f0e79..4131a7ebbe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -94,6 +94,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem if (value === this._containerId) return; const oldValue = this._containerId; this._containerId = value; + this.#addPropertyModal.setUniquePathValue('container-id', value === null ? 'root' : value); this.requestUpdate('containerId', oldValue); } @@ -103,7 +104,6 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } public set containerName(value: string | undefined) { this._propertyStructureHelper.setContainerName(value); - this.#addPropertyModal.setUniquePathValue('container-name', value); } @property({ type: String, attribute: 'container-type', reflect: false }) @@ -112,7 +112,6 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } public set containerType(value: UmbPropertyContainerTypes | undefined) { this._propertyStructureHelper.setContainerType(value); - this.#addPropertyModal.setUniquePathValue('container-type', value); } #addPropertyModal: UmbModalRouteRegistrationController; @@ -167,12 +166,12 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem // Note: Route for adding a new property this.#addPropertyModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addUniquePaths(['container-type', 'container-name']) + .addUniquePaths(['container-id']) .addAdditionalPath('add-property/:sortOrder') .onSetup(async (params) => { - if (!this._ownerDocumentType) return false; + if (!this._ownerDocumentType || !this._containerId) return false; - const propertyData = await this._propertyStructureHelper.createPropertyScaffold(); + const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); if (propertyData === undefined) return false; if (params.sortOrder !== undefined) { let sortOrderInt = parseInt(params.sortOrder, 10); diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index ab01b797aa..2f888c4a5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -84,7 +84,7 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) { - ` + ` : html` - `; + `; } static styles = [ From 9fbdd270a14b62a29cffbbe659e3ff71d4c5873a Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 11:22:55 +0000 Subject: [PATCH 097/285] Adds `UmbReferenceByUniqueAndType` interface --- src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts index da63c4ae49..443a993919 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts @@ -23,3 +23,8 @@ export interface NumberRangeValueType { export interface UmbReferenceByUnique { unique: string; } + +export interface UmbReferenceByUniqueAndType { + type: string; + unique: string; +} From f72eba67506821bd1b7f2902a59bc1981033d755 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 11:24:29 +0000 Subject: [PATCH 098/285] TreePicker: refactored workspace context retrieval As it is only being used to get the current entity's unique ID. --- .../property-editor-ui-tree-picker.element.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts index d1952c415c..f13df12ed8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts @@ -42,8 +42,6 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen #dynamicRootRepository = new UmbDynamicRootRepository(this); - #workspaceContext?: typeof UMB_WORKSPACE_CONTEXT.TYPE; - @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { const startNode: UmbTreePickerSource | undefined = config?.getValueByAlias('startNode'); @@ -62,14 +60,6 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen this.ignoreUserStartNodes = config?.getValueByAlias('ignoreUserStartNodes'); } - constructor() { - super(); - - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#workspaceContext = workspaceContext; - }); - } - connectedCallback() { super.connectedCallback(); @@ -79,9 +69,10 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen async #setStartNodeId() { if (this.startNodeId) return; - const unique = this.#workspaceContext?.getUnique(); // TODO: Awaiting the workspace context to have a parent entity ID value. [LK] // e.g. const parentEntityId = this.#workspaceContext?.getParentEntityId(); + const workspaceContext = await this.getContext(UMB_WORKSPACE_CONTEXT); + const unique = workspaceContext.getUnique(); if (unique && this.#dynamicRoot) { const result = await this.#dynamicRootRepository.postDynamicRootQuery(this.#dynamicRoot, unique); if (result && result.length > 0) { From 7191f2173f014b114254bcc76cc276c58dd5881d Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 11:26:30 +0000 Subject: [PATCH 099/285] TreePicker: refactored to use updated data structure value e.g. `[ { type: 'document', unique: 'a3a37004-139f-4254-ba56-3ed381b3007c' } ]` --- .../property-editor-ui-tree-picker.element.ts | 10 ++-- .../input-tree/input-tree.element.ts | 46 +++++++++++-------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts index f13df12ed8..0a7819007b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts @@ -11,11 +11,10 @@ import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components'; /** * @element umb-property-editor-ui-tree-picker */ - @customElement('umb-property-editor-ui-tree-picker') export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - @property() - value = ''; + @property({ type: Array }) + value: UmbInputTreeElement['items'] = []; @state() type: UmbTreePickerSource['type'] = 'content'; @@ -82,13 +81,14 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen } #onChange(e: CustomEvent) { - this.value = (e.target as UmbInputTreeElement).value as string; + const input = e.target as UmbInputTreeElement; + this.value = input.items; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { return html`) { + this.#selectedIds = items?.map((item) => item.unique) ?? []; + this.value = items?.map((item) => item.unique).join(','); } - public get value(): string { - return super.value as string; + public get items(): Array { + return this.#selectedIds.map((id) => ({ type: this.#entityTypeLookup[this._type], unique: id })); } - selectedIds: Array = []; + #selectedIds: Array = []; #onChange(event: CustomEvent) { switch (this._type) { case 'content': - this.value = (event.target as UmbInputDocumentElement).selectedIds.join(','); + { + const input = event.target as UmbInputDocumentElement; + this.#selectedIds = input.selectedIds; + this.value = input.selectedIds.join(','); + } break; - case 'media': - this.value = (event.target as UmbInputMediaElement).selectedIds.join(','); + case 'media': { + const input = event.target as UmbInputMediaElement; + this.#selectedIds = input.selectedIds; + this.value = input.selectedIds.join(','); break; - case 'member': - this.value = (event.target as UmbInputMemberElement).selectedIds.join(','); + } + case 'member': { + const input = event.target as UmbInputMemberElement; + this.#selectedIds = input.selectedIds; + this.value = input.selectedIds.join(','); break; + } default: break; } @@ -102,7 +110,7 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) { #renderContentPicker() { return html` Date: Tue, 12 Mar 2024 11:26:53 +0000 Subject: [PATCH 100/285] TreePicker: Input element tidyup --- .../tree/components/input-tree/input-tree.element.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/components/input-tree/input-tree.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/components/input-tree/input-tree.element.ts index d9a568da77..b3abc71622 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/components/input-tree/input-tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/components/input-tree/input-tree.element.ts @@ -1,10 +1,11 @@ -import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member'; import { css, html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; -import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import type { UmbInputMemberElement } from '@umbraco-cms/backoffice/member'; +import type { UmbReferenceByUniqueAndType } from '@umbraco-cms/backoffice/models'; import type { UmbTreePickerSource } from '@umbraco-cms/backoffice/components'; @customElement('umb-input-tree') @@ -98,7 +99,7 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) { render() { switch (this._type) { case 'content': - return this.#renderContentPicker(); + return this.#renderDocumentPicker(); case 'media': return this.#renderMediaPicker(); case 'member': @@ -108,7 +109,7 @@ export class UmbInputTreeElement extends FormControlMixin(UmbLitElement) { } } - #renderContentPicker() { + #renderDocumentPicker() { return html` Date: Tue, 12 Mar 2024 12:32:41 +0100 Subject: [PATCH 101/285] move crops merge into the input --- .../input-image-cropper.element.ts | 36 +++++++++++++------ ...roperty-editor-ui-image-cropper.element.ts | 27 ++++---------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts index 1172d1863e..902f1f082a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-image-cropper/input-image-cropper.element.ts @@ -5,7 +5,7 @@ import type { UUIFileDropzoneElement, UUIFileDropzoneEvent } from '@umbraco-cms/ import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { type TemporaryFileQueueItem, UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; +import { UmbTemporaryFileManager } from '@umbraco-cms/backoffice/temporary-file'; import { assignToFrozenObject } from '@umbraco-cms/backoffice/observable-api'; import './image-cropper.element.js'; @@ -25,6 +25,9 @@ export class UmbInputImageCropperElement extends UmbLitElement { focalPoint: { left: 0.5, top: 0.5 }, }; + @property({ attribute: false }) + crops: UmbImageCropperPropertyEditorValue['crops'] = []; + @state() file?: File; @@ -36,18 +39,11 @@ export class UmbInputImageCropperElement extends UmbLitElement { constructor() { super(); this.#manager = new UmbTemporaryFileManager(this); - - // this.observe(this.#manager.isReady, (value) => (this.error = !value)); - this.observe(this.#manager.queue, this.#onQueueUpdate); } - #onQueueUpdate = (value: TemporaryFileQueueItem[]) => { - if (value.length) { - // this.file = value[0].file; - // this.fileUnique = value[0].unique; - // this.value.src = value[0].unique; - } - }; + protected firstUpdated(): void { + this.#mergeCrops(); + } #onUpload(e: UUIFileDropzoneEvent) { const file = e.detail.files[0]; @@ -79,6 +75,24 @@ export class UmbInputImageCropperElement extends UmbLitElement { this.dispatchEvent(new UmbChangeEvent()); }; + #mergeCrops() { + // Replace crops from the value with the crops from the config while keeping the coordinates from the value if they exist. + const filteredCrops = this.crops.map((crop) => { + const cropFromValue = this.value.crops.find((valueCrop) => valueCrop.alias === crop.alias); + const result = { + ...crop, + coordinates: cropFromValue?.coordinates ?? undefined, + }; + + return result; + }); + + this.value = { + ...this.value, + crops: filteredCrops, + }; + } + render() { if (this.value.src || this.file) { return this.#renderImageCropper(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts index 3162b622a2..6f04f15b06 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/image-cropper/property-editor-ui-image-cropper.element.ts @@ -1,5 +1,5 @@ -import type { UmbImageCropperPropertyEditorValue } from '../../components/index.js'; -import { html, customElement, property, nothing, state, PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbImageCropperPropertyEditorValue, UmbInputImageCropperElement } from '../../components/index.js'; +import { html, customElement, property, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import '../../components/input-image-cropper/input-image-cropper.element.js'; @@ -39,33 +39,20 @@ export class UmbPropertyEditorUIImageCropperElement extends UmbLitElement implem @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { this.crops = config?.getValueByAlias('crops') ?? []; - - // Replace crops from the value with the crops from the config while keeping the coordinates from the value if they exist. - const filteredCrops = this.crops.map((crop) => { - const cropFromValue = this.value.crops.find((valueCrop) => valueCrop.alias === crop.alias); - const result = { - ...crop, - coordinates: cropFromValue?.coordinates ?? undefined, - }; - - return result; - }); - - this.value = { - ...this.value, - crops: filteredCrops, - }; } #onChange(e: Event) { - this.value = (e.target as any).value; + this.value = (e.target as UmbInputImageCropperElement).value; this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { if (!this.value) return nothing; - return html``; + return html``; } } From 960ff842ecb332f83537d1eb900d7fabd9515179 Mon Sep 17 00:00:00 2001 From: JesmoDev <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:33:13 +0100 Subject: [PATCH 102/285] add type for media input property value --- .../property-editors/media-picker/index.ts | 8 +++++ ...property-editor-ui-media-picker.element.ts | 33 +++++++++++-------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/index.ts index 7eddcc7572..54a8835b52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/index.ts @@ -1 +1,9 @@ export * from './property-editor-ui-media-picker.element.js'; + +export type UmbMediaPickerPropertyValue = { + key: string; + mediaKey: string; + mediaTypeAlias: string; + focalPoint: { left: number; top: number } | null; + crops: Array<{ alias: string; width: number; height: number }>; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts index 1636c5fe24..54ccf9af0f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/property-editors/media-picker/property-editor-ui-media-picker.element.ts @@ -1,8 +1,11 @@ import type { UmbInputMediaElement } from '../../components/input-media/input-media.element.js'; import '../../components/input-media/input-media.element.js'; -import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; +import type { UmbMediaPickerPropertyValue } from './index.js'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import { + UmbPropertyValueChangeEvent, + type UmbPropertyEditorConfigCollection, +} from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbId } from '@umbraco-cms/backoffice/id'; @@ -13,9 +16,15 @@ import { UmbId } from '@umbraco-cms/backoffice/id'; @customElement('umb-property-editor-ui-media-picker') export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { @property({ attribute: false }) - public value: Array<{ key: string; mediaKey: string; mediaTypeAlias: string }> = []; + public set value(value: Array) { + this.#value = value; + this._items = this.value ? this.value.map((x) => x.mediaKey) : []; + } //TODO: Add support for document specific crops. The server side already supports this. - //TODO: Add crops and focalpoint to value. + + public get value() { + return this.#value; + } @property({ attribute: false }) public set config(config: UmbPropertyEditorConfigCollection | undefined) { @@ -39,32 +48,30 @@ export class UmbPropertyEditorUIMediaPickerElement extends UmbLitElement impleme @state() private _limitMax: number = Infinity; - protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { - super.firstUpdated(_changedProperties); + #value: Array = []; - this._items = this.value ? this.value.map((x) => x.mediaKey) : []; - } - - private _onChange(event: CustomEvent) { + #onChange(event: CustomEvent) { const selectedIds = (event.target as UmbInputMediaElement).selectedIds; const result = selectedIds.map((mediaKey) => { return { key: UmbId.new(), mediaKey, - mediaTypeAlias: 'Image', + mediaTypeAlias: '', + focalPoint: null, + crops: [], }; }); this.value = result; this._items = this.value ? this.value.map((x) => x.mediaKey) : []; - this.dispatchEvent(new CustomEvent('property-value-change')); + this.dispatchEvent(new UmbPropertyValueChangeEvent()); } render() { return html` From 70d7145fa930ffd25a2e18b1411d30ca134b691d Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:46:04 +0100 Subject: [PATCH 103/285] Update src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> --- .../components/log-viewer-message-templates-overview.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts index b4b8c53919..9edcbc0b0e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-message-templates-overview.element.ts @@ -39,7 +39,6 @@ export class UmbLogViewerMessageTemplatesOverviewElement extends UmbLitElement { } #getMessageTemplates() { - //const take = this._messageTemplates?.length ?? 0; const skip = this.#currentPage * this.#itemsPerPage - this.#itemsPerPage; this.#logViewerContext?.getMessageTemplates(skip, this.#itemsPerPage); } From bb5dea3c1fdfb0e3be0feb4cc1f5d959fd48c584 Mon Sep 17 00:00:00 2001 From: Lone Iversen <108085781+loivsen@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:09:11 +0100 Subject: [PATCH 104/285] take 999 --- .../src/packages/log-viewer/workspace/logviewer.context.ts | 2 +- .../components/log-viewer-saved-searches-overview.element.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts index 39fc6efa54..c5f467d934 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer.context.ts @@ -168,7 +168,7 @@ export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements U return this.#dateRange.getValue(); } - async getSavedSearches({ skip = 0, take = 15 }: { skip?: number; take?: number } = {}) { + async getSavedSearches({ skip = 0, take = 999 }: { skip?: number; take?: number } = {}) { const { data } = await this.#repository.getSavedSearches({ skip, take }); if (data) { this.#savedSearches.setValue(data); diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts index 9ecc2ef98f..c77d0f5e21 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/views/overview/components/log-viewer-saved-searches-overview.element.ts @@ -8,7 +8,7 @@ import type { UUIPaginationEvent } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-log-viewer-saved-searches-overview') export class UmbLogViewerSavedSearchesOverviewElement extends UmbLitElement { - #itemsPerPage = 15; + #itemsPerPage = 999; #currentPage = 1; @state() From bbf2b64f6094eca8f3ef03e3dbac2f19aca1fe26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 13:17:59 +0100 Subject: [PATCH 105/285] further refactoring --- ...nt-type-property-structure-helper.class.ts | 13 ++++- .../content-type-structure-manager.class.ts | 3 +- ...-type-workspace-view-edit-group.element.ts | 2 +- ...-workspace-view-edit-properties.element.ts | 9 +-- ...pe-workspace-view-edit-property.element.ts | 58 +++++++++++++++---- 5 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts index 6bfddc917e..840deed847 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-property-structure-helper.class.ts @@ -4,6 +4,8 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; +type UmbPropertyTypeId = UmbPropertyTypeModel['id']; + /** * This class is a helper class for managing the structure of containers in a content type. * This requires a structure manager {@link UmbContentTypePropertyStructureManager} to manage the structure. @@ -101,7 +103,7 @@ export class UmbContentTypePropertyStructureHelper { - // If this need to be able to remove properties, we need to clean out the ones of this group.id before inserting them: + // If property was removed, we want to make sure that we clean out the ones of this group.id before inserting them again: const _propertyStructure = this.#propertyStructure.getValue().filter((x) => x.container?.id !== groupId); properties?.forEach((property) => { @@ -117,6 +119,13 @@ export class UmbContentTypePropertyStructureHelper x?.properties.some((y) => y.id === propertyId)); + } + // TODO: consider moving this to another class, to separate 'viewer' from 'manipulator': /** Manipulate methods: */ @@ -151,7 +160,7 @@ export class UmbContentTypePropertyStructureHelper { - this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); - }} - @property-delete=${() => { - this._propertyStructureHelper.removeProperty(property.id); - }}> + .propertyStructureHelper=${this._propertyStructureHelper} + .property=${property}> `; }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts index 58b20f267f..321c866fc8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts @@ -12,6 +12,8 @@ import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UMB_PROPERTY_SETTINGS_MODAL, + type UmbContentTypeModel, + type UmbContentTypePropertyStructureHelper, type UmbPropertyTypeModel, type UmbPropertyTypeScaffoldModel, } from '@umbraco-cms/backoffice/content-type'; @@ -29,6 +31,17 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { #settingsModal; #workspaceModal; + @property({ attribute: false }) + public set propertyStructureHelper(value: UmbContentTypePropertyStructureHelper | undefined) { + if (value === this._propertyStructureHelper) return; + this._propertyStructureHelper = value; + this.#checkInherited(); + } + public get propertyStructureHelper(): UmbContentTypePropertyStructureHelper | undefined { + return this._propertyStructureHelper; + } + private _propertyStructureHelper?: UmbContentTypePropertyStructureHelper | undefined; + /** * Property, the data object for the property. * @type {UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined} @@ -42,8 +55,9 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { public set property(value: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined) { const oldValue = this._property; this._property = value; + this.#checkInherited(); this.#settingsModal.setUniquePathValue('propertyId', value?.id?.toString()); - this.#workspaceModal.setUniquePathValue('propertyId', value?.id?.toString()); + //this.#workspaceModal.setUniquePathValue('propertyId', value?.id?.toString()); this.setDataType(this._property?.dataType?.unique); this.requestUpdate('property', oldValue); } @@ -99,7 +113,7 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { }); this.#workspaceModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addUniquePaths(['propertyId']) + //.addUniquePaths(['propertyId']) .addAdditionalPath('document-type') .onSetup(() => { return { data: { entityType: 'document-type', preset: {} } }; @@ -109,15 +123,34 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { }); } - _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); + async #checkInherited() { + if (this._propertyStructureHelper && this._property) { + console.log('checkInherited'); + // We can first match with something if we have a name [NL] + this.observe( + await this._propertyStructureHelper!.isOwnerProperty(this._property.id), + (isOwned) => { + console.log('inherited', isOwned); + this.inherited = !isOwned; + }, + 'observeIsOwnerProperty', + ); + } } - _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { - const partialObject = {} as any; - partialObject[propertyName] = value; + _partialUpdate(partialObject: UmbPropertyTypeModel) { + if (!this._property || !this._propertyStructureHelper) return; + this._propertyStructureHelper.partialUpdateProperty(this._property.id, partialObject); + } - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); + _singleValueUpdate( + propertyName: PropertyNameType, + value: UmbPropertyTypeModel[PropertyNameType], + ) { + if (!this._property || !this._propertyStructureHelper) return; + const partialObject: Partial = {}; + partialObject[propertyName] = value === null ? undefined : value; + this._propertyStructureHelper.partialUpdateProperty(this._property.id, partialObject); } #onToggleAliasLock() { @@ -132,21 +165,22 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { async #requestRemove(e: Event) { e.preventDefault(); e.stopImmediatePropagation(); - if (!this.property || !this.property.id) return; + if (!this._property || !this._property.id) return; + // TODO: Do proper localization here: [NL] await umbConfirmModal(this, { headline: `${this.localize.term('actions_delete')} property`, content: html` - Are you sure you want to delete the property ${this.property.name ?? this.property.id} + Are you sure you want to delete the property ${this._property.name ?? this._property.id}
    `, confirmLabel: this.localize.term('actions_delete'), color: 'danger', }); - this.dispatchEvent(new CustomEvent('property-delete')); + this._propertyStructureHelper?.removeProperty(this._property.id); } #onNameChange(event: UUIInputEvent) { From 5195ff1e66b9f4f7b21014e51a02b356660811dc Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:30:28 +0000 Subject: [PATCH 106/285] Collection View Bundle: Corrected icon markup --- .../collection/components/collection-view-bundle.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts index c651449d27..70684c1c66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/components/collection-view-bundle.element.ts @@ -85,7 +85,7 @@ export class UmbCollectionViewBundleElement extends UmbLitElement { } #renderItemDisplay(view: ManifestCollectionView) { - return html``; + return html``; } static styles = [ From c03cca293e6707874e1deca291338cb9eabe1d78 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:39:15 +0000 Subject: [PATCH 107/285] Changed `umb-collection-selection-actions` to use `footer` slot instead of the `footer-info` slot. This removes/hides the placeholder UI for the footer. --- .../core/collection/default/collection-default.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts index 929fb667ac..3bacd4e26a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.element.ts @@ -69,7 +69,7 @@ export class UmbCollectionDefaultElement extends UmbLitElement { } protected renderSelectionActions() { - return html``; + return html``; } static styles = [ From 3c9009ee2745f9dfced57b1578a6bd1e00eb6844 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:40:47 +0000 Subject: [PATCH 108/285] Bugfix: UmbExtensionWithApiSlotElement assigns the `apiProps` once initialized. --- .../extension-with-api-slot/extension-with-api-slot.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-with-api-slot/extension-with-api-slot.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-with-api-slot/extension-with-api-slot.element.ts index 40248f67a3..1cd00a2c24 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-with-api-slot/extension-with-api-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/extension-with-api-slot/extension-with-api-slot.element.ts @@ -161,6 +161,7 @@ export class UmbExtensionWithApiSlotElement extends UmbLitElement { undefined, // We can leave the alias to undefined, as we destroy this our selfs. this.defaultElement, ); + this.#extensionsController.apiProperties = this.#apiProps; this.#extensionsController.elementProperties = this.#elProps; } } From f94055eb05f44939bb1e9149c5dfe9259a4b119c Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:42:30 +0000 Subject: [PATCH 109/285] SelectionManager: sets `#selectable` default to `true` --- .../packages/core/utils/selection-manager/selection.manager.ts | 2 +- .../documents/collection/document-collection.context.ts | 1 - .../packages/media/media/collection/media-collection.context.ts | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts index 099c3d744e..6bfb64d0b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/selection-manager/selection.manager.ts @@ -9,7 +9,7 @@ import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observab * @class UmbSelectionManager */ export class UmbSelectionManager extends UmbControllerBase { - #selectable = new UmbBooleanState(false); + #selectable = new UmbBooleanState(true); public readonly selectable = this.#selectable.asObservable(); #selection = new UmbArrayState(>[], (x) => x); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts index 139d393fd6..37fe554b42 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts @@ -10,6 +10,5 @@ export class UmbDocumentCollectionContext extends UmbDefaultCollectionContext< constructor(host: UmbControllerHost) { super(host, UMB_DOCUMENT_TABLE_COLLECTION_VIEW_ALIAS); - this.selection.setSelectable(true); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts index 3b4d29afac..bcf2f7ffe2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.context.ts @@ -9,7 +9,5 @@ export class UmbMediaCollectionContext extends UmbDefaultCollectionContext< > { constructor(host: UmbControllerHost) { super(host, UMB_MEDIA_GRID_COLLECTION_VIEW_ALIAS); - - this.selection.setSelectable(true); } } From 95af522e60ab7795d4265f9e7d419c3af78cca8b Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:45:11 +0000 Subject: [PATCH 110/285] Document/Media Collection Repositories to extend from the `UmbRepositoryBase` for potential future usage. --- .../collection/repository/document-collection.repository.ts | 5 ++++- .../collection/repository/media-collection.repository.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.repository.ts index 47042ffd2d..9364d5ab58 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.repository.ts @@ -1,12 +1,15 @@ import type { UmbDocumentCollectionFilterModel } from '../types.js'; import { UmbDocumentCollectionServerDataSource } from './document-collection.server.data-source.js'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import type { UmbCollectionRepository } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbDocumentCollectionRepository implements UmbCollectionRepository { +export class UmbDocumentCollectionRepository extends UmbRepositoryBase implements UmbCollectionRepository { #collectionSource: UmbDocumentCollectionServerDataSource; constructor(host: UmbControllerHost) { + super(host); + this.#collectionSource = new UmbDocumentCollectionServerDataSource(host); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.repository.ts index f5be70970c..ffb21ff449 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/repository/media-collection.repository.ts @@ -1,12 +1,15 @@ import type { UmbMediaCollectionFilterModel } from '../types.js'; import { UmbMediaCollectionServerDataSource } from './media-collection.server.data-source.js'; +import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import type { UmbCollectionRepository } from '@umbraco-cms/backoffice/collection'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbMediaCollectionRepository implements UmbCollectionRepository { +export class UmbMediaCollectionRepository extends UmbRepositoryBase implements UmbCollectionRepository { #collectionSource: UmbMediaCollectionServerDataSource; constructor(host: UmbControllerHost) { + super(host); + this.#collectionSource = new UmbMediaCollectionServerDataSource(host); } From f3ee0b2cb5ad729bffff91ed3a5d9fceb4b701c1 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:46:56 +0000 Subject: [PATCH 111/285] Document Collection Table View - tidied up reference --- .../views/table/document-table-collection-view.element.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts index 70a08a5ae7..d66f93d783 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/document-table-collection-view.element.ts @@ -1,11 +1,11 @@ import { getPropertyValueByAlias } from '../index.js'; import type { UmbCollectionColumnConfiguration } from '../../../../../core/collection/types.js'; -import type { UmbDocumentCollectionFilterModel, UmbDocumentCollectionItemModel } from '../../types.js'; +import type { UmbDocumentCollectionItemModel } from '../../types.js'; +import type { UmbDocumentCollectionContext } from '../../document-collection.context.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UMB_DEFAULT_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/collection'; -import type { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection'; import type { UmbTableColumn, UmbTableConfig, @@ -62,7 +62,7 @@ export class UmbDocumentTableCollectionViewElement extends UmbLitElement { @state() private _skip: number = 0; - #collectionContext?: UmbDefaultCollectionContext; + #collectionContext?: UmbDocumentCollectionContext; constructor() { super(); From f8fe9e97581656e51642174b60e13e98a4a291c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 13:55:32 +0100 Subject: [PATCH 112/285] correct insert property for inherited containers --- .../structure/content-type-structure-manager.class.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index c12ede1639..48434165e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -201,10 +201,14 @@ export class UmbContentTypePropertyStructureManager { await this.#init; - const containers = this.#contentTypes.getValue().find((x) => x.unique === contentTypeUnique)?.containers; + const contentType = this.#contentTypes.getValue().find((x) => x.unique === contentTypeUnique); + if (!contentType) { + throw new Error('Could not find the Content Type to ensure containers for'); + } + const containers = contentType?.containers; const container = containers?.find((x) => x.id === containerId); if (!container) { return this.cloneContainerTo(containerId, contentTypeUnique); @@ -253,7 +257,7 @@ export class UmbContentTypePropertyStructureManager Date: Tue, 12 Mar 2024 12:56:14 +0000 Subject: [PATCH 113/285] Document Collection: Create button style --- .../action/create-document-collection-action.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/action/create-document-collection-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/action/create-document-collection-action.element.ts index abc2c4a7af..a414f3f365 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/action/create-document-collection-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/action/create-document-collection-action.element.ts @@ -111,7 +111,7 @@ export class UmbCreateDocumentCollectionActionElement extends UmbLitElement { const label = this.manifest?.meta.label ?? this.localize.term('general_create'); return html` - + ${label} From d97c5f205092803994abdc9aea0a4ebfc2c0ef1d Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 12:58:34 +0000 Subject: [PATCH 114/285] Document/Media Collections: Don't send the DataType ID to the server, as the Management API already has logic to deal with it. Otherwise it throws an error. We still use the it on the frontend to get UI configuration. Removed the `uui-loader` as it can appear jarring. --- .../repository/document-collection.server.data-source.ts | 6 +----- .../document-workspace-view-collection.element.ts | 5 ++--- .../collection/media-workspace-view-collection.element.ts | 5 ++--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts index d2709afa67..8ddf18a51e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/repository/document-collection.server.data-source.ts @@ -17,13 +17,9 @@ export class UmbDocumentCollectionServerDataSource implements UmbCollectionDataS throw new Error('Unique ID is required to fetch a collection.'); } - if (!query.dataTypeId) { - throw new Error('Data type ID is required to fetch a collection.'); - } - const params = { id: query.unique, - dataTypeId: query.dataTypeId, + dataTypeId: query.dataTypeId ?? '', orderBy: query.orderBy ?? 'updateDate', orderCulture: query.orderCulture ?? 'en-US', orderDirection: query.orderDirection === 'asc' ? DirectionModel.ASCENDING : DirectionModel.DESCENDING, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/collection/document-workspace-view-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/collection/document-workspace-view-collection.element.ts index 758a11482d..47f4fff704 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/collection/document-workspace-view-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/collection/document-workspace-view-collection.element.ts @@ -2,7 +2,7 @@ import type { UmbCollectionBulkActionPermissions, UmbCollectionConfiguration, } from '../../../../../core/collection/types.js'; -import { customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -58,7 +58,6 @@ export class UmbDocumentWorkspaceViewCollectionElement extends UmbLitElement imp const config = new UmbPropertyEditorConfigCollection(dataType.values); return { unique: this._documentUnique, - dataTypeId: dataType.unique, allowedEntityBulkActions: config?.getValueByAlias('bulkActionPermissions'), orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate', orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc', @@ -69,7 +68,7 @@ export class UmbDocumentWorkspaceViewCollectionElement extends UmbLitElement imp } render() { - if (!this._config?.unique || !this._config?.dataTypeId) return html``; + if (!this._config?.unique) return nothing; return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/collection/media-workspace-view-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/collection/media-workspace-view-collection.element.ts index 70a18ba74e..a8d12c291f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/collection/media-workspace-view-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/collection/media-workspace-view-collection.element.ts @@ -1,4 +1,4 @@ -import { customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -58,7 +58,6 @@ export class UmbMediaWorkspaceViewCollectionElement extends UmbLitElement implem const config = new UmbPropertyEditorConfigCollection(dataType.values); return { unique: this._mediaUnique, - dataTypeId: dataType.unique, allowedEntityBulkActions: config?.getValueByAlias('bulkActionPermissions'), orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate', orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc', @@ -69,7 +68,7 @@ export class UmbMediaWorkspaceViewCollectionElement extends UmbLitElement implem } render() { - if (!this._config?.unique || !this._config?.dataTypeId) return html``; + if (!this._config?.unique) return nothing; return html``; } } From 63a67b4c7a7b8e1debb0190e769ed02044b0ae4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 13:59:30 +0100 Subject: [PATCH 115/285] remove bold logging --- .../views/design/content-type-workspace-view-edit-tab.element.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index f610127db2..618e74a6f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -191,7 +191,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this._groups, (group) => group.id, (group) => html` - ${group.id} - ${group.name} Date: Tue, 12 Mar 2024 14:02:32 +0100 Subject: [PATCH 116/285] remove logs --- .../design/content-type-workspace-view-edit-property.element.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts index 321c866fc8..8863b56a29 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts @@ -125,12 +125,10 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { async #checkInherited() { if (this._propertyStructureHelper && this._property) { - console.log('checkInherited'); // We can first match with something if we have a name [NL] this.observe( await this._propertyStructureHelper!.isOwnerProperty(this._property.id), (isOwned) => { - console.log('inherited', isOwned); this.inherited = !isOwned; }, 'observeIsOwnerProperty', From 76bd3e877bd79564d7863c7efb48beb24e89834c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 14:12:04 +0100 Subject: [PATCH 117/285] clean up --- .../structure/content-type-structure-manager.class.ts | 1 - .../views/design/content-type-workspace-view-edit.element.ts | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 48434165e6..f1bdbf73aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -590,7 +590,6 @@ export class UmbContentTypePropertyStructureManager parentId ? x.parent?.id === parentId : x.parent === null && x.type === containerType, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 82f2ea197f..1871ede58b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -168,7 +168,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } private _createRoutes() { - if (!this._workspaceContext || !this._tabs) return; + if (!this._workspaceContext || !this._tabs || this._hasRootGroups === undefined) return; const routes: UmbRoute[] = []; if (this._tabs.length > 0) { @@ -240,11 +240,13 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem : ''; } async #addTab() { + /* const inputEl = this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement; if (inputEl?.value === '') { this.#focusInput(); return; } + */ if (!this._tabs) return; From c6de7f8fd563cc6dc2a6763bcbffd6752d8ac4b6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 12 Mar 2024 10:37:05 +0100 Subject: [PATCH 118/285] generate new server models --- .../src/external/backend-api/src/index.ts | 1 + .../models/ContentTreeItemResponseModel.ts | 1 - .../src/models/PagedWebhookResponseModel.ts | 12 +++++++++ .../src/models/TreeItemPresentationModel.ts | 1 - .../src/services/WebhookResource.ts | 25 +++++++++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/PagedWebhookResponseModel.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/index.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/index.ts index 0d1532cf44..3efb987f34 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/index.ts @@ -291,6 +291,7 @@ export type { PagedTagResponseModel } from './models/PagedTagResponseModel'; export type { PagedTelemetryResponseModel } from './models/PagedTelemetryResponseModel'; export type { PagedUserGroupResponseModel } from './models/PagedUserGroupResponseModel'; export type { PagedUserResponseModel } from './models/PagedUserResponseModel'; +export type { PagedWebhookResponseModel } from './models/PagedWebhookResponseModel'; export type { PartialViewFolderResponseModel } from './models/PartialViewFolderResponseModel'; export type { PartialViewItemResponseModel } from './models/PartialViewItemResponseModel'; export type { PartialViewResponseModel } from './models/PartialViewResponseModel'; diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/ContentTreeItemResponseModel.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/ContentTreeItemResponseModel.ts index b2e1c8d529..bb5f9c106d 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/ContentTreeItemResponseModel.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/ContentTreeItemResponseModel.ts @@ -6,7 +6,6 @@ import type { ReferenceByIdModel } from './ReferenceByIdModel'; export type ContentTreeItemResponseModel = { - type: string; hasChildren: boolean; parent?: ReferenceByIdModel | null; noAccess: boolean; diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/PagedWebhookResponseModel.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/PagedWebhookResponseModel.ts new file mode 100644 index 0000000000..1efc883d28 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/PagedWebhookResponseModel.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { WebhookResponseModel } from './WebhookResponseModel'; + +export type PagedWebhookResponseModel = { + total: number; + items: Array; +}; + diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/TreeItemPresentationModel.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/TreeItemPresentationModel.ts index b6e40540ce..3f84670b27 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/TreeItemPresentationModel.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/models/TreeItemPresentationModel.ts @@ -4,7 +4,6 @@ /* eslint-disable */ export type TreeItemPresentationModel = { - type: string; hasChildren: boolean; }; diff --git a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services/WebhookResource.ts b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services/WebhookResource.ts index bbe951c01d..4e7f80c31f 100644 --- a/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services/WebhookResource.ts +++ b/src/Umbraco.Web.UI.Client/src/external/backend-api/src/services/WebhookResource.ts @@ -3,6 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { CreateWebhookRequestModel } from '../models/CreateWebhookRequestModel'; +import type { PagedWebhookResponseModel } from '../models/PagedWebhookResponseModel'; import type { UpdateWebhookRequestModel } from '../models/UpdateWebhookRequestModel'; import type { WebhookItemResponseModel } from '../models/WebhookItemResponseModel'; import type { WebhookResponseModel } from '../models/WebhookResponseModel'; @@ -13,6 +14,30 @@ import { request as __request } from '../core/request'; export class WebhookResource { + /** + * @returns PagedWebhookResponseModel Success + * @throws ApiError + */ + public static getWebhook({ + skip, + take = 100, + }: { + skip?: number, + take?: number, + }): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/umbraco/management/api/v1/webhook', + query: { + 'skip': skip, + 'take': take, + }, + errors: { + 401: `The resource is protected and requires an authentication token`, + }, + }); + } + /** * @returns string Created * @throws ApiError From a97d3bb0af823fcb5a009f08949495e0a1d3fd91 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 12 Mar 2024 10:56:19 +0100 Subject: [PATCH 119/285] clean up mocks --- .../src/mocks/data/data-type/data-type.data.ts | 4 +--- .../src/mocks/data/dictionary/dictionary.data.ts | 4 +--- .../src/mocks/data/dictionary/dictionary.db.ts | 2 +- .../src/mocks/data/document-type/document-type.data.ts | 4 +--- .../src/mocks/data/document-type/document-type.db.ts | 4 +--- .../src/mocks/data/document/document.data.ts | 4 +--- .../src/mocks/data/document/document.db.ts | 2 +- .../src/mocks/data/media-type/media-type.data.ts | 10 ++++------ .../src/mocks/data/media-type/media-type.db.ts | 2 +- .../src/mocks/data/media/media.data.ts | 4 +--- .../src/mocks/data/media/media.db.ts | 2 +- .../src/mocks/data/partial-view/partial-view.data.ts | 4 +--- .../src/mocks/data/relations/relation-type.data.ts | 5 ----- .../src/mocks/data/script/script.data.ts | 4 +--- .../src/mocks/data/static-file/static-file.data.ts | 3 +-- .../src/mocks/data/stylesheet/stylesheet.data.ts | 4 +--- .../src/mocks/data/template/template.data.ts | 4 +--- src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts | 3 +-- .../mocks/data/utils/entity/entity-folder.manager.ts | 2 +- .../src/mocks/data/utils/entity/entity-recycle-bin.ts | 4 +--- .../src/mocks/data/utils/entity/entity-tree.manager.ts | 2 +- .../data/utils/file-system/file-system-tree.manager.ts | 6 +++--- 22 files changed, 26 insertions(+), 57 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts index 52f12c19dc..492fd72563 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type/data-type.data.ts @@ -4,9 +4,7 @@ import type { DataTypeTreeItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockDataTypeModelHack = DataTypeResponseModel & DataTypeTreeItemResponseModel & DataTypeItemResponseModel; - -export interface UmbMockDataTypeModel extends Omit {} +export type UmbMockDataTypeModel = DataTypeResponseModel & DataTypeTreeItemResponseModel & DataTypeItemResponseModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.data.ts index b135a1b516..3f68fa97f5 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.data.ts @@ -5,13 +5,11 @@ import type { NamedEntityTreeItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockDictionaryModelHack = DictionaryItemResponseModel & +export type UmbMockDictionaryModel = DictionaryItemResponseModel & NamedEntityTreeItemResponseModel & DictionaryItemItemResponseModel & DictionaryOverviewResponseModel; -export interface UmbMockDictionaryModel extends Omit {} - export const data: Array = [ { name: 'Hello', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.db.ts index 191f854604..bc8977c9ab 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/dictionary/dictionary.db.ts @@ -38,7 +38,7 @@ export class UmbDictionaryMockDB extends UmbEntityMockDbBase => { +const treeItemMapper = (model: UmbMockDictionaryModel): NamedEntityTreeItemResponseModel => { return { name: model.name, id: model.id, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts index 182e04ef9c..22a13f84ce 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.data.ts @@ -5,12 +5,10 @@ import type { DocumentTypeTreeItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockDocumentTypeModelHack = DocumentTypeResponseModel & +export type UmbMockDocumentTypeModel = DocumentTypeResponseModel & DocumentTypeTreeItemResponseModel & DocumentTypeItemResponseModel; -export interface UmbMockDocumentTypeModel extends Omit {} - export const data: Array = [ { allowedTemplates: [], diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.db.ts index 47428a85b5..24a362a1cb 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document-type/document-type.db.ts @@ -122,9 +122,7 @@ const documentTypeDetailMapper = (item: UmbMockDocumentTypeModel): DocumentTypeR }; }; -const documentTypeTreeItemMapper = ( - item: UmbMockDocumentTypeModel, -): Omit => { +const documentTypeTreeItemMapper = (item: UmbMockDocumentTypeModel): DocumentTypeTreeItemResponseModel => { return { name: item.name, hasChildren: item.hasChildren, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts index cf47af845a..1c0a6b60a2 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.data.ts @@ -5,9 +5,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockDocumentTypeModelHack = DocumentResponseModel & DocumentTreeItemResponseModel & DocumentItemResponseModel; - -export interface UmbMockDocumentModel extends Omit {} +export type UmbMockDocumentModel = DocumentResponseModel & DocumentTreeItemResponseModel & DocumentItemResponseModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts index 76e2dfaafd..fdd0e3f23e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/document/document.db.ts @@ -41,7 +41,7 @@ export class UmbDocumentMockDB extends UmbEntityMockDbBase } } -const treeItemMapper = (model: UmbMockDocumentModel): Omit => { +const treeItemMapper = (model: UmbMockDocumentModel): DocumentTreeItemResponseModel => { const documentType = umbDocumentTypeMockDb.read(model.documentType.id); if (!documentType) throw new Error(`Document type with id ${model.documentType.id} not found`); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.data.ts index a7d235712b..aeba10c756 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.data.ts @@ -4,9 +4,9 @@ import type { MediaTypeTreeItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockMediaTypeModelHack = MediaTypeResponseModel & MediaTypeTreeItemResponseModel & MediaTypeItemResponseModel; - -export interface UmbMockMediaTypeModel extends Omit {} +export type UmbMockMediaTypeModel = MediaTypeResponseModel & + MediaTypeTreeItemResponseModel & + MediaTypeItemResponseModel; export const data: Array = [ { @@ -91,9 +91,7 @@ export const data: Array = [ variesByCulture: false, variesBySegment: false, isElement: false, - allowedMediaTypes: [ - { mediaType: { id: 'media-type-1-id' }, sortOrder: 0 }, - ], + allowedMediaTypes: [{ mediaType: { id: 'media-type-1-id' }, sortOrder: 0 }], compositions: [], isFolder: false, hasChildren: false, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.db.ts index 630e0d7e7c..874a4e4374 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/media-type/media-type.db.ts @@ -110,7 +110,7 @@ const mediaTypeDetailMapper = (item: UmbMockMediaTypeModel): MediaTypeResponseMo }; }; -const mediaTypeTreeItemMapper = (item: UmbMockMediaTypeModel): Omit => { +const mediaTypeTreeItemMapper = (item: UmbMockMediaTypeModel): MediaTypeTreeItemResponseModel => { return { name: item.name, hasChildren: item.hasChildren, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.data.ts index 211c9aa3f2..012ab54620 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.data.ts @@ -4,9 +4,7 @@ import type { MediaTreeItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockMediaModelHack = MediaResponseModel & MediaTreeItemResponseModel & MediaItemResponseModel; - -export interface UmbMockMediaModel extends Omit {} +export type UmbMockMediaModel = MediaResponseModel & MediaTreeItemResponseModel & MediaItemResponseModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.db.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.db.ts index a87acd02d9..e0a6337152 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.db.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/media/media.db.ts @@ -28,7 +28,7 @@ export class UmbMediaMockDB extends UmbEntityMockDbBase { } } -const treeItemMapper = (model: UmbMockMediaModel): Omit => { +const treeItemMapper = (model: UmbMockMediaModel): MediaTreeItemResponseModel => { const mediaType = umbMediaTypeMockDb.read(model.mediaType.id); if (!mediaType) throw new Error(`Media type with id ${model.mediaType.id} not found`); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/partial-view/partial-view.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/partial-view/partial-view.data.ts index 86efd983d6..0fa1bf78c7 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/partial-view/partial-view.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/partial-view/partial-view.data.ts @@ -5,12 +5,10 @@ import type { PartialViewSnippetResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockPartialViewModelHack = PartialViewResponseModel & +export type UmbMockPartialViewModel = PartialViewResponseModel & FileSystemTreeItemPresentationModel & PartialViewItemResponseModel; -export interface UmbMockPartialViewModel extends Omit {} - export const data: Array = [ { name: 'blockgrid', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/relations/relation-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/relations/relation-type.data.ts index ae17352881..77d6c8cb05 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/relations/relation-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/relations/relation-type.data.ts @@ -79,7 +79,6 @@ export const treeData: Array = [ id: 'e0d39ff5-71d8-453f-b682-9d8d31ee5e06', parent: null, name: 'Relate Document On Copy', - type: 'relation-type', hasChildren: false, }, { @@ -87,28 +86,24 @@ export const treeData: Array = [ parent: null, name: 'Relate Parent Document On Delete', - type: 'relation-type', hasChildren: false, }, { id: '6f9b800c-762c-42d4-85d9-bf40a77d689e', parent: null, name: 'Relate Parent Media Folder On Delete', - type: 'relation-type', hasChildren: false, }, { id: 'd421727d-43de-4205-b4c6-037404f309ad', parent: null, name: 'Related Media', - type: 'relation-type', hasChildren: false, }, { id: 'e9a0a28e-2d5b-4229-ac00-66f2df230513', parent: null, name: 'Related Document', - type: 'relation-type', hasChildren: false, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/script/script.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/script/script.data.ts index a71cfe367d..24b7fb53e0 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/script/script.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/script/script.data.ts @@ -4,9 +4,7 @@ import type { ScriptResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockScriptModelHack = ScriptResponseModel & FileSystemTreeItemPresentationModel & ScriptItemResponseModel; - -export interface UmbMockScriptModel extends Omit {} +export type UmbMockScriptModel = ScriptResponseModel & FileSystemTreeItemPresentationModel & ScriptItemResponseModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/static-file/static-file.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/static-file/static-file.data.ts index 052cdaca11..b0eb91d277 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/static-file/static-file.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/static-file/static-file.data.ts @@ -3,8 +3,7 @@ import type { StaticFileItemResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockStaticFileModelHack = StaticFileItemResponseModel & FileSystemTreeItemPresentationModel; -export interface UmbMockStaticFileModel extends Omit {} +export type UmbMockStaticFileModel = StaticFileItemResponseModel & FileSystemTreeItemPresentationModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet/stylesheet.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet/stylesheet.data.ts index 2ce1bcde20..1145bb66e9 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet/stylesheet.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/stylesheet/stylesheet.data.ts @@ -4,12 +4,10 @@ import type { StylesheetResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockStylesheetModelHack = StylesheetResponseModel & +export type UmbMockStylesheetModel = StylesheetResponseModel & FileSystemTreeItemPresentationModel & StylesheetItemResponseModel; -export interface UmbMockStylesheetModel extends Omit {} - export const data: Array = [ { name: 'Stylesheet File 1.css', diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/template/template.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/template/template.data.ts index fd6988429a..4aadfd758e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/template/template.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/template/template.data.ts @@ -7,9 +7,7 @@ import type { } from '@umbraco-cms/backoffice/external/backend-api'; import { TemplateQueryPropertyTypeModel, OperatorModel } from '@umbraco-cms/backoffice/external/backend-api'; -type UmbMockTemplateModelHack = TemplateResponseModel & NamedEntityTreeItemResponseModel & TemplateItemResponseModel; - -export interface UmbMockTemplateModel extends Omit {} +export type UmbMockTemplateModel = TemplateResponseModel & NamedEntityTreeItemResponseModel & TemplateItemResponseModel; export const data: Array = [ { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts index 54e1eee8ff..f3a9ac812e 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils.ts @@ -7,7 +7,6 @@ import type { export const createEntityTreeItem = (item: any): NamedEntityTreeItemResponseModel => { return { name: item.name, - type: item.type, hasChildren: item.hasChildren, id: item.id, parent: item.parent, @@ -21,7 +20,7 @@ export const folderTreeItemMapper = (item: any): FolderTreeItemResponseModel => }; }; -export const createFileSystemTreeItem = (item: any): Omit => { +export const createFileSystemTreeItem = (item: any): FileSystemTreeItemPresentationModel => { return { path: item.path, parent: item.parent ?? null, diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-folder.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-folder.manager.ts index 968fe7f8e7..87eaef9361 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-folder.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-folder.manager.ts @@ -6,7 +6,7 @@ import type { UpdateFolderResponseModel, } from '@umbraco-cms/backoffice/external/backend-api'; -export class UmbMockEntityFolderManager> { +export class UmbMockEntityFolderManager { #db: UmbEntityMockDbBase; #createMockFolderMapper: (request: CreateFolderRequestModel) => MockItemType; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-recycle-bin.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-recycle-bin.ts index fb6940cc7a..2cb6918b2d 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-recycle-bin.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-recycle-bin.ts @@ -2,9 +2,7 @@ import { UmbEntityMockDbBase } from './entity-base.js'; import { UmbMockEntityTreeManager } from './entity-tree.manager.js'; import type { ContentTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -export class UmbEntityRecycleBin< - MockType extends Omit, -> extends UmbEntityMockDbBase { +export class UmbEntityRecycleBin extends UmbEntityMockDbBase { tree; constructor(data: Array, treeItemMapper: (model: MockType) => any) { diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts index 38c08ccad5..def3178707 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/entity/entity-tree.manager.ts @@ -3,7 +3,7 @@ import type { UmbEntityMockDbBase } from './entity-base.js'; import { UmbId } from '@umbraco-cms/backoffice/id'; import type { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; -export class UmbMockEntityTreeManager> { +export class UmbMockEntityTreeManager { #db: UmbEntityMockDbBase; #treeItemMapper: (item: T) => any; diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts index cad195d8f9..2641be20f2 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/utils/file-system/file-system-tree.manager.ts @@ -3,7 +3,7 @@ import { createFileSystemTreeItem } from '../../utils.js'; import { pagedResult } from '../paged-result.js'; import type { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/external/backend-api'; -export class UmbMockFileSystemTreeManager> { +export class UmbMockFileSystemTreeManager { #db: UmbMockDBBase; constructor(mockDb: UmbMockDBBase) { @@ -11,7 +11,7 @@ export class UmbMockFileSystemTreeManager>; + items: Array; total: number; } { const items = this.#db.getAll().filter((item) => item.parent === null); @@ -19,7 +19,7 @@ export class UmbMockFileSystemTreeManager>; + items: Array; total: number; } { const items = this.#db.getAll().filter((item) => item.parent?.path === parentPath); From 3b4b22e6c61d50134215e20701bdbd3c9d432a5d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:16:15 +0100 Subject: [PATCH 120/285] remove variant picker element --- ...ocument-variant-language-picker.element.ts | 93 -------- .../document-variant-picker-modal.element.ts | 203 ------------------ .../document-variant-picker-modal.stories.ts | 156 -------------- .../document-variant-picker-modal.token.ts | 23 -- .../documents/modals/variant-picker/index.ts | 1 - 5 files changed, 476 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.stories.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.token.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts deleted file mode 100644 index 7c3c54c3fd..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-language-picker.element.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; -import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; - -@customElement('umb-document-variant-language-picker') -export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { - @property({ type: Array, attribute: false }) - variantLanguageOptions: Array = []; - - @property({ attribute: false }) - selectionManager!: UmbSelectionManager; - - @state() - _selection: Array = []; - - constructor() { - super(); - this.observe(this.selectionManager.selection, (selection) => { - this._selection = selection; - }); - } - - render() { - return repeat( - this.variantLanguageOptions, - (option) => option.unique, - (option) => html` - this.selectionManager.select(option.unique)} - @deselected=${() => this.selectionManager.deselect(option.unique)} - ?selected=${this._selection.includes(option.language.unique)}> - - ${this.#renderLabel(option)} - - `, - ); - } - - #renderLabel(option: UmbDocumentVariantOptionModel) { - return html`
    - - ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name} - -
    ${this.#renderVariantStatus(option)}
    - ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED - ? html`
    - Mandatory language -
    ` - : ''} -
    `; - } - - #renderVariantStatus(option: UmbDocumentVariantOptionModel) { - switch (option.variant?.state) { - case UmbDocumentVariantState.PUBLISHED: - return this.localize.term('content_published'); - case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES: - return this.localize.term('content_publishedPendingChanges'); - case UmbDocumentVariantState.DRAFT: - return this.localize.term('content_unpublished'); - case UmbDocumentVariantState.NOT_CREATED: - default: - return this.localize.term('content_notCreated'); - } - } - - static styles = [ - UmbTextStyles, - css` - #subtitle { - margin-top: 0; - } - .label { - padding: 0.5rem 0; - } - .label-status { - font-size: 0.8rem; - } - `, - ]; -} - -export default UmbDocumentVariantLanguagePickerElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-document-variant-language-picker': UmbDocumentVariantLanguagePickerElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts deleted file mode 100644 index 22c07a7555..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.element.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; -import type { - UmbDocumentVariantPickerModalValue, - UmbDocumentVariantPickerModalData, -} from './document-variant-picker-modal.token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, customElement, repeat, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; -import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; -import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; - -@customElement('umb-document-variant-picker-modal') -export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement< - UmbDocumentVariantPickerModalData, - UmbDocumentVariantPickerModalValue -> { - #selectionManager = new UmbSelectionManager(this); - - @state() - _selection: Array = []; - - constructor() { - super(); - this.observe(this.#selectionManager.selection, (selection) => { - this._selection = selection; - }); - } - - connectedCallback(): void { - super.connectedCallback(); - this.#setInitialSelection(); - } - - async #setInitialSelection() { - let selected = this.value?.selection ?? []; - - // Filter selection based on options: - selected = selected.filter((s) => this.data?.options.some((o) => o.unique === s)); - - if (selected.length === 0) { - const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); - const appCulture = context.getAppCulture(); - // If the app language is one of the options, select it by default: - if (appCulture && this.data?.options.some((o) => o.language.unique === appCulture)) { - selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); - } - } - - this.#selectionManager.setMultiple(true); - this.#selectionManager.setSelectable(true); - this.#selectionManager.setSelection(selected); - - if (this.data?.type !== 'unpublish') { - this.#selectMandatoryVariants(); - } - } - - #selectMandatoryVariants() { - this.data?.options.forEach((variant) => { - if (variant.language?.isMandatory) { - this.#selectionManager.select(variant.unique); - } - }); - } - - get #headline(): string { - switch (this.data?.type) { - case 'publish': - return 'content_readyToPublish'; - case 'unpublish': - return 'content_unpublish'; - case 'schedule': - return 'content_readyToPublish'; - default: - return 'content_readyToSave'; - } - } - - get #subtitle(): string { - switch (this.data?.type) { - case 'publish': - return 'content_variantsToPublish'; - case 'unpublish': - return 'content_languagesToUnpublish'; - case 'schedule': - return 'content_languagesToSchedule'; - default: - return 'content_variantsToSave'; - } - } - - get #confirmLabel(): string { - switch (this.data?.type) { - case 'publish': - return 'buttons_saveAndPublish'; - case 'unpublish': - return 'actions_unpublish'; - case 'schedule': - return 'buttons_schedulePublish'; - default: - return 'buttons_saveAndClose'; - } - } - - #submit() { - this.value = { selection: this.#selectionManager.getSelection() }; - this.modalContext?.submit(); - } - - #close() { - this.modalContext?.reject(); - } - - render() { - return html` -

    ${this.localize.term(this.#subtitle)}

    - ${repeat( - this.data?.options ?? [], - (option) => option.unique, - (option) => html` - this.#selectionManager.select(option.unique)} - @deselected=${() => this.#selectionManager.deselect(option.unique)} - ?selected=${this._selection.includes(option.language.unique)}> - - ${this.#renderLabel(option)} - - `, - )} - ${this.data?.type === 'publish' ? html`

    ${this.localize.term('content_variantsWillBeSaved')}

    ` : ''} - -
    - - -
    -
    `; - } - - #renderLabel(option: UmbDocumentVariantOptionModel) { - return html`
    - ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? - option.language.name} -
    ${this.#renderVariantStatus(option)}
    - ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED - ? html`
    - Mandatory language -
    ` - : ''} -
    `; - } - - #renderVariantStatus(option: UmbDocumentVariantOptionModel) { - switch (option.variant?.state) { - case UmbDocumentVariantState.PUBLISHED: - return this.localize.term('content_published'); - case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES: - return this.localize.term('content_publishedPendingChanges'); - case UmbDocumentVariantState.DRAFT: - return this.localize.term('content_unpublished'); - case UmbDocumentVariantState.NOT_CREATED: - default: - return this.localize.term('content_notCreated'); - } - } - - static styles = [ - UmbTextStyles, - css` - :host { - display: block; - width: 400px; - max-width: 90vw; - } - #subtitle { - margin-top: 0; - } - .label { - padding: 0.5rem 0; - } - .label-status { - font-size: 0.8rem; - } - `, - ]; -} - -export default UmbDocumentVariantPickerModalElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-document-variant-picker-modal': UmbDocumentVariantPickerModalElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.stories.ts deleted file mode 100644 index 904d926407..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.stories.ts +++ /dev/null @@ -1,156 +0,0 @@ -import '../../../../core/components/body-layout/body-layout.element.js'; -import './document-variant-picker-modal.element.js'; - -import type { Meta, StoryObj } from '@storybook/web-components'; -import { UmbDocumentVariantState } from '../../types.js'; -import type { UmbDocumentVariantPickerModalElement } from './document-variant-picker-modal.element.js'; -import type { - UmbDocumentVariantPickerModalData, - UmbDocumentVariantPickerModalValue, -} from './document-variant-picker-modal.token.js'; -import { html } from '@umbraco-cms/backoffice/external/lit'; - -const modalData: UmbDocumentVariantPickerModalData = { - type: 'save', - options: [ - { - unique: 'en-us', - culture: 'en-us', - segment: null, - variant: { - name: 'English variant name', - culture: 'en-us', - state: UmbDocumentVariantState.PUBLISHED, - createDate: '2021-08-25T14:00:00Z', - publishDate: null, - updateDate: null, - segment: null, - }, - language: { - entityType: 'language', - name: 'English', - unique: 'en-us', - isDefault: true, - isMandatory: true, - fallbackIsoCode: null, - }, - }, - /* - // TODO: We do not support segments currently - { - name: 'English', - culture: 'en-us', - state: UmbDocumentVariantState.DRAFT, - createDate: '2021-08-25T14:00:00Z', - publishDate: null, - updateDate: null, - segment: 'GTM', - }, - */ - { - unique: 'da-dk', - culture: 'da-dk', - segment: null, - variant: { - name: 'Danish variant name', - culture: 'da-dk', - state: UmbDocumentVariantState.NOT_CREATED, - createDate: null, - publishDate: null, - updateDate: null, - segment: null, - }, - language: { - entityType: 'language', - name: 'Danish', - unique: 'da-dk', - isDefault: false, - isMandatory: false, - fallbackIsoCode: null, - }, - }, - ], -}; - -const modalValue: UmbDocumentVariantPickerModalValue = { - selection: ['en-us'], -}; - -const meta: Meta = { - title: 'Workspaces/Document/Modals/Variant Picker', - component: 'umb-document-variant-picker-modal', - id: 'umb-document-variant-picker-modal', - args: { - data: modalData, - value: modalValue, - }, - decorators: [(Story) => html`
    ${Story()}
    `], - parameters: { - layout: 'centered', - docs: { - source: { - code: ` -import { UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, UmbDocumentVariantState } from '@umbraco-cms/backoffice/document'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; - -this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => { - modalManager.open(this, UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, { - data: { - type: 'save', - variants: [ - { - name: 'English', - culture: 'en-us', - state: UmbDocumentVariantState.PUBLISHED, - createDate: '2021-08-25T14:00:00Z', - publishDate: '2021-08-25T14:00:00Z', - updateDate: null, - segment: null, - }, - { - name: 'English', - culture: 'en-us', - state: UmbDocumentVariantState.PUBLISHED, - createDate: '2021-08-25T14:00:00Z', - publishDate: '2021-08-25T14:00:00Z', - updateDate: null, - segment: 'GTM', - }, - { - name: 'Danish', - culture: 'da-dk', - state: UmbDocumentVariantState.NOT_CREATED, - createDate: null, - publishDate: null, - updateDate: null, - segment: null, - }, - ], - } - } -}); - `, - }, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Save: Story = {}; -export const Publish: Story = { - args: { - data: { ...modalData, type: 'publish' }, - }, -}; -export const Schedule: Story = { - args: { - data: { ...modalData, type: 'schedule' }, - }, -}; -export const Unpublish: Story = { - args: { - data: { ...modalData, type: 'unpublish' }, - }, -}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.token.ts deleted file mode 100644 index 8ce9615f0e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/document-variant-picker-modal.token.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS } from '../manifests.js'; -import type { UmbDocumentVariantOptionModel } from '../../types.js'; -import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; - -export type UmbDocumentVariantPickerModalType = 'save' | 'publish' | 'schedule' | 'unpublish'; - -export interface UmbDocumentVariantPickerModalData { - type: UmbDocumentVariantPickerModalType; - options: Array; -} - -export interface UmbDocumentVariantPickerModalValue { - selection: Array; -} - -export const UMB_DOCUMENT_LANGUAGE_PICKER_MODAL = new UmbModalToken< - UmbDocumentVariantPickerModalData, - UmbDocumentVariantPickerModalValue ->(UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS, { - modal: { - type: 'dialog', - }, -}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/index.ts deleted file mode 100644 index 1c69ee726c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/variant-picker/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './document-variant-picker-modal.token.js'; From 9ec98bea771b3963dc00f71f204c523a5d449655 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:16:33 +0100 Subject: [PATCH 121/285] add shared modal component --- .../documents/documents/modals/index.ts | 5 +- ...ocument-variant-language-picker.element.ts | 100 ++++++++++++++++++ .../documents/modals/shared/index.ts | 2 + .../pick-document-variant-modal.controller.ts | 48 +++++++++ .../documents/documents/modals/types.ts | 9 ++ 5 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/pick-document-variant-modal.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts index 5edb21c156..c11dcf1900 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts @@ -1,3 +1,4 @@ -export * from './variant-picker/index.js'; -export * from './pick-document-variant-modal.controller.js'; +export * from './publish-modal/index.js'; +export * from './shared/index.js'; export * from './document-picker-modal.token.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts new file mode 100644 index 0000000000..6f6dda2d78 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -0,0 +1,100 @@ +import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; +import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; + +@customElement('umb-document-variant-language-picker') +export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { + #selectionManager!: UmbSelectionManager; + + @property({ type: Array, attribute: false }) + variantLanguageOptions: Array = []; + + @property({ attribute: false }) + set selectionManager(value: UmbSelectionManager) { + this.#selectionManager = value; + this.observe( + this.selectionManager.selection, + (selection) => { + this._selection = selection; + }, + '_selectionManager', + ); + } + get selectionManager() { + return this.#selectionManager; + } + + @state() + _selection: Array = []; + + render() { + return repeat( + this.variantLanguageOptions, + (option) => option.unique, + (option) => html` + this.selectionManager.select(option.unique)} + @deselected=${() => this.selectionManager.deselect(option.unique)} + ?selected=${this._selection.includes(option.language.unique)}> + + ${this.#renderLabel(option)} + + `, + ); + } + + #renderLabel(option: UmbDocumentVariantOptionModel) { + return html`
    + + ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name} + +
    ${this.#renderVariantStatus(option)}
    + ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED + ? html`
    + Mandatory language +
    ` + : ''} +
    `; + } + + #renderVariantStatus(option: UmbDocumentVariantOptionModel) { + switch (option.variant?.state) { + case UmbDocumentVariantState.PUBLISHED: + return this.localize.term('content_published'); + case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES: + return this.localize.term('content_publishedPendingChanges'); + case UmbDocumentVariantState.DRAFT: + return this.localize.term('content_unpublished'); + case UmbDocumentVariantState.NOT_CREATED: + default: + return this.localize.term('content_notCreated'); + } + } + + static styles = [ + UmbTextStyles, + css` + #subtitle { + margin-top: 0; + } + .label { + padding: 0.5rem 0; + } + .label-status { + font-size: 0.8rem; + } + `, + ]; +} + +export default UmbDocumentVariantLanguagePickerElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-variant-language-picker': UmbDocumentVariantLanguagePickerElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/index.ts new file mode 100644 index 0000000000..a732caa13d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/index.ts @@ -0,0 +1,2 @@ +export * from './document-variant-language-picker.element.js'; +export * from './pick-document-variant-modal.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/pick-document-variant-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/pick-document-variant-modal.controller.ts new file mode 100644 index 0000000000..4a5c624f0c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/pick-document-variant-modal.controller.ts @@ -0,0 +1,48 @@ +import type { UmbDocumentVariantOptionModel } from '../../types.js'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export interface UmbPickDocumentVariantModalArgs { + type: UmbDocumentVariantPickerModalType; + options: Array; + selected?: Array; +} + +export class UmbPickDocumentVariantModalController extends UmbControllerBase { + async open(args: UmbPickDocumentVariantModalArgs): Promise { + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const selected = args.selected ?? []; + + const modalData: UmbDocumentVariantPickerModalData = { + type: args.type, + options: args.options, + }; + + if (modalData.options.length === 0) { + // TODO: What do to when there is no options? [NL] + } + + const modal = modalManager.open(this, UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, { + data: modalData, + // We need to turn the selected variant ids into strings for them to be serializable to the value state, in other words the value of a modal cannot hold class instances: + // Make selection unique by filtering out duplicates: + value: { selection: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i) ?? [] }, + }); + + const p = modal.onSubmit(); + p.catch(() => this.destroy()); + const result = await p; + + // This is a one time off, so we can destroy our selfs. + this.destroy(); + + // Map back into UmbVariantId instances: + return result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + } +} + +export function umbPickDocumentVariantModal(host: UmbControllerHost, args: UmbPickDocumentVariantModalArgs) { + return new UmbPickDocumentVariantModalController(host).open(args); +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts new file mode 100644 index 0000000000..d131490619 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/types.ts @@ -0,0 +1,9 @@ +import type { UmbDocumentVariantOptionModel } from '../types.js'; + +export interface UmbDocumentVariantPickerData { + options: Array; +} + +export interface UmbDocumentVariantPickerValue { + selection: Array; +} From c488e984c0da383bd59d4d8591ee0c39b67650d8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:16:45 +0100 Subject: [PATCH 122/285] remove controller --- .../documents/documents/modals/manifests.ts | 9 +--- .../pick-document-variant-modal.controller.ts | 53 ------------------- 2 files changed, 1 insertion(+), 61 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts index 6799fb055d..a1b80e5e85 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts @@ -1,20 +1,13 @@ import type { ManifestModal } from '@umbraco-cms/backoffice/extension-registry'; -export const UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS = 'Umb.Modal.DocumentVariantPicker'; export const UMB_DOCUMENT_PUBLISH_MODAL_ALIAS = 'Umb.Modal.DocumentPublish'; const modals: Array = [ - { - type: 'modal', - alias: UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS, - name: 'Document Variant Picker Modal', - js: () => import('./variant-picker/document-variant-picker-modal.element.js'), - }, { type: 'modal', alias: UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, name: 'Document Publish Modal', - js: () => import('./variant-picker/document-variant-picker-modal.element.js'), + js: () => import('./publish-modal/document-publish-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts deleted file mode 100644 index 3e3de7311e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/pick-document-variant-modal.controller.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { UmbDocumentVariantOptionModel } from '../types.js'; -import { - UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, - type UmbDocumentVariantPickerModalData, - type UmbDocumentVariantPickerModalType, -} from './variant-picker/document-variant-picker-modal.token.js'; -import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; - -export interface UmbPickDocumentVariantModalArgs { - type: UmbDocumentVariantPickerModalType; - options: Array; - selected?: Array; -} - -export class UmbPickDocumentVariantModalController extends UmbControllerBase { - async open(args: UmbPickDocumentVariantModalArgs): Promise { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const selected = args.selected ?? []; - - const modalData: UmbDocumentVariantPickerModalData = { - type: args.type, - options: args.options, - }; - - if (modalData.options.length === 0) { - // TODO: What do to when there is no options? [NL] - } - - const modal = modalManager.open(this, UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, { - data: modalData, - // We need to turn the selected variant ids into strings for them to be serializable to the value state, in other words the value of a modal cannot hold class instances: - // Make selection unique by filtering out duplicates: - value: { selection: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i) ?? [] }, - }); - - const p = modal.onSubmit(); - p.catch(() => this.destroy()); - const result = await p; - - // This is a one time off, so we can destroy our selfs. - this.destroy(); - - // Map back into UmbVariantId instances: - return result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; - } -} - -export function umbPickDocumentVariantModal(host: UmbControllerHost, args: UmbPickDocumentVariantModalArgs) { - return new UmbPickDocumentVariantModalController(host).open(args); -} From 6b6922b53a7a116b78ec8b61340ed6c49afa695c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:17:22 +0100 Subject: [PATCH 123/285] use interfaces for publish modal --- .../modals/publish-modal/document-publish-modal.element.ts | 2 +- .../modals/publish-modal/document-publish-modal.stories.ts | 1 - .../modals/publish-modal/document-publish-modal.token.ts | 6 +++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index 9694a10372..df334681c4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -7,7 +7,7 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import '../variant-picker/document-variant-language-picker.element.js'; +import '../shared/document-variant-language-picker.element.js'; @customElement('umb-document-variant-picker-modal') export class UmbDocumentPublishModalElement extends UmbModalBaseElement< diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts index 1e555f69a9..60eb76c0cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -8,7 +8,6 @@ import type { UmbDocumentPublishModalElement } from './document-publish-modal.el import { html } from '@umbraco-cms/backoffice/external/lit'; const modalData: UmbDocumentPublishModalData = { - type: 'save', options: [ { unique: 'en-us', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts index 226b199ef4..15aa43854a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts @@ -1,12 +1,12 @@ -import type { UmbDocumentVariantPickerModalData, UmbDocumentVariantPickerModalValue } from '../variant-picker/index.js'; +import type { UmbDocumentVariantPickerData, UmbDocumentVariantPickerValue } from '../types.js'; import { UMB_DOCUMENT_PUBLISH_MODAL_ALIAS } from '../manifests.js'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerModalData { +export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerData { allowScheduledPublish?: boolean; } -export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerModalValue {} +export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerValue {} export const UMB_DOCUMENT_PUBLISH_MODAL = new UmbModalToken( UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, From 15c9154109c45b2b341618cd5f5b49eefe3d246e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:17:58 +0100 Subject: [PATCH 124/285] use modal manager to open publish modal --- .../workspace/document-workspace.context.ts | 109 +++++++++++++----- 1 file changed, 83 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index dd8f95e9b7..bdd7c13c9c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -8,7 +8,7 @@ import type { UmbDocumentVariantModel, UmbDocumentVariantOptionModel, } from '../types.js'; -import { umbPickDocumentVariantModal, type UmbDocumentVariantPickerModalType } from '../modals/index.js'; +import { UMB_DOCUMENT_PUBLISH_MODAL } from '../modals/index.js'; import { UmbDocumentPublishingRepository } from '../repository/publishing/index.js'; import { UmbUnpublishDocumentEntityAction } from '../entity-actions/unpublish.action.js'; import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js'; @@ -33,6 +33,7 @@ import { type Observable, firstValueFrom } from '@umbraco-cms/backoffice/externa import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; type EntityType = UmbDocumentDetailModel; export class UmbDocumentWorkspaceContext @@ -353,7 +354,7 @@ export class UmbDocumentWorkspaceContext } } - async #runUserFlorFor(type: UmbDocumentVariantPickerModalType): Promise { + async #determineVariantOptions() { const activeVariants = this.splitView.getActiveVariants(); const activeVariantIds = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant)); @@ -364,21 +365,10 @@ export class UmbDocumentWorkspaceContext const options = await firstValueFrom(this.variantOptions); - // If there is only one variant, we don't need to open the modal. - if (options.length === 0) { - throw new Error('No variants are available'); - } else if (options.length === 1) { - // If only one option we will skip ahead and save the document with the only variant available: - const firstVariant = UmbVariantId.Create(options[0]); - return this.#performSaveOrCreate([firstVariant]); - } - - const selectedVariants = await umbPickDocumentVariantModal(this, { type, options, selected }); - - // If no variants are selected, we don't save anything. - if (!selectedVariants.length) return []; - - return this.#performSaveOrCreate(selectedVariants); + return { + options, + selected: selected.map((x) => x.toString()).filter((v, i, a) => a.indexOf(v) === i), + }; } #buildSaveData(selectedVariants: Array): UmbDocumentDetailModel { @@ -472,7 +462,45 @@ export class UmbDocumentWorkspaceContext } async save() { - await this.#runUserFlorFor('save'); + const { options, selected } = await this.#determineVariantOptions(); + + // If there is only one variant, we don't need to open the modal. + if (options.length === 0) { + throw new Error('No variants are available'); + } else if (options.length === 1) { + // If only one option we will skip ahead and save the document with the only variant available: + const firstVariant = UmbVariantId.Create(options[0]); + await this.#performSaveOrCreate([firstVariant]); + + const data = this.getData(); + if (!data) throw new Error('Data is missing'); + + this.#persistedData.setValue(data); + this.#currentData.setValue(data); + + this.workspaceComplete(data); + + this.workspaceComplete(this.#currentData.getValue()); + return; + } + + // If there are multiple variants, we will open the modal to let the user pick which variants to save. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); + + if (!result?.selection.length) return; + + const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + await this.#performSaveOrCreate(variantIds); + const data = this.getData(); if (!data) throw new Error('Data is missing'); @@ -483,16 +511,45 @@ export class UmbDocumentWorkspaceContext } public async publish() { - const variantIds = await this.#runUserFlorFor('publish'); - const unique = this.getUnique(); - if (variantIds.length && unique) { - await this.publishingRepository.publish(unique, variantIds); - this.workspaceComplete(this.#currentData.getValue()); - } + throw new Error('Method not implemented.'); } - public async saveAndPublish() { - await this.publish(); + public async saveAndPublish(): Promise { + const unique = this.getUnique(); + if (!unique) throw new Error('Unique is missing'); + + const { options, selected } = await this.#determineVariantOptions(); + + // If there is only one variant, we don't need to open the modal. + if (options.length === 0) { + throw new Error('No variants are available'); + } else if (options.length === 1) { + // If only one option we will skip ahead and save the document with the only variant available: + const firstVariant = UmbVariantId.Create(options[0]); + const variants = await this.#performSaveOrCreate([firstVariant]); + await this.publishingRepository.publish(unique, variants); + this.workspaceComplete(this.#currentData.getValue()); + return; + } + + // If there are multiple variants, we will open the modal to let the user pick which variants to publish. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); + + if (!result?.selection.length || unique) return; + + const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + const variants = await this.#performSaveOrCreate(variantIds); + await this.publishingRepository.publish(unique, variants); + this.workspaceComplete(this.#currentData.getValue()); } public async unpublish() { From 2794906fc23e0e0172f88fd0d5a28db64d41d198 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:20:49 +0100 Subject: [PATCH 125/285] remove old modal --- .../documents/documents/entity-actions/publish.action.ts | 1 - .../documents/documents/entity-actions/unpublish.action.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts index 96455cad05..55c5e321b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts @@ -1,4 +1,3 @@ -import { umbPickDocumentVariantModal } from '../modals/pick-document-variant-modal.controller.js'; import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js'; import { UmbDocumentVariantState } from '../types.js'; import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts index 06f2f630e6..1fc11be51b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/unpublish.action.ts @@ -1,4 +1,3 @@ -import { umbPickDocumentVariantModal } from '../modals/pick-document-variant-modal.controller.js'; import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js'; import { UmbDocumentVariantState } from '../types.js'; import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; From d853253ecf89499d885be49a1289a299d92343dc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:20:58 +0100 Subject: [PATCH 126/285] check if unique exists --- .../documents/documents/workspace/document-workspace.context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index bdd7c13c9c..b825db0878 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -544,7 +544,7 @@ export class UmbDocumentWorkspaceContext .onSubmit() .catch(() => undefined); - if (!result?.selection.length || unique) return; + if (!result?.selection.length || !unique) return; const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; const variants = await this.#performSaveOrCreate(variantIds); From 998254eb59976d8954dba9c541653c062b81d151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 14:21:40 +0100 Subject: [PATCH 127/285] clean up --- .../content-type-structure-manager.class.ts | 4 ++ ...ontent-type-workspace-view-edit.element.ts | 50 ++++++++++--------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index f1bdbf73aa..24c3054d92 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -191,6 +191,10 @@ export class UmbContentTypePropertyStructureManager(this); + //private _hasRootProperties = false; @state() @@ -91,16 +95,10 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem @state() private _sortModeActive?: boolean; + // TODO: investigate if we really want this: @state() private _buttonDisabled: boolean = false; - private _workspaceContext?: (typeof UMB_CONTENT_TYPE_WORKSPACE_CONTEXT)['TYPE']; - - private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); - - @state() - private _compositionConfiguration?: (typeof UMB_COMPOSITION_PICKER_MODAL)['DATA']; - constructor() { super(); @@ -134,18 +132,6 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem '_observeIsSorting', ); - const unique = workspaceContext.getUnique(); - - //TODO Figure out the correct data that needs to be sent to the compositions modal. Do we really have to send isElement, currentPropertyAliases - isn't unique enough? - this.observe(workspaceContext.structure.contentTypes, (contentTypes) => { - this._compositionConfiguration = { - unique: unique ?? '', - selection: contentTypes.map((contentType) => contentType.unique).filter((id) => id !== unique), - isElement: contentTypes.find((contentType) => contentType.unique === unique)?.isElement ?? false, - currentPropertyAliases: [], - }; - }); - this._observeRootGroups(); }); } @@ -297,9 +283,29 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } async #openCompositionModal() { + if (!this._workspaceContext) return; + + const unique = this._workspaceContext.getUnique(); + if (!unique) { + throw new Error('Content Type unique is undefined'); + } + const contentTypes = this._workspaceContext.structure.getContentTypes(); + const ownerContentType = this._workspaceContext.structure.getOwnerContentType(); + if (!ownerContentType) { + throw new Error('Owner Content Type not found'); + } + + const compositionConfiguration = { + unique: unique, + // Here we use the loaded content types to declare what we already inherit. That puts a pressure on cleaning up, but thats a good thing. [NL] + selection: contentTypes.map((contentType) => contentType.unique).filter((id) => id !== unique), + isElement: ownerContentType.isElement, + currentPropertyAliases: [], + }; + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); const modalContext = modalManagerContext.open(this, UMB_COMPOSITION_PICKER_MODAL, { - data: this._compositionConfiguration, + data: compositionConfiguration, }); await modalContext?.onSubmit(); @@ -316,9 +322,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem return html` Date: Tue, 12 Mar 2024 14:26:15 +0100 Subject: [PATCH 128/285] simplify logic --- .../workspace/document-workspace.context.ts | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index b825db0878..aa6a972373 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -464,41 +464,32 @@ export class UmbDocumentWorkspaceContext async save() { const { options, selected } = await this.#determineVariantOptions(); + let variantIds: Array = []; + // If there is only one variant, we don't need to open the modal. if (options.length === 0) { throw new Error('No variants are available'); } else if (options.length === 1) { // If only one option we will skip ahead and save the document with the only variant available: - const firstVariant = UmbVariantId.Create(options[0]); - await this.#performSaveOrCreate([firstVariant]); + variantIds.push(UmbVariantId.Create(options[0])); + } else { + // If there are multiple variants, we will open the modal to let the user pick which variants to save. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); - const data = this.getData(); - if (!data) throw new Error('Data is missing'); + if (!result?.selection.length) return; - this.#persistedData.setValue(data); - this.#currentData.setValue(data); - - this.workspaceComplete(data); - - this.workspaceComplete(this.#currentData.getValue()); - return; + variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; } - // If there are multiple variants, we will open the modal to let the user pick which variants to save. - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { - data: { - options, - }, - value: { selection: selected }, - }) - .onSubmit() - .catch(() => undefined); - - if (!result?.selection.length) return; - - const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; await this.#performSaveOrCreate(variantIds); const data = this.getData(); @@ -518,6 +509,8 @@ export class UmbDocumentWorkspaceContext const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); + let variantIds: Array = []; + const { options, selected } = await this.#determineVariantOptions(); // If there is only one variant, we don't need to open the modal. @@ -525,28 +518,25 @@ export class UmbDocumentWorkspaceContext throw new Error('No variants are available'); } else if (options.length === 1) { // If only one option we will skip ahead and save the document with the only variant available: - const firstVariant = UmbVariantId.Create(options[0]); - const variants = await this.#performSaveOrCreate([firstVariant]); - await this.publishingRepository.publish(unique, variants); - this.workspaceComplete(this.#currentData.getValue()); - return; + variantIds.push(UmbVariantId.Create(options[0])); + } else { + // If there are multiple variants, we will open the modal to let the user pick which variants to publish. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); + + if (!result?.selection.length || !unique) return; + + variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; } - // If there are multiple variants, we will open the modal to let the user pick which variants to publish. - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { - data: { - options, - }, - value: { selection: selected }, - }) - .onSubmit() - .catch(() => undefined); - - if (!result?.selection.length || !unique) return; - - const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; const variants = await this.#performSaveOrCreate(variantIds); await this.publishingRepository.publish(unique, variants); this.workspaceComplete(this.#currentData.getValue()); From a675ba893b29e17c37c8efcb4e26e172f4cf0322 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:41:17 +0100 Subject: [PATCH 129/285] add jsdocs for default slot --- .../packages/core/components/body-layout/body-layout.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts index 00da0fd877..866fbf9638 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts @@ -15,6 +15,7 @@ import { /** * @element umb-body-layout * @description Layout element to arrange elements in a body layout. A general layout for most views. + * @slot - Slot for main content * @slot icon - Slot for icon * @slot name - Slot for name * @slot footer - Slot for footer element From 367f53d51a36446b7f89e239a0b1c57950e95844 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:41:30 +0100 Subject: [PATCH 130/285] move logic to modal picker --- .../entity-actions/publish.action.ts | 35 +++++++++++-------- .../document-publish-modal.element.ts | 27 ++++++++++---- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts index 55c5e321b9..582936de80 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts @@ -1,10 +1,12 @@ +import { UMB_DOCUMENT_PUBLISH_MODAL } from '../modals/publish-modal/index.js'; import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js'; -import { UmbDocumentVariantState } from '../types.js'; +import type { UmbDocumentVariantOptionModel } from '../types.js'; import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -30,7 +32,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase return; } - const allOptions = (languageData?.items ?? []).map((language) => ({ + const options: Array = (languageData?.items ?? []).map((language) => ({ culture: language.unique, segment: null, language: language, @@ -38,22 +40,25 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase unique: new UmbVariantId(language.unique, null).toString(), })); - // TODO: Maybe move this to modal [NL] - // Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes: - const options = allOptions.filter( - (option) => - option.variant && - (option.variant.state === UmbDocumentVariantState.DRAFT || - option.variant.state === UmbDocumentVariantState.PUBLISHED || - option.variant.state === UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES), - ); + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] + value: { selection: [] }, + }) + .onSubmit() + .catch(() => undefined); - // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] - const selectedVariants = await umbPickDocumentVariantModal(this, { type: 'publish', options }); + if (!result?.selection.length) return; - if (selectedVariants.length) { + const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + + if (variantIds.length) { const publishingRepository = new UmbDocumentPublishingRepository(this._host); - await publishingRepository.publish(this.args.unique, selectedVariants); + await publishingRepository.publish(this.args.unique, variantIds); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index df334681c4..6622d1719f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -1,3 +1,4 @@ +import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; @@ -19,35 +20,49 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< @state() _selection: Array = []; + @state() + _options: Array = []; + constructor() { super(); this.observe(this.#selectionManager.selection, (selection) => { this._selection = selection; }); + } + firstUpdated() { this.#configureSelectionManager(); } async #configureSelectionManager() { + this.#selectionManager.setMultiple(true); + this.#selectionManager.setSelectable(true); + + // Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes: + this._options = + this.data?.options.filter( + (option) => option.variant && option.variant.state !== UmbDocumentVariantState.NOT_CREATED, + ) ?? []; + let selected = this.value?.selection ?? []; // Filter selection based on options: - selected = selected.filter((s) => this.data?.options.some((o) => o.unique === s)); + selected = selected.filter((s) => this._options.some((o) => o.unique === s)); + // If no selections were provided, select the app language by default: if (selected.length === 0) { const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); const appCulture = context.getAppCulture(); // If the app language is one of the options, select it by default: - if (appCulture && this.data?.options.some((o) => o.language.unique === appCulture)) { + if (appCulture && this._options.some((o) => o.language.unique === appCulture)) { selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); } } - this.#selectionManager.setMultiple(true); - this.#selectionManager.setSelectable(true); this.#selectionManager.setSelection(selected); - this.data?.options.forEach((variant) => { + // Additionally select mandatory languages: + this._options.forEach((variant) => { if (variant.language?.isMandatory) { this.#selectionManager.select(variant.unique); } @@ -72,7 +87,7 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement<

    + .variantLanguageOptions=${this._options}> ${when( this.data?.allowScheduledPublish, From 18612a2ab6d01fbac33fcb19ffb8c05ef8243cfc Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:43:13 +0100 Subject: [PATCH 131/285] no need to observe selection --- .../publish-modal/document-publish-modal.element.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index 6622d1719f..462001f556 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -17,19 +17,9 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< > { #selectionManager = new UmbSelectionManager(this); - @state() - _selection: Array = []; - @state() _options: Array = []; - constructor() { - super(); - this.observe(this.#selectionManager.selection, (selection) => { - this._selection = selection; - }); - } - firstUpdated() { this.#configureSelectionManager(); } From 2826e34929ab77227dc2a3e4a8f3407b87a95ea5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:50:05 +0100 Subject: [PATCH 132/285] wire up save and schedule --- .../documents/workspace/actions/save-and-schedule.action.ts | 5 ++--- .../documents/workspace/document-workspace.context.ts | 5 ++++- .../src/packages/documents/documents/workspace/manifests.ts | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts index 1f0f7a6daf..a4acafb444 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts @@ -1,10 +1,9 @@ import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../document-workspace.context-token.js'; import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; -export class UmbSaveAndScheduleDocumentWorkspaceAction extends UmbWorkspaceActionBase { +export class UmbDocumentSaveAndScheduleWorkspaceAction extends UmbWorkspaceActionBase { async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); - //workspaceContext.repository.saveAndSchedule(); - //Remember to return a promise. + return workspaceContext.saveAndSchedule(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index aa6a972373..69a40428b7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -40,7 +40,6 @@ export class UmbDocumentWorkspaceContext extends UmbEditableWorkspaceContextBase implements UmbVariantableWorkspaceContextInterface, UmbPublishableWorkspaceContextInterface { - // public readonly repository = new UmbDocumentDetailRepository(this); public readonly publishingRepository = new UmbDocumentPublishingRepository(this); @@ -542,6 +541,10 @@ export class UmbDocumentWorkspaceContext this.workspaceComplete(this.#currentData.getValue()); } + saveAndSchedule() { + throw new Error('Method not implemented.'); + } + public async unpublish() { const unique = this.getUnique(); const entityType = this.getEntityType(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 9ab6259089..2b9d51eb54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -12,6 +12,7 @@ import type { ManifestWorkspaceActionMenuItem, ManifestWorkspaceView, } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbDocumentSaveAndScheduleWorkspaceAction } from './actions/save-and-schedule.action.js'; export const UMB_DOCUMENT_WORKSPACE_ALIAS = 'Umb.Workspace.Document'; @@ -190,10 +191,10 @@ const workspaceActionMenuItems: Array = [ alias: 'Umb.Document.WorkspaceActionMenuItem.SchedulePublishing', name: 'Schedule publishing', weight: 20, - api: UmbDocumentSaveAndPublishWorkspaceAction, + api: UmbDocumentSaveAndScheduleWorkspaceAction, forWorkspaceActions: 'Umb.WorkspaceAction.Document.SaveAndPublish', meta: { - label: 'Schedule publishing (TBD)', + label: 'Schedule publishing', icon: 'icon-globe', }, }, From 088efd71f0c2750c3fb108ababc1a05370d92c9a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:01:41 +0100 Subject: [PATCH 133/285] remove comment --- .../documents/documents/workspace/manifests.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 2b9d51eb54..babbeec347 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -139,22 +139,6 @@ const workspaceActions: Array = [ }, ], }, - { - type: 'workspaceAction', - alias: 'Umb.WorkspaceAction.Document.SaveAndSchedule', - name: 'Save And Schedule Document Workspace Action', - weight: 100, - api: UmbSaveAndScheduleDocumentWorkspaceAction, - meta: { - label: 'Save And Schedule', - }, - conditions: [ - { - alias: 'Umb.Condition.WorkspaceAlias', - match: workspace.alias, - }, - ], - }, */ ]; From 1a2d26af93731a378f83dceb5cefb34d5db885b5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:14:33 +0100 Subject: [PATCH 134/285] wire up save and schedule --- .../entity-actions/publish.action.ts | 9 ++- .../workspace/document-workspace.context.ts | 75 ++++++++++--------- .../documents/workspace/manifests.ts | 1 + 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts index 582936de80..d68135cddb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts @@ -8,8 +8,12 @@ import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { +export type UmbPublishDocumentEntityActionMeta = { + allowScheduledPublish: boolean; +}; + +export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { + constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { super(host, args); } @@ -45,6 +49,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options, + allowScheduledPublish: this.args.meta.allowScheduledPublish, }, // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] value: { selection: [] }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 69a40428b7..6d3912ab8e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -460,6 +460,44 @@ export class UmbDocumentWorkspaceContext return selectedVariants; } + async #handleSaveAndPublish(allowScheduledPublish?: boolean) { + const unique = this.getUnique(); + if (!unique) throw new Error('Unique is missing'); + + let variantIds: Array = []; + + const { options, selected } = await this.#determineVariantOptions(); + + // If there is only one variant, we don't need to open the modal. + if (options.length === 0) { + throw new Error('No variants are available'); + } else if (options.length === 1) { + // If only one option we will skip ahead and save the document with the only variant available: + variantIds.push(UmbVariantId.Create(options[0])); + } else { + // If there are multiple variants, we will open the modal to let the user pick which variants to publish. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + allowScheduledPublish: allowScheduledPublish ?? false, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); + + if (!result?.selection.length || !unique) return; + + variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + } + + const variants = await this.#performSaveOrCreate(variantIds); + await this.publishingRepository.publish(unique, variants); + this.workspaceComplete(this.#currentData.getValue()); + } + async save() { const { options, selected } = await this.#determineVariantOptions(); @@ -505,44 +543,11 @@ export class UmbDocumentWorkspaceContext } public async saveAndPublish(): Promise { - const unique = this.getUnique(); - if (!unique) throw new Error('Unique is missing'); - - let variantIds: Array = []; - - const { options, selected } = await this.#determineVariantOptions(); - - // If there is only one variant, we don't need to open the modal. - if (options.length === 0) { - throw new Error('No variants are available'); - } else if (options.length === 1) { - // If only one option we will skip ahead and save the document with the only variant available: - variantIds.push(UmbVariantId.Create(options[0])); - } else { - // If there are multiple variants, we will open the modal to let the user pick which variants to publish. - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { - data: { - options, - }, - value: { selection: selected }, - }) - .onSubmit() - .catch(() => undefined); - - if (!result?.selection.length || !unique) return; - - variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; - } - - const variants = await this.#performSaveOrCreate(variantIds); - await this.publishingRepository.publish(unique, variants); - this.workspaceComplete(this.#currentData.getValue()); + return this.#handleSaveAndPublish(); } saveAndSchedule() { - throw new Error('Method not implemented.'); + return this.#handleSaveAndPublish(true); } public async unpublish() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index babbeec347..6ea8bb1cef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -180,6 +180,7 @@ const workspaceActionMenuItems: Array = [ meta: { label: 'Schedule publishing', icon: 'icon-globe', + allowScheduledPublish: true, }, }, ]; From 54b6ff3ff892bc85c89bbf2f9927d047d1db1ce5 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:19:16 +0100 Subject: [PATCH 135/285] wire up schedule --- .../modals/publish-modal/document-publish-modal.token.ts | 5 ++++- .../repository/publishing/document-publishing.repository.ts | 5 +++-- .../publishing/document-publishing.server.data-source.ts | 5 +++-- .../documents/workspace/document-workspace.context.ts | 5 ++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts index 15aa43854a..695e183189 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts @@ -1,12 +1,15 @@ import type { UmbDocumentVariantPickerData, UmbDocumentVariantPickerValue } from '../types.js'; import { UMB_DOCUMENT_PUBLISH_MODAL_ALIAS } from '../manifests.js'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerData { allowScheduledPublish?: boolean; } -export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerValue {} +export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerValue { + schedule?: ScheduleRequestModel; +} export const UMB_DOCUMENT_PUBLISH_MODAL = new UmbModalToken( UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts index b2a3def404..ce888ede4e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts @@ -3,6 +3,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import { UMB_NOTIFICATION_CONTEXT, type UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbDocumentPublishingRepository extends UmbRepositoryBase { #init!: Promise; @@ -28,12 +29,12 @@ export class UmbDocumentPublishingRepository extends UmbRepositoryBase { * @return {*} * @memberof UmbDocumentPublishingRepository */ - async publish(unique: string, variantIds: Array) { + async publish(unique: string, variantIds: Array, schedule?: ScheduleRequestModel) { if (!unique) throw new Error('id is missing'); if (!variantIds) throw new Error('variant IDs are missing'); await this.#init; - const { error } = await this.#publishingDataSource.publish(unique, variantIds); + const { error } = await this.#publishingDataSource.publish(unique, variantIds, schedule); if (!error) { const notification = { data: { message: `Document published` } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts index d353957ca8..9c0943c3c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts @@ -1,6 +1,7 @@ import type { CultureAndScheduleRequestModel, PublishDocumentRequestModel, + ScheduleRequestModel, UnpublishDocumentRequestModel, } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -33,7 +34,7 @@ export class UmbDocumentPublishingServerDataSource { * @return {*} * @memberof UmbDocumentServerDataSource */ - async publish(unique: string, variantIds: Array) { + async publish(unique: string, variantIds: Array, schedule?: ScheduleRequestModel) { if (!unique) throw new Error('Id is missing'); const publishSchedules: CultureAndScheduleRequestModel[] = variantIds.map( @@ -41,7 +42,7 @@ export class UmbDocumentPublishingServerDataSource { return { culture: variant.isCultureInvariant() ? null : variant.toCultureString(), // TODO: NO, this does not belong as part of the UmbVariantID, we need another way to parse that around: - //schedule: variant.schedule, + schedule, }; }, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 6d3912ab8e..d144a8c921 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -34,6 +34,7 @@ import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; type EntityType = UmbDocumentDetailModel; export class UmbDocumentWorkspaceContext @@ -465,6 +466,7 @@ export class UmbDocumentWorkspaceContext if (!unique) throw new Error('Unique is missing'); let variantIds: Array = []; + let schedule: ScheduleRequestModel | undefined = undefined; const { options, selected } = await this.#determineVariantOptions(); @@ -491,10 +493,11 @@ export class UmbDocumentWorkspaceContext if (!result?.selection.length || !unique) return; variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + schedule = result.schedule; } const variants = await this.#performSaveOrCreate(variantIds); - await this.publishingRepository.publish(unique, variants); + await this.publishingRepository.publish(unique, variants, schedule); this.workspaceComplete(this.#currentData.getValue()); } From 327b14a4aea4fd61433a19e0fb3af2d0d985be79 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 16:44:26 +0000 Subject: [PATCH 136/285] Corrected manifests --- .../src/packages/dynamic-root/modals/manifests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/manifests.ts index 97a63c351d..a04fc0ec86 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/manifests.ts @@ -12,13 +12,13 @@ const modals: Array = [ type: 'modal', alias: UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS, name: 'Choose an origin', - js: () => import('./dynamic-root-origin-picker-modal.element.js'), + element: () => import('./dynamic-root-origin-picker-modal.element.js'), }, { type: 'modal', alias: UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL_ALIAS, name: 'Append step to query', - js: () => import('./dynamic-root-query-step-picker-modal.element.js'), + element: () => import('./dynamic-root-query-step-picker-modal.element.js'), }, ]; From 82f9f5c3c8b58377406164bdbe2fb396f9656cc3 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 16:44:59 +0000 Subject: [PATCH 137/285] Defined modal-data for the DynamicRoot modals --- .../src/packages/dynamic-root/modals/index.ts | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/index.ts index 1a7a4d6fdc..3fcf6dc3f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/index.ts @@ -2,16 +2,31 @@ import { UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS, UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL_ALIAS, } from './manifests.js'; +import type { + ManifestDynamicRootOrigin, + ManifestDynamicRootQueryStep, +} from '@umbraco-cms/backoffice/extension-registry'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -export const UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL = new UmbModalToken(UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS, { - modal: { - type: 'sidebar', - size: 'small', - }, -}); +export interface UmbDynamicRootOriginModalData { + items: Array; +} -export const UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL = new UmbModalToken( +export interface UmbDynamicRootQueryStepModalData { + items: Array; +} + +export const UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL = new UmbModalToken( + UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL_ALIAS, + { + modal: { + type: 'sidebar', + size: 'small', + }, + }, +); + +export const UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL = new UmbModalToken( UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL_ALIAS, { modal: { From f66903dc59c71aa8ef9983056f3713d8659cbae3 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 12 Mar 2024 16:46:18 +0000 Subject: [PATCH 138/285] Wired up the DynamicRoot manifests with the modals This reduces extra calls to the `umbExtensionsRegistry` to re-get the manifests. --- .../input-document-root-picker.element.ts | 8 ++++++-- .../dynamic-root-origin-picker-modal.element.ts | 14 +++++++++----- ...amic-root-query-step-picker-modal.element.ts | 17 +++++++---------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-root-picker/input-document-root-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-root-picker/input-document-root-picker.element.ts index 7c635c2a29..d8b2b46b98 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-root-picker/input-document-root-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document-root-picker/input-document-root-picker.element.ts @@ -87,7 +87,9 @@ export class UmbInputDocumentRootPickerElement extends FormControlMixin(UmbLitEl }); #openDynamicRootOriginPicker() { - this.#openModal = this.#modalContext?.open(this, UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL, {}); + this.#openModal = this.#modalContext?.open(this, UMB_DYNAMIC_ROOT_ORIGIN_PICKER_MODAL, { + data: { items: this._originManifests }, + }); this.#openModal?.onSubmit().then((data: UmbTreePickerDynamicRoot) => { const existingData = { ...this.data }; existingData.originKey = undefined; @@ -98,7 +100,9 @@ export class UmbInputDocumentRootPickerElement extends FormControlMixin(UmbLitEl } #openDynamicRootQueryStepPicker() { - this.#openModal = this.#modalContext?.open(this, UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL, {}); + this.#openModal = this.#modalContext?.open(this, UMB_DYNAMIC_ROOT_QUERY_STEP_PICKER_MODAL, { + data: { items: this._queryStepManifests }, + }); this.#openModal?.onSubmit().then((step) => { if (this.data) { const querySteps = [...(this.data.querySteps ?? []), step]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-origin-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-origin-picker-modal.element.ts index 156a2dcc6d..c641283f66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-origin-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-origin-picker-modal.element.ts @@ -1,12 +1,12 @@ import { UmbDocumentPickerContext } from '../../documents/documents/components/input-document/input-document.context.js'; +import type { UmbDynamicRootOriginModalData } from './index.js'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { css, html, customElement, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import type { ManifestDynamicRootOrigin } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbTreePickerDynamicRoot } from '@umbraco-cms/backoffice/components'; @customElement('umb-dynamic-root-origin-picker-modal') -export class UmbDynamicRootOriginPickerModalModalElement extends UmbModalBaseElement { +export class UmbDynamicRootOriginPickerModalModalElement extends UmbModalBaseElement { @state() private _origins: Array = []; @@ -16,10 +16,14 @@ export class UmbDynamicRootOriginPickerModalModalElement extends UmbModalBaseEle super(); this.#documentPickerContext.max = 1; + } - this.observe(umbExtensionsRegistry.byType('dynamicRootOrigin'), (origins: Array) => { - this._origins = origins; - }); + connectedCallback() { + super.connectedCallback(); + + if (this.data) { + this._origins = this.data.items; + } } #choose(item: ManifestDynamicRootOrigin) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-query-step-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-query-step-picker-modal.element.ts index f322d47a7a..9eee8c8d58 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-query-step-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dynamic-root/modals/dynamic-root-query-step-picker-modal.element.ts @@ -1,28 +1,25 @@ import { UmbDocumentTypePickerContext } from '../../documents/document-types/components/input-document-type/input-document-type.context.js'; +import type { UmbDynamicRootQueryStepModalData } from './index.js'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbTreePickerDynamicRootQueryStep } from '@umbraco-cms/backoffice/components'; import type { ManifestDynamicRootQueryStep } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-dynamic-root-query-step-picker-modal') -export class UmbDynamicRootQueryStepPickerModalModalElement extends UmbModalBaseElement { +export class UmbDynamicRootQueryStepPickerModalModalElement extends UmbModalBaseElement { @state() private _querySteps: Array = []; #documentTypePickerContext = new UmbDocumentTypePickerContext(this); - constructor() { - super(); + connectedCallback() { + super.connectedCallback(); - this.observe( - umbExtensionsRegistry.byType('dynamicRootQueryStep'), - (querySteps: Array) => { - this._querySteps = querySteps; - }, - ); + if (this.data) { + this._querySteps = this.data.items; + } } #choose(item: ManifestDynamicRootQueryStep) { From e6d94a45256f22cddda86f1aba9a678d85b27ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 21:24:11 +0100 Subject: [PATCH 139/285] rename tabs --- ...t-type-container-structure-helper.class.ts | 26 +++++--- ...ontent-type-workspace-view-edit.element.ts | 63 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index fa628b8904..eaa4a17a66 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -93,6 +93,7 @@ export class UmbContentTypeContainerStructureHelper { @@ -166,7 +168,9 @@ export class UmbContentTypeContainerStructureHelper { this.observe( this.#structure!.containersOfParentKey(container.id, this._childType!), - this._insertChildContainers, + (containers) => { + this.#containers.append(this._insertChildContainers(containers)); + }, '_observeGroupsOf_' + container.id, ); }); @@ -178,23 +182,25 @@ export class UmbContentTypeContainerStructureHelper { - this.#containers.setValue([]); - this._insertChildContainers(rootContainers); + this.#containers.setValue(this._insertChildContainers(rootContainers)); this._parentOwnerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; }, '_observeRootContainers', ); } - private _insertChildContainers = (childContainers: UmbPropertyTypeContainerModel[]) => { - childContainers.forEach((group) => { + private _insertChildContainers( + childContainers: UmbPropertyTypeContainerModel[], + ): Array { + return childContainers.flatMap((group, i, value) => { if (group.name !== null && group.name !== undefined) { - if (!this.#containers.getValue().find((x) => x.name === group.name)) { - this.#containers.appendOne(group); + if (value.find((x) => x.name === group.name)) { + return group; } } + return []; }); - }; + } /** * Returns true if the container is an owner container. diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index e909cb80d5..4810532959 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -1,6 +1,6 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; import type { UmbContentTypeWorkspaceViewEditTabElement } from './content-type-workspace-view-edit-tab.element.js'; -import { css, html, customElement, state, repeat, nothing, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UUIInputElement, UUIInputEvent, UUITabElement } from '@umbraco-cms/backoffice/external/uui'; import { UMB_COMPOSITION_PICKER_MODAL, @@ -92,13 +92,11 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem @state() private _activePath = ''; + private _activeTabId?: string; + @state() private _sortModeActive?: boolean; - // TODO: investigate if we really want this: - @state() - private _buttonDisabled: boolean = false; - constructor() { super(); @@ -154,16 +152,24 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } private _createRoutes() { + // TODO: How about storing a set of elements based on tab ids? to prevent re-initializing the element when renaming..[NL] if (!this._workspaceContext || !this._tabs || this._hasRootGroups === undefined) return; const routes: UmbRoute[] = []; + // We gather the activeTab name to check for rename, this is a bit hacky way to redirect the user without noticing to the new name [NL] + let activeTabName: string | undefined = undefined; + if (this._tabs.length > 0) { this._tabs?.forEach((tab) => { const tabName = tab.name ?? ''; + if (tab.id === this._activeTabId) { + activeTabName = tabName; + } routes.push({ path: `tab/${encodeFolderName(tabName).toString()}`, component: () => import('./content-type-workspace-view-edit-tab.element.js'), setup: (component) => { + // Or just cache the current view here, and use it if the same is begin requested?. [NL] (component as UmbContentTypeWorkspaceViewEditTabElement).tabName = tabName; (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = tab.id; }, @@ -184,15 +190,33 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem routes.push({ path: '', redirectTo: 'root', + guards: [() => !this._activeTabId], }); - } else if (routes.length !== 0) { + } else if (this._tabs.length !== 0) { routes.push({ path: '', redirectTo: routes[0]?.path, }); + // TODO: Look at this case. } + // If we have an active tab name, then we might have a active tab name re-name, then we will redirect to the new name if it has been changed: [NL] + if (activeTabName) { + if (this._activePath && this._routerPath) { + const oldPath = this._activePath.split(this._routerPath)[1]; + const newPath = '/tab/' + encodeFolderName(activeTabName); + if (oldPath !== newPath) { + // Lets cheat a bit and update the activePath already, in this way our input does not loose focus [NL] + this._activePath = this._routerPath + newPath; + // Update the current URL, so we are still on this specific tab: + window.history.replaceState(null, '', this._activePath); + } + } + } + + //if (jsonStringComparison(this._routes, routes) === false) { this._routes = routes; + //} } async #requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) { @@ -226,13 +250,13 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem : ''; } async #addTab() { - /* + // If there is already a Tab with no name, then focus it instead of adding a new one: + // TODO: Optimize this so it looks at the data instead of the DOM [NL] const inputEl = this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement; if (inputEl?.value === '') { this.#focusInput(); return; } - */ if (!this._tabs) return; @@ -240,7 +264,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem const sortOrder = len === 0 ? 0 : this._tabs[len - 1].sortOrder + 1; const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab', sortOrder); if (tab) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); + const path = this._routerPath + (tab.name ? '/tab/' + encodeFolderName(tab.name) : '/tab'); window.history.replaceState(null, '', path); this.#focusInput(); } @@ -253,15 +277,9 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } async #tabNameChanged(event: InputEvent, tab: PropertyTypeContainerModelBaseModel) { - if (this._buttonDisabled) this._buttonDisabled = !this._buttonDisabled; + this._activeTabId = tab.id; let newName = (event.target as HTMLInputElement).value; - if (newName === '') { - // TODO: Localize this: - newName = 'Unnamed'; - (event.target as HTMLInputElement).value = 'Unnamed'; - } - const changedName = this._workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( newName, 'Tab', @@ -277,9 +295,10 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem this._tabsStructureHelper.partialUpdateContainer(tab.id!, { name: newName, }); + } - // Update the current URL, so we are still on this specific tab: - window.history.replaceState(null, '', this._routerPath + '/tab/' + encodeFolderName(newName)); + async #tabNameBlur() { + this._activeTabId = undefined; } async #openCompositionModal() { @@ -397,7 +416,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } renderTab(tab: PropertyTypeContainerModelBaseModel) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); + const path = this._routerPath + (tab.name ? '/tab/' + encodeFolderName(tab.name) : '/tab'); const tabActive = path === this._activePath; const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); @@ -436,9 +455,8 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem value="${tab.name!}" auto-width @change=${(e: InputEvent) => this.#tabNameChanged(e, tab)} - @blur=${(e: InputEvent) => this.#tabNameChanged(e, tab)} - @input=${() => (this._buttonDisabled = true)} - @focus=${(e: UUIInputEvent) => (e.target.value ? nothing : (this._buttonDisabled = true))}> + @input=${(e: InputEvent) => this.#tabNameChanged(e, tab)} + @blur=${(e: InputEvent) => this.#tabNameBlur()}> ${this.renderDeleteFor(tab)} `; @@ -462,7 +480,6 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem label=${this.localize.term('actions_remove')} class="trash" slot="append" - ?disabled=${this._buttonDisabled} @click=${() => this.#requestRemoveTab(tab)} compact> From 4298c2d7723a5f19cb2b5c837c3b34b39136f7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 12 Mar 2024 22:05:36 +0100 Subject: [PATCH 140/285] note for further refactoring and merging of containers --- ...t-type-container-structure-helper.class.ts | 37 ++++++++++++++++--- .../content-type-structure-manager.class.ts | 8 ++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index eaa4a17a66..395ca7be7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -27,13 +27,14 @@ export class UmbContentTypeContainerStructureHelper([], (x) => x.id); readonly containers = this.#containers.asObservable(); + // Owner containers are containers owned by the owner Content Type (The specific one up for editing) + private _ownerContainers: UmbPropertyTypeContainerModel[] = []; + #hasProperties = new UmbBooleanState(false); readonly hasProperties = this.#hasProperties.asObservable(); @@ -103,6 +104,7 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); - this._parentOwnerContainers = parentContainers.filter((x) => x.id === this._parentId) || []; + //this._parentOwnerContainers = parentContainers.filter((x) => x.id === this._parentId) || []; this._parentMatchingContainers = parentContainers ?? []; if (this._parentMatchingContainers.length > 0) { this._observeChildProperties(); @@ -165,11 +167,33 @@ export class UmbContentTypeContainerStructureHelper { this.observe( this.#structure!.containersOfParentKey(container.id, this._childType!), (containers) => { - this.#containers.append(this._insertChildContainers(containers)); + // TODO: This stinks, we need to finder a smarter way to do merging on the way. Think about the case when a container is appended but matches a container already in the list. + const old = this.#containers.getValue(); + const appends = this._insertChildContainers(containers); + // Make sure entries are unique on name and type: + const oldFiltered = old.filter( + (x) => + !appends.some( + (y) => + y.name === x.name && y.type === x.type && this._ownerContainers.some((oc) => oc.id === y.parent?.id), + ), + ); + const newFiltered = oldFiltered.concat(appends); + // Filter the newFiltered so it becomes unique name and type: + newFiltered.forEach((x, i, value) => !value.some((y) => y.name === x.name && y.type === x.type)); + + this.#containers.setValue(newFiltered); + // TODO: make sure we filter away any inherited containers: }, '_observeGroupsOf_' + container.id, ); @@ -183,7 +207,7 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue(this._insertChildContainers(rootContainers)); - this._parentOwnerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; + this._ownerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; }, '_observeRootContainers', ); @@ -192,6 +216,7 @@ export class UmbContentTypeContainerStructureHelper { + // TODO: This place needs to be more intelligent about the existing containers. Now it only takes care of it self. [NL] return childContainers.flatMap((group, i, value) => { if (group.name !== null && group.name !== undefined) { if (value.find((x) => x.name === group.name)) { @@ -218,7 +243,7 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); + return this._ownerContainers.some((x) => x.id === containerId); } /** Manipulate methods: */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 24c3054d92..05facd034e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -251,9 +251,11 @@ export class UmbContentTypePropertyStructureManager x.unique === toContentTypeUnique)?.containers ?? []), - ]; + // Correction the spread is removed now, cause we do a filter: [NL] + // And then we remove the existing one, to have the more local one replacing it. [NL] + const containers = ( + this.#contentTypes.getValue().find((x) => x.unique === toContentTypeUnique)?.containers ?? [] + ).filter((x) => x.name !== clonedContainer.name && x.type === clonedContainer.type); containers.push(clonedContainer); // eslint-disable-next-line @typescript-eslint/ban-ts-comment From 0f5df81b0f495895048a9c263a6cfcd7bf8e07c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 09:39:11 +0100 Subject: [PATCH 141/285] fix routing --- .../design/content-type-workspace-view-edit.element.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 4810532959..2a8a8fa2e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -190,18 +190,20 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem routes.push({ path: '', redirectTo: 'root', - guards: [() => !this._activeTabId], + guards: [() => this._activeTabId === undefined], }); - } else if (this._tabs.length !== 0) { + } else if (routes.length !== 0) { routes.push({ path: '', redirectTo: routes[0]?.path, + guards: [() => this._activeTabId === undefined], }); // TODO: Look at this case. } // If we have an active tab name, then we might have a active tab name re-name, then we will redirect to the new name if it has been changed: [NL] - if (activeTabName) { + // TODOD: this._activeTabId could be left out. + if (this._activeTabId && activeTabName) { if (this._activePath && this._routerPath) { const oldPath = this._activePath.split(this._routerPath)[1]; const newPath = '/tab/' + encodeFolderName(activeTabName); From ac6a007dfd9be4f8b76ff50c889072f5ab6b12cf Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:10:47 +0100 Subject: [PATCH 142/285] rename label --- .../documents/workspace/document-workspace.context.ts | 2 +- .../src/packages/documents/documents/workspace/manifests.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index d144a8c921..6be567d58e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -34,7 +34,7 @@ import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '@umbraco-cms/backoffice/tree'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; type EntityType = UmbDocumentDetailModel; export class UmbDocumentWorkspaceContext diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts index 6ea8bb1cef..d04c8061aa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/manifests.ts @@ -178,9 +178,8 @@ const workspaceActionMenuItems: Array = [ api: UmbDocumentSaveAndScheduleWorkspaceAction, forWorkspaceActions: 'Umb.WorkspaceAction.Document.SaveAndPublish', meta: { - label: 'Schedule publishing', + label: 'Schedule...', icon: 'icon-globe', - allowScheduledPublish: true, }, }, ]; From 6984d2becbea30256ebfe1541e1c5d44bd10c7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 11:13:56 +0100 Subject: [PATCH 143/285] further merge refactor --- ...t-type-container-structure-helper.class.ts | 59 +++++++++++++------ .../content-type-structure-manager.class.ts | 14 +++-- ...nt-type-workspace-view-edit-tab.element.ts | 4 +- ...ontent-type-workspace-view-edit.element.ts | 38 ++++++------ 4 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 395ca7be7d..cc02411639 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -114,6 +114,7 @@ export class UmbContentTypeContainerStructureHelper { + this._ownerContainers = containers ?? []; + //console.log('this._ownerContainers', this._ownerContainers); + }, + '_observeOwnerContainers', + ); this._parentMatchingContainers.forEach((container) => { this.observe( @@ -179,20 +186,21 @@ export class UmbContentTypeContainerStructureHelper { // TODO: This stinks, we need to finder a smarter way to do merging on the way. Think about the case when a container is appended but matches a container already in the list. const old = this.#containers.getValue(); - const appends = this._insertChildContainers(containers); + + //const appends = this._insertChildContainers(containers); // Make sure entries are unique on name and type: const oldFiltered = old.filter( - (x) => - !appends.some( - (y) => - y.name === x.name && y.type === x.type && this._ownerContainers.some((oc) => oc.id === y.parent?.id), - ), + (x) => !containers.some((y) => y.id === x.id || (y.name === x.name && y.type === x.type)), ); - const newFiltered = oldFiltered.concat(appends); + + const newFiltered = oldFiltered.concat(containers); + /* // Filter the newFiltered so it becomes unique name and type: newFiltered.forEach((x, i, value) => !value.some((y) => y.name === x.name && y.type === x.type)); - - this.#containers.setValue(newFiltered); +*/ + this.#containers.setValue(this._filterContainers(newFiltered)); + //console.log('new set of containers:', this.#containers.getValue(), this._parentId); + //debugger; // TODO: make sure we filter away any inherited containers: }, '_observeGroupsOf_' + container.id, @@ -200,19 +208,33 @@ export class UmbContentTypeContainerStructureHelper) { + return old.filter( + (anyCon) => + !this._ownerContainers.some( + (ownerCon) => + // We would like to keep the owner container in the anyCons, so do not filter that + ownerCon.id !== anyCon.id || + (ownerCon.name === anyCon.name && + ownerCon.type === anyCon.type && + ownerCon.parent?.id === anyCon.parent?.id), + ), + ); + } + private _observeRootContainers() { if (!this.#structure || !this._isRoot || !this._childType || this._parentId === undefined) return; this.observe( this.#structure.rootContainers(this._childType), (rootContainers) => { - this.#containers.setValue(this._insertChildContainers(rootContainers)); + this.#containers.setValue(rootContainers); this._ownerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; }, '_observeRootContainers', ); } - + /* private _insertChildContainers( childContainers: UmbPropertyTypeContainerModel[], ): Array { @@ -225,7 +247,7 @@ export class UmbContentTypeContainerStructureHelper x.id === containerId); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 05facd034e..2290534137 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -591,13 +591,19 @@ export class UmbContentTypePropertyStructureManager x.containers?.filter((x) => x.type === containerType) ?? []); + ownerContainersOf(containerType: UmbPropertyContainerTypes, parentId: string | null) { + return this.ownerContentTypeObservablePart( + (x) => + x.containers?.filter( + (x) => (parentId ? x.parent?.id === parentId : x.parent === null) && x.type === containerType, + ) ?? [], + ); } getOwnerContainers(containerType: UmbPropertyContainerTypes, parentId: string | null) { - return this.getOwnerContentType()?.containers?.filter((x) => - parentId ? x.parent?.id === parentId : x.parent === null && x.type === containerType, + console.log(this.getOwnerContentType()?.containers); + return this.getOwnerContentType()?.containers?.filter( + (x) => (parentId ? x.parent?.id === parentId : x.parent === null) && x.type === containerType, ); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 618e74a6f3..dcb5b18cd3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -1,7 +1,7 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-workspace-view-edit-group.element.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbContentTypeContainerStructureHelper, type UmbContentTypeModel, @@ -32,7 +32,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { throw new Error('OwnerTabId is not set, we have not made a local duplicated of this container.'); return; } - console.log('_ownerTabId', this._inherited); /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... @@ -161,7 +160,6 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { const len = this._groups.length; const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; const container = this.#groupStructureHelper.addContainer(this._containerId, sortOrder); - console.log('container', container); }; render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 2a8a8fa2e4..5111e75874 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -134,6 +134,10 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem }); } + #toggleSortMode() { + this._workspaceContext?.setIsSorting(!this._sortModeActive); + } + private _observeRootGroups() { if (!this._workspaceContext) return; @@ -147,10 +151,6 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem ); } - #changeMode() { - this._workspaceContext?.setIsSorting(!this._sortModeActive); - } - private _createRoutes() { // TODO: How about storing a set of elements based on tab ids? to prevent re-initializing the element when renaming..[NL] if (!this._workspaceContext || !this._tabs || this._hasRootGroups === undefined) return; @@ -202,8 +202,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } // If we have an active tab name, then we might have a active tab name re-name, then we will redirect to the new name if it has been changed: [NL] - // TODOD: this._activeTabId could be left out. - if (this._activeTabId && activeTabName) { + if (activeTabName) { if (this._activePath && this._routerPath) { const oldPath = this._activePath.split(this._routerPath)[1]; const newPath = '/tab/' + encodeFolderName(activeTabName); @@ -216,9 +215,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } } - //if (jsonStringComparison(this._routes, routes) === false) { this._routes = routes; - //} } async #requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) { @@ -382,7 +379,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem ${this.localize.term('contentTypeEditor_compositions')} - + ${sortButtonText} @@ -420,34 +417,35 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem renderTab(tab: PropertyTypeContainerModelBaseModel) { const path = this._routerPath + (tab.name ? '/tab/' + encodeFolderName(tab.name) : '/tab'); const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); + const ownedTab = this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ?? false; + console.log('ownedTab', ownedTab); return html` - ${this.renderTabInner(tab, tabActive, tabInherited)} + ${this.renderTabInner(tab, tabActive, ownedTab)} `; } - renderTabInner(tab: PropertyTypeContainerModelBaseModel, tabActive: boolean, tabInherited: boolean) { + renderTabInner(tab: PropertyTypeContainerModelBaseModel, tabActive: boolean, ownedTab: boolean) { // TODO: Localize this: if (this._sortModeActive) { return html`
    - ${tabInherited - ? html`${tab.name!}` - : html` ${tab.name!} + ${ownedTab + ? html` ${tab.name!} this.#changeOrderNumber(tab, e)}>`} + @change=${(e: UUIInputEvent) => this.#changeOrderNumber(tab, e)}>` + : html`${tab.name!}`}
    `; } - if (tabActive && !tabInherited) { + if (tabActive && ownedTab) { return html`
    `; } - if (tabInherited) { - return html`
    ${tab.name!}
    `; - } else { + if (ownedTab) { return html`
    ${tab.name!} ${this.renderDeleteFor(tab)}
    `; + } else { + return html`
    ${tab.name!}
    `; } } From 62c27611601ebc0b248393f3e0ace148f9a0394e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:19:33 +0100 Subject: [PATCH 144/285] try and remove top margin and padding from first element inside the main slot of the body layout --- .../core/components/body-layout/body-layout.element.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts index 866fbf9638..1d589df449 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/body-layout/body-layout.element.ts @@ -212,6 +212,11 @@ export class UmbBodyLayoutElement extends LitElement { overflow-y: auto; padding: var(--uui-size-layout-1); } + + #main > slot::slotted(*:first-child) { + padding-top: 0; + margin-top: 0; + } `, ]; } From 7ca0d0f3c20a79fd0aadbccc74165b43434fde3b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:20:25 +0100 Subject: [PATCH 145/285] remove schedule from publish modal --- .../document-publish-modal.element.ts | 18 ++++-------------- .../document-publish-modal.token.ts | 9 ++------- .../workspace/document-workspace.context.ts | 9 +++------ 3 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index 462001f556..e41b57a6b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -1,6 +1,6 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; -import { css, customElement, html, nothing, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; @@ -71,28 +71,18 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< render() { return html`

    - ${this.localize.term( - this.data?.allowScheduledPublish ? 'content_languagesToSchedule' : 'content_variantsToPublish', - )} + Which variants would you like to publish?

    - ${when( - this.data?.allowScheduledPublish, - () => html`This is a scheduled publish`, - () => nothing, - )} - -

    ${this.localize.term('content_variantsWillBeSaved')}

    +

    All new variants will be saved.

    diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts index 695e183189..f05f650c8f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.token.ts @@ -1,15 +1,10 @@ import type { UmbDocumentVariantPickerData, UmbDocumentVariantPickerValue } from '../types.js'; import { UMB_DOCUMENT_PUBLISH_MODAL_ALIAS } from '../manifests.js'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; -export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerData { - allowScheduledPublish?: boolean; -} +export interface UmbDocumentPublishModalData extends UmbDocumentVariantPickerData {} -export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerValue { - schedule?: ScheduleRequestModel; -} +export interface UmbDocumentPublishModalValue extends UmbDocumentVariantPickerValue {} export const UMB_DOCUMENT_PUBLISH_MODAL = new UmbModalToken( UMB_DOCUMENT_PUBLISH_MODAL_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 6be567d58e..968d631e71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -461,12 +461,11 @@ export class UmbDocumentWorkspaceContext return selectedVariants; } - async #handleSaveAndPublish(allowScheduledPublish?: boolean) { + async #handleSaveAndPublish() { const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); let variantIds: Array = []; - let schedule: ScheduleRequestModel | undefined = undefined; const { options, selected } = await this.#determineVariantOptions(); @@ -483,7 +482,6 @@ export class UmbDocumentWorkspaceContext .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options, - allowScheduledPublish: allowScheduledPublish ?? false, }, value: { selection: selected }, }) @@ -493,11 +491,10 @@ export class UmbDocumentWorkspaceContext if (!result?.selection.length || !unique) return; variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; - schedule = result.schedule; } const variants = await this.#performSaveOrCreate(variantIds); - await this.publishingRepository.publish(unique, variants, schedule); + await this.publishingRepository.publish(unique, variants); this.workspaceComplete(this.#currentData.getValue()); } @@ -550,7 +547,7 @@ export class UmbDocumentWorkspaceContext } saveAndSchedule() { - return this.#handleSaveAndPublish(true); + return this.#handleSaveAndPublish(); } public async unpublish() { From e7c86ff33deb758d57a1188ab1a5a85b6644bda2 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:21:05 +0100 Subject: [PATCH 146/285] register element with correct element name --- .../modals/publish-modal/document-publish-modal.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index e41b57a6b0..bc944c258b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -10,7 +10,7 @@ import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import '../shared/document-variant-language-picker.element.js'; -@customElement('umb-document-variant-picker-modal') +@customElement('umb-document-publish-modal') export class UmbDocumentPublishModalElement extends UmbModalBaseElement< UmbDocumentPublishModalData, UmbDocumentPublishModalValue From 33b6e3fbe854792adbe26a6f9264bf1cc68c7a48 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:21:38 +0100 Subject: [PATCH 147/285] remove schedule from story --- .../document-publish-modal.stories.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts index 60eb76c0cc..282c5e4180 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -144,17 +144,3 @@ export default meta; type Story = StoryObj; export const Overview: Story = {}; -export const Schedule: Story = { - args: { - data: { ...modalData, allowScheduledPublish: true }, - }, - parameters: { - docs: { - source: { - code: ` -modalManager.open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options: [], allowScheduledPublish: true } }); - `, - }, - }, - }, -}; From 9834af252bacbc00bca9d6dfe1f141bebb5bad9c Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:31:01 +0100 Subject: [PATCH 148/285] fix up publish story --- .../document-publish-modal.stories.ts | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts index 282c5e4180..31ad74e5e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -1,4 +1,3 @@ -import '../../../../core/components/body-layout/body-layout.element.js'; import './document-publish-modal.element.js'; import type { Meta, StoryObj } from '@storybook/web-components'; @@ -100,39 +99,56 @@ import { UMB_DOCUMENT_PUBLISH_MODAL, UmbDocumentVariantState } from '@umbraco-cm import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => { - modalManager.open(this, UMB_DOCUMENT_PUBLISH_MODAL, { + const result = modalManager.open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options: [ { - name: 'English', + unique: 'en-us', culture: 'en-us', - state: UmbDocumentVariantState.PUBLISHED, - createDate: '2021-08-25T14:00:00Z', - publishDate: '2021-08-25T14:00:00Z', - updateDate: null, segment: null, + variant: { + name: 'English variant name', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'English', + unique: 'en-us', + isDefault: true, + isMandatory: true, + fallbackIsoCode: null, + }, }, { - name: 'English', - culture: 'en-us', - state: UmbDocumentVariantState.PUBLISHED, - createDate: '2021-08-25T14:00:00Z', - publishDate: '2021-08-25T14:00:00Z', - updateDate: null, - segment: 'GTM', - }, - { - name: 'Danish', + unique: 'da-dk', culture: 'da-dk', - state: UmbDocumentVariantState.NOT_CREATED, - createDate: null, - publishDate: null, - updateDate: null, segment: null, + variant: { + name: 'Danish variant name', + culture: 'da-dk', + state: UmbDocumentVariantState.NOT_CREATED, + createDate: null, + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'Danish', + unique: 'da-dk', + isDefault: false, + isMandatory: false, + fallbackIsoCode: null, + }, }, ], } - } + }).onSubmit().catch(() => undefined); }); `, }, From cb2c0433ea3675b75be3ea01104f012622edb3f9 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:32:35 +0100 Subject: [PATCH 149/285] add new schedule modal --- .../documents/documents/modals/index.ts | 1 + .../documents/documents/modals/manifests.ts | 7 ++ .../document-schedule-modal.element.ts | 111 ++++++++++++++++++ .../document-schedule-modal.stories.ts | 96 +++++++++++++++ .../document-schedule-modal.token.ts | 19 +++ .../documents/modals/schedule-modal/index.ts | 1 + 6 files changed, 235 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts index c11dcf1900..2c42f5dfd3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/index.ts @@ -1,4 +1,5 @@ export * from './publish-modal/index.js'; +export * from './schedule-modal/index.js'; export * from './shared/index.js'; export * from './document-picker-modal.token.js'; export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts index a1b80e5e85..f711323740 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/manifests.ts @@ -1,6 +1,7 @@ import type { ManifestModal } from '@umbraco-cms/backoffice/extension-registry'; export const UMB_DOCUMENT_PUBLISH_MODAL_ALIAS = 'Umb.Modal.DocumentPublish'; +export const UMB_DOCUMENT_SCHEDULE_MODAL_ALIAS = 'Umb.Modal.DocumentSchedule'; const modals: Array = [ { @@ -9,6 +10,12 @@ const modals: Array = [ name: 'Document Publish Modal', js: () => import('./publish-modal/document-publish-modal.element.js'), }, + { + type: 'modal', + alias: UMB_DOCUMENT_SCHEDULE_MODAL_ALIAS, + name: 'Document Schedule Modal', + js: () => import('./schedule-modal/document-schedule-modal.element.js'), + }, ]; export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts new file mode 100644 index 0000000000..a5c7ac6baf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -0,0 +1,111 @@ +import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; +import type { UmbDocumentScheduleModalData, UmbDocumentScheduleModalValue } from './document-schedule-modal.token.js'; +import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; +import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; + +import '../shared/document-variant-language-picker.element.js'; + +@customElement('umb-document-schedule-modal') +export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< + UmbDocumentScheduleModalData, + UmbDocumentScheduleModalValue +> { + #selectionManager = new UmbSelectionManager(this); + #schedule?: ScheduleRequestModel; + + @state() + _options: Array = []; + + firstUpdated() { + this.#configureSelectionManager(); + } + + async #configureSelectionManager() { + this.#selectionManager.setMultiple(true); + this.#selectionManager.setSelectable(true); + + // Only display variants that are relevant to pick from, i.e. variants that are draft or published with pending changes: + this._options = + this.data?.options.filter( + (option) => option.variant && option.variant.state !== UmbDocumentVariantState.NOT_CREATED, + ) ?? []; + + let selected = this.value?.selection ?? []; + + // Filter selection based on options: + selected = selected.filter((s) => this._options.some((o) => o.unique === s)); + + // If no selections were provided, select the app language by default: + if (selected.length === 0) { + const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + const appCulture = context.getAppCulture(); + // If the app language is one of the options, select it by default: + if (appCulture && this._options.some((o) => o.language.unique === appCulture)) { + selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); + } + } + + this.#selectionManager.setSelection(selected); + + // Additionally select mandatory languages: + this._options.forEach((variant) => { + if (variant.language?.isMandatory) { + this.#selectionManager.select(variant.unique); + } + }); + } + + #submit() { + this.value = { selection: this.#selectionManager.getSelection(), schedule: this.#schedule }; + this.modalContext?.submit(); + } + + #close() { + this.modalContext?.reject(); + } + + render() { + return html` +

    + Which languages would you like to schedule? +

    + + +
    + + +
    +
    `; + } + + static styles = [ + UmbTextStyles, + css` + :host { + display: block; + width: 400px; + max-width: 90vw; + } + `, + ]; +} + +export default UmbDocumentScheduleModalElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-document-schedule-modal': UmbDocumentScheduleModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts new file mode 100644 index 0000000000..64a9f88a55 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts @@ -0,0 +1,96 @@ +import './document-schedule-modal.element.js'; + +import type { Meta, StoryObj } from '@storybook/web-components'; +import { UmbDocumentVariantState } from '../../types.js'; +import type { UmbDocumentScheduleModalData, UmbDocumentScheduleModalValue } from './document-schedule-modal.token.js'; +import type { UmbDocumentScheduleModalElement } from './document-schedule-modal.element.js'; +import { html } from '@umbraco-cms/backoffice/external/lit'; + +const modalData: UmbDocumentScheduleModalData = { + options: [ + { + unique: 'en-us', + culture: 'en-us', + segment: null, + variant: { + name: 'English variant name', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'English', + unique: 'en-us', + isDefault: true, + isMandatory: true, + fallbackIsoCode: null, + }, + }, + ], +}; + +const modalValue: UmbDocumentScheduleModalValue = { + selection: ['en-us'], +}; + +const meta: Meta = { + title: 'Workspaces/Document/Modals/Schedule', + component: 'umb-document-schedule-modal', + id: 'umb-document-schedule-modal', + args: { + data: modalData, + value: modalValue, + }, + decorators: [(Story) => html`
    ${Story()}
    `], + parameters: { + layout: 'centered', + docs: { + source: { + code: ` +import { UMB_DOCUMENT_SCHEDULE_MODAL, UmbDocumentVariantState } from '@umbraco-cms/backoffice/document'; +import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; + +this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => { + const result = await modalManager.open(this, UMB_DOCUMENT_SCHEDULE_MODAL, { + data: { + options: [ + { + unique: 'en-us', + culture: 'en-us', + segment: null, + variant: { + name: 'English variant name', + culture: 'en-us', + state: UmbDocumentVariantState.PUBLISHED, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'English', + unique: 'en-us', + isDefault: true, + isMandatory: true, + fallbackIsoCode: null, + }, + }, + ], + } + }).onSubmit().catch(() => undefined); +}); + `, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Overview: Story = {}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts new file mode 100644 index 0000000000..0007d4d7e6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts @@ -0,0 +1,19 @@ +import type { UmbDocumentVariantPickerData, UmbDocumentVariantPickerValue } from '../types.js'; +import { UMB_DOCUMENT_SCHEDULE_MODAL_ALIAS } from '../manifests.js'; +import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; + +export interface UmbDocumentScheduleModalData extends UmbDocumentVariantPickerData {} + +export interface UmbDocumentScheduleModalValue extends UmbDocumentVariantPickerValue { + schedule?: ScheduleRequestModel; +} + +export const UMB_DOCUMENT_SCHEDULE_MODAL = new UmbModalToken< + UmbDocumentScheduleModalData, + UmbDocumentScheduleModalValue +>(UMB_DOCUMENT_SCHEDULE_MODAL_ALIAS, { + modal: { + type: 'dialog', + }, +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/index.ts new file mode 100644 index 0000000000..0307e54b17 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/index.ts @@ -0,0 +1 @@ +export * from './document-schedule-modal.token.js'; From e186aba8f38a92421c9bae9b1caca9101851f92a Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 11:36:27 +0100 Subject: [PATCH 150/285] wire up the schedule modal --- .../actions/save-and-schedule.action.ts | 2 +- .../workspace/document-workspace.context.ts | 37 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts index a4acafb444..6419b22d54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/actions/save-and-schedule.action.ts @@ -4,6 +4,6 @@ import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; export class UmbDocumentSaveAndScheduleWorkspaceAction extends UmbWorkspaceActionBase { async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); - return workspaceContext.saveAndSchedule(); + return workspaceContext.schedule(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 968d631e71..9ab9be63d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -8,7 +8,7 @@ import type { UmbDocumentVariantModel, UmbDocumentVariantOptionModel, } from '../types.js'; -import { UMB_DOCUMENT_PUBLISH_MODAL } from '../modals/index.js'; +import { UMB_DOCUMENT_PUBLISH_MODAL, UMB_DOCUMENT_SCHEDULE_MODAL } from '../modals/index.js'; import { UmbDocumentPublishingRepository } from '../repository/publishing/index.js'; import { UmbUnpublishDocumentEntityAction } from '../entity-actions/unpublish.action.js'; import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js'; @@ -546,8 +546,39 @@ export class UmbDocumentWorkspaceContext return this.#handleSaveAndPublish(); } - saveAndSchedule() { - return this.#handleSaveAndPublish(); + public async schedule() { + const { options, selected } = await this.#determineVariantOptions(); + + // If there are multiple variants, we will open the modal to let the user pick which variants to save. + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + const result = await modalManagerContext + .open(this, UMB_DOCUMENT_SCHEDULE_MODAL, { + data: { + options, + }, + value: { selection: selected }, + }) + .onSubmit() + .catch(() => undefined); + + if (!result?.selection.length || !result?.schedule) return; + + const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; + const schedule = result?.schedule; + + if (!variantIds.length || !schedule) return; + + const unique = this.getUnique(); + if (!unique) throw new Error('Unique is missing'); + await this.publishingRepository.publish(unique, variantIds, schedule); + + const data = this.getData(); + if (!data) throw new Error('Data is missing'); + + this.#persistedData.setValue(data); + this.#currentData.setValue(data); + + this.workspaceComplete(data); } public async unpublish() { From e90960c7727c76278e50292f8cc059205463d051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 13:16:03 +0100 Subject: [PATCH 151/285] corrected container merging --- ...t-type-container-structure-helper.class.ts | 65 +++++++++++-------- ...-type-workspace-view-edit-group.element.ts | 17 +++-- ...nt-type-workspace-view-edit-tab.element.ts | 2 +- 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index cc02411639..6e4cb4d9df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -140,10 +140,20 @@ export class UmbContentTypeContainerStructureHelper { this.#containers.setValue([]); //this._parentOwnerContainers = parentContainers.filter((x) => x.id === this._parentId) || []; + // Stop observing a the previous _parentMatchingContainers... + this._parentMatchingContainers.forEach((container) => { + this.removeControllerByAlias('_observeParentHasProperties_' + container.id); + this.removeControllerByAlias('_observeGroupsOf_' + container.id); + }); this._parentMatchingContainers = parentContainers ?? []; if (this._parentMatchingContainers.length > 0) { this._observeChildProperties(); this._observeChildContainers(); + } else { + // Do some reset: + this.#hasProperties.setValue(false); + this._ownerContainers = []; + this.removeControllerByAlias('_observeOwnerContainers'); } }, '_observeParentContainers', @@ -175,51 +185,50 @@ export class UmbContentTypeContainerStructureHelper { this._ownerContainers = containers ?? []; - //console.log('this._ownerContainers', this._ownerContainers); + this.#containers.setValue(this.#filterNonOwnerContainers(this.#containers.getValue())); }, '_observeOwnerContainers', ); - this._parentMatchingContainers.forEach((container) => { + this._parentMatchingContainers.forEach((parentCon) => { this.observe( - this.#structure!.containersOfParentKey(container.id, this._childType!), + this.#structure!.containersOfParentKey(parentCon.id, this._childType!), (containers) => { - // TODO: This stinks, we need to finder a smarter way to do merging on the way. Think about the case when a container is appended but matches a container already in the list. + // First we will filter out non-owner containers: const old = this.#containers.getValue(); - - //const appends = this._insertChildContainers(containers); - // Make sure entries are unique on name and type: + // Then filter out the containers that are in the new list, either based on id or a match on name & type. + // Matching on name & type will result in the latest being the one we include, notice will only counts for non-owner containers. const oldFiltered = old.filter( (x) => !containers.some((y) => y.id === x.id || (y.name === x.name && y.type === x.type)), ); const newFiltered = oldFiltered.concat(containers); - /* - // Filter the newFiltered so it becomes unique name and type: - newFiltered.forEach((x, i, value) => !value.some((y) => y.name === x.name && y.type === x.type)); -*/ - this.#containers.setValue(this._filterContainers(newFiltered)); - //console.log('new set of containers:', this.#containers.getValue(), this._parentId); - //debugger; - // TODO: make sure we filter away any inherited containers: + + // Filter out non owners again: + this.#containers.setValue(this.#filterNonOwnerContainers(newFiltered)); }, - '_observeGroupsOf_' + container.id, + '_observeGroupsOf_' + parentCon.id, ); }); } - private _filterContainers(old: Array) { - return old.filter( - (anyCon) => - !this._ownerContainers.some( - (ownerCon) => - // We would like to keep the owner container in the anyCons, so do not filter that - ownerCon.id !== anyCon.id || - (ownerCon.name === anyCon.name && - ownerCon.type === anyCon.type && - ownerCon.parent?.id === anyCon.parent?.id), - ), - ); + /** + * This filters our local containers, so we only have one pr. type and name. + * This method is used to ensure we prioritize a Owner Container over the inherited containers. + * This method does not ensure that there is only one of each + */ + #filterNonOwnerContainers(containers: Array) { + return this._ownerContainers.length > 0 + ? containers.filter((anyCon) => + this._ownerContainers.some( + (ownerCon) => + // We would like to keep the owner container in the anyCons, so do not filter that + ownerCon.id === anyCon.id || + // Then if this is not the owner container but matches by name & type, then we do not want it. + !(ownerCon.id !== anyCon.id && ownerCon.name === anyCon.name && ownerCon.type === anyCon.type), + ), + ) + : containers; } private _observeRootContainers() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts index 19edb6e1e2..99c64781e5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts @@ -113,6 +113,18 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { this._groupStructureHelper.partialUpdateContainer(this.group.id, partialObject); } + #renameGroup(e: InputEvent) { + if (!this.groupStructureHelper || !this._group) return; + let newName = (e.target as HTMLInputElement).value; + const changedName = this.groupStructureHelper + .getStructureManager()! + .makeContainerNameUniqueForOwnerContentType(newName, 'Group', this._group.parent?.id ?? null); + if (changedName) { + newName = changedName; + } + this._singleValueUpdate('name', newName); + } + render() { return this._inherited !== undefined ? html` @@ -157,10 +169,7 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { placeholder=${this.localize.term('placeholders_entername')} .value=${this.group!.name} ?disabled=${!this._hasOwnerContainer} - @change=${(e: InputEvent) => { - const newName = (e.target as HTMLInputElement).value; - this._singleValueUpdate('name', newName); - }}>`; + @change=${this.#renameGroup}>`; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index dcb5b18cd3..9c7b4dccd7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -159,7 +159,7 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? const len = this._groups.length; const sortOrder = len === 0 ? 0 : this._groups[len - 1].sortOrder + 1; - const container = this.#groupStructureHelper.addContainer(this._containerId, sortOrder); + this.#groupStructureHelper.addContainer(this._containerId, sortOrder); }; render() { From beb3f1c16d4cd1f1980bb190800af364c61082e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 13:20:50 +0100 Subject: [PATCH 152/285] 'contentTypeDesignEditor' --- .../core/content-type/workspace/views/design/manifest.ts | 4 ++-- .../core/extension-registry/models/workspace-view.model.ts | 2 +- .../packages/documents/document-types/workspace/manifests.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts index e099cf7c32..926cb1ead3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts @@ -3,11 +3,11 @@ import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extensio export const contentTypeDesignEditorManifest: UmbBackofficeManifestKind = { type: 'kind', alias: 'Umb.Kind.WorkspaceView.ContentTypeDesignEditor', - matchKind: 'contentTypeDesign', + matchKind: 'contentTypeDesignEditor', matchType: 'workspaceView', manifest: { type: 'workspaceView', - kind: 'contentTypeDesign', + kind: 'contentTypeDesignEditor', element: () => import('./content-type-workspace-view-edit.element.js'), weight: 1000, meta: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts index 7a2ce47a34..6a3c939985 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace-view.model.ts @@ -17,5 +17,5 @@ export interface MetaWorkspaceView extends MetaManifestWithView {} export interface ManifestWorkspaceViewContentTypeDesignEditorKind extends ManifestWorkspaceView { type: 'workspaceView'; - kind: 'contentTypeDesign'; + kind: 'contentTypeDesignEditor'; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts index f960265d14..2fbf606452 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/manifests.ts @@ -20,7 +20,7 @@ const workspace: ManifestWorkspace = { const workspaceViews: Array = [ { type: 'workspaceView', - kind: 'contentTypeDesign', + kind: 'contentTypeDesignEditor', alias: 'Umb.WorkspaceView.DocumentType.Design', name: 'Document Type Workspace Design View', meta: { From e11472c1feb7356f039d50f0c37f9019a17c7f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 13:22:37 +0100 Subject: [PATCH 153/285] switch media type design to kind --- .../media/media-types/workspace/manifests.ts | 7 +- ...-workspace-view-edit-properties.element.ts | 228 -------- ...pe-workspace-view-edit-property.element.ts | 469 ---------------- ...ia-type-workspace-view-edit-tab.element.ts | 282 ---------- .../media-type-workspace-view-edit.element.ts | 509 ------------------ 5 files changed, 3 insertions(+), 1492 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts index 85fabefde5..a6b558b6db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/manifests.ts @@ -1,7 +1,7 @@ import type { ManifestWorkspace, ManifestWorkspaceActions, - ManifestWorkspaceView, + ManifestWorkspaceViews, } from '@umbraco-cms/backoffice/extension-registry'; import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; @@ -16,13 +16,12 @@ const workspace: ManifestWorkspace = { }, }; -const workspaceViews: Array = [ +const workspaceViews: Array = [ { type: 'workspaceView', + kind: 'contentTypeDesignEditor', alias: 'Umb.WorkspaceView.MediaType.Design', name: 'Media Type Workspace Design View', - js: () => import('./views/design/media-type-workspace-view-edit.element.js'), - weight: 1000, meta: { label: 'Design', pathname: 'design', diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts deleted file mode 100644 index 7864a7df61..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-properties.element.ts +++ /dev/null @@ -1,228 +0,0 @@ -import type { UmbMediaTypeWorkspaceContext } from '../../media-type-workspace.context.js'; -import './media-type-workspace-view-edit-property.element.js'; -import type { UmbMediaTypeDetailModel } from '../../../types.js'; -import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -import { - UmbContentTypePropertyStructureHelper, - UMB_PROPERTY_SETTINGS_MODAL, -} from '@umbraco-cms/backoffice/content-type'; -import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.getAttribute('data-umb-tabs-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.id; - }, - identifier: 'content-type-property-sorter', - itemSelector: '[data-umb-property-id]', - disabledItemSelector: '[inherited]', - containerSelector: '#property-list', -}; - -@customElement('umb-media-type-workspace-view-edit-properties') -export class UmbMediaTypeWorkspaceViewEditPropertiesElement extends UmbLitElement { - #propertySorter = new UmbSorterController(this, { - ...SORTER_CONFIG, - performItemInsert: (args) => { - let sortOrder = 0; - if (this._propertyStructure.length > 0) { - if (args.newIndex === 0) { - sortOrder = (this._propertyStructure[0].sortOrder ?? 0) - 1; - } else { - sortOrder = - (this._propertyStructure[Math.min(args.newIndex, this._propertyStructure.length - 1)].sortOrder ?? 0) + 1; - } - } - return this._propertyStructureHelper.insertProperty(args.item, sortOrder); - }, - performItemRemove: (args) => { - return this._propertyStructureHelper.removeProperty(args.item.id!); - }, - performItemMove: (args) => { - this._propertyStructureHelper.removeProperty(args.item.id!); - let sortOrder = 0; - if (this._propertyStructure.length > 0) { - if (args.newIndex === 0) { - sortOrder = (this._propertyStructure[0].sortOrder ?? 0) - 1; - } else { - sortOrder = - (this._propertyStructure[Math.min(args.newIndex, this._propertyStructure.length - 1)].sortOrder ?? 0) + 1; - } - } - return this._propertyStructureHelper.insertProperty(args.item, sortOrder); - }, - }); - - private _containerId: string | undefined; - - @property({ type: String, attribute: 'container-id', reflect: false }) - public get containerId(): string | undefined { - return this._containerId; - } - public set containerId(value: string | undefined) { - if (value === this._containerId) return; - const oldValue = this._containerId; - this._containerId = value; - this.requestUpdate('containerId', oldValue); - } - - @property({ type: String, attribute: 'container-name', reflect: false }) - public get containerName(): string | undefined { - return this._propertyStructureHelper.getContainerName(); - } - public set containerName(value: string | undefined) { - this._propertyStructureHelper.setContainerName(value); - } - - @property({ type: String, attribute: 'container-type', reflect: false }) - public get containerType(): UmbPropertyContainerTypes | undefined { - return this._propertyStructureHelper.getContainerType(); - } - public set containerType(value: UmbPropertyContainerTypes | undefined) { - this._propertyStructureHelper.setContainerType(value); - } - - _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); - - @state() - _propertyStructure: Array = []; - - @state() - _ownerMediaTypes?: UmbMediaTypeDetailModel[]; - - @state() - protected _modalRouteNewProperty?: string; - - @state() - _sortModeActive?: boolean; - - constructor() { - super(); - - this.consumeContext(UMB_WORKSPACE_CONTEXT, async (workspaceContext) => { - this._propertyStructureHelper.setStructureManager((workspaceContext as UmbMediaTypeWorkspaceContext).structure); - this.observe( - (workspaceContext as UmbMediaTypeWorkspaceContext).isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - this.#setModel(isSorting); - }, - '_observeIsSorting', - ); - - const mediaTypesObservable = await this._propertyStructureHelper.contentTypes(); - if (!mediaTypesObservable) return; - this.observe( - mediaTypesObservable, - (medias) => { - this._ownerMediaTypes = medias; - }, - 'observeOwnerMediaTypes', - ); - }); - this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { - this._propertyStructure = propertyStructure; - }); - - // Note: Route for adding a new property - new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addAdditionalPath('new-property') - .onSetup(async () => { - const mediaTypeId = this._ownerMediaTypes?.find((types) => - types.containers?.find((containers) => containers.id === this.containerId), - )?.unique; - if (mediaTypeId === undefined) return false; - const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); - if (propertyData === undefined) return false; - return { data: { documentTypeId: mediaTypeId }, value: propertyData }; //TODO: Should we have a separate modal for mediaTypes? - }) - .onSubmit((value) => { - if (!value.dataType) { - throw new Error('No data type selected'); - } - this.#addProperty(value as UmbPropertyTypeModel); - }) - .observeRouteBuilder((routeBuilder) => { - this._modalRouteNewProperty = routeBuilder(null); - }); - } - - #setModel(isSorting?: boolean) { - if (isSorting) { - this.#propertySorter.setModel(this._propertyStructure); - } else { - this.#propertySorter.setModel([]); - } - } - - async #addProperty(propertyData: UmbPropertyTypeModel) { - const propertyPlaceholder = await this._propertyStructureHelper.addProperty(this._containerId); - if (!propertyPlaceholder) return; - - this._propertyStructureHelper.partialUpdateProperty(propertyPlaceholder.id, propertyData); - } - - render() { - return html`
    - ${repeat( - this._propertyStructure, - (property) => property.id ?? '' + property.container?.id ?? '' + property.sortOrder ?? '', - (property) => { - // Note: This piece might be moved into the property component - const inheritedFromMedia = this._ownerMediaTypes?.find((types) => - types.containers?.find((containers) => containers.id === property.container?.id), - ); - - return html` { - this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); - }} - @property-delete=${() => { - this._propertyStructureHelper.removeProperty(property.id!); - }}> - `; - }, - )} -
    - ${!this._sortModeActive - ? html` - Add property - ` - : ''} `; - } - - static styles = [ - UmbTextStyles, - css` - #add { - width: 100%; - } - `, - ]; -} - -export default UmbMediaTypeWorkspaceViewEditPropertiesElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-media-type-workspace-view-edit-properties': UmbMediaTypeWorkspaceViewEditPropertiesElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts deleted file mode 100644 index 3313594dca..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-property.element.ts +++ /dev/null @@ -1,469 +0,0 @@ -import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; -import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; -import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; -import { - UMB_WORKSPACE_MODAL, - UmbModalRouteRegistrationController, - umbConfirmModal, -} from '@umbraco-cms/backoffice/modal'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { generateAlias } from '@umbraco-cms/backoffice/utils'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { type UmbPropertyTypeModel, UMB_PROPERTY_SETTINGS_MODAL } from '@umbraco-cms/backoffice/content-type'; - -/** - * @element umb-media-type-workspace-view-edit-property - * @description - Element for displaying a property in an workspace. - * @slot editor - Slot for rendering the Property Editor - */ -@customElement('umb-media-type-workspace-view-edit-property') -export class UmbMediaTypeWorkspacePropertyElement extends UmbLitElement { - private _property?: UmbPropertyTypeModel | undefined; - /** - * Property, the data object for the property. - * @type {UmbPropertyTypeModel} - * @attr - * @default undefined - */ - @property({ type: Object }) - public get property(): UmbPropertyTypeModel | undefined { - return this._property; - } - public set property(value: UmbPropertyTypeModel | undefined) { - const oldValue = this._property; - this._property = value; - this.#modalRegistration.setUniquePathValue('propertyId', value?.id?.toString()); - this.setDataType(this._property?.dataType.unique); - this.requestUpdate('property', oldValue); - } - - /** - * Inherited, Determines if the property is part of the main media type thats being edited. - * If true, then the property is inherited from another media type, not a part of the main media type. - * @type {boolean} - * @attr - * @default undefined - */ - @property({ type: Boolean }) - public inherited?: boolean; - - @property({ type: Boolean, reflect: true, attribute: 'sort-mode-active' }) - public sortModeActive = false; - - #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); - - #modalRegistration; - - @state() - protected _modalRoute?: string; - - @state() - protected _editMediaTypePath?: string; - - @property() - public get modalRoute() { - return this._modalRoute; - } - - @property({ type: String, attribute: 'owner-media-type-id' }) - public ownerMediaTypeId?: string; - - @property({ type: String, attribute: 'owner-media-type-name' }) - public ownerMediaTypeName?: string; - - @state() - private _dataTypeName?: string; - - async setDataType(dataTypeId: string | undefined) { - if (!dataTypeId) return; - this.#dataTypeDetailRepository.requestByUnique(dataTypeId).then((x) => (this._dataTypeName = x?.data?.name)); - } - - constructor() { - super(); - this.#modalRegistration = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addUniquePaths(['propertyId']) - .onSetup(() => { - const mediaTypeId = this.ownerMediaTypeId; - if (mediaTypeId === undefined) return false; - const propertyData = this.property; - if (propertyData === undefined) return false; - return { data: { documentTypeId: mediaTypeId }, value: propertyData }; //TODO: Should we have a separate modal for mediaTypes? - }) - .onSubmit((result) => { - this._partialUpdate(result as UmbPropertyTypeModel); - }) - .observeRouteBuilder((routeBuilder) => { - this._modalRoute = routeBuilder(null); - }); - - new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath('media-type') - .onSetup(() => { - return { data: { entityType: 'media-type', preset: {} } }; - }) - .observeRouteBuilder((routeBuilder) => { - this._editMediaTypePath = routeBuilder({}); - }); - } - - _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); - } - - _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { - const partialObject = {} as any; - partialObject[propertyName] = value; - - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); - } - - @state() - private _aliasLocked = true; - - #onToggleAliasLock() { - this._aliasLocked = !this._aliasLocked; - } - - async #requestRemove(e: Event) { - e.preventDefault(); - e.stopImmediatePropagation(); - if (!this.property || !this.property.id) return; - - const modalData: UmbConfirmModalData = { - headline: `${this.localize.term('actions_delete')} property`, - content: html` - Are you sure you want to delete the property ${this.property.name || this.property.id} - -
    `, - confirmLabel: this.localize.term('actions_delete'), - color: 'danger', - }; - - await umbConfirmModal(this, modalData); - - this.dispatchEvent(new CustomEvent('property-delete')); - } - - #onNameChange(event: UUIInputEvent) { - if (event instanceof UUIInputEvent) { - const target = event.composedPath()[0] as UUIInputElement; - - if (typeof target?.value === 'string') { - const oldName = this.property?.name ?? ''; - const oldAlias = this.property?.alias ?? ''; - const newName = event.target.value.toString(); - if (this._aliasLocked) { - const expectedOldAlias = generateAlias(oldName ?? ''); - // Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.) - if (expectedOldAlias === oldAlias) { - this._singleValueUpdate('alias', generateAlias(newName ?? '')); - } - } - this._singleValueUpdate('name', newName); - } - } - } - - renderSortableProperty() { - if (!this.property) return; - return html` -
    - - ${this.property.name} (${this.property.alias}) -
    - - `; - } - - renderEditableProperty() { - if (!this.property) return; - - if (this.sortModeActive) { - return this.renderSortableProperty(); - } else { - return html` - - - ${this.renderPropertyTags()} - - - - - - - `; - } - } - - renderInheritedProperty() { - if (!this.property) return; - - if (this.sortModeActive) { - return this.renderSortableProperty(); - } else { - return html` - -
    - ${this.renderPropertyTags()} - - - ${this.localize.term('contentTypeEditor_inheritedFrom')} - - ${this.ownerMediaTypeName ?? '??'} - - - -
    - `; - } - } - - renderPropertyAlias() { - return this.property - ? html` { - if (e.target) this._singleValueUpdate('alias', (e.target as HTMLInputElement).value); - }}> - - -
    ''} id="alias-lock" slot="prepend"> - -
    -
    ` - : ''; - } - - renderPropertyTags() { - return this.property - ? html`
    - ${this.property.dataType?.unique ? html`${this._dataTypeName}` : nothing} - ${this.property.variesByCulture - ? html` - ${this.localize.term('contentTypeEditor_cultureVariantLabel')} - ` - : nothing} - ${this.property.appearance?.labelOnTop == true - ? html` - ${this.localize.term('contentTypeEditor_displaySettingsLabelOnTop')} - ` - : nothing} - ${this.property.validation.mandatory === true - ? html` - * ${this.localize.term('general_mandatory')} - ` - : nothing} -
    ` - : nothing; - } - - render() { - // TODO: Only show alias on label if user has access to MediaType within settings: - return this.inherited ? this.renderInheritedProperty() : this.renderEditableProperty(); - } - - static styles = [ - UmbTextStyles, - css` - :host(:not([sort-mode-active])) { - display: grid; - grid-template-columns: 200px auto; - column-gap: var(--uui-size-layout-2); - border-bottom: 1px solid var(--uui-color-divider); - padding: var(--uui-size-layout-1) 0; - container-type: inline-size; - } - - :host > div { - grid-column: span 2; - } - - @container (width > 600px) { - :host(:not([orientation='vertical'])) > div { - grid-column: span 1; - } - } - - :host(:first-of-type) { - padding-top: 0; - } - :host(:last-of-type) { - border-bottom: none; - } - - :host([sort-mode-active]) { - position: relative; - display: flex; - padding: 0; - margin-bottom: var(--uui-size-3); - } - - :host([sort-mode-active]:last-of-type) { - margin-bottom: 0; - } - - :host([sort-mode-active]:not([inherited])) { - cursor: grab; - } - - :host([sort-mode-active]) .sortable { - flex: 1; - display: flex; - background-color: var(--uui-color-divider); - align-items: center; - padding: 0 var(--uui-size-3); - gap: var(--uui-size-3); - } - - :host([sort-mode-active]) uui-input { - max-width: 75px; - } - - /* Placeholder style, used when property is being dragged.*/ - :host(.--umb-sorter-placeholder) > * { - visibility: hidden; - } - - :host(.--umb-sorter-placeholder)::after { - content: ''; - inset: 0; - position: absolute; - border: 1px dashed var(--uui-color-divider-emphasis); - border-radius: var(--uui-border-radius); - } - - p { - margin-bottom: 0; - } - - #header { - position: sticky; - top: var(--uui-size-space-4); - height: min-content; - z-index: 2; - } - - #editor { - position: relative; - background-color: var(--uui-color-background); - } - #alias-input, - #label-input, - #description-input { - width: 100%; - } - - #alias-input { - border-color: transparent; - background: var(--uui-color-surface); - } - - #label-input { - font-weight: bold; /* TODO: UUI Input does not support bold text yet */ - --uui-input-border-color: transparent; - } - #label-input input { - font-weight: bold; - --uui-input-border-color: transparent; - } - - #alias-lock { - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - } - #alias-lock uui-icon { - margin-bottom: 2px; - /* margin: 0; */ - } - #description-input { - --uui-textarea-border-color: transparent; - font-weight: 0.5rem; /* TODO: Cant change font size of UUI textarea yet */ - } - - .types > div uui-icon, - .inherited uui-icon { - vertical-align: sub; - } - - .inherited { - position: absolute; - top: var(--uui-size-space-2); - right: var(--uui-size-space-2); - } - - .types { - position: absolute; - top: var(--uui-size-space-2); - left: var(--uui-size-space-2); - display: flex; - gap: var(--uui-size-space-2); - } - - #editor uui-action-bar { - position: absolute; - top: var(--uui-size-space-2); - right: var(--uui-size-space-2); - display: none; - } - #editor:hover uui-action-bar, - #editor:focus uui-action-bar { - display: block; - } - - a { - color: inherit; - } - `, - ]; -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-media-type-workspace-view-edit-property': UmbMediaTypeWorkspacePropertyElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts deleted file mode 100644 index d3dd37bf51..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit-tab.element.ts +++ /dev/null @@ -1,282 +0,0 @@ -import type { UmbMediaTypeWorkspaceContext } from '../../media-type-workspace.context.js'; -import type { UmbMediaTypeDetailModel } from '../../../types.js'; -import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -import './media-type-workspace-view-edit-properties.element.js'; - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.getAttribute('data-umb-group-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.id; - }, - identifier: 'content-type-group-sorter', - itemSelector: '[data-umb-group-id]', - disabledItemSelector: '[inherited]', - containerSelector: '#group-list', -}; - -@customElement('umb-media-type-workspace-view-edit-tab') -export class UmbMediaTypeWorkspaceViewEditTabElement extends UmbLitElement { - public sorter?: UmbSorterController; - - config: UmbSorterConfig = { - ...SORTER_CONFIG, - // TODO: Missing handlers to work properly: performItemMove and performItemRemove - performItemInsert: async (args) => { - if (!this._groups) return false; - const oldIndex = this._groups.findIndex((group) => group.id! === args.item.id); - if (args.newIndex === oldIndex) return true; - - let sortOrder = 0; - //TODO the sortOrder set is not correct - if (this._groups.length > 0) { - if (args.newIndex === 0) { - sortOrder = (this._groups[0].sortOrder ?? 0) - 1; - } else { - sortOrder = (this._groups[Math.min(args.newIndex, this._groups.length - 1)].sortOrder ?? 0) + 1; - } - - if (sortOrder !== args.item.sortOrder) { - await this._groupStructureHelper.partialUpdateContainer(args.item.id!, { sortOrder }); - } - } - - return true; - }, - }; - - private _ownerTabId?: string | null; - - // TODO: get rid of this: - @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; - } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - const oldValue = this._ownerTabId; - this._ownerTabId = value; - this._groupStructureHelper.setParentId(value); - this.requestUpdate('ownerTabId', oldValue); - } - - private _tabName?: string | undefined; - - @property({ type: String }) - public get tabName(): string | undefined { - return this._groupStructureHelper.getName(); - } - public set tabName(value: string | undefined) { - if (value === this._tabName) return; - const oldValue = this._tabName; - this._tabName = value; - this._groupStructureHelper.setName(value); - this.requestUpdate('tabName', oldValue); - } - - @state() - private _noTabName?: boolean; - - @property({ type: Boolean }) - public get noTabName(): boolean { - return this._groupStructureHelper.getIsRoot(); - } - public set noTabName(value: boolean) { - this._noTabName = value; - this._groupStructureHelper.setIsRoot(value); - } - - _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); - - @state() - _groups: Array = []; - - @state() - _hasProperties = false; - - @state() - _sortModeActive?: boolean; - - constructor() { - super(); - - this._groupStructureHelper.setParentType('Tab'); - - this.sorter = new UmbSorterController(this, this.config); - - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { - this._groupStructureHelper.setStructureManager((context as UmbMediaTypeWorkspaceContext).structure); - this.observe( - (context as UmbMediaTypeWorkspaceContext).isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - if (isSorting) { - this.sorter?.setModel(this._groups); - } else { - this.sorter?.setModel([]); - } - }, - '_observeIsSorting', - ); - }); - this.observe(this._groupStructureHelper.containers, (groups) => { - this._groups = groups; - this.requestUpdate('_groups'); - }); - this.observe(this._groupStructureHelper.hasProperties, (hasProperties) => { - this._hasProperties = hasProperties; - this.requestUpdate('_hasProperties'); - }); - } - - #onAddGroup = () => { - // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? - this._groupStructureHelper.addContainer(this._ownerTabId); - }; - - render() { - return html` - ${!this._noTabName - ? html` - - - - ` - : ''} -
    - ${repeat( - this._groups, - (group) => group.id ?? '' + group.name, - (group) => html` - - ${ - this._groupStructureHelper.isOwnerChildContainer(group.id!) - ? html` -
    -
    - ${this._sortModeActive ? html`` : ''} - - { - throw new Error('Not implemented'); - /*const newName = (e.target as HTMLInputElement).value; - this._groupStructureHelper.updateContainerName( - group.id!, - group.parent?.id ?? null, - newName, - ); - */ - }}> - -
    - ${this._sortModeActive - ? html`` - : ''} -
    - ` - : html`
    -
    ${group.name ?? ''} (Inherited)
    - ${!this._sortModeActive - ? html`` - : ''} -
    ` - } -
    - - `, - )} -
    - ${!this._sortModeActive - ? html` - ` - : ''} - `; - } - - static styles = [ - UmbTextStyles, - css` - #add { - width: 100%; - } - - #add:first-child { - margin-top: var(--uui-size-layout-1); - } - uui-box { - margin-bottom: var(--uui-size-layout-1); - } - - [data-umb-group-id] { - display: block; - position: relative; - } - - div[slot='header'] { - display: flex; - align-items: center; - justify-content: space-between; - } - - div[slot='header'] > div { - display: flex; - align-items: center; - gap: var(--uui-size-3); - } - - uui-input[type='number'] { - max-width: 75px; - } - - .sorting { - cursor: grab; - } - - .--umb-sorter-placeholder > uui-box { - visibility: hidden; - } - - .--umb-sorter-placeholder::after { - content: ''; - inset: 0; - position: absolute; - border-radius: var(--uui-border-radius); - border: 1px dashed var(--uui-color-divider-emphasis); - } - `, - ]; -} - -export default UmbMediaTypeWorkspaceViewEditTabElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-media-type-workspace-view-edit-tab': UmbMediaTypeWorkspaceViewEditTabElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit.element.ts deleted file mode 100644 index 8a613e70b4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/design/media-type-workspace-view-edit.element.ts +++ /dev/null @@ -1,509 +0,0 @@ -import type { UmbMediaTypeWorkspaceContext } from '../../media-type-workspace.context.js'; -import type { UmbMediaTypeDetailModel } from '../../../types.js'; -import type { UmbMediaTypeWorkspaceViewEditTabElement } from './media-type-workspace-view-edit-tab.element.js'; -import { css, html, customElement, state, repeat, nothing, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; -import { encodeFolderName } from '@umbraco-cms/backoffice/router'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/external/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import type { UmbRoute, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; -import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; -import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbSorterConfig } from '@umbraco-cms/backoffice/sorter'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -const SORTER_CONFIG: UmbSorterConfig = { - getUniqueOfElement: (element) => { - return element.getAttribute('data-umb-tabs-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.id; - }, - identifier: 'content-type-tabs-sorter', - itemSelector: '[data-umb-tabs-id]', - containerSelector: '#tabs-group', - disabledItemSelector: '[inherited]', - resolvePlacement: () => { - return false; - }, -}; - -@customElement('umb-media-type-workspace-view-edit') -export class UmbMediaTypeWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { - public sorter?: UmbSorterController; - - config: UmbSorterConfig = { - ...SORTER_CONFIG, - // TODO: Missing handlers for these: performItemRemove, performItemMove - performItemInsert: async (args) => { - if (!this._tabs) return false; - const oldIndex = this._tabs.findIndex((tab) => tab.id! === args.item.id); - if (args.newIndex === oldIndex) return true; - - let sortOrder = 0; - //TODO the sortOrder set is not correct - if (this._tabs.length > 0) { - if (args.newIndex === 0) { - sortOrder = (this._tabs[0].sortOrder ?? 0) - 1; - } else { - sortOrder = (this._tabs[Math.min(args.newIndex, this._tabs.length - 1)].sortOrder ?? 0) + 1; - } - - if (sortOrder !== args.item.sortOrder) { - await this._tabsStructureHelper.partialUpdateContainer(args.item.id!, { sortOrder }); - } - } - - return true; - }, - }; - - //private _hasRootProperties = false; - - @state() - private _hasRootGroups = false; - - @state() - private _routes: UmbRoute[] = []; - - @state() - _tabs?: Array; - - @state() - private _routerPath?: string; - - @state() - private _activePath = ''; - - @state() - private sortModeActive?: boolean; - - @state() - private _buttonDisabled: boolean = false; - - private _workspaceContext?: UmbMediaTypeWorkspaceContext; - - private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); - - constructor() { - super(); - this.sorter = new UmbSorterController(this, this.config); - - //TODO: We need to differentiate between local and composition tabs (and hybrids) - - this._tabsStructureHelper.setIsRoot(true); - this._tabsStructureHelper.setContainerChildType('Tab'); - this.observe(this._tabsStructureHelper.containers, (tabs) => { - this._tabs = tabs; - this._createRoutes(); - }); - - // _hasRootProperties can be gotten via _tabsStructureHelper.hasProperties. But we do not support root properties currently. - - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._workspaceContext = workspaceContext as UmbMediaTypeWorkspaceContext; - this._tabsStructureHelper.setStructureManager((workspaceContext as UmbMediaTypeWorkspaceContext).structure); - this.observe( - this._workspaceContext.isSorting, - (isSorting) => (this.sortModeActive = isSorting), - '_observeIsSorting', - ); - this._observeRootGroups(); - }); - } - - private _observeRootGroups() { - if (!this._workspaceContext) return; - - this.observe( - this._workspaceContext.structure.hasRootContainers('Group'), - (hasRootGroups) => { - this._hasRootGroups = hasRootGroups; - this._createRoutes(); - }, - '_observeGroups', - ); - } - - #changeMode() { - this._workspaceContext?.setIsSorting(!this.sortModeActive); - - if (this.sortModeActive && this._tabs) { - this.sorter?.setModel(this._tabs); - } else { - this.sorter?.setModel([]); - } - } - - private _createRoutes() { - if (!this._workspaceContext || !this._tabs) return; - const routes: UmbRoute[] = []; - - if (this._tabs.length > 0) { - this._tabs?.forEach((tab) => { - const tabName = tab.name ?? ''; - routes.push({ - path: `tab/${encodeFolderName(tabName).toString()}`, - component: () => import('./media-type-workspace-view-edit-tab.element.js'), - setup: (component) => { - (component as UmbMediaTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbMediaTypeWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; - }, - }); - }); - } - - routes.push({ - path: 'root', - component: () => import('./media-type-workspace-view-edit-tab.element.js'), - setup: (component) => { - (component as UmbMediaTypeWorkspaceViewEditTabElement).noTabName = true; - (component as UmbMediaTypeWorkspaceViewEditTabElement).ownerTabId = null; - }, - }); - - if (this._hasRootGroups) { - routes.push({ - path: '', - redirectTo: 'root', - }); - } else if (routes.length !== 0) { - routes.push({ - path: '', - redirectTo: routes[0]?.path, - }); - } - - this._routes = routes; - } - - async #requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) { - const modalData: UmbConfirmModalData = { - headline: 'Delete tab', - content: html` - Are you sure you want to delete the tab ${tab?.name ?? tab?.id} - -
    - - This will delete all items that doesn't belong to a composition. - -
    `, - confirmLabel: this.localize.term('actions_delete'), - color: 'danger', - }; - - // TODO: If this tab is composed of other tabs, then notify that it will only delete the local tab. - await umbConfirmModal(this, modalData); - - this.#remove(tab?.id); - } - #remove(tabId?: string) { - if (!tabId) return; - this._workspaceContext?.structure.removeContainer(null, tabId); - this._tabsStructureHelper?.isOwnerChildContainer(tabId) - ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) - : ''; - } - async #addTab() { - if ( - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement) && - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement).value === '' - ) { - this.#focusInput(); - return; - } - - const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab'); - if (tab) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); - window.history.replaceState(null, '', path); - this.#focusInput(); - } - } - - async #focusInput() { - setTimeout(() => { - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement | undefined)?.focus(); - }, 100); - } - - async #tabNameChanged(event: InputEvent, tab: PropertyTypeContainerModelBaseModel) { - if (this._buttonDisabled) this._buttonDisabled = !this._buttonDisabled; - let newName = (event.target as HTMLInputElement).value; - - if (newName === '') { - newName = 'Unnamed'; - (event.target as HTMLInputElement).value = 'Unnamed'; - } - - const changedName = this._workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( - newName, - 'Tab', - tab.id, - ); - - // Check if it collides with another tab name of this same media-type, if so adjust name: - if (changedName) { - newName = changedName; - (event.target as HTMLInputElement).value = newName; - } - - this._tabsStructureHelper.partialUpdateContainer(tab.id!, { - name: newName, - }); - - // Update the current URL, so we are still on this specific tab: - window.history.replaceState(null, '', this._routerPath + '/tab/' + encodeFolderName(newName)); - } - - render() { - return html` - - - { - this._routerPath = event.target.absoluteRouterPath; - }} - @change=${(event: UmbRouterSlotChangeEvent) => { - this._activePath = event.target.absoluteActiveViewPath || ''; - }}> - - - `; - } - - renderAddButton() { - if (this.sortModeActive) return; - return html` - - Add tab - `; - } - - renderActions() { - const sortButtonText = this.sortModeActive - ? this.localize.term('general_reorderDone') - : this.localize.term('general_reorder'); - - return html`
    - - - ${this.localize.term('contentTypeEditor_compositions')} - - - - ${sortButtonText} - -
    `; - } - - renderTabsNavigation() { - if (!this._tabs) return; - - return html`
    - - ${this.renderRootTab()} - ${repeat( - this._tabs, - (tab) => tab.id! + tab.name, - (tab) => this.renderTab(tab), - )} - -
    `; - } - - renderRootTab() { - const rootTabPath = this._routerPath + '/root'; - const rootTabActive = rootTabPath === this._activePath; - return html` - ${this.localize.term('general_content')} - `; - } - - renderTab(tab: PropertyTypeContainerModelBaseModel) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); - const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); - - return html` - ${this.renderTabInner(tab, tabActive, tabInherited)} - `; - } - - renderTabInner(tab: PropertyTypeContainerModelBaseModel, tabActive: boolean, tabInherited: boolean) { - if (this.sortModeActive) { - return html`
    - ${tabInherited - ? html`${tab.name!}` - : html` ${tab.name!} - this.#changeOrderNumber(tab, e)}>`} -
    `; - } - - if (tabActive && !tabInherited) { - return html`
    - this.#tabNameChanged(e, tab)} - @blur=${(e: InputEvent) => this.#tabNameChanged(e, tab)} - @input=${() => (this._buttonDisabled = true)} - @focus=${(e: UUIInputEvent) => (e.target.value ? nothing : (this._buttonDisabled = true))}> - ${this.renderDeleteFor(tab)} - -
    `; - } - - if (tabInherited) { - return html`
    ${tab.name!}
    `; - } else { - return html`
    ${tab.name!} ${this.renderDeleteFor(tab)}
    `; - } - } - - #changeOrderNumber(tab: PropertyTypeContainerModelBaseModel, e: UUIInputEvent) { - if (!e.target.value || !tab.id) return; - const sortOrder = Number(e.target.value); - this._tabsStructureHelper.partialUpdateContainer(tab.id, { sortOrder }); - } - - renderDeleteFor(tab: PropertyTypeContainerModelBaseModel) { - return html` this.#requestRemoveTab(tab)} - compact> - - `; - } - - static styles = [ - UmbTextStyles, - css` - #buttons-wrapper { - flex: 1; - display: flex; - align-items: center; - justify-content: space-between; - align-items: stretch; - } - - :host { - position: relative; - display: flex; - flex-direction: column; - height: 100%; - --uui-tab-background: var(--uui-color-surface); - } - - /* TODO: This should be replaced with a general workspace bar — naming is hard */ - #header { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: nowrap; - } - - .flex { - display: flex; - } - uui-tab-group { - flex-wrap: nowrap; - } - - .content-tab-is-empty { - align-self: center; - border-radius: 3px; - --uui-tab-text: var(--uui-color-text-alt); - border: dashed 1px var(--uui-color-border-emphasis); - } - - uui-tab { - position: relative; - border-left: 1px hidden transparent; - border-right: 1px solid var(--uui-color-border); - } - - .no-edit uui-input { - pointer-events: auto; - } - - .no-edit { - pointer-events: none; - display: inline-flex; - padding-left: var(--uui-size-space-3); - border: 1px solid transparent; - align-items: center; - gap: var(--uui-size-space-3); - } - - .trash { - opacity: 1; - transition: opacity 120ms; - } - - uui-tab:not(:hover, :focus) .trash { - opacity: 0; - transition: opacity 120ms; - } - - uui-input:not(:focus, :hover) { - border: 1px solid transparent; - } - - .inherited { - vertical-align: sub; - } - - .--umb-sorter-placeholder > * { - visibility: hidden; - } - - .--umb-sorter-placeholder::after { - content: ''; - position: absolute; - inset: 2px; - border: 1px dashed var(--uui-color-divider-emphasis); - } - `, - ]; -} - -export default UmbMediaTypeWorkspaceViewEditElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-media-type-workspace-view-edit': UmbMediaTypeWorkspaceViewEditElement; - } -} From 7daeeda42d254740bd329e3c8ce5584c2ebf3ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 13:36:45 +0100 Subject: [PATCH 154/285] rename to content type + use workspace entity type --- .../property-settings-modal.element.ts | 20 ++++---- .../property-settings-modal.token.ts | 2 +- ...-workspace-view-edit-properties.element.ts | 51 +++++++++++++------ ...pe-workspace-view-edit-property.element.ts | 42 +++++---------- ...nt-type-workspace-view-edit-tab.element.ts | 2 +- ...ontent-type-workspace-view-edit.element.ts | 4 +- ...-workspace-view-edit-properties.element.ts | 2 +- ...pe-workspace-view-edit-property.element.ts | 2 +- 8 files changed, 65 insertions(+), 60 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts index 1142f3cb8b..f9ab69f77e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts @@ -3,13 +3,13 @@ import { UmbPropertyTypeWorkspaceContext, } from './property-settings-modal.context.js'; import type { UmbPropertySettingsModalData, UmbPropertySettingsModalValue } from './property-settings-modal.token.js'; -import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/document-type'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIBooleanInputEvent, UUIInputEvent, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; -import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; +import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; // TODO: Could base take a token to get its types?. @customElement('umb-property-settings-modal') export class UmbPropertySettingsModalElement extends UmbModalBaseElement< @@ -52,19 +52,19 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< #context = new UmbPropertyTypeWorkspaceContext(this); @state() - private _documentVariesByCulture?: boolean; + private _contentTypeVariesByCulture?: boolean; @state() - private _documentVariesBySegment?: boolean; + private _contentTypeVariesBySegment?: boolean; connectedCallback(): void { super.connectedCallback(); - this.consumeContext(UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT, (instance) => { - if (!this.data?.documentTypeId) return; + this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (instance) => { + if (!this.data?.contentTypeId) return; - this.observe(instance.variesByCulture, (variesByCulture) => (this._documentVariesByCulture = variesByCulture)); - this.observe(instance.variesBySegment, (variesBySegment) => (this._documentVariesBySegment = variesBySegment)); + this.observe(instance.variesByCulture, (variesByCulture) => (this._contentTypeVariesByCulture = variesByCulture)); + this.observe(instance.variesBySegment, (variesBySegment) => (this._contentTypeVariesBySegment = variesBySegment)); }).skipHost(); this._originalPropertyData = this.value; @@ -362,10 +362,10 @@ export class UmbPropertySettingsModalElement extends UmbModalBaseElement< } #renderVariationControls() { - return this._documentVariesByCulture || this._documentVariesBySegment + return this._contentTypeVariesByCulture || this._contentTypeVariesBySegment ? html`
    Variation - ${this._documentVariesByCulture ? this.#renderVaryByCulture() : ''} + ${this._contentTypeVariesByCulture ? this.#renderVaryByCulture() : ''}

    ` : ''; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts index b4d609c1d6..722c92d0eb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.token.ts @@ -2,7 +2,7 @@ import { UmbModalToken } from '../../../modal/token/modal-token.js'; import type { UmbPropertyTypeModel, UmbPropertyTypeScaffoldModel } from '@umbraco-cms/backoffice/content-type'; export type UmbPropertySettingsModalData = { - documentTypeId: string; + contentTypeId: string; }; export type UmbPropertySettingsModalValue = UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts index 047ead9097..167eeaa24b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts @@ -14,7 +14,11 @@ import { UMB_PROPERTY_SETTINGS_MODAL, } from '@umbraco-cms/backoffice/content-type'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { type UmbModalRouteBuilder, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { + type UmbModalRouteBuilder, + UmbModalRouteRegistrationController, + UMB_WORKSPACE_MODAL, +} from '@umbraco-cms/backoffice/modal'; const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { @@ -23,7 +27,7 @@ const SORTER_CONFIG: UmbSorterConfig { return modelEntry.id; }, - identifier: 'document-type-property-sorter', + identifier: 'content-type-property-sorter', itemSelector: 'umb-content-type-workspace-view-edit-property', //disabledItemSelector: '[inherited]', //TODO: Set the property list (sorter wrapper) to inherited, if its inherited @@ -115,20 +119,24 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } #addPropertyModal: UmbModalRouteRegistrationController; + #workspaceModal?: UmbModalRouteRegistrationController; _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); @state() - _propertyStructure: Array = []; + private _propertyStructure: Array = []; @state() - _ownerDocumentType?: UmbContentTypeModel; + private _ownerContentType?: UmbContentTypeModel; @state() - protected _modalRouteBuilderNewProperty?: UmbModalRouteBuilder; + private _modalRouteBuilderNewProperty?: UmbModalRouteBuilder; @state() - _sortModeActive?: boolean; + private _editContentTypePath?: string; + + @state() + private _sortModeActive?: boolean; constructor() { super(); @@ -138,6 +146,18 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, async (workspaceContext) => { this._propertyStructureHelper.setStructureManager(workspaceContext.structure); + const entityType = workspaceContext.getEntityType(); + + this.#workspaceModal?.destroy(); + this.#workspaceModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) + .addAdditionalPath(entityType) + .onSetup(async () => { + return { data: { entityType: entityType, preset: {} } }; + }) + .observeRouteBuilder((routeBuilder) => { + this._editContentTypePath = routeBuilder({}); + }); + this.observe( workspaceContext.isSorting, (isSorting) => { @@ -153,10 +173,10 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem const docTypeObservable = workspaceContext.structure.ownerContentType; this.observe( docTypeObservable, - (document) => { - this._ownerDocumentType = document; + (contentType) => { + this._ownerContentType = contentType; }, - 'observeOwnerDocumentType', + 'observeOwnerContentType', ); }); this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { @@ -169,7 +189,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem .addUniquePaths(['container-id']) .addAdditionalPath('add-property/:sortOrder') .onSetup(async (params) => { - if (!this._ownerDocumentType || !this._containerId) return false; + if (!this._ownerContentType || !this._containerId) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); if (propertyData === undefined) return false; @@ -181,10 +201,10 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } propertyData.sortOrder = sortOrderInt + 1; } - return { data: { documentTypeId: this._ownerDocumentType.unique }, value: propertyData }; + return { data: { contentTypeId: this._ownerContentType.unique }, value: propertyData }; }) .onSubmit(async (value) => { - if (!this._ownerDocumentType) return false; + if (!this._ownerContentType) return false; // TODO: The model requires a data-type to be set, we cheat currently. But this should be re-though when we implement validation(As we most likely will have to com up with partial models for the runtime model.) [NL] this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); return true; @@ -195,7 +215,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } render() { - return this._ownerDocumentType + return this._ownerContentType ? html`
    ${repeat( @@ -205,8 +225,9 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem return html` | undefined) { @@ -57,7 +52,6 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { this._property = value; this.#checkInherited(); this.#settingsModal.setUniquePathValue('propertyId', value?.id?.toString()); - //this.#workspaceModal.setUniquePathValue('propertyId', value?.id?.toString()); this.setDataType(this._property?.dataType?.unique); this.requestUpdate('property', oldValue); } @@ -76,18 +70,18 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { @property({ type: Boolean, reflect: true, attribute: 'sort-mode-active' }) public sortModeActive = false; - @property({ type: String, attribute: 'owner-document-type-id' }) - public ownerDocumentTypeId?: string; + @property({ type: String, attribute: 'owner-content-type-id' }) + public ownerContentTypeId?: string; - @property({ type: String, attribute: 'owner-document-type-name' }) - public ownerDocumentTypeName?: string; + @property({ type: String, attribute: 'owner-content-type-name' }) + public ownerContentTypeName?: string; + + @property({ type: String, attribute: 'edit-content-type-path' }) + public editContentTypePath?: string; @state() protected _modalRoute?: string; - @state() - protected _editDocumentTypePath?: string; - @state() private _dataTypeName?: string; @@ -99,11 +93,11 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { this.#settingsModal = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) .addUniquePaths(['propertyId']) .onSetup(() => { - const documentTypeId = this.ownerDocumentTypeId; - if (documentTypeId === undefined) return false; + const id = this.ownerContentTypeId; + if (id === undefined) return false; const propertyData = this.property; if (propertyData === undefined) return false; - return { data: { documentTypeId }, value: propertyData }; + return { data: { contentTypeId: id }, value: propertyData }; }) .onSubmit((result) => { this._partialUpdate(result as UmbPropertyTypeModel); @@ -111,16 +105,6 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { .observeRouteBuilder((routeBuilder) => { this._modalRoute = routeBuilder(null); }); - - this.#workspaceModal = new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - //.addUniquePaths(['propertyId']) - .addAdditionalPath('document-type') - .onSetup(() => { - return { data: { entityType: 'document-type', preset: {} } }; - }) - .observeRouteBuilder((routeBuilder) => { - this._editDocumentTypePath = routeBuilder({}); - }); } async #checkInherited() { @@ -224,8 +208,8 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { ${this.localize.term('contentTypeEditor_inheritedFrom')} - - ${this.ownerDocumentTypeName ?? '??'} + + ${this.ownerContentTypeName ?? '??'} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts index 9c7b4dccd7..16eb1ed8ea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts @@ -15,7 +15,7 @@ import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffi const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => element.group?.id, getUniqueOfModel: (modelEntry) => modelEntry.id, - identifier: 'document-type-container-sorter', + identifier: 'content-type-container-sorter', itemSelector: '.container-handle', containerSelector: '.container-list', }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts index 5111e75874..28e0526942 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts @@ -25,7 +25,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => element.getAttribute('data-umb-tabs-id'), getUniqueOfModel: (tab) => tab.id, - identifier: 'document-type-tabs-sorter', + identifier: 'content-type-tabs-sorter', itemSelector: 'uui-tab', containerSelector: 'uui-tab-group', disabledItemSelector: '#root-tab', @@ -285,7 +285,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem tab.id, ); - // Check if it collides with another tab name of this same document-type, if so adjust name: + // Check if it collides with another tab name of this same content-type, if so adjust name: if (changedName) { newName = changedName; (event.target as HTMLInputElement).value = newName; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts index 22a8782520..d5ca43230b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts @@ -169,7 +169,7 @@ export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitEleme if (memberTypeId === undefined) return false; const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); if (propertyData === undefined) return false; - return { data: { documentTypeId: memberTypeId }, value: propertyData }; + return { data: { contentTypeId: memberTypeId }, value: propertyData }; }) .onSubmit((value) => { if (!value.dataType) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts index a4545f4940..8ea9e5a1c6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts @@ -95,7 +95,7 @@ export class UmbMemberTypeWorkspacePropertyElement extends UmbLitElement { if (memberTypeId === undefined) return false; const propertyData = this.property; if (propertyData === undefined) return false; - return { data: { documentTypeId: memberTypeId }, value: propertyData }; + return { data: { contentTypeId: memberTypeId }, value: propertyData }; }) .onSubmit((result) => { if (!result.dataType) { From 117a9105114ce1bb9c3509070651557d712c41d8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:04:03 +0100 Subject: [PATCH 155/285] update styling on story --- .../modals/publish-modal/document-publish-modal.stories.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts index 31ad74e5e4..197e56fc6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -89,7 +89,7 @@ const meta: Meta = { data: modalData, value: modalValue, }, - decorators: [(Story) => html`
    ${Story()}
    `], + decorators: [(Story) => html`
    ${Story()}
    `], parameters: { layout: 'centered', docs: { From 4993164d33851cc1bfcab713ec854c2868ed9121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 14:13:33 +0100 Subject: [PATCH 156/285] renaming --- .../composition-picker-modal.token.ts | 1 + ...ontent-type-workspace-context.interface.ts | 6 +- ...ntent-type-design-editor-group.element.ts} | 10 +-- ...-type-design-editor-properties.element.ts} | 76 +++++++++--------- ...nt-type-design-editor-property.element.ts} | 8 +- ...content-type-design-editor-tab.element.ts} | 27 ++++--- .../content-type-design-editor.context.ts | 33 ++++++++ ... => content-type-design-editor.element.ts} | 77 ++++++++++--------- .../workspace/views/design/manifest.ts | 2 +- .../document-type-workspace.context.ts | 17 +--- .../workspace/media-type-workspace.context.ts | 13 +++- 11 files changed, 152 insertions(+), 118 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/{content-type-workspace-view-edit-group.element.ts => content-type-design-editor-group.element.ts} (95%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/{content-type-workspace-view-edit-properties.element.ts => content-type-design-editor-properties.element.ts} (83%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/{content-type-workspace-view-edit-property.element.ts => content-type-design-editor-property.element.ts} (98%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/{content-type-workspace-view-edit-tab.element.ts => content-type-design-editor-tab.element.ts} (89%) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.context.ts rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/{content-type-workspace-view-edit.element.ts => content-type-design-editor.element.ts} (89%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts index 7a006e6642..838de4d9fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/composition-picker/composition-picker-modal.token.ts @@ -1,5 +1,6 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +// TODO: Stop sending the initial selection as part of data [NL], it should just be in the value: export interface UmbCompositionPickerModalData { selection: Array; unique: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts index ab72d6db64..1dcc49ba71 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts @@ -15,15 +15,11 @@ export interface UmbContentTypeWorkspaceContext; readonly variesByCulture: Observable; readonly variesBySegment: Observable; - readonly isElement: Observable; + //readonly isElement: Observable; readonly allowedContentTypes: Observable; readonly compositions: Observable; readonly structure: UmbContentTypePropertyStructureManager; - readonly isSorting: Observable; - - getIsSorting(): boolean; - setIsSorting(isSorting: boolean): void; setAlias(alias: string): void; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-group.element.ts similarity index 95% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-group.element.ts index 99c64781e5..45da79d610 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-group.element.ts @@ -7,9 +7,9 @@ import type { UmbPropertyTypeContainerModel, } from '@umbraco-cms/backoffice/content-type'; -import './content-type-workspace-view-edit-properties.element.js'; +import './content-type-design-editor-properties.element.js'; -@customElement('umb-content-type-workspace-view-edit-group') +@customElement('umb-content-type-design-editor-group') export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { //private _ownerGroupId?: string | null; @@ -130,10 +130,10 @@ export class UmbContentTypeWorkspaceViewEditGroupElement extends UmbLitElement { ? html` ${this.#renderContainerHeader()} - + container-name=${this.group?.name ?? ''}> ` : ''; @@ -210,6 +210,6 @@ export default UmbContentTypeWorkspaceViewEditGroupElement; declare global { interface HTMLElementTagNameMap { - 'umb-content-type-workspace-view-edit-group': UmbContentTypeWorkspaceViewEditGroupElement; + 'umb-content-type-design-editor-group': UmbContentTypeWorkspaceViewEditGroupElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-properties.element.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-properties.element.ts index 167eeaa24b..a29c67f78e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-properties.element.ts @@ -1,6 +1,7 @@ -import './content-type-workspace-view-edit-property.element.js'; +import './content-type-design-editor-property.element.js'; import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; -import type { UmbContentTypeWorkspacePropertyElement } from './content-type-workspace-view-edit-property.element.js'; +import type { UmbContentTypeDesignEditorPropertyElement } from './content-type-design-editor-property.element.js'; +import { UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT } from './content-type-design-editor.context.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @@ -20,7 +21,7 @@ import { UMB_WORKSPACE_MODAL, } from '@umbraco-cms/backoffice/modal'; -const SORTER_CONFIG: UmbSorterConfig = { +const SORTER_CONFIG: UmbSorterConfig = { getUniqueOfElement: (element) => { return element.getAttribute('data-umb-property-id'); }, @@ -28,7 +29,7 @@ const SORTER_CONFIG: UmbSorterConfig(this, { +@customElement('umb-content-type-design-editor-properties') +export class UmbContentTypeDesignEditorPropertiesElement extends UmbLitElement { + #sorter = new UmbSorterController(this, { ...SORTER_CONFIG, onChange: ({ model }) => { this._propertyStructure = model; @@ -69,7 +70,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem } // increase the prevSortOrder and use it for the moved item, - this._propertyStructureHelper.partialUpdateProperty(item.id, { + this.#propertyStructureHelper.partialUpdateProperty(item.id, { sortOrder: ++prevSortOrder, }); @@ -79,7 +80,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem // As long as there is an item with the index & the sortOrder is less or equal to the prevSortOrder, we will update the sortOrder: while ((entry = model[i]) !== undefined && entry.sortOrder <= prevSortOrder) { // Increase the prevSortOrder and use it for the item: - this._propertyStructureHelper.partialUpdateProperty(entry.id, { + this.#propertyStructureHelper.partialUpdateProperty(entry.id, { sortOrder: ++prevSortOrder, }); @@ -104,24 +105,24 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem @property({ type: String, attribute: 'container-name', reflect: false }) public get containerName(): string | undefined { - return this._propertyStructureHelper.getContainerName(); + return this.#propertyStructureHelper.getContainerName(); } public set containerName(value: string | undefined) { - this._propertyStructureHelper.setContainerName(value); + this.#propertyStructureHelper.setContainerName(value); } @property({ type: String, attribute: 'container-type', reflect: false }) public get containerType(): UmbPropertyContainerTypes | undefined { - return this._propertyStructureHelper.getContainerType(); + return this.#propertyStructureHelper.getContainerType(); } public set containerType(value: UmbPropertyContainerTypes | undefined) { - this._propertyStructureHelper.setContainerType(value); + this.#propertyStructureHelper.setContainerType(value); } #addPropertyModal: UmbModalRouteRegistrationController; #workspaceModal?: UmbModalRouteRegistrationController; - _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); + #propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); @state() private _propertyStructure: Array = []; @@ -143,8 +144,23 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this.#sorter.disable(); + this.consumeContext(UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT, (context) => { + this.observe( + context.isSorting, + (isSorting) => { + this._sortModeActive = isSorting; + if (isSorting) { + this.#sorter.enable(); + } else { + this.#sorter.disable(); + } + }, + '_observeIsSorting', + ); + }); + this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, async (workspaceContext) => { - this._propertyStructureHelper.setStructureManager(workspaceContext.structure); + this.#propertyStructureHelper.setStructureManager(workspaceContext.structure); const entityType = workspaceContext.getEntityType(); @@ -158,18 +174,6 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem this._editContentTypePath = routeBuilder({}); }); - this.observe( - workspaceContext.isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - if (isSorting) { - this.#sorter.enable(); - } else { - this.#sorter.disable(); - } - }, - '_observeIsSorting', - ); const docTypeObservable = workspaceContext.structure.ownerContentType; this.observe( docTypeObservable, @@ -179,7 +183,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem 'observeOwnerContentType', ); }); - this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { + this.observe(this.#propertyStructureHelper.propertyStructure, (propertyStructure) => { this._propertyStructure = propertyStructure; this.#sorter.setModel(this._propertyStructure); }); @@ -191,7 +195,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem .onSetup(async (params) => { if (!this._ownerContentType || !this._containerId) return false; - const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); + const propertyData = await this.#propertyStructureHelper.createPropertyScaffold(this._containerId); if (propertyData === undefined) return false; if (params.sortOrder !== undefined) { let sortOrderInt = parseInt(params.sortOrder, 10); @@ -206,7 +210,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem .onSubmit(async (value) => { if (!this._ownerContentType) return false; // TODO: The model requires a data-type to be set, we cheat currently. But this should be re-though when we implement validation(As we most likely will have to com up with partial models for the runtime model.) [NL] - this._propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); + this.#propertyStructureHelper.insertProperty(value as UmbPropertyTypeModel); return true; }) .observeRouteBuilder((routeBuilder) => { @@ -223,16 +227,16 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem (property) => property.id, (property) => { return html` - - + `; }, )} @@ -258,7 +262,7 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem width: 100%; } - #property-list[sort-mode-active]:not(:has(umb-content-type-workspace-view-edit-property)) { + #property-list[sort-mode-active]:not(:has(umb-content-type-design-editor-property)) { /* Some height so that the sorter can target the area if the group is empty*/ min-height: var(--uui-size-layout-1); } @@ -266,10 +270,10 @@ export class UmbContentTypeWorkspaceViewEditPropertiesElement extends UmbLitElem ]; } -export default UmbContentTypeWorkspaceViewEditPropertiesElement; +export default UmbContentTypeDesignEditorPropertiesElement; declare global { interface HTMLElementTagNameMap { - 'umb-content-type-workspace-view-edit-properties': UmbContentTypeWorkspaceViewEditPropertiesElement; + 'umb-content-type-design-editor-properties': UmbContentTypeDesignEditorPropertiesElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-property.element.ts similarity index 98% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-property.element.ts index 0ea65d8b82..1e9072bd1b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-property.element.ts @@ -15,12 +15,12 @@ import { } from '@umbraco-cms/backoffice/content-type'; /** - * @element umb-content-type-workspace-view-edit-property + * @element umb-content-type-design-editor-property * @description - Element for displaying a property in an workspace. * @slot editor - Slot for rendering the Property Editor */ -@customElement('umb-content-type-workspace-view-edit-property') -export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { +@customElement('umb-content-type-design-editor-property') +export class UmbContentTypeDesignEditorPropertyElement extends UmbLitElement { // #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); @@ -489,6 +489,6 @@ export class UmbContentTypeWorkspacePropertyElement extends UmbLitElement { declare global { interface HTMLElementTagNameMap { - 'umb-content-type-workspace-view-edit-property': UmbContentTypeWorkspacePropertyElement; + 'umb-content-type-design-editor-property': UmbContentTypeDesignEditorPropertyElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts index 16eb1ed8ea..d0408772b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts @@ -1,5 +1,6 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; -import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-workspace-view-edit-group.element.js'; +import type { UmbContentTypeWorkspaceViewEditGroupElement } from './content-type-design-editor-group.element.js'; +import { UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT } from './content-type-design-editor.context.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { @@ -8,8 +9,8 @@ import { type UmbPropertyTypeContainerModel, } from '@umbraco-cms/backoffice/content-type'; -import './content-type-workspace-view-edit-properties.element.js'; -import './content-type-workspace-view-edit-group.element.js'; +import './content-type-design-editor-properties.element.js'; +import './content-type-design-editor-group.element.js'; import { type UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; const SORTER_CONFIG: UmbSorterConfig = { @@ -20,8 +21,8 @@ const SORTER_CONFIG: UmbSorterConfig(this, { ...SORTER_CONFIG, onChange: ({ model }) => { @@ -131,6 +132,8 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (context) => { this.#groupStructureHelper.setStructureManager(context.structure); + }); + this.consumeContext(UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT, (context) => { this.observe( context.isSorting, (isSorting) => { @@ -176,10 +179,10 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { !this._noTabName ? html` - + container-name=${this.tabName ?? ''}> ` : '' @@ -189,12 +192,12 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { this._groups, (group) => group.id, (group) => html` - - + `, )}
    @@ -232,7 +235,7 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { margin-top: var(--uui-size-layout-1); } - umb-content-type-workspace-view-edit-group { + umb-content-type-design-editor-group { margin-bottom: var(--uui-size-layout-1); } @@ -249,10 +252,10 @@ export class UmbContentTypeWorkspaceViewEditTabElement extends UmbLitElement { ]; } -export default UmbContentTypeWorkspaceViewEditTabElement; +export default UmbContentTypeDesignEditorTabElement; declare global { interface HTMLElementTagNameMap { - 'umb-content-type-workspace-view-edit-tab': UmbContentTypeWorkspaceViewEditTabElement; + 'umb-content-type-design-editor-tab': UmbContentTypeDesignEditorTabElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.context.ts new file mode 100644 index 0000000000..84cc6c4ff6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.context.ts @@ -0,0 +1,33 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; + +export const UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT = new UmbContextToken< + UmbContentTypeDesignEditorContext, + UmbContentTypeDesignEditorContext +>('UmbContentTypeDesignEditorContext'); + +export class UmbContentTypeDesignEditorContext extends UmbContextBase { + #isSorting = new UmbBooleanState(false); + readonly isSorting = this.#isSorting.asObservable(); + + constructor(host: UmbControllerHost) { + super(host, UMB_CONTENT_TYPE_DESIGN_EDITOR_CONTEXT); + } + + getIsSorting() { + return this.#isSorting.getValue(); + } + + setIsSorting(isSorting: boolean) { + this.#isSorting.setValue(isSorting); + } + + public destroy(): void { + this.#isSorting.destroy(); + super.destroy(); + } +} + +export { UmbContentTypeDesignEditorContext as api }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts index 28e0526942..537b7008fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -1,5 +1,6 @@ import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '../../content-type-workspace.context-token.js'; -import type { UmbContentTypeWorkspaceViewEditTabElement } from './content-type-workspace-view-edit-tab.element.js'; +import type { UmbContentTypeDesignEditorTabElement } from './content-type-design-editor-tab.element.js'; +import { UmbContentTypeDesignEditorContext } from './content-type-design-editor.context.js'; import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import type { UUIInputElement, UUIInputEvent, UUITabElement } from '@umbraco-cms/backoffice/external/uui'; import { @@ -20,8 +21,8 @@ import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoff import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -@customElement('umb-content-type-workspace-view-edit') -export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { +@customElement('umb-content-type-design-editor') +export class UmbContentTypeDesignEditorElement extends UmbLitElement implements UmbWorkspaceViewElement { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => element.getAttribute('data-umb-tabs-id'), getUniqueOfModel: (tab) => tab.id, @@ -71,7 +72,8 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem }, }); - private _workspaceContext?: (typeof UMB_CONTENT_TYPE_WORKSPACE_CONTEXT)['TYPE']; + #workspaceContext?: (typeof UMB_CONTENT_TYPE_WORKSPACE_CONTEXT)['TYPE']; + #designContext = new UmbContentTypeDesignEditorContext(this); private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); @@ -102,6 +104,19 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem this.#sorter.disable(); + this.observe( + this.#designContext.isSorting, + (isSorting) => { + this._sortModeActive = isSorting; + if (isSorting) { + this.#sorter.enable(); + } else { + this.#sorter.disable(); + } + }, + '_observeIsSorting', + ); + //TODO: We need to differentiate between local and composition tabs (and hybrids) this._tabsStructureHelper.setIsRoot(true); @@ -115,34 +130,22 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem // _hasRootProperties can be gotten via _tabsStructureHelper.hasProperties. But we do not support root properties currently. this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => { - this._workspaceContext = workspaceContext; + this.#workspaceContext = workspaceContext; this._tabsStructureHelper.setStructureManager(workspaceContext.structure); - this.observe( - workspaceContext.isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - if (isSorting) { - this.#sorter.enable(); - } else { - this.#sorter.disable(); - } - }, - '_observeIsSorting', - ); this._observeRootGroups(); }); } #toggleSortMode() { - this._workspaceContext?.setIsSorting(!this._sortModeActive); + this.#designContext?.setIsSorting(!this._sortModeActive); } private _observeRootGroups() { - if (!this._workspaceContext) return; + if (!this.#workspaceContext) return; this.observe( - this._workspaceContext.structure.hasRootContainers('Group'), + this.#workspaceContext.structure.hasRootContainers('Group'), (hasRootGroups) => { this._hasRootGroups = hasRootGroups; this._createRoutes(); @@ -153,7 +156,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem private _createRoutes() { // TODO: How about storing a set of elements based on tab ids? to prevent re-initializing the element when renaming..[NL] - if (!this._workspaceContext || !this._tabs || this._hasRootGroups === undefined) return; + if (!this.#workspaceContext || !this._tabs || this._hasRootGroups === undefined) return; const routes: UmbRoute[] = []; // We gather the activeTab name to check for rename, this is a bit hacky way to redirect the user without noticing to the new name [NL] @@ -167,11 +170,11 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } routes.push({ path: `tab/${encodeFolderName(tabName).toString()}`, - component: () => import('./content-type-workspace-view-edit-tab.element.js'), + component: () => import('./content-type-design-editor-tab.element.js'), setup: (component) => { // Or just cache the current view here, and use it if the same is begin requested?. [NL] - (component as UmbContentTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = tab.id; + (component as UmbContentTypeDesignEditorTabElement).tabName = tabName; + (component as UmbContentTypeDesignEditorTabElement).containerId = tab.id; }, }); }); @@ -179,10 +182,10 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem routes.push({ path: 'root', - component: () => import('./content-type-workspace-view-edit-tab.element.js'), + component: () => import('./content-type-design-editor-tab.element.js'), setup: (component) => { - (component as UmbContentTypeWorkspaceViewEditTabElement).noTabName = true; - (component as UmbContentTypeWorkspaceViewEditTabElement).containerId = null; + (component as UmbContentTypeDesignEditorTabElement).noTabName = true; + (component as UmbContentTypeDesignEditorTabElement).containerId = null; }, }); @@ -242,7 +245,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } #remove(tabId?: string) { if (!tabId) return; - this._workspaceContext?.structure.removeContainer(null, tabId); + this.#workspaceContext?.structure.removeContainer(null, tabId); // TODO: We should only navigate away if it was the last tab and if it was the active one... this._tabsStructureHelper?.isOwnerChildContainer(tabId) ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) @@ -261,7 +264,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem const len = this._tabs.length; const sortOrder = len === 0 ? 0 : this._tabs[len - 1].sortOrder + 1; - const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab', sortOrder); + const tab = await this.#workspaceContext?.structure.createContainer(null, null, 'Tab', sortOrder); if (tab) { const path = this._routerPath + (tab.name ? '/tab/' + encodeFolderName(tab.name) : '/tab'); window.history.replaceState(null, '', path); @@ -279,7 +282,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem this._activeTabId = tab.id; let newName = (event.target as HTMLInputElement).value; - const changedName = this._workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( + const changedName = this.#workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( newName, 'Tab', tab.id, @@ -301,14 +304,14 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem } async #openCompositionModal() { - if (!this._workspaceContext) return; + if (!this.#workspaceContext) return; - const unique = this._workspaceContext.getUnique(); + const unique = this.#workspaceContext.getUnique(); if (!unique) { throw new Error('Content Type unique is undefined'); } - const contentTypes = this._workspaceContext.structure.getContentTypes(); - const ownerContentType = this._workspaceContext.structure.getOwnerContentType(); + const contentTypes = this.#workspaceContext.structure.getContentTypes(); + const ownerContentType = this.#workspaceContext.structure.getOwnerContentType(); if (!ownerContentType) { throw new Error('Owner Content Type not found'); } @@ -331,7 +334,7 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem const compositionIds = modalContext.getValue().selection; - this._workspaceContext?.setCompositions( + this.#workspaceContext?.setCompositions( compositionIds.map((unique) => ({ contentType: { unique }, compositionType: CompositionTypeModel.COMPOSITION })), ); } @@ -582,10 +585,10 @@ export class UmbContentTypeWorkspaceViewEditElement extends UmbLitElement implem ]; } -export default UmbContentTypeWorkspaceViewEditElement; +export default UmbContentTypeDesignEditorElement; declare global { interface HTMLElementTagNameMap { - 'umb-content-type-workspace-view-edit': UmbContentTypeWorkspaceViewEditElement; + 'umb-content-type-design-editor': UmbContentTypeDesignEditorElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts index 926cb1ead3..42b52d383f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/manifest.ts @@ -8,7 +8,7 @@ export const contentTypeDesignEditorManifest: UmbBackofficeManifestKind = { manifest: { type: 'workspaceView', kind: 'contentTypeDesignEditor', - element: () => import('./content-type-workspace-view-edit.element.js'), + element: () => import('./content-type-design-editor.element.js'), weight: 1000, meta: { label: 'Design', diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts index 26dd72bfe4..e997d1a1ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -3,7 +3,7 @@ import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '../entity.js'; import type { UmbDocumentTypeDetailModel } from '../types.js'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; import { UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; -import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbContentTypeCompositionModel, UmbContentTypeSortModel, @@ -50,9 +50,6 @@ export class UmbDocumentTypeWorkspaceContext readonly structure = new UmbContentTypePropertyStructureManager(this, this.repository); - #isSorting = new UmbBooleanState(false); - isSorting = this.#isSorting.asObservable(); - constructor(host: UmbControllerHost) { super(host, 'Umb.Workspace.DocumentType'); @@ -79,15 +76,6 @@ export class UmbDocumentTypeWorkspaceContext protected resetState(): void { super.resetState(); this.#persistedData.setValue(undefined); - this.#isSorting.setValue(false); - } - - getIsSorting() { - return this.#isSorting.getValue(); - } - - setIsSorting(isSorting: boolean) { - this.#isSorting.setValue(isSorting); } getData() { @@ -163,7 +151,6 @@ export class UmbDocumentTypeWorkspaceContext if (!data) return undefined; this.setIsNew(true); - this.setIsSorting(false); this.#persistedData.setValue(data); return data; } @@ -174,7 +161,6 @@ export class UmbDocumentTypeWorkspaceContext if (!data) return undefined; this.setIsNew(false); - this.setIsSorting(false); this.#persistedData.setValue(data); return data; } @@ -219,7 +205,6 @@ export class UmbDocumentTypeWorkspaceContext public destroy(): void { this.#persistedData.destroy(); this.structure.destroy(); - this.#isSorting.destroy(); this.repository.destroy(); super.destroy(); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts index 374818b70e..6300f0c4a4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context.ts @@ -6,7 +6,11 @@ import { UmbEditableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspa import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; -import type { UmbContentTypeCompositionModel, UmbContentTypeSortModel } from '@umbraco-cms/backoffice/content-type'; +import type { + UmbContentTypeCompositionModel, + UmbContentTypeSortModel, + UmbContentTypeWorkspaceContext, +} from '@umbraco-cms/backoffice/content-type'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; @@ -16,8 +20,9 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbMediaTypeDetailModel; export class UmbMediaTypeWorkspaceContext extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface + implements UmbContentTypeWorkspaceContext { + readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true; // public readonly repository: UmbMediaTypeDetailRepository = new UmbMediaTypeDetailRepository(this); // Draft is located in structure manager @@ -33,6 +38,8 @@ export class UmbMediaTypeWorkspaceContext readonly icon; readonly allowedAtRoot; + readonly variesByCulture; + readonly variesBySegment; readonly allowedContentTypes; readonly compositions; readonly collection; @@ -52,6 +59,8 @@ export class UmbMediaTypeWorkspaceContext this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description); this.icon = this.structure.ownerContentTypeObservablePart((data) => data?.icon); this.allowedAtRoot = this.structure.ownerContentTypeObservablePart((data) => data?.allowedAtRoot); + this.variesByCulture = this.structure.ownerContentTypeObservablePart((data) => data?.variesByCulture); + this.variesBySegment = this.structure.ownerContentTypeObservablePart((data) => data?.variesBySegment); this.allowedContentTypes = this.structure.ownerContentTypeObservablePart((data) => data?.allowedContentTypes); this.compositions = this.structure.ownerContentTypeObservablePart((data) => data?.compositions); this.collection = this.structure.ownerContentTypeObservablePart((data) => data?.collection); From 2c2e59424301362d3b476e27f62d6285b42d5bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 14:15:04 +0100 Subject: [PATCH 157/285] remove member type workspace views --- .../member-type/workspace/manifests.ts | 3 +- ...-workspace-view-edit-properties.element.ts | 257 -------- ...pe-workspace-view-edit-property.element.ts | 487 --------------- ...er-type-workspace-view-edit-tab.element.ts | 326 ---------- ...member-type-workspace-view-edit.element.ts | 573 ------------------ 5 files changed, 1 insertion(+), 1645 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/manifests.ts index 08eb408fe3..215314effe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/manifests.ts @@ -20,10 +20,9 @@ const workspace: ManifestWorkspace = { const workspaceViews: Array = [ { type: 'workspaceView', + kind: 'contentTypeDesignEditor', alias: 'Umb.WorkspaceView.MemberType.Design', name: 'Member Type Workspace Design View', - js: () => import('./views/design/member-type-workspace-view-edit.element.js'), - weight: 1000, meta: { label: 'Design', pathname: 'design', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts deleted file mode 100644 index d5ca43230b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-properties.element.ts +++ /dev/null @@ -1,257 +0,0 @@ -import type { UmbMemberTypeWorkspaceContext } from '../../member-type-workspace.context.js'; -import './member-type-workspace-view-edit-property.element.js'; -import type { UmbMemberTypeDetailModel } from '../../../types.js'; -import type { UmbMemberTypeWorkspacePropertyElement } from './member-type-workspace-view-edit-property.element.js'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; -import { - UmbContentTypePropertyStructureHelper, - UMB_PROPERTY_SETTINGS_MODAL, -} from '@umbraco-cms/backoffice/content-type'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; - -@customElement('umb-member-type-workspace-view-edit-properties') -export class UmbMemberTypeWorkspaceViewEditPropertiesElement extends UmbLitElement { - #model: Array = []; - #sorter = new UmbSorterController(this, { - getUniqueOfElement: (element) => { - return element.getAttribute('data-umb-property-id'); - }, - getUniqueOfModel: (modelEntry) => { - return modelEntry.id; - }, - identifier: 'member-type-property-sorter', - itemSelector: 'umb-member-type-workspace-view-edit-property', - //disabledItemSelector: '[inherited]', - //TODO: Set the property list (sorter wrapper) to inherited, if its inherited - // This is because we don't want to move local properties into an inherited group container. - // Or maybe we do, but we still need to check if the group exists locally, if not, then it needs to be created before we move a property into it. - // TODO: Fix bug where a local property turn into an inherited when moved to a new group container. - containerSelector: '#property-list', - onChange: ({ item, model }) => { - this.#model = model; - this._propertyStructure = model; - }, - onEnd: ({ item }) => { - /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. - * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update - * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... - */ - const model = this.#model; - const newIndex = model.findIndex((entry) => entry.id === item.id); - - // Doesn't exist in model - if (newIndex === -1) return; - - // First in list - if (newIndex === 0 && model.length > 1) { - this._propertyStructureHelper.partialUpdateProperty(item.id, { - sortOrder: model[1].sortOrder - 1, - container: this._containerId ? { id: this._containerId } : null, - }); - return; - } - - // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; - - let weight = 1; - this._propertyStructureHelper.partialUpdateProperty(item.id, { - sortOrder: prevItemSortOrder + weight, - container: this._containerId ? { id: this._containerId } : null, - }); - - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this._propertyStructureHelper.partialUpdateProperty(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; - }); - } - }, - }); - - private _containerId: string | undefined; - - @property({ type: String, attribute: 'container-id', reflect: false }) - public get containerId(): string | undefined { - return this._containerId; - } - public set containerId(value: string | undefined) { - if (value === this._containerId) return; - const oldValue = this._containerId; - this._containerId = value; - this.requestUpdate('containerId', oldValue); - } - - @property({ type: String, attribute: 'container-name', reflect: false }) - public get containerName(): string | undefined { - return this._propertyStructureHelper.getContainerName(); - } - public set containerName(value: string | undefined) { - this._propertyStructureHelper.setContainerName(value); - } - - @property({ type: String, attribute: 'container-type', reflect: false }) - public get containerType(): UmbPropertyContainerTypes | undefined { - return this._propertyStructureHelper.getContainerType(); - } - public set containerType(value: UmbPropertyContainerTypes | undefined) { - this._propertyStructureHelper.setContainerType(value); - } - - _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); - - @state() - _propertyStructure: Array = []; - - @state() - _ownerMemberTypes?: UmbMemberTypeDetailModel[]; - - @state() - protected _modalRouteNewProperty?: string; - - @state() - _sortModeActive?: boolean; - - constructor() { - super(); - - this.consumeContext(UMB_WORKSPACE_CONTEXT, async (workspaceContext) => { - this._propertyStructureHelper.setStructureManager((workspaceContext as UmbMemberTypeWorkspaceContext).structure); - this.observe( - (workspaceContext as UmbMemberTypeWorkspaceContext).isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - if (isSorting) { - this.#sorter.setModel(this._propertyStructure); - } else { - this.#sorter.setModel([]); - } - }, - '_observeIsSorting', - ); - const docTypesObservable = await this._propertyStructureHelper.contentTypes(); - if (!docTypesObservable) return; - this.observe( - docTypesObservable, - (members) => { - this._ownerMemberTypes = members; - }, - 'observeOwnerMemberTypes', - ); - }); - this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { - this._propertyStructure = propertyStructure; - if (this._sortModeActive) { - this.#sorter.setModel(this._propertyStructure); - } else { - this.#sorter.setModel([]); - } - }); - - // Note: Route for adding a new property - new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addAdditionalPath('new-property') - .onSetup(async () => { - const memberTypeId = this._ownerMemberTypes?.find((types) => - types.containers?.find((containers) => containers.id === this.containerId), - )?.unique; - if (memberTypeId === undefined) return false; - const propertyData = await this._propertyStructureHelper.createPropertyScaffold(this._containerId); - if (propertyData === undefined) return false; - return { data: { contentTypeId: memberTypeId }, value: propertyData }; - }) - .onSubmit((value) => { - if (!value.dataType) { - throw new Error('No data type selected'); - } - this.#addProperty(value as UmbPropertyTypeModel); - }) - .observeRouteBuilder((routeBuilder) => { - this._modalRouteNewProperty = routeBuilder(null); - }); - } - - async #addProperty(propertyData: UmbPropertyTypeModel) { - const propertyPlaceholder = await this._propertyStructureHelper.addProperty(this._containerId); - if (!propertyPlaceholder) return; - - this._propertyStructureHelper.partialUpdateProperty(propertyPlaceholder.id, propertyData); - } - - render() { - return html` -
    - ${repeat( - this._propertyStructure, - (property) => '' + property.container?.id + property.id + '' + property.sortOrder, - (property) => { - // Note: This piece might be moved into the property component - const inheritedFromMember = this._ownerMemberTypes?.find((types) => - types.containers?.find((containers) => containers.id === property.container?.id), - ); - - return html` - { - this._propertyStructureHelper.partialUpdateProperty(property.id, event.detail); - }} - @property-delete=${() => { - this._propertyStructureHelper.removeProperty(property.id!); - }}> - - `; - }, - )} -
    - - ${!this._sortModeActive - ? html` - Add property - ` - : ''} - `; - } - - static styles = [ - UmbTextStyles, - css` - #add { - width: 100%; - } - - #property-list[sort-mode-active]:not(:has(umb-member-type-workspace-view-edit-property)) { - /* Some height so that the sorter can target the area if the group is empty*/ - min-height: var(--uui-size-layout-1); - } - `, - ]; -} - -export default UmbMemberTypeWorkspaceViewEditPropertiesElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-member-type-workspace-view-edit-properties': UmbMemberTypeWorkspaceViewEditPropertiesElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts deleted file mode 100644 index 8ea9e5a1c6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-property.element.ts +++ /dev/null @@ -1,487 +0,0 @@ -import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type'; -import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; -import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { css, html, customElement, property, state, ifDefined, nothing } from '@umbraco-cms/backoffice/external/lit'; -import { - UMB_MODAL_MANAGER_CONTEXT, - UMB_WORKSPACE_MODAL, - UmbModalRouteRegistrationController, - umbConfirmModal, -} from '@umbraco-cms/backoffice/modal'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { generateAlias } from '@umbraco-cms/backoffice/utils'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { - UMB_PROPERTY_SETTINGS_MODAL, - type UmbPropertyTypeModel, - type UmbPropertyTypeScaffoldModel, -} from '@umbraco-cms/backoffice/content-type'; - -/** - * @element umb-member-type-workspace-view-edit-property - * @description - Element for displaying a property in an workspace. - * @slot editor - Slot for rendering the Property Editor - */ -@customElement('umb-member-type-workspace-view-edit-property') -export class UmbMemberTypeWorkspacePropertyElement extends UmbLitElement { - private _property?: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined; - /** - * Property, the data object for the property. - * @type {UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined} - * @attr - * @default undefined - */ - @property({ type: Object }) - public get property(): UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined { - return this._property; - } - public set property(value: UmbPropertyTypeModel | UmbPropertyTypeScaffoldModel | undefined) { - const oldValue = this._property; - this._property = value; - this.#modalRegistration.setUniquePathValue('propertyId', value?.id?.toString()); - this.setDataType(this._property?.dataType?.unique); - this.requestUpdate('property', oldValue); - } - - /** - * Inherited, Determines if the property is part of the main member type thats being edited. - * If true, then the property is inherited from another member type, not a part of the main member type. - * @type {boolean} - * @attr - * @default undefined - */ - @property({ type: Boolean }) - public inherited?: boolean; - - @property({ type: Boolean, reflect: true, attribute: 'sort-mode-active' }) - public sortModeActive = false; - - #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); - - #modalRegistration; - private _modalManagerContext?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; - - @state() - protected _modalRoute?: string; - - @state() - protected _editMemberTypePath?: string; - - @property() - public get modalRoute() { - return this._modalRoute; - } - - @property({ type: String, attribute: 'owner-member-type-id' }) - public ownerMemberTypeId?: string; - - @property({ type: String, attribute: 'owner-member-type-name' }) - public ownerMemberTypeName?: string; - - @state() - private _dataTypeName?: string; - - async setDataType(dataTypeId: string | undefined) { - if (!dataTypeId) return; - this.#dataTypeDetailRepository.requestByUnique(dataTypeId).then((x) => (this._dataTypeName = x?.data?.name)); - } - - constructor() { - super(); - this.#modalRegistration = new UmbModalRouteRegistrationController(this, UMB_PROPERTY_SETTINGS_MODAL) - .addUniquePaths(['propertyId']) - .onSetup(() => { - const memberTypeId = this.ownerMemberTypeId; - if (memberTypeId === undefined) return false; - const propertyData = this.property; - if (propertyData === undefined) return false; - return { data: { contentTypeId: memberTypeId }, value: propertyData }; - }) - .onSubmit((result) => { - if (!result.dataType) { - throw new Error('No dataType found on property'); - } - this._partialUpdate(result as UmbPropertyTypeModel); - }) - .observeRouteBuilder((routeBuilder) => { - this._modalRoute = routeBuilder(null); - }); - - new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL) - .addAdditionalPath('member-type') - .onSetup(() => { - return { data: { entityType: 'member-type', preset: {} } }; - }) - .observeRouteBuilder((routeBuilder) => { - this._editMemberTypePath = routeBuilder({}); - }); - - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (context) => { - this._modalManagerContext = context; - }); - } - - _partialUpdate(partialObject: UmbPropertyTypeModel) { - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); - } - - _singleValueUpdate(propertyName: string, value: string | number | boolean | null | undefined) { - const partialObject = {} as any; - partialObject[propertyName] = value; - - this.dispatchEvent(new CustomEvent('umb:partial-property-update', { detail: partialObject })); - } - - @state() - private _aliasLocked = true; - - #onToggleAliasLock() { - this._aliasLocked = !this._aliasLocked; - } - - async #requestRemove(e: Event) { - e.preventDefault(); - e.stopImmediatePropagation(); - if (!this.property || !this.property.id) return; - - await umbConfirmModal(this, { - headline: `${this.localize.term('actions_delete')} property`, - content: html` - Are you sure you want to delete the property ${this.property.name || this.property.id} - - `, - confirmLabel: this.localize.term('actions_delete'), - color: 'danger', - }); - - this.dispatchEvent(new CustomEvent('property-delete')); - } - - #onNameChange(event: UUIInputEvent) { - if (event instanceof UUIInputEvent) { - const target = event.composedPath()[0] as UUIInputElement; - - if (typeof target?.value === 'string') { - const oldName = this.property?.name ?? ''; - const oldAlias = this.property?.alias ?? ''; - const newName = event.target.value.toString(); - if (this._aliasLocked) { - const expectedOldAlias = generateAlias(oldName ?? ''); - // Only update the alias if the alias matches a generated alias of the old name (otherwise the alias is considered one written by the user.) - if (expectedOldAlias === oldAlias) { - this._singleValueUpdate('alias', generateAlias(newName ?? '')); - } - } - this._singleValueUpdate('name', newName); - } - } - } - renderSortableProperty() { - if (!this.property) return; - return html` -
    - - ${this.property.name} (${this.property.alias}) -
    - - this._partialUpdate({ sortOrder: parseInt(e.target.value as string) || 0 } as UmbPropertyTypeModel)} - .value=${this.property.sortOrder ?? 0}> - `; - } - - renderEditableProperty() { - if (!this.property) return; - - if (this.sortModeActive) { - return this.renderSortableProperty(); - } else { - return html` - - - ${this.renderPropertyTags()} - - - - - - - `; - } - } - - renderInheritedProperty() { - if (!this.property) return; - - if (this.sortModeActive) { - return this.renderSortableProperty(); - } else { - return html` - -
    - ${this.renderPropertyTags()} - - - ${this.localize.term('contentTypeEditor_inheritedFrom')} - - ${this.ownerMemberTypeName ?? '??'} - - - -
    - `; - } - } - - renderPropertyAlias() { - return this.property - ? html` { - if (e.target) this._singleValueUpdate('alias', (e.target as HTMLInputElement).value); - }}> - - -
    ''} id="alias-lock" slot="prepend"> - -
    -
    ` - : ''; - } - - renderPropertyTags() { - return this.property - ? html`
    - ${this.property.dataType?.unique ? html`${this._dataTypeName}` : nothing} - ${this.property.variesByCulture - ? html` - ${this.localize.term('contentTypeEditor_cultureVariantLabel')} - ` - : nothing} - ${this.property.appearance?.labelOnTop == true - ? html` - ${this.localize.term('contentTypeEditor_displaySettingsLabelOnTop')} - ` - : nothing} - ${this.property.validation.mandatory === true - ? html` - * ${this.localize.term('general_mandatory')} - ` - : nothing} -
    ` - : nothing; - } - - render() { - // TODO: Only show alias on label if user has access to MemberType within settings: - return this.inherited ? this.renderInheritedProperty() : this.renderEditableProperty(); - } - - static styles = [ - UmbTextStyles, - css` - :host(:not([sort-mode-active])) { - display: grid; - grid-template-columns: 200px auto; - column-gap: var(--uui-size-layout-2); - border-bottom: 1px solid var(--uui-color-divider); - padding: var(--uui-size-layout-1) 0; - container-type: inline-size; - } - - :host > div { - grid-column: span 2; - } - - @container (width > 600px) { - :host(:not([orientation='vertical'])) > div { - grid-column: span 1; - } - } - - :host(:first-of-type) { - padding-top: 0; - } - :host(:last-of-type) { - border-bottom: none; - } - - :host([sort-mode-active]) { - position: relative; - display: flex; - padding: 0; - margin-bottom: var(--uui-size-3); - } - - :host([sort-mode-active]:last-of-type) { - margin-bottom: 0; - } - - :host([sort-mode-active]:not([inherited])) { - cursor: grab; - } - - :host([sort-mode-active]) .sortable { - flex: 1; - display: flex; - background-color: var(--uui-color-divider); - align-items: center; - padding: 0 var(--uui-size-3); - gap: var(--uui-size-3); - } - - :host([sort-mode-active]) uui-input { - max-width: 75px; - } - - /* Placeholder style, used when property is being dragged.*/ - :host(.--umb-sorter-placeholder) > * { - visibility: hidden; - } - - :host(.--umb-sorter-placeholder)::after { - content: ''; - inset: 0; - position: absolute; - border: 1px dashed var(--uui-color-divider-emphasis); - border-radius: var(--uui-border-radius); - } - - p { - margin-bottom: 0; - } - - #header { - position: sticky; - top: var(--uui-size-space-4); - height: min-content; - z-index: 2; - } - - #editor { - position: relative; - background-color: var(--uui-color-background); - } - #alias-input, - #label-input, - #description-input { - width: 100%; - } - - #alias-input { - border-color: transparent; - background: var(--uui-color-surface); - } - - #label-input { - font-weight: bold; /* TODO: UUI Input does not support bold text yet */ - --uui-input-border-color: transparent; - } - #label-input input { - font-weight: bold; - --uui-input-border-color: transparent; - } - - #alias-lock { - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - } - #alias-lock uui-icon { - margin-bottom: 2px; - /* margin: 0; */ - } - #description-input { - --uui-textarea-border-color: transparent; - font-weight: 0.5rem; /* TODO: Cant change font size of UUI textarea yet */ - } - - .types > div uui-icon, - .inherited uui-icon { - vertical-align: sub; - } - - .inherited { - position: absolute; - top: var(--uui-size-space-2); - right: var(--uui-size-space-2); - } - - .types { - position: absolute; - top: var(--uui-size-space-2); - left: var(--uui-size-space-2); - display: flex; - gap: var(--uui-size-space-2); - } - - #editor uui-action-bar { - position: absolute; - top: var(--uui-size-space-2); - right: var(--uui-size-space-2); - display: none; - } - #editor:hover uui-action-bar, - #editor:focus uui-action-bar { - display: block; - } - - a { - color: inherit; - } - - :host([drag-placeholder]) { - opacity: 0.5; - } - :host([drag-placeholder]) uui-input { - visibility: hidden; - } - `, - ]; -} - -declare global { - interface HTMLElementTagNameMap { - 'umb-member-type-workspace-view-edit-property': UmbMemberTypeWorkspacePropertyElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts deleted file mode 100644 index ac0ac5f400..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit-tab.element.ts +++ /dev/null @@ -1,326 +0,0 @@ -import type { UmbMemberTypeDetailModel } from '../../../types.js'; -import type { UmbMemberTypeWorkspaceContext } from '../../member-type-workspace.context.js'; -import type { UmbMemberTypeWorkspaceViewEditPropertiesElement } from './member-type-workspace-view-edit-properties.element.js'; -import type { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { css, html, customElement, property, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { - UmbContentTypeContainerStructureHelper, - type UmbPropertyTypeContainerModel, -} from '@umbraco-cms/backoffice/content-type'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; - -import './member-type-workspace-view-edit-properties.element.js'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -@customElement('umb-member-type-workspace-view-edit-tab') -export class UmbMemberTypeWorkspaceViewEditTabElement extends UmbLitElement { - #model: Array = []; - #sorter = new UmbSorterController( - this, - { - getUniqueOfElement: (element) => - element.querySelector('umb-member-type-workspace-view-edit-properties')!.getAttribute('container-id'), - getUniqueOfModel: (modelEntry) => modelEntry.id, - identifier: 'member-type-container-sorter', - itemSelector: '.container-handle', - containerSelector: '.container-list', - onChange: ({ model }) => { - this._groups = model; - this.#model = model; - }, - onEnd: ({ item }) => { - /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. - * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update - * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... - */ - const model = this.#model; - const newIndex = model.findIndex((entry) => entry.id === item.id); - - // Doesn't exist in model - if (newIndex === -1) return; - - // First in list - if (newIndex === 0 && model.length > 1) { - this._groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: model[1].sortOrder - 1 }); - return; - } - - // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; - - let weight = 1; - this._groupStructureHelper.partialUpdateContainer(item.id, { sortOrder: prevItemSortOrder + weight }); - - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this._groupStructureHelper.partialUpdateContainer(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; - }); - } - }, - }, - ); - - private _ownerTabId?: string | null; - - // TODO: get rid of this: - @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; - } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - const oldValue = this._ownerTabId; - this._ownerTabId = value; - this._groupStructureHelper.setParentId(value); - this.requestUpdate('ownerTabId', oldValue); - } - - private _tabName?: string | undefined; - - @property({ type: String }) - public get tabName(): string | undefined { - return this._groupStructureHelper.getName(); - } - public set tabName(value: string | undefined) { - if (value === this._tabName) return; - const oldValue = this._tabName; - this._tabName = value; - this._groupStructureHelper.setName(value); - this.requestUpdate('tabName', oldValue); - } - - @state() - private _noTabName?: boolean; - - @property({ type: Boolean }) - public get noTabName(): boolean { - return this._groupStructureHelper.getIsRoot(); - } - public set noTabName(value: boolean) { - this._noTabName = value; - this._groupStructureHelper.setIsRoot(value); - } - - _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); - - @state() - _groups: Array = []; - - @state() - _hasProperties = false; - - @state() - _sortModeActive?: boolean; - - constructor() { - super(); - - this._groupStructureHelper.setParentType('Tab'); - - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { - this._groupStructureHelper.setStructureManager((context as UmbMemberTypeWorkspaceContext).structure); - this.observe( - (context as UmbMemberTypeWorkspaceContext).isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - - if (isSorting) { - this.#sorter.setModel(this._groups); - } else { - this.#sorter.setModel([]); - } - }, - '_observeIsSorting', - ); - }); - this.observe(this._groupStructureHelper.containers, (groups) => { - this._groups = groups; - if (this._sortModeActive) { - this.#sorter.setModel(this._groups); - } else { - this.#sorter.setModel([]); - } - this.requestUpdate('_groups'); - }); - this.observe(this._groupStructureHelper.hasProperties, (hasProperties) => { - this._hasProperties = hasProperties; - this.requestUpdate('_hasProperties'); - }); - } - - #onAddGroup = () => { - // Idea, maybe we can gather the sortOrder from the last group rendered and add 1 to it? - this._groupStructureHelper.addContainer(this._ownerTabId); - }; - - render() { - return html` - ${ - this._sortModeActive - ? html`` - : '' - } - - ${ - !this._noTabName - ? html` - - - - ` - : '' - } -
    - ${repeat( - this._groups, - (group) => group.id + '' + group.name + group.sortOrder, - (group) => - html` - ${this.#renderHeader(group)} - - `, - )} -
    - ${this.#renderAddGroupButton()} - - `; - } - - #renderHeader(group: UmbPropertyTypeContainerModel) { - const inherited = !this._groupStructureHelper.isOwnerChildContainer(group.id!); - - if (this._sortModeActive) { - return html`
    -
    - - ${this.#renderInputGroupName(group)} -
    - - this._groupStructureHelper.partialUpdateContainer(group.id!, { - sortOrder: parseInt(e.target.value as string) || 0, - })} - .value=${group.sortOrder || 0} - ?disabled=${inherited}> -
    `; - } else { - return html`
    - ${inherited ? html`` : this.#renderInputGroupName(group)} -
    `; - } - } - - #renderInputGroupName(group: UmbPropertyTypeContainerModel) { - return html` { - throw new Error('Not implemented'); - /* - const newName = (e.target as HTMLInputElement).value; - this._groupStructureHelper.updateContainerName(group.id!, group.parent?.id ?? null, newName); - */ - }}>`; - } - - #renderAddGroupButton() { - if (this._sortModeActive) return; - return html` - ${this.localize.term('contentTypeEditor_addGroup')} - `; - } - - static styles = [ - UmbTextStyles, - css` - [drag-placeholder] { - opacity: 0.5; - } - - [drag-placeholder] > * { - visibility: hidden; - } - - #add { - width: 100%; - } - - #add:first-child { - margin-top: var(--uui-size-layout-1); - } - - uui-box { - margin-bottom: var(--uui-size-layout-1); - } - - [data-umb-group-id] { - display: block; - position: relative; - } - - div[slot='header'] { - flex: 1; - display: flex; - align-items: center; - justify-content: space-between; - } - - div[slot='header'] > div { - display: flex; - align-items: center; - gap: var(--uui-size-3); - } - - uui-input[type='number'] { - max-width: 75px; - } - - [sort-mode-active] div[slot='header'] { - cursor: grab; - } - - .container-list { - display: grid; - gap: 10px; - } - - #convert-to-tab { - margin-bottom: var(--uui-size-layout-1); - display: flex; - } - `, - ]; -} - -export default UmbMemberTypeWorkspaceViewEditTabElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-member-type-workspace-view-edit-tab': UmbMemberTypeWorkspaceViewEditTabElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit.element.ts deleted file mode 100644 index 51813eae0d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/views/design/member-type-workspace-view-edit.element.ts +++ /dev/null @@ -1,573 +0,0 @@ -// import { UMB_COMPOSITION_PICKER_MODAL, type UmbCompositionPickerModalData } from '../../../modals/index.js'; -import type { UmbMemberTypeWorkspaceContext } from '../../member-type-workspace.context.js'; -import type { UmbMemberTypeDetailModel } from '../../../types.js'; -import type { UmbMemberTypeWorkspaceViewEditTabElement } from './member-type-workspace-view-edit-tab.element.js'; -import { css, html, customElement, state, repeat, nothing, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UUIInputElement, UUIInputEvent, UUITabElement } from '@umbraco-cms/backoffice/external/uui'; -import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; -import { encodeFolderName } from '@umbraco-cms/backoffice/router'; -import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { - CompositionTypeModel, - type PropertyTypeContainerModelBaseModel, -} from '@umbraco-cms/backoffice/external/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -import type { UmbRoute, UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; -import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; -import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; -import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; - -@customElement('umb-member-type-workspace-view-edit') -export class UmbMemberTypeWorkspaceViewEditElement extends UmbLitElement implements UmbWorkspaceViewElement { - #model: Array = []; - #sorter = new UmbSorterController(this, { - getUniqueOfElement: (element) => element.getAttribute('data-umb-tabs-id'), - getUniqueOfModel: (modelEntry) => modelEntry.id, - identifier: 'member-type-tabs-sorter', - itemSelector: 'uui-tab', - containerSelector: 'uui-tab-group', - disabledItemSelector: '#root-tab', - resolvePlacement: () => false, - onChange: ({ model }) => { - this.#model = model; - this._tabs = model; - }, - onEnd: ({ item }) => { - /** Explanation: If the item is the first in list, we compare it to the item behind it to set a sortOrder. - * If it's not the first in list, we will compare to the item in before it, and check the following item to see if it caused overlapping sortOrder, then update - * the overlap if true, which may cause another overlap, so we loop through them till no more overlaps... - */ - const model = this.#model; - const newIndex = model.findIndex((entry) => entry.id === item.id); - - // Doesn't exist in model - if (newIndex === -1) return; - - // First in list - if (newIndex === 0 && model.length > 1) { - this._tabsStructureHelper.partialUpdateContainer(item.id, { sortOrder: model[1].sortOrder - 1 }); - return; - } - - // Not first in list - if (newIndex > 0 && model.length > 1) { - const prevItemSortOrder = model[newIndex - 1].sortOrder; - - let weight = 1; - this._tabsStructureHelper.partialUpdateContainer(item.id, { sortOrder: prevItemSortOrder + weight }); - - // Check for overlaps - model.some((entry, index) => { - if (index <= newIndex) return; - if (entry.sortOrder === prevItemSortOrder + weight) { - weight++; - this._tabsStructureHelper.partialUpdateContainer(entry.id, { sortOrder: prevItemSortOrder + weight }); - } - // Break the loop - return true; - }); - } - }, - }); - - //private _hasRootProperties = false; - - @state() - private _hasRootGroups = false; - - @state() - private _routes: UmbRoute[] = []; - - @state() - _tabs?: Array; - - @state() - private _routerPath?: string; - - @state() - private _activePath = ''; - - @state() - private _sortModeActive?: boolean; - - @state() - private _buttonDisabled: boolean = false; - - private _workspaceContext?: UmbMemberTypeWorkspaceContext; - - private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); - - private _modalManagerContext?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; - - // @state() - // private _compositionConfiguration?: UmbCompositionPickerModalData; - - constructor() { - super(); - - //TODO: We need to differentiate between local and composition tabs (and hybrids) - - this._tabsStructureHelper.setIsRoot(true); - this._tabsStructureHelper.setContainerChildType('Tab'); - this.observe(this._tabsStructureHelper.containers, (tabs) => { - this._tabs = tabs; - if (this._sortModeActive) { - this.#sorter.setModel(tabs); - } else { - this.#sorter.setModel([]); - } - - this._createRoutes(); - }); - - // _hasRootProperties can be gotten via _tabsStructureHelper.hasProperties. But we do not support root properties currently. - - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._workspaceContext = workspaceContext as UmbMemberTypeWorkspaceContext; - this._tabsStructureHelper.setStructureManager((workspaceContext as UmbMemberTypeWorkspaceContext).structure); - this.observe( - this._workspaceContext.isSorting, - (isSorting) => { - this._sortModeActive = isSorting; - if (isSorting) { - this.#sorter.setModel(this._tabs!); - } else { - this.#sorter.setModel([]); - } - }, - '_observeIsSorting', - ); - - const unique = this._workspaceContext.getUnique(); - - // //TODO Figure out the correct data that needs to be sent to the compositions modal. Do we really have to send isElement, currentPropertyAliases - isn't unique enough? - // this.observe(this._workspaceContext.structure.contentTypes, (contentTypes) => { - // this._compositionConfiguration = { - // unique: unique ?? '', - // selection: contentTypes.map((contentType) => contentType.unique).filter((id) => id !== unique), - // isElement: contentTypes.find((contentType) => contentType.unique === unique)?.isElement ?? false, - // currentPropertyAliases: [], - // }; - // }); - - this._observeRootGroups(); - }); - - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (context) => { - this._modalManagerContext = context; - }); - } - - private _observeRootGroups() { - if (!this._workspaceContext) return; - - this.observe( - this._workspaceContext.structure.hasRootContainers('Group'), - (hasRootGroups) => { - this._hasRootGroups = hasRootGroups; - this._createRoutes(); - }, - '_observeGroups', - ); - } - - #changeMode() { - this._workspaceContext?.setIsSorting(!this._sortModeActive); - } - - private _createRoutes() { - if (!this._workspaceContext || !this._tabs) return; - const routes: UmbRoute[] = []; - - if (this._tabs.length > 0) { - this._tabs?.forEach((tab) => { - const tabName = tab.name ?? ''; - routes.push({ - path: `tab/${encodeFolderName(tabName).toString()}`, - component: () => import('./member-type-workspace-view-edit-tab.element.js'), - setup: (component) => { - (component as UmbMemberTypeWorkspaceViewEditTabElement).tabName = tabName; - (component as UmbMemberTypeWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; - }, - }); - }); - } - - routes.push({ - path: 'root', - component: () => import('./member-type-workspace-view-edit-tab.element.js'), - setup: (component) => { - (component as UmbMemberTypeWorkspaceViewEditTabElement).noTabName = true; - (component as UmbMemberTypeWorkspaceViewEditTabElement).ownerTabId = null; - }, - }); - - if (this._hasRootGroups) { - routes.push({ - path: '', - redirectTo: 'root', - }); - } else if (routes.length !== 0) { - routes.push({ - path: '', - redirectTo: routes[0]?.path, - }); - } - - this._routes = routes; - } - - async #requestRemoveTab(tab: PropertyTypeContainerModelBaseModel | undefined) { - const modalData: UmbConfirmModalData = { - headline: 'Delete tab', - content: html` - Are you sure you want to delete the tab ${tab?.name ?? tab?.id} - -
    - - This will delete all items that doesn't belong to a composition. - -
    `, - confirmLabel: this.localize.term('actions_delete'), - color: 'danger', - }; - - // TODO: If this tab is composed of other tabs, then notify that it will only delete the local tab. - - await umbConfirmModal(this, modalData); - - this.#remove(tab?.id); - } - - #remove(tabId?: string) { - if (!tabId) return; - this._workspaceContext?.structure.removeContainer(null, tabId); - this._tabsStructureHelper?.isOwnerChildContainer(tabId) - ? window.history.replaceState(null, '', this._routerPath + (this._routes[0]?.path ?? '/root')) - : ''; - } - async #addTab() { - if ( - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement) && - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement).value === '' - ) { - this.#focusInput(); - return; - } - - const tab = await this._workspaceContext?.structure.createContainer(null, null, 'Tab'); - if (tab) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); - window.history.replaceState(null, '', path); - this.#focusInput(); - } - } - - async #focusInput() { - setTimeout(() => { - (this.shadowRoot?.querySelector('uui-tab[active] uui-input') as UUIInputElement | undefined)?.focus(); - }, 100); - } - - async #tabNameChanged(event: InputEvent, tab: PropertyTypeContainerModelBaseModel) { - if (this._buttonDisabled) this._buttonDisabled = !this._buttonDisabled; - let newName = (event.target as HTMLInputElement).value; - - if (newName === '') { - newName = 'Unnamed'; - (event.target as HTMLInputElement).value = 'Unnamed'; - } - - const changedName = this._workspaceContext?.structure.makeContainerNameUniqueForOwnerContentType( - newName, - 'Tab', - tab.id, - ); - - // Check if it collides with another tab name of this same member-type, if so adjust name: - if (changedName) { - newName = changedName; - (event.target as HTMLInputElement).value = newName; - } - - this._tabsStructureHelper.partialUpdateContainer(tab.id!, { - name: newName, - }); - - // Update the current URL, so we are still on this specific tab: - window.history.replaceState(null, '', this._routerPath + '/tab/' + encodeFolderName(newName)); - } - - async #openCompositionModal() { - throw new Error('Not implemented'); - // const modalContext = this._modalManagerContext?.open(UMB_COMPOSITION_PICKER_MODAL, { - // data: this._compositionConfiguration, - // }); - // await modalContext?.onSubmit(); - - // if (!modalContext?.value) return; - - // const compositionIds = modalContext.getValue().selection; - - // this._workspaceContext?.setCompositions( - // compositionIds.map((unique) => ({ contentType: { unique }, compositionType: CompositionTypeModel.COMPOSITION })), - // ); - } - - render() { - return html` - - - { - this._routerPath = event.target.absoluteRouterPath; - }} - @change=${(event: UmbRouterSlotChangeEvent) => { - this._activePath = event.target.absoluteActiveViewPath || ''; - }}> - - - `; - } - - renderAddButton() { - if (this._sortModeActive) return; - return html` - - Add tab - `; - } - - renderActions() { - const sortButtonText = this._sortModeActive - ? this.localize.term('general_reorderDone') - : this.localize.term('general_reorder'); - - return html`
    - - - ${this.localize.term('contentTypeEditor_compositions')} - - - - ${sortButtonText} - -
    `; - } - - renderTabsNavigation() { - if (!this._tabs) return; - - return html`
    - - ${this.renderRootTab()} - ${repeat( - this._tabs, - (tab) => tab.id! + tab.name, - (tab) => this.renderTab(tab), - )} - -
    `; - } - - renderRootTab() { - const rootTabPath = this._routerPath + '/root'; - const rootTabActive = rootTabPath === this._activePath; - return html` - ${this.localize.term('general_content')} - `; - } - - renderTab(tab: PropertyTypeContainerModelBaseModel) { - const path = this._routerPath + '/tab/' + encodeFolderName(tab.name || ''); - const tabActive = path === this._activePath; - const tabInherited = !this._tabsStructureHelper.isOwnerChildContainer(tab.id!); - - return html` - ${this.renderTabInner(tab, tabActive, tabInherited)} - `; - } - - renderTabInner(tab: PropertyTypeContainerModelBaseModel, tabActive: boolean, tabInherited: boolean) { - if (this._sortModeActive) { - return html`
    - ${tabInherited - ? html`${tab.name!}` - : html` ${tab.name!} - this.#changeOrderNumber(tab, e)}>`} -
    `; - } - - if (tabActive && !tabInherited) { - return html`
    - this.#tabNameChanged(e, tab)} - @blur=${(e: InputEvent) => this.#tabNameChanged(e, tab)} - @input=${() => (this._buttonDisabled = true)} - @focus=${(e: UUIInputEvent) => (e.target.value ? nothing : (this._buttonDisabled = true))}> - ${this.renderDeleteFor(tab)} - -
    `; - } - - if (tabInherited) { - return html`
    ${tab.name!}
    `; - } else { - return html`
    ${tab.name!} ${this.renderDeleteFor(tab)}
    `; - } - } - - #changeOrderNumber(tab: PropertyTypeContainerModelBaseModel, e: UUIInputEvent) { - if (!e.target.value || !tab.id) return; - const sortOrder = Number(e.target.value); - this._tabsStructureHelper.partialUpdateContainer(tab.id, { sortOrder }); - } - - renderDeleteFor(tab: PropertyTypeContainerModelBaseModel) { - return html` this.#requestRemoveTab(tab)} - compact> - - `; - } - - static styles = [ - UmbTextStyles, - css` - #buttons-wrapper { - flex: 1; - display: flex; - align-items: center; - justify-content: space-between; - align-items: stretch; - } - - :host { - position: relative; - display: flex; - flex-direction: column; - height: 100%; - --uui-tab-background: var(--uui-color-surface); - } - - [drag-placeholder] { - opacity: 0.5; - } - - [drag-placeholder] uui-input { - visibility: hidden; - } - - /* TODO: This should be replaced with a general workspace bar — naming is hard */ - - #header { - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - flex-wrap: nowrap; - } - - .flex { - display: flex; - } - - uui-tab-group { - flex-wrap: nowrap; - } - - .content-tab-is-empty { - align-self: center; - border-radius: 3px; - --uui-tab-text: var(--uui-color-text-alt); - border: dashed 1px var(--uui-color-border-emphasis); - } - - uui-tab { - position: relative; - border-left: 1px hidden transparent; - border-right: 1px solid var(--uui-color-border); - } - - .no-edit uui-input { - pointer-events: auto; - } - - .no-edit { - pointer-events: none; - display: inline-flex; - padding-left: var(--uui-size-space-3); - border: 1px solid transparent; - align-items: center; - gap: var(--uui-size-space-3); - } - - .trash { - opacity: 1; - transition: opacity 120ms; - } - - uui-tab:not(:hover, :focus) .trash { - opacity: 0; - transition: opacity 120ms; - } - - uui-input:not(:focus, :hover) { - border: 1px solid transparent; - } - - .inherited { - vertical-align: sub; - } - - [drag-placeholder] { - opacity: 0.2; - } - `, - ]; -} - -export default UmbMemberTypeWorkspaceViewEditElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-member-type-workspace-view-edit': UmbMemberTypeWorkspaceViewEditElement; - } -} From 7162746188e105072d6f7eede3211655b50c6537 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:25:52 +0100 Subject: [PATCH 158/285] generalise a bit of logic to select the app_language by default --- .../document-publish-modal.element.ts | 13 ------------- .../document-schedule-modal.element.ts | 15 +-------------- .../document-variant-language-picker.element.ts | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts index bc944c258b..de30402886 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.element.ts @@ -1,12 +1,9 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; import type { UmbDocumentPublishModalData, UmbDocumentPublishModalValue } from './document-publish-modal.token.js'; import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; -import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import '../shared/document-variant-language-picker.element.js'; @@ -39,16 +36,6 @@ export class UmbDocumentPublishModalElement extends UmbModalBaseElement< // Filter selection based on options: selected = selected.filter((s) => this._options.some((o) => o.unique === s)); - // If no selections were provided, select the app language by default: - if (selected.length === 0) { - const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); - const appCulture = context.getAppCulture(); - // If the app language is one of the options, select it by default: - if (appCulture && this._options.some((o) => o.language.unique === appCulture)) { - selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); - } - } - this.#selectionManager.setSelection(selected); // Additionally select mandatory languages: diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index a5c7ac6baf..88284dd191 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -1,12 +1,9 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; import type { UmbDocumentScheduleModalData, UmbDocumentScheduleModalValue } from './document-schedule-modal.token.js'; -import { css, customElement, html, state } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; +import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; -import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import '../shared/document-variant-language-picker.element.js'; @@ -41,16 +38,6 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< // Filter selection based on options: selected = selected.filter((s) => this._options.some((o) => o.unique === s)); - // If no selections were provided, select the app language by default: - if (selected.length === 0) { - const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); - const appCulture = context.getAppCulture(); - // If the app language is one of the options, select it by default: - if (appCulture && this._options.some((o) => o.language.unique === appCulture)) { - selected = appendToFrozenArray(selected, new UmbVariantId(appCulture, null).toString()); - } - } - this.#selectionManager.setSelection(selected); // Additionally select mandatory languages: diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts index 6f6dda2d78..54db4fab0d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -1,8 +1,11 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; +import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; +import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; @customElement('umb-document-variant-language-picker') export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { @@ -16,8 +19,18 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { this.#selectionManager = value; this.observe( this.selectionManager.selection, - (selection) => { + async (selection) => { this._selection = selection; + + // If no selections were provided, select the app language by default: + if (!this._selection.length) { + const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + const appCulture = context.getAppCulture(); + // If the app language is one of the options, select it by default: + if (appCulture && this.variantLanguageOptions.some((o) => o.language.unique === appCulture)) { + this._selection = appendToFrozenArray(this._selection, new UmbVariantId(appCulture, null).toString()); + } + } }, '_selectionManager', ); From 2fde1d543a923f3d09b861227f8c734cd3373452 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:35:45 +0100 Subject: [PATCH 159/285] move logic to publish action --- .../entity-actions/publish.action.ts | 24 +++++++++++-------- ...ocument-variant-language-picker.element.ts | 15 +----------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts index d68135cddb..8435ba0eac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/publish.action.ts @@ -1,19 +1,15 @@ import { UMB_DOCUMENT_PUBLISH_MODAL } from '../modals/publish-modal/index.js'; import { UmbDocumentDetailRepository, UmbDocumentPublishingRepository } from '../repository/index.js'; import type { UmbDocumentVariantOptionModel } from '../types.js'; -import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; +import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -export type UmbPublishDocumentEntityActionMeta = { - allowScheduledPublish: boolean; -}; - -export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { +export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { + constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { super(host, args); } @@ -44,15 +40,23 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase = []; + const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + const appCulture = context.getAppCulture(); + // If the app language is one of the options, select it by default: + if (appCulture && options.some((o) => o.unique === appCulture)) { + selection.push(new UmbVariantId(appCulture, null).toString()); + } + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); const result = await modalManagerContext .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { data: { options, - allowScheduledPublish: this.args.meta.allowScheduledPublish, }, - // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] - value: { selection: [] }, + value: { selection }, }) .onSubmit() .catch(() => undefined); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts index 54db4fab0d..16ae8a43f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -1,11 +1,8 @@ import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; -import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { css, customElement, html, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; -import { appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; -import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; @customElement('umb-document-variant-language-picker') export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { @@ -21,16 +18,6 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { this.selectionManager.selection, async (selection) => { this._selection = selection; - - // If no selections were provided, select the app language by default: - if (!this._selection.length) { - const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); - const appCulture = context.getAppCulture(); - // If the app language is one of the options, select it by default: - if (appCulture && this.variantLanguageOptions.some((o) => o.language.unique === appCulture)) { - this._selection = appendToFrozenArray(this._selection, new UmbVariantId(appCulture, null).toString()); - } - } }, '_selectionManager', ); @@ -52,7 +39,7 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { label=${option.variant?.name ?? option.language.name} @selected=${() => this.selectionManager.select(option.unique)} @deselected=${() => this.selectionManager.deselect(option.unique)} - ?selected=${this._selection.includes(option.language.unique)}> + ?selected=${this._selection.includes(option.unique)}> ${this.#renderLabel(option)} From 4dac719f0c30215e32b5919d39830c34f1b14ed6 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:37:34 +0100 Subject: [PATCH 160/285] remove segment --- .../document-publish-modal.stories.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts index 197e56fc6e..904d767b16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/publish-modal/document-publish-modal.stories.ts @@ -31,13 +31,13 @@ const modalData: UmbDocumentPublishModalData = { }, }, { - unique: 'en-us-gtm', - culture: 'en-us', - segment: 'GTM', + unique: 'en-gb', + culture: 'en-gb', + segment: null, variant: { - name: 'English', + name: 'English (GB)', culture: 'en-us', - segment: 'GTM', + segment: null, state: UmbDocumentVariantState.DRAFT, createDate: '2021-08-25T14:00:00Z', publishDate: null, @@ -45,10 +45,10 @@ const modalData: UmbDocumentPublishModalData = { }, language: { entityType: 'language', - name: 'English', - unique: 'en-us', + name: 'English (GB)', + unique: 'en-gb', isDefault: true, - isMandatory: true, + isMandatory: false, fallbackIsoCode: null, }, }, From 79390bc16a9fffef23a0eb6469f9061f01492617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 14:55:30 +0100 Subject: [PATCH 161/285] add margin to tab properties --- .../views/design/content-type-design-editor-tab.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts index d0408772b4..945b2d806e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts @@ -235,6 +235,7 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { margin-top: var(--uui-size-layout-1); } + uui-box, umb-content-type-design-editor-group { margin-bottom: var(--uui-size-layout-1); } From 0ca3ebe47ed716345a545cd83fbdc0a4f5ca3510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 14:58:50 +0100 Subject: [PATCH 162/285] clean up --- ...t-type-container-structure-helper.class.ts | 41 ------------------- .../content-type-design-editor.element.ts | 1 + 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 6e4cb4d9df..69e27d15d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -119,27 +119,12 @@ export class UmbContentTypeContainerStructureHelper { - this._parentContainers = containers ?? []; - console.log('root parent containers', this._parentContainers); - if (this._parentContainers.length !== 1) { - console.log( - '!!! We did not just get one parentContainer, I would have expected this, so I have to re-evaluate my understanding. !!!', - ); - } - this._parentAlikeContainers = []; - }, - '_observeParentContainers', - );*/ } else if (this._parentName && this._parentType) { this.#containers.setValue([]); this.observe( this.#structure.containersByNameAndType(this._parentName, this._parentType), (parentContainers) => { this.#containers.setValue([]); - //this._parentOwnerContainers = parentContainers.filter((x) => x.id === this._parentId) || []; // Stop observing a the previous _parentMatchingContainers... this._parentMatchingContainers.forEach((container) => { this.removeControllerByAlias('_observeParentHasProperties_' + container.id); @@ -243,38 +228,12 @@ export class UmbContentTypeContainerStructureHelper { - // TODO: This place needs to be more intelligent about the existing containers. Now it only takes care of it self. [NL] - return childContainers.flatMap((group, i, value) => { - if (group.name !== null && group.name !== undefined) { - if (value.find((x) => x.name === group.name)) { - return group; - } - } - return []; - }); - }*/ - - /** - * Returns true if the container is an owner container. - */ - /* - isOwnerContainer(containerId?: string) { - if (!this.#structure || !containerId) return; - - return this._parentOwnerContainers.find((x) => x.id === containerId) !== undefined; - } - */ /** * Returns true if the container is an owner container. */ isOwnerChildContainer(containerId?: string) { if (!this.#structure || !containerId) return; - console.log('isOwnerChildContainer: ', this._parentId, this._ownerContainers, containerId); return this._ownerContainers.some((x) => x.id === containerId); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts index 537b7008fd..1579961358 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -122,6 +122,7 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements this._tabsStructureHelper.setIsRoot(true); this._tabsStructureHelper.setContainerChildType('Tab'); this.observe(this._tabsStructureHelper.containers, (tabs) => { + console.log('tabs', tabs); this._tabs = tabs; this.#sorter.setModel(tabs); this._createRoutes(); From 21c940ba9df5d3c92a3d755d1ee0ebd64621ab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 15:06:11 +0100 Subject: [PATCH 163/285] more clean up --- .../structure/content-type-structure-manager.class.ts | 1 - .../workspace/views/design/content-type-design-editor.element.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts index 2290534137..83aecf3ef1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-structure-manager.class.ts @@ -601,7 +601,6 @@ export class UmbContentTypePropertyStructureManager (parentId ? x.parent?.id === parentId : x.parent === null) && x.type === containerType, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts index 1579961358..f8c8d0ada3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -422,7 +422,6 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements const path = this._routerPath + (tab.name ? '/tab/' + encodeFolderName(tab.name) : '/tab'); const tabActive = path === this._activePath; const ownedTab = this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ?? false; - console.log('ownedTab', ownedTab); return html` Date: Wed, 13 Mar 2024 15:15:56 +0100 Subject: [PATCH 164/285] make methods static --- .../document-variant-language-picker.element.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts index 16ae8a43f5..fdad3ddf16 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/shared/document-variant-language-picker.element.ts @@ -41,18 +41,18 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement { @deselected=${() => this.selectionManager.deselect(option.unique)} ?selected=${this._selection.includes(option.unique)}> - ${this.#renderLabel(option)} + ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)} `, ); } - #renderLabel(option: UmbDocumentVariantOptionModel) { + static renderLabel(option: UmbDocumentVariantOptionModel) { return html`
    ${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ?? option.language.name} -
    ${this.#renderVariantStatus(option)}
    +
    ${UmbDocumentVariantLanguagePickerElement.renderVariantStatus(option)}
    ${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED ? html`
    Mandatory language @@ -61,17 +61,17 @@ export class UmbDocumentVariantLanguagePickerElement extends UmbLitElement {
    `; } - #renderVariantStatus(option: UmbDocumentVariantOptionModel) { + static renderVariantStatus(option: UmbDocumentVariantOptionModel) { switch (option.variant?.state) { case UmbDocumentVariantState.PUBLISHED: - return this.localize.term('content_published'); + return html`Published`; case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES: - return this.localize.term('content_publishedPendingChanges'); + return html`Published with pending changes`; case UmbDocumentVariantState.DRAFT: - return this.localize.term('content_unpublished'); + return html`Draft`; case UmbDocumentVariantState.NOT_CREATED: default: - return this.localize.term('content_notCreated'); + return html`Not created`; } } From dde73a53eb26a60fbcd6274db41462b9036f1555 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:19:35 +0100 Subject: [PATCH 165/285] pass schedule through value --- .../documents/workspace/document-workspace.context.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 9ab9be63d4..375bb5884d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -556,21 +556,20 @@ export class UmbDocumentWorkspaceContext data: { options, }, - value: { selection: selected }, + value: { selection: selected.map((unique) => ({ unique, schedule: {} })) }, }) .onSubmit() .catch(() => undefined); - if (!result?.selection.length || !result?.schedule) return; + if (!result?.selection.length) return; - const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? []; - const schedule = result?.schedule; + const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x.unique)) ?? []; - if (!variantIds.length || !schedule) return; + if (!variantIds.length) return; const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); - await this.publishingRepository.publish(unique, variantIds, schedule); + await this.publishingRepository.publish(unique, variantIds); const data = this.getData(); if (!data) throw new Error('Data is missing'); From 55ab10ff8b8fe8e9d4de30d594af00356968dbb8 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:01:15 +0100 Subject: [PATCH 166/285] ensure we use the correct type including the publish schedule when publishing --- .../src/packages/core/variant/index.ts | 2 +- .../src/packages/core/variant/types.ts | 7 +++++++ .../documents/entity-actions/publish.action.ts | 7 +++++-- .../src/packages/documents/documents/index.ts | 2 +- .../document-publishing.repository.ts | 8 ++++---- .../document-publishing.server.data-source.ts | 11 +++++------ .../src/packages/documents/documents/types.ts | 4 +++- .../workspace/document-workspace.context.ts | 17 +++++++++++++---- 8 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/variant/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/variant/index.ts index cf4b565e59..64906c41c0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/variant/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/variant/index.ts @@ -1,2 +1,2 @@ export * from './variant-id.class.js'; -export * from './types.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/variant/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/variant/types.ts index 13b41b3079..f0703003f6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/variant/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/variant/types.ts @@ -1,4 +1,6 @@ +import type { UmbVariantId } from './variant-id.class.js'; import type { UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language'; +import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; export interface UmbVariantModel { createDate: string | null; @@ -18,3 +20,8 @@ export interface UmbVariantOptionModel if (documentData.variants.length === 1) { const variantId = UmbVariantId.Create(documentData.variants[0]); const publishingRepository = new UmbDocumentPublishingRepository(this._host); - await publishingRepository.publish(this.args.unique, [variantId]); + await publishingRepository.publish(this.args.unique, [{ variantId }]); return; } @@ -67,7 +67,10 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase if (variantIds.length) { const publishingRepository = new UmbDocumentPublishingRepository(this._host); - await publishingRepository.publish(this.args.unique, variantIds); + await publishingRepository.publish( + this.args.unique, + variantIds.map((variantId) => ({ variantId })), + ); } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts index f8e2d03e2d..b36b97c02f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/index.ts @@ -15,4 +15,4 @@ export * from './tree/index.js'; export { UMB_CONTENT_MENU_ALIAS } from './menu.manifests.js'; export { UMB_DOCUMENT_COLLECTION_ALIAS } from './collection/index.js'; -export type { UmbDocumentVariantModel } from './types.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts index ce888ede4e..d72e0d1ead 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.repository.ts @@ -1,9 +1,9 @@ +import type { UmbDocumentVariantPublishModel } from '../../types.js'; import { UmbDocumentPublishingServerDataSource } from './document-publishing.server.data-source.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbRepositoryBase } from '@umbraco-cms/backoffice/repository'; import { UMB_NOTIFICATION_CONTEXT, type UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; export class UmbDocumentPublishingRepository extends UmbRepositoryBase { #init!: Promise; @@ -29,12 +29,12 @@ export class UmbDocumentPublishingRepository extends UmbRepositoryBase { * @return {*} * @memberof UmbDocumentPublishingRepository */ - async publish(unique: string, variantIds: Array, schedule?: ScheduleRequestModel) { + async publish(unique: string, variants: Array) { if (!unique) throw new Error('id is missing'); - if (!variantIds) throw new Error('variant IDs are missing'); + if (!variants.length) throw new Error('variant IDs are missing'); await this.#init; - const { error } = await this.#publishingDataSource.publish(unique, variantIds, schedule); + const { error } = await this.#publishingDataSource.publish(unique, variants); if (!error) { const notification = { data: { message: `Document published` } }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts index 9c0943c3c9..eb3efdf3f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/repository/publishing/document-publishing.server.data-source.ts @@ -1,7 +1,7 @@ +import type { UmbDocumentVariantPublishModel } from '../../types.js'; import type { CultureAndScheduleRequestModel, PublishDocumentRequestModel, - ScheduleRequestModel, UnpublishDocumentRequestModel, } from '@umbraco-cms/backoffice/external/backend-api'; import { DocumentResource } from '@umbraco-cms/backoffice/external/backend-api'; @@ -34,15 +34,14 @@ export class UmbDocumentPublishingServerDataSource { * @return {*} * @memberof UmbDocumentServerDataSource */ - async publish(unique: string, variantIds: Array, schedule?: ScheduleRequestModel) { + async publish(unique: string, variants: Array) { if (!unique) throw new Error('Id is missing'); - const publishSchedules: CultureAndScheduleRequestModel[] = variantIds.map( + const publishSchedules: CultureAndScheduleRequestModel[] = variants.map( (variant) => { return { - culture: variant.isCultureInvariant() ? null : variant.toCultureString(), - // TODO: NO, this does not belong as part of the UmbVariantID, we need another way to parse that around: - schedule, + culture: variant.variantId.isCultureInvariant() ? null : variant.variantId.toCultureString(), + schedule: variant.schedule, }; }, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts index 3ba9e2e0af..8c5903cfa5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/types.ts @@ -1,5 +1,5 @@ import type { UmbDocumentEntityType } from './entity.js'; -import type { UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant'; +import type { UmbVariantModel, UmbVariantOptionModel, UmbVariantPublishModel } from '@umbraco-cms/backoffice/variant'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; import { DocumentVariantStateModel as UmbDocumentVariantState } from '@umbraco-cms/backoffice/external/backend-api'; export { UmbDocumentVariantState }; @@ -36,3 +36,5 @@ export interface UmbDocumentValueModel { } export interface UmbDocumentVariantOptionModel extends UmbVariantOptionModel {} + +export interface UmbDocumentVariantPublishModel extends UmbVariantPublishModel {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts index 375bb5884d..b20a8dfed1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts @@ -3,6 +3,7 @@ import { UmbDocumentPropertyDataContext } from '../property-dataset-context/docu import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import { UmbDocumentDetailRepository } from '../repository/index.js'; import type { + UmbDocumentVariantPublishModel, UmbDocumentDetailModel, UmbDocumentValueModel, UmbDocumentVariantModel, @@ -494,7 +495,10 @@ export class UmbDocumentWorkspaceContext } const variants = await this.#performSaveOrCreate(variantIds); - await this.publishingRepository.publish(unique, variants); + await this.publishingRepository.publish( + unique, + variants.map((variantId) => ({ variantId })), + ); this.workspaceComplete(this.#currentData.getValue()); } @@ -563,13 +567,18 @@ export class UmbDocumentWorkspaceContext if (!result?.selection.length) return; - const variantIds = result?.selection.map((x) => UmbVariantId.FromString(x.unique)) ?? []; + // Map to the correct format for the API (UmbDocumentVariantPublishModel) + const variants = + result?.selection.map((x) => ({ + variantId: UmbVariantId.FromString(x.unique), + schedule: x.schedule, + })) ?? []; - if (!variantIds.length) return; + if (!variants.length) return; const unique = this.getUnique(); if (!unique) throw new Error('Unique is missing'); - await this.publishingRepository.publish(unique, variantIds); + await this.publishingRepository.publish(unique, variants); const data = this.getData(); if (!data) throw new Error('Data is missing'); From a3d559594971f5d1e114a8fe4afd5a35de830bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 16:02:13 +0100 Subject: [PATCH 167/285] filter root containers for proper merge --- ...t-type-container-structure-helper.class.ts | 26 ++++++++++++------- .../content-type-design-editor-tab.element.ts | 1 - .../content-type-design-editor.element.ts | 3 ++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 69e27d15d4..280f05e183 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -204,14 +204,13 @@ export class UmbContentTypeContainerStructureHelper) { return this._ownerContainers.length > 0 - ? containers.filter((anyCon) => - this._ownerContainers.some( - (ownerCon) => - // We would like to keep the owner container in the anyCons, so do not filter that - ownerCon.id === anyCon.id || - // Then if this is not the owner container but matches by name & type, then we do not want it. - !(ownerCon.id !== anyCon.id && ownerCon.name === anyCon.name && ownerCon.type === anyCon.type), - ), + ? containers.filter( + (anyCon) => + !this._ownerContainers.some( + (ownerCon) => + // Then if this is not the owner container but matches one by name & type, then we do not want it. + ownerCon.id !== anyCon.id && ownerCon.name === anyCon.name && ownerCon.type === anyCon.type, + ), ) : containers; } @@ -222,8 +221,17 @@ export class UmbContentTypeContainerStructureHelper { - this.#containers.setValue(rootContainers); + // Here (When getting root containers) we get containers from all ContentTypes. It also means we need to do an extra filtering to ensure we only get one of each containers. [NL] + + // For that we get the owner containers first (We do not need to observe as this observation will be triggered if one of the owner containers change) [NL] this._ownerContainers = this.#structure!.getOwnerContainers(this._childType!, this._parentId!) ?? []; + + // Then we filter out the duplicate containers based on type and name: + rootContainers = rootContainers.filter( + (x, i, cons) => i === cons.findIndex((y) => y.name === x.name && y.type === x.type), + ); + + this.#containers.setValue(this.#filterNonOwnerContainers(rootContainers)); }, '_observeRootContainers', ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts index 945b2d806e..0c56d734ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts @@ -150,7 +150,6 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { this.observe(this.#groupStructureHelper.containers, (groups) => { this._groups = groups; this.#sorter.setModel(this._groups); - this.requestUpdate('_groups'); }); this.observe(this.#groupStructureHelper.hasProperties, (hasProperties) => { this._hasProperties = hasProperties; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts index f8c8d0ada3..230e8531e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -122,7 +122,6 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements this._tabsStructureHelper.setIsRoot(true); this._tabsStructureHelper.setContainerChildType('Tab'); this.observe(this._tabsStructureHelper.containers, (tabs) => { - console.log('tabs', tabs); this._tabs = tabs; this.#sorter.setModel(tabs); this._createRoutes(); @@ -215,6 +214,8 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements this._activePath = this._routerPath + newPath; // Update the current URL, so we are still on this specific tab: window.history.replaceState(null, '', this._activePath); + // TODO: We have some flickering when renaming, this could potentially be fixed if we cache the view and re-use it if the same is requested [NL] + // Or maybe its just about we just send the updated tabName to the view, and let it handle the update itself [NL] } } } From e8a8c8abe6c4e0102568504eb7bbaca443f8c563 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:05:20 +0100 Subject: [PATCH 168/285] update value for scheduling --- .../document-schedule-modal.element.ts | 116 ++++++++++++++++-- .../document-schedule-modal.token.ts | 11 +- 2 files changed, 112 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index 88284dd191..adff86a416 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -1,12 +1,11 @@ +import { UmbDocumentVariantLanguagePickerElement } from '../shared/document-variant-language-picker.element.js'; import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js'; import type { UmbDocumentScheduleModalData, UmbDocumentScheduleModalValue } from './document-schedule-modal.token.js'; -import { css, customElement, html, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; -import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; - -import '../shared/document-variant-language-picker.element.js'; +import type { UmbInputDateElement } from '@umbraco-cms/backoffice/components'; @customElement('umb-document-schedule-modal') export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< @@ -14,11 +13,26 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< UmbDocumentScheduleModalValue > { #selectionManager = new UmbSelectionManager(this); - #schedule?: ScheduleRequestModel; @state() _options: Array = []; + @state() + _selection: UmbDocumentScheduleModalValue['selection'] = []; + + constructor() { + super(); + this.observe( + this.#selectionManager.selection, + (selection) => { + this._selection = selection.map((unique) => { + return { unique, schedule: {} }; + }); + }, + '_selection', + ); + } + firstUpdated() { this.#configureSelectionManager(); } @@ -36,9 +50,9 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< let selected = this.value?.selection ?? []; // Filter selection based on options: - selected = selected.filter((s) => this._options.some((o) => o.unique === s)); + selected = selected.filter((s) => this._options.some((o) => o.unique === s.unique)); - this.#selectionManager.setSelection(selected); + this.#selectionManager.setSelection(selected.map((s) => s.unique)); // Additionally select mandatory languages: this._options.forEach((variant) => { @@ -49,7 +63,7 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< } #submit() { - this.value = { selection: this.#selectionManager.getSelection(), schedule: this.#schedule }; + this.value = { selection: this._selection }; this.modalContext?.submit(); } @@ -57,14 +71,27 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< this.modalContext?.reject(); } + #isSelected(unique: string) { + return this._selection.some((s) => s.unique === unique); + } + render() { return html`

    - Which languages would you like to schedule? + ${when( + this._options.length > 1, + () => html` + Which languages would you like to schedule? + `, + () => html` + + Select the date and time to publish and/or unpublish the content item. + + `, + )}

    - + + ${this.#renderOptions()}
    @@ -77,6 +104,56 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< `; } + #renderOptions() { + return repeat( + this._options, + (option) => option.unique, + (option) => html` + this.#selectionManager.select(option.unique)} + @deselected=${() => this.#selectionManager.deselect(option.unique)} + ?selected=${this.#isSelected(option.unique)}> + + ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)} + + ${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option.unique))} + `, + ); + } + + #renderPublishDateInput(unique: string) { + return html`
    + this.#onFromDateChange(e, unique)} + label=${this.localize.term('general_publishDate')}> + this.#onToDateChange(e, unique)} + label=${this.localize.term('general_publishDate')}> +
    `; + } + + #onFromDateChange(e: Event, unique: string) { + const variant = this._selection.find((s) => s.unique === unique); + if (variant) { + variant.schedule = { + ...variant.schedule, + publishTime: (e.target as UmbInputDateElement).value.toString(), + }; + } + } + + #onToDateChange(e: Event, unique: string) { + const variant = this._selection.find((s) => s.unique === unique); + if (variant) { + variant.schedule = { + ...variant.schedule, + unpublishTime: (e.target as UmbInputDateElement).value.toString(), + }; + } + } + static styles = [ UmbTextStyles, css` @@ -85,6 +162,21 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< width: 400px; max-width: 90vw; } + + .label { + padding: 0.5rem 0; + } + .label-status { + font-size: 0.8rem; + } + + .publish-date { + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 1rem; + margin: 0.5rem 0; + } `, ]; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts index 0007d4d7e6..326cc01342 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.token.ts @@ -1,12 +1,17 @@ -import type { UmbDocumentVariantPickerData, UmbDocumentVariantPickerValue } from '../types.js'; +import type { UmbDocumentVariantPickerData } from '../types.js'; import { UMB_DOCUMENT_SCHEDULE_MODAL_ALIAS } from '../manifests.js'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; import type { ScheduleRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; +export interface UmbDocumentScheduleSelectionModel { + unique: string; + schedule?: ScheduleRequestModel | null; +} + export interface UmbDocumentScheduleModalData extends UmbDocumentVariantPickerData {} -export interface UmbDocumentScheduleModalValue extends UmbDocumentVariantPickerValue { - schedule?: ScheduleRequestModel; +export interface UmbDocumentScheduleModalValue { + selection: Array; } export const UMB_DOCUMENT_SCHEDULE_MODAL = new UmbModalToken< From 4aa4a7225e87b842e0d9db35bc321786c80e06ef Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:07:57 +0100 Subject: [PATCH 169/285] render publish date based on option --- .../schedule-modal/document-schedule-modal.element.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index adff86a416..06556f425b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -118,18 +118,18 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< ${UmbDocumentVariantLanguagePickerElement.renderLabel(option)} - ${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option.unique))} + ${when(this.#isSelected(option.unique), () => this.#renderPublishDateInput(option))} `, ); } - #renderPublishDateInput(unique: string) { + #renderPublishDateInput(option: UmbDocumentVariantOptionModel) { return html`
    this.#onFromDateChange(e, unique)} + @change=${(e: Event) => this.#onFromDateChange(e, option.unique)} label=${this.localize.term('general_publishDate')}> this.#onToDateChange(e, unique)} + @change=${(e: Event) => this.#onToDateChange(e, option.unique)} label=${this.localize.term('general_publishDate')}>
    `; } From 819062af42fa4b03ba7b1a18d7936cf8af9e7fce Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:08:08 +0100 Subject: [PATCH 170/285] add variant data and styling to the story --- .../document-schedule-modal.stories.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts index 64a9f88a55..7ee6a08e9a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts @@ -30,6 +30,28 @@ const modalData: UmbDocumentScheduleModalData = { fallbackIsoCode: null, }, }, + { + unique: 'da-dk', + culture: 'da-dk', + segment: null, + variant: { + name: 'Danish variant name', + culture: 'da-dk', + state: UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES, + createDate: '2021-08-25T14:00:00Z', + publishDate: null, + updateDate: null, + segment: null, + }, + language: { + entityType: 'language', + name: 'Danish', + unique: 'da-dk', + isDefault: true, + isMandatory: false, + fallbackIsoCode: null, + }, + }, ], }; @@ -45,7 +67,7 @@ const meta: Meta = { data: modalData, value: modalValue, }, - decorators: [(Story) => html`
    ${Story()}
    `], + decorators: [(Story) => html`
    ${Story()}
    `], parameters: { layout: 'centered', docs: { @@ -94,3 +116,11 @@ export default meta; type Story = StoryObj; export const Overview: Story = {}; + +export const WithoutVariants: Story = { + args: { + data: { + options: modalData.options.slice(0, 1), + }, + }, +}; From 2fd148c62439ec7e8ef7c2b4ea2c14c1d0e41c9e Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:08:56 +0100 Subject: [PATCH 171/285] update story example code --- .../schedule-modal/document-schedule-modal.stories.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts index 7ee6a08e9a..120f4833a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.stories.ts @@ -56,7 +56,7 @@ const modalData: UmbDocumentScheduleModalData = { }; const modalValue: UmbDocumentScheduleModalValue = { - selection: ['en-us'], + selection: [{ unique: 'en-us', schedule: null }], }; const meta: Meta = { @@ -103,7 +103,10 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => { }, }, ], - } + }, + value: { + selection: [{ unique: 'en-us', schedule: null }], + }, }).onSubmit().catch(() => undefined); }); `, From 11910bf62b8681a6bd2c23fb55289f6f7de65702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 16:16:25 +0100 Subject: [PATCH 172/285] correct document workspace --- ...t-type-container-structure-helper.class.ts | 2 +- .../content-type-design-editor-tab.element.ts | 2 - ...-workspace-view-edit-properties.element.ts | 35 ++++++++-------- ...ocument-workspace-view-edit-tab.element.ts | 41 ++++++++----------- .../document-workspace-view-edit.element.ts | 5 +-- 5 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts index 280f05e183..84a3502f60 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/structure/content-type-container-structure-helper.class.ts @@ -14,7 +14,7 @@ export class UmbContentTypeContainerStructureHelper; - private _parentType?: UmbPropertyContainerTypes; + private _parentType?: UmbPropertyContainerTypes = 'Tab'; private _childType?: UmbPropertyContainerTypes = 'Group'; private _isRoot = false; /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts index 0c56d734ef..198e4a118f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor-tab.element.ts @@ -128,8 +128,6 @@ export class UmbContentTypeDesignEditorTabElement extends UmbLitElement { constructor() { super(); - this.#groupStructureHelper.setParentType('Tab'); - this.consumeContext(UMB_CONTENT_TYPE_WORKSPACE_CONTEXT, (context) => { this.#groupStructureHelper.setStructureManager(context.structure); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts index 1c985a5601..9f8744e49b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-properties.element.ts @@ -4,38 +4,39 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyContainerTypes, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; import { UmbContentTypePropertyStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/document-type'; @customElement('umb-document-workspace-view-edit-properties') export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement { @property({ type: String, attribute: 'container-name', reflect: false }) public get containerName(): string | undefined { - return this._propertyStructureHelper.getContainerName(); + return this.#propertyStructureHelper.getContainerName(); } public set containerName(value: string | undefined) { - this._propertyStructureHelper.setContainerName(value); + this.#propertyStructureHelper.setContainerName(value); } @property({ type: String, attribute: 'container-type', reflect: false }) public get containerType(): UmbPropertyContainerTypes | undefined { - return this._propertyStructureHelper.getContainerType(); + return this.#propertyStructureHelper.getContainerType(); } public set containerType(value: UmbPropertyContainerTypes | undefined) { - this._propertyStructureHelper.setContainerType(value); + this.#propertyStructureHelper.setContainerType(value); } - _propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); + #propertyStructureHelper = new UmbContentTypePropertyStructureHelper(this); @state() - _propertyStructure: Array = []; + _propertyStructure?: Array; constructor() { super(); this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { - this._propertyStructureHelper.setStructureManager(workspaceContext.structure); + this.#propertyStructureHelper.setStructureManager(workspaceContext.structure); }); this.observe( - this._propertyStructureHelper.propertyStructure, + this.#propertyStructureHelper.propertyStructure, (propertyStructure) => { this._propertyStructure = propertyStructure; }, @@ -44,14 +45,16 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement } render() { - return repeat( - this._propertyStructure, - (property) => property.alias, - (property) => - html` `, - ); + return this._propertyStructure + ? repeat( + this._propertyStructure, + (property) => property.alias, + (property) => + html` `, + ) + : ''; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts index 8c3f3789dd..9d7674bd31 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts @@ -8,40 +8,33 @@ import type { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffic import './document-workspace-view-edit-properties.element.js'; @customElement('umb-document-workspace-view-edit-tab') export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { - private _tabName?: string | undefined; - @property({ type: String }) public get tabName(): string | undefined { - return this._groupStructureHelper.getName(); + return this.#groupStructureHelper.getName(); } public set tabName(value: string | undefined) { - if (value === this._tabName) return; - const oldValue = this._tabName; - this._tabName = value; - this._groupStructureHelper.setName(value); - this.requestUpdate('tabName', oldValue); + const oldName = this.#groupStructureHelper.getName(); + this.#groupStructureHelper.setName(value); + this.requestUpdate('tabName', oldName); } @property({ type: Boolean }) public get noTabName(): boolean { - return this._groupStructureHelper.getIsRoot(); + return this.#groupStructureHelper.getIsRoot(); } public set noTabName(value: boolean) { - this._groupStructureHelper.setIsRoot(value); + this.#groupStructureHelper.setIsRoot(value); } - private _ownerTabId?: string | null; @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; + public get containerId(): string | null | undefined { + return this.#groupStructureHelper.getParentId(); } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - this._ownerTabId = value; - this._groupStructureHelper.setParentId(value); + public set containerId(value: string | null | undefined) { + this.#groupStructureHelper.setParentId(value); } - _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); + #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @state() _groups: Array = []; @@ -53,17 +46,17 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { super(); this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { - this._groupStructureHelper.setStructureManager(workspaceContext.structure); + this.#groupStructureHelper.setStructureManager(workspaceContext.structure); }); this.observe( - this._groupStructureHelper.containers, + this.#groupStructureHelper.containers, (groups) => { this._groups = groups; }, null, ); this.observe( - this._groupStructureHelper.hasProperties, + this.#groupStructureHelper.hasProperties, (hasProperties) => { this._hasProperties = hasProperties; }, @@ -79,7 +72,7 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { + container-name=${this.tabName ?? ''}> ` : ''} @@ -87,11 +80,11 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { this._groups, (group) => group.id, (group) => - html` + html` + container-name=${group.name ?? ''}> `, )} `; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts index 31adb746b0..d209f9a9f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit.element.ts @@ -83,8 +83,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement implement (component as UmbDocumentWorkspaceViewEditTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. // Instead have the structure manager looking at wether one of the OwnerALikecontainers is in the owner document. - (component as UmbDocumentWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; + (component as UmbDocumentWorkspaceViewEditTabElement).containerId = tab.id; }, }); }); @@ -96,7 +95,7 @@ export class UmbDocumentWorkspaceViewEditElement extends UmbLitElement implement component: () => import('./document-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbDocumentWorkspaceViewEditTabElement).noTabName = true; - (component as UmbDocumentWorkspaceViewEditTabElement).ownerTabId = null; + (component as UmbDocumentWorkspaceViewEditTabElement).containerId = null; }, }); } From 967f618e79ef98248ec65b2c5c4e53ab862db253 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:19:53 +0100 Subject: [PATCH 173/285] style the publish fields --- .../document-schedule-modal.element.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index 06556f425b..1492320a5b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -125,12 +125,18 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< #renderPublishDateInput(option: UmbDocumentVariantOptionModel) { return html`
    - this.#onFromDateChange(e, option.unique)} - label=${this.localize.term('general_publishDate')}> - this.#onToDateChange(e, option.unique)} - label=${this.localize.term('general_publishDate')}> + + Publish at + this.#onFromDateChange(e, option.unique)} + label=${this.localize.term('general_publishDate')}> + + + Unpublish at + this.#onToDateChange(e, option.unique)} + label=${this.localize.term('general_publishDate')}> +
    `; } @@ -175,7 +181,18 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< flex-direction: row; justify-content: space-between; gap: 1rem; - margin: 0.5rem 0; + border-top: 1px solid var(--uui-color-border); + border-bottom: 1px solid var(--uui-color-border); + } + + .publish-date > uui-form-layout-item { + flex: 1; + margin: 0; + padding: 0.5rem 0 1rem; + } + + .publish-date > uui-form-layout-item:first-child { + border-right: 1px solid var(--uui-color-border); } `, ]; From d896d1a8e75fe172484b6a068b97fd10cb3e215b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 16:21:07 +0100 Subject: [PATCH 174/285] member + media corrections --- ...ocument-workspace-view-edit-tab.element.ts | 20 +++------- .../media-workspace-view-edit-tab.element.ts | 37 +++++++------------ .../edit/media-workspace-view-edit.element.ts | 5 +-- ...mber-workspace-view-content-tab.element.ts | 37 +++++++------------ .../member-workspace-view-content.element.ts | 5 +-- 5 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts index 9d7674bd31..447442f0d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/edit/document-workspace-view-edit-tab.element.ts @@ -48,20 +48,12 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { this.#groupStructureHelper.setStructureManager(workspaceContext.structure); }); - this.observe( - this.#groupStructureHelper.containers, - (groups) => { - this._groups = groups; - }, - null, - ); - this.observe( - this.#groupStructureHelper.hasProperties, - (hasProperties) => { - this._hasProperties = hasProperties; - }, - null, - ); + this.observe(this.#groupStructureHelper.containers, (groups) => { + this._groups = groups; + }); + this.observe(this.#groupStructureHelper.hasProperties, (hasProperties) => { + this._hasProperties = hasProperties; + }); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts index afcf72ebb3..f1c7e9a19f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit-tab.element.ts @@ -8,40 +8,33 @@ import type { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffic import './media-workspace-view-edit-properties.element.js'; @customElement('umb-media-workspace-view-edit-tab') export class UmbMediaWorkspaceViewEditTabElement extends UmbLitElement { - private _tabName?: string | undefined; - @property({ type: String }) public get tabName(): string | undefined { - return this._groupStructureHelper.getName(); + return this.#groupStructureHelper.getName(); } public set tabName(value: string | undefined) { - if (value === this._tabName) return; - const oldValue = this._tabName; - this._tabName = value; - this._groupStructureHelper.setName(value); - this.requestUpdate('tabName', oldValue); + const oldName = this.#groupStructureHelper.getName(); + this.#groupStructureHelper.setName(value); + this.requestUpdate('tabName', oldName); } @property({ type: Boolean }) public get noTabName(): boolean { - return this._groupStructureHelper.getIsRoot(); + return this.#groupStructureHelper.getIsRoot(); } public set noTabName(value: boolean) { - this._groupStructureHelper.setIsRoot(value); + this.#groupStructureHelper.setIsRoot(value); } - private _ownerTabId?: string | null; @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; + public get containerId(): string | null | undefined { + return this.#groupStructureHelper.getParentId(); } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - this._ownerTabId = value; - this._groupStructureHelper.setParentId(value); + public set containerId(value: string | null | undefined) { + this.#groupStructureHelper.setParentId(value); } - _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); + #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @state() _groups: Array = []; @@ -52,15 +45,13 @@ export class UmbMediaWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); - this._groupStructureHelper.setParentType('Tab'); - this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (workspaceContext) => { - this._groupStructureHelper.setStructureManager(workspaceContext.structure); + this.#groupStructureHelper.setStructureManager(workspaceContext.structure); }); - this.observe(this._groupStructureHelper.containers, (groups) => { + this.observe(this.#groupStructureHelper.containers, (groups) => { this._groups = groups; }); - this.observe(this._groupStructureHelper.hasProperties, (hasProperties) => { + this.observe(this.#groupStructureHelper.hasProperties, (hasProperties) => { this._hasProperties = hasProperties; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit.element.ts index 41f1c6e896..8f3f7f75ca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/edit/media-workspace-view-edit.element.ts @@ -78,8 +78,7 @@ export class UmbMediaWorkspaceViewEditElement extends UmbLitElement implements U setup: (component) => { (component as UmbMediaWorkspaceViewEditTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. - (component as UmbMediaWorkspaceViewEditTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; + (component as UmbMediaWorkspaceViewEditTabElement).containerId = tab.id; }, }); }); @@ -91,7 +90,7 @@ export class UmbMediaWorkspaceViewEditElement extends UmbLitElement implements U component: () => import('./media-workspace-view-edit-tab.element.js'), setup: (component) => { (component as UmbMediaWorkspaceViewEditTabElement).noTabName = true; - (component as UmbMediaWorkspaceViewEditTabElement).ownerTabId = null; + (component as UmbMediaWorkspaceViewEditTabElement).containerId = null; }, }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts index bcd9425a75..0256197809 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content-tab.element.ts @@ -8,40 +8,33 @@ import type { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffic import './member-workspace-view-content-properties.element.js'; @customElement('umb-member-workspace-view-content-tab') export class UmbMemberWorkspaceViewContentTabElement extends UmbLitElement { - private _tabName?: string | undefined; - @property({ type: String }) public get tabName(): string | undefined { - return this._groupStructureHelper.getName(); + return this.#groupStructureHelper.getName(); } public set tabName(value: string | undefined) { - if (value === this._tabName) return; - const oldValue = this._tabName; - this._tabName = value; - this._groupStructureHelper.setName(value); - this.requestUpdate('tabName', oldValue); + const oldName = this.#groupStructureHelper.getName(); + this.#groupStructureHelper.setName(value); + this.requestUpdate('tabName', oldName); } @property({ type: Boolean }) public get noTabName(): boolean { - return this._groupStructureHelper.getIsRoot(); + return this.#groupStructureHelper.getIsRoot(); } public set noTabName(value: boolean) { - this._groupStructureHelper.setIsRoot(value); + this.#groupStructureHelper.setIsRoot(value); } - private _ownerTabId?: string | null; @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; + public get containerId(): string | null | undefined { + return this.#groupStructureHelper.getParentId(); } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - this._ownerTabId = value; - this._groupStructureHelper.setParentId(value); + public set containerId(value: string | null | undefined) { + this.#groupStructureHelper.setParentId(value); } - _groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); + #groupStructureHelper = new UmbContentTypeContainerStructureHelper(this); @state() _groups: Array = []; @@ -52,15 +45,13 @@ export class UmbMemberWorkspaceViewContentTabElement extends UmbLitElement { constructor() { super(); - this._groupStructureHelper.setParentType('Tab'); - this.consumeContext(UMB_MEMBER_WORKSPACE_CONTEXT, (workspaceContext) => { - this._groupStructureHelper.setStructureManager(workspaceContext.structure); + this.#groupStructureHelper.setStructureManager(workspaceContext.structure); }); - this.observe(this._groupStructureHelper.containers, (groups) => { + this.observe(this.#groupStructureHelper.containers, (groups) => { this._groups = groups; }); - this.observe(this._groupStructureHelper.hasProperties, (hasProperties) => { + this.observe(this.#groupStructureHelper.hasProperties, (hasProperties) => { this._hasProperties = hasProperties; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content.element.ts index bf7b9bb5fc..be6129d3f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/content/member-workspace-view-content.element.ts @@ -78,8 +78,7 @@ export class UmbMemberWorkspaceViewEditElement extends UmbLitElement implements setup: (component) => { (component as UmbMemberWorkspaceViewContentTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. - (component as UmbMemberWorkspaceViewContentTabElement).ownerTabId = - this._tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; + (component as UmbMemberWorkspaceViewContentTabElement).containerId = tab.id; }, }); }); @@ -91,7 +90,7 @@ export class UmbMemberWorkspaceViewEditElement extends UmbLitElement implements component: () => import('./member-workspace-view-content-tab.element.js'), setup: (component) => { (component as UmbMemberWorkspaceViewContentTabElement).noTabName = true; - (component as UmbMemberWorkspaceViewContentTabElement).ownerTabId = null; + (component as UmbMemberWorkspaceViewContentTabElement).containerId = null; }, }); } From 30133ca7662bf3ddc56ffd911c6b99a9fea21db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 16:22:50 +0100 Subject: [PATCH 175/285] block corrections --- .../edit/block-workspace-view-edit-tab.element.ts | 11 +++-------- .../views/edit/block-workspace-view-edit.element.ts | 5 ++--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts index 15737c0b84..2518c814d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit-tab.element.ts @@ -45,14 +45,11 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { this.#groupStructureHelper.setIsRoot(value); } - private _ownerTabId?: string | null; @property({ type: String }) - public get ownerTabId(): string | null | undefined { - return this._ownerTabId; + public get containerId(): string | null | undefined { + return this.#groupStructureHelper.getParentId(); } - public set ownerTabId(value: string | null | undefined) { - if (value === this._ownerTabId) return; - this._ownerTabId = value; + public set containerId(value: string | null | undefined) { this.#groupStructureHelper.setParentId(value); } @@ -72,8 +69,6 @@ export class UmbBlockWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); - this.#groupStructureHelper.setParentType('Tab'); - this.consumeContext(UMB_BLOCK_WORKSPACE_CONTEXT, (workspaceContext) => { this.#blockWorkspace = workspaceContext; this.#setStructureManager(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts index 35d18e315c..c671edc40d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/views/edit/block-workspace-view-edit.element.ts @@ -98,8 +98,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U (component as UmbBlockWorkspaceViewEditTabElement).tabName = tabName; // TODO: Consider if we can link these more simple, and not parse this on. // Instead have the structure manager looking at wether one of the OwnerALikecontainers is in the owner document. - (component as UmbBlockWorkspaceViewEditTabElement).ownerTabId = - this.#tabsStructureHelper.isOwnerChildContainer(tab.id!) ? tab.id : undefined; + (component as UmbBlockWorkspaceViewEditTabElement).containerId = tab.id; }, }); }); @@ -112,7 +111,7 @@ export class UmbBlockWorkspaceViewEditElement extends UmbLitElement implements U setup: (component) => { (component as UmbBlockWorkspaceViewEditTabElement).managerName = this.#managerName; (component as UmbBlockWorkspaceViewEditTabElement).noTabName = true; - (component as UmbBlockWorkspaceViewEditTabElement).ownerTabId = null; + (component as UmbBlockWorkspaceViewEditTabElement).containerId = null; }, }); } From dd6c2421e7007a678ef2d7ec03179bbf430625f7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:24:51 +0100 Subject: [PATCH 176/285] dashed gap --- .../modals/schedule-modal/document-schedule-modal.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index 1492320a5b..8f68627ec2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -192,7 +192,7 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< } .publish-date > uui-form-layout-item:first-child { - border-right: 1px solid var(--uui-color-border); + border-right: 1px dashed var(--uui-color-border); } `, ]; From 3475de840f7227b902f1d4eb714586a4fb720b9b Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:31:49 +0100 Subject: [PATCH 177/285] form layout --- .../document-schedule-modal.element.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts index 8f68627ec2..e4dd00521d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/modals/schedule-modal/document-schedule-modal.element.ts @@ -126,16 +126,22 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< #renderPublishDateInput(option: UmbDocumentVariantOptionModel) { return html`
    - Publish at - this.#onFromDateChange(e, option.unique)} - label=${this.localize.term('general_publishDate')}> + Publish at +
    + this.#onFromDateChange(e, option.unique)} + label=${this.localize.term('general_publishDate')}> +
    - Unpublish at - this.#onToDateChange(e, option.unique)} - label=${this.localize.term('general_publishDate')}> + Unpublish at +
    + this.#onToDateChange(e, option.unique)} + label=${this.localize.term('general_publishDate')}> +
    `; } @@ -165,7 +171,7 @@ export class UmbDocumentScheduleModalElement extends UmbModalBaseElement< css` :host { display: block; - width: 400px; + min-width: 600px; max-width: 90vw; } From c912bbb2d024dfe5f9a15a60fc58280c682a77dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 13 Mar 2024 16:31:57 +0100 Subject: [PATCH 178/285] rename to property type settings modal --- .../core/content-type/modals/index.ts | 2 +- .../core/content-type/modals/manifests.ts | 6 +-- .../property-type-settings-modal.context.ts} | 2 +- .../property-type-settings-modal.element.ts} | 37 ++++++++++--------- .../property-type-settings-modal.token.ts} | 12 +++--- ...t-type-design-editor-properties.element.ts | 4 +- ...ent-type-design-editor-property.element.ts | 4 +- 7 files changed, 35 insertions(+), 32 deletions(-) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/{property-settings/property-settings-modal.context.ts => property-type-settings/property-type-settings-modal.context.ts} (89%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/{property-settings/property-settings-modal.element.ts => property-type-settings/property-type-settings-modal.element.ts} (93%) rename src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/{property-settings/property-settings-modal.token.ts => property-type-settings/property-type-settings-modal.token.ts} (53%) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts index e6a2919e14..15bf3de62f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/index.ts @@ -1,2 +1,2 @@ export * from './composition-picker/composition-picker-modal.token.js'; -export * from './property-settings/property-settings-modal.token.js'; +export * from './property-type-settings/property-type-settings-modal.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts index c5ceaaa20d..ce5f178b56 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/manifests.ts @@ -9,9 +9,9 @@ const modals: Array = [ }, { type: 'modal', - alias: 'Umb.Modal.PropertySettings', - name: 'Property Settings Modal', - js: () => import('.//property-settings/property-settings-modal.element.js'), + alias: 'Umb.Modal.PropertyTypeSettings', + name: 'Property Type Settings Modal', + js: () => import('./property-type-settings/property-type-settings-modal.element.js'), }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts index 82d2f7bc4f..e22f3f0aa2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts @@ -6,7 +6,7 @@ import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType'; /** - * This is a very simplified workspace context, just to serve one for the imitated property type workspace. (As its not a real workspace) + * This is a very simplified workspace context, just to serve one for the imitated property type workspace. (As its not a real workspace, but this does as well provide the ability for extension-conditions to match with this workspace, as entity type and alias is available.) [NL] */ export class UmbPropertyTypeWorkspaceContext extends UmbContextBase diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.element.ts similarity index 93% rename from src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.element.ts index f9ab69f77e..c28a24bcd3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-settings/property-settings-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.element.ts @@ -1,8 +1,11 @@ import { UMB_PROPERTY_TYPE_WORKSPACE_ALIAS, UmbPropertyTypeWorkspaceContext, -} from './property-settings-modal.context.js'; -import type { UmbPropertySettingsModalData, UmbPropertySettingsModalValue } from './property-settings-modal.token.js'; +} from './property-type-settings-modal.context.js'; +import type { + UmbPropertyTypeSettingsModalData, + UmbPropertyTypeSettingsModalValue, +} from './property-type-settings-modal.token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIBooleanInputEvent, UUIInputEvent, UUISelectEvent } from '@umbraco-cms/backoffice/external/uui'; import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; @@ -10,14 +13,14 @@ import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffic import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; import { UMB_CONTENT_TYPE_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/content-type'; -// TODO: Could base take a token to get its types?. -@customElement('umb-property-settings-modal') -export class UmbPropertySettingsModalElement extends UmbModalBaseElement< - UmbPropertySettingsModalData, - UmbPropertySettingsModalValue +// TODO: Could base take a token to get its types? [NL] +@customElement('umb-property-type-settings-modal') +export class UmbPropertyTypeSettingsModalElement extends UmbModalBaseElement< + UmbPropertyTypeSettingsModalData, + UmbPropertyTypeSettingsModalValue > { - //TODO: Should these options come from the server? - // TODO: Or should they come from a extension point? + //TODO: Should these options come from the server? [NL] + // TODO: Or should they come from a extension point? [NL] @state() private _customValidationOptions: Array