diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-context.interface.ts index 5df6b41d25a8..85f73bb7430b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-context.interface.ts @@ -1,9 +1,8 @@ -import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbContentCollectionManager } from './manager/content-collection-manager.controller.js'; import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; import type { UmbEntityWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export interface UmbContentCollectionWorkspaceContext extends UmbEntityWorkspaceContext { - contentTypeHasCollection: Observable; - getCollectionAlias(): string; + collection: UmbContentCollectionManager; structure: UmbContentTypeStructureManager; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-view.element.ts index 55fe2cf3743f..ce6d73d497e5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace-view.element.ts @@ -1,13 +1,9 @@ import { UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT } from './content-collection-workspace.context-token.js'; 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'; -import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type'; -import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace'; import type { UmbCollectionConfiguration } from '@umbraco-cms/backoffice/collection'; +import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace'; -const elementName = 'umb-content-collection-workspace-view'; @customElement('umb-content-collection-workspace-view') export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement implements UmbWorkspaceViewElement { @state() @@ -19,39 +15,18 @@ export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement impl @state() private _collectionAlias?: string; - @state() - private _documentUnique?: string; - - #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); - constructor() { super(); - this.#observeConfig(); - } - async #observeConfig() { this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => { - this._collectionAlias = workspaceContext?.getCollectionAlias(); - this._documentUnique = workspaceContext?.getUnique() ?? ''; + this._collectionAlias = workspaceContext?.collection.getCollectionAlias(); this.observe( - workspaceContext?.structure.ownerContentType, - async (contentType) => { - if (!contentType || !contentType.collection) return; - - const dataTypeUnique = contentType.collection.unique; - - if (dataTypeUnique) { - await this.#dataTypeDetailRepository.requestByUnique(dataTypeUnique); - this.observe( - await this.#dataTypeDetailRepository.byUnique(dataTypeUnique), - (dataType) => { - if (!dataType) return; - this._config = this.#mapDataTypeConfigToCollectionConfig(dataType); - this._loading = false; - }, - '_observeConfigDataType', - ); + workspaceContext?.collection.collectionConfig, + (config) => { + if (config) { + this._config = config; + this._loading = false; } }, '_observeConfigContentType', @@ -59,19 +34,6 @@ export class UmbContentCollectionWorkspaceViewElement extends UmbLitElement impl }); } - #mapDataTypeConfigToCollectionConfig(dataType: UmbDataTypeDetailModel): UmbCollectionConfiguration { - const config = new UmbPropertyEditorConfigCollection(dataType.values); - const pageSize = Number(config.getValueByAlias('pageSize')); - return { - unique: this._documentUnique, - layouts: config?.getValueByAlias('layouts'), - orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate', - orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc', - pageSize: isNaN(pageSize) ? 50 : pageSize, - userDefinedProperties: config?.getValueByAlias('includeProperties'), - }; - } - override render() { if (this._loading) return nothing; return html``; @@ -82,6 +44,6 @@ export { UmbContentCollectionWorkspaceViewElement as element }; declare global { interface HTMLElementTagNameMap { - [elementName]: UmbContentCollectionWorkspaceViewElement; + 'umb-content-collection-workspace-view': UmbContentCollectionWorkspaceViewElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace.context-token.ts index eab9a2a5bb88..1364c6dcd8ec 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/content-collection-workspace.context-token.ts @@ -10,5 +10,5 @@ export const UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT = new UmbContextToken< 'UmbWorkspaceContext', undefined, (context): context is UmbContentCollectionWorkspaceContext => - (context as UmbContentCollectionWorkspaceContext).contentTypeHasCollection !== undefined, + (context as UmbContentCollectionWorkspaceContext).collection.hasCollection !== undefined, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/index.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/index.ts index 0ff461eb07c3..91bf96e53276 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/index.ts @@ -1,2 +1,3 @@ export type * from './content-collection-workspace-context.interface.js'; export * from './content-collection-workspace.context-token.js'; +export * from './manager/content-collection-manager.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manager/content-collection-manager.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manager/content-collection-manager.controller.ts new file mode 100644 index 000000000000..c80a039ad012 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manager/content-collection-manager.controller.ts @@ -0,0 +1,108 @@ +import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import { UmbDataTypeDetailRepository, type UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type'; +import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; +import type { ManifestWorkspaceView, UmbEntityWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import type { UmbCollectionConfiguration } from '@umbraco-cms/backoffice/collection'; +import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +type partialManifestWorkspaceView = Omit, 'meta'> & { + meta: Partial; +}; + +export class UmbContentCollectionManager< + ContentTypeDetailModelType extends UmbContentTypeModel = UmbContentTypeModel, +> extends UmbControllerBase { + #host: UmbEntityWorkspaceContext & UmbControllerHost; + + #collectionAlias?: string; + + #collectionConfig = new UmbObjectState(undefined); + readonly collectionConfig = this.#collectionConfig.asObservable(); + + #manifestOverrides = new UmbObjectState(undefined); + readonly manifestOverrides = this.#manifestOverrides.asObservable(); + + #hasCollection = new UmbBooleanState(false); + readonly hasCollection = this.#hasCollection.asObservable(); + + #dataTypeDetailRepository = new UmbDataTypeDetailRepository(this); + + constructor( + host: UmbEntityWorkspaceContext & UmbControllerHost, + structureManager: UmbContentTypeStructureManager, + collectionAlias?: string, + ) { + super(host); + + this.#host = host; + this.#collectionAlias = collectionAlias; + + this.observe( + collectionAlias ? structureManager.ownerContentType : undefined, + async (contentType) => { + this.#hasCollection.setValue(!!contentType?.collection); + + const dataTypeUnique = contentType?.collection?.unique; + if (dataTypeUnique) { + this.#dataTypeDetailRepository.requestByUnique(dataTypeUnique); + this.observe( + await this.#dataTypeDetailRepository.byUnique(dataTypeUnique), + (dataType) => { + this.#gotDataType(dataType); + }, + '_observeConfigDataType', + ); + } + }, + null, + ); + } + + getCollectionAlias() { + return this.#collectionAlias; + } + + #gotDataType(dataType?: UmbDataTypeDetailModel): void { + if (!dataType) { + this.#collectionConfig.setValue(undefined); + this.#manifestOverrides.setValue(undefined); + return; + } + + const config = new UmbPropertyEditorConfigCollection(dataType.values); + const pageSize = Number(config.getValueByAlias('pageSize')); + + this.#collectionConfig.setValue({ + unique: this.#host.getUnique(), + layouts: config?.getValueByAlias('layouts'), + orderBy: config?.getValueByAlias('orderBy') ?? 'updateDate', + orderDirection: config?.getValueByAlias('orderDirection') ?? 'asc', + pageSize: isNaN(pageSize) ? 50 : pageSize, + userDefinedProperties: config?.getValueByAlias('includeProperties'), + }); + + const overrides: partialManifestWorkspaceView = { + alias: 'Umb.WorkspaceView.Content.Collection', + meta: {}, + }; + + const overrideIcon = config?.getValueByAlias('icon'); + if (overrideIcon && overrideIcon !== '') { + overrides.meta!.icon = overrideIcon; + } + + const overrideLabel = config?.getValueByAlias('tabName'); + if (overrideLabel && overrideLabel !== '') { + overrides.meta!.label = overrideLabel; + } + + const showFirst = config?.getValueByAlias('showContentFirst'); + if (showFirst === true) { + overrides.weight = 150; + } + + this.#manifestOverrides.setValue(overrides); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manifests.ts index c2a2a34eca39..5b14e35e11bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/manifests.ts @@ -1,4 +1,6 @@ +import { UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS } from './workspace-has-content-collection/constants.js'; import { manifests as workspaceHasContentCollectionManifests } from './workspace-has-content-collection/manifests.js'; +import { UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; import type { UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; export const manifests: Array = [ @@ -19,5 +21,26 @@ export const manifests: Array = }, }, }, + { + type: 'workspaceView', + kind: 'contentCollection', + alias: 'Umb.WorkspaceView.Content.Collection', + name: 'Content Workspace Collection View', + weight: 1000, + meta: { + label: 'Collection', + pathname: 'collection', + icon: 'icon-grid', + }, + conditions: [ + { + alias: UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS, + }, + { + alias: UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS, + match: false, + }, + ], + }, ...workspaceHasContentCollectionManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/workspace-has-content-collection/workspace-has-content-collection.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/workspace-has-content-collection/workspace-has-content-collection.condition.ts index 9cb170922e0a..5f9a1312c659 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/workspace-has-content-collection/workspace-has-content-collection.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/collection/workspace-has-content-collection/workspace-has-content-collection.condition.ts @@ -18,7 +18,7 @@ export class UmbWorkspaceHasContentCollectionCondition this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (context) => { this.observe( - context?.contentTypeHasCollection, + context?.collection.hasCollection, (hasCollection) => { this.permitted = hasCollection ?? false; }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-detail-workspace-base.ts b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-detail-workspace-base.ts index 4459544348a8..7d4dd3a84f05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-detail-workspace-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/content/content/workspace/content-detail-workspace-base.ts @@ -1,61 +1,58 @@ import type { UmbContentDetailModel, UmbElementValueModel } from '../types.js'; +import { UmbContentCollectionManager } from '../collection/index.js'; import { UmbContentWorkspaceDataManager } from '../manager/index.js'; import { UmbMergeContentVariantDataController } from '../controller/merge-content-variant-data.controller.js'; import type { UmbContentVariantPickerData, UmbContentVariantPickerValue } from '../variant-picker/index.js'; import type { UmbContentPropertyDatasetContext } from '../property-dataset-context/index.js'; import type { UmbContentValidationRepository } from '../repository/content-validation-repository.interface.js'; +import type { UmbContentCollectionWorkspaceContext } from '../collection/content-collection-workspace-context.interface.js'; import type { UmbContentWorkspaceContext } from './content-workspace-context.interface.js'; import { UmbContentDetailValidationPathTranslator } from './content-detail-validation-path-translator.js'; import { UmbContentValidationToHintsManager } from './content-validation-to-hints.manager.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbDetailRepository, UmbDetailRepositoryConstructor } from '@umbraco-cms/backoffice/repository'; -import { - UmbEntityDetailWorkspaceContextBase, - UmbWorkspaceSplitViewManager, - type UmbEntityDetailWorkspaceContextArgs, - type UmbEntityDetailWorkspaceContextCreateArgs, - type UmbSaveableWorkspaceContext, -} from '@umbraco-cms/backoffice/workspace'; -import { - UmbContentTypeStructureManager, - type UmbContentTypeModel, - type UmbPropertyTypeModel, -} from '@umbraco-cms/backoffice/content-type'; -import { - UmbVariantId, - type UmbEntityVariantModel, - type UmbEntityVariantOptionModel, -} from '@umbraco-cms/backoffice/variant'; -import { UmbDeprecation, UmbReadOnlyVariantGuardManager } from '@umbraco-cms/backoffice/utils'; -import { UmbDataTypeDetailRepository, UmbDataTypeItemRepositoryManager } from '@umbraco-cms/backoffice/data-type'; import { appendToFrozenArray, mergeObservables, UmbArrayState } from '@umbraco-cms/backoffice/observable-api'; -import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language'; -import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import { firstValueFrom, map } from '@umbraco-cms/backoffice/external/rxjs'; -import { - UMB_VALIDATION_CONTEXT, - UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, - UmbDataPathVariantQuery, - UmbServerModelValidatorContext, - UmbValidationController, -} from '@umbraco-cms/backoffice/validation'; -import type { UmbModalToken } from '@umbraco-cms/backoffice/modal'; import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; -import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; +import { UmbDataTypeDetailRepository, UmbDataTypeItemRepositoryManager } from '@umbraco-cms/backoffice/data-type'; +import { UmbDeprecation, UmbReadOnlyVariantGuardManager } from '@umbraco-cms/backoffice/utils'; +import { UmbEntityDetailWorkspaceContextBase, UmbWorkspaceSplitViewManager } from '@umbraco-cms/backoffice/workspace'; import { UmbEntityUpdatedEvent, UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, } from '@umbraco-cms/backoffice/entity-action'; -import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api'; +import { UmbHintContext } from '@umbraco-cms/backoffice/hint'; +import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import { UmbPropertyValuePresetVariantBuilderController, UmbVariantPropertyGuardManager, - type UmbPropertyTypePresetModel, - type UmbPropertyTypePresetModelTypeModel, } from '@umbraco-cms/backoffice/property'; -import { UmbSegmentCollectionRepository, type UmbSegmentCollectionItemModel } from '@umbraco-cms/backoffice/segment'; -import { UmbHintContext, type UmbVariantHint } from '@umbraco-cms/backoffice/hint'; +import { UmbSegmentCollectionRepository } from '@umbraco-cms/backoffice/segment'; +import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { + UMB_VALIDATION_CONTEXT, + UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + UmbDataPathVariantQuery, + UmbServerModelValidatorContext, + UmbValidationController, +} from '@umbraco-cms/backoffice/validation'; +import type { ClassConstructor } from '@umbraco-cms/backoffice/extension-api'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbContentTypeModel, UmbPropertyTypeModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbDetailRepository, UmbDetailRepositoryConstructor } from '@umbraco-cms/backoffice/repository'; +import type { + UmbEntityDetailWorkspaceContextArgs, + UmbEntityDetailWorkspaceContextCreateArgs, + UmbSaveableWorkspaceContext, +} from '@umbraco-cms/backoffice/workspace'; +import type { UmbEntityVariantModel, UmbEntityVariantOptionModel } from '@umbraco-cms/backoffice/variant'; +import type { UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language'; +import type { UmbPropertyTypePresetModel, UmbPropertyTypePresetModelTypeModel } from '@umbraco-cms/backoffice/property'; +import type { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +import type { UmbSegmentCollectionItemModel } from '@umbraco-cms/backoffice/segment'; +import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint'; export interface UmbContentDetailWorkspaceContextArgs< DetailModelType extends UmbContentDetailModel, @@ -71,6 +68,7 @@ export interface UmbContentDetailWorkspaceContextArgs< ignoreValidationResultOnSubmit?: boolean; contentVariantScaffold: VariantModelType; contentTypePropertyName: string; + collectionAlias?: string; saveModalToken?: UmbModalToken, UmbContentVariantPickerValue>; } @@ -102,7 +100,8 @@ export abstract class UmbContentDetailWorkspaceContextBase< extends UmbEntityDetailWorkspaceContextBase implements UmbContentWorkspaceContext, - UmbSaveableWorkspaceContext + UmbSaveableWorkspaceContext, + UmbContentCollectionWorkspaceContext { public readonly IS_CONTENT_WORKSPACE_CONTEXT = true as const; @@ -140,6 +139,8 @@ export abstract class UmbContentDetailWorkspaceContextBase< /* Split View */ readonly splitView = new UmbWorkspaceSplitViewManager(); + readonly collection: UmbContentCollectionManager; + /* Hints */ readonly hints = new UmbHintContext(this); @@ -210,6 +211,12 @@ export abstract class UmbContentDetailWorkspaceContextBase< x ? x.variesByCulture || x.variesBySegment : undefined, ); + this.collection = new UmbContentCollectionManager( + this, + this.structure, + args.collectionAlias, + ); + new UmbContentValidationToHintsManager( this, this.structure, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.element.ts index 357e2eb0ac9d..ec378b6737c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection.element.ts @@ -5,8 +5,7 @@ import { customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbExtensionElementAndApiSlotElementBase } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -const elementName = 'umb-collection'; -@customElement(elementName) +@customElement('umb-collection') export class UmbCollectionElement< ConfigType extends UmbCollectionConfiguration = UmbCollectionConfiguration, FilterType extends UmbCollectionFilterModel = UmbCollectionFilterModel, @@ -62,6 +61,6 @@ export class UmbCollectionElement< declare global { interface HTMLElementTagNameMap { - [elementName]: UmbCollectionElement; + 'umb-collection': UmbCollectionElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/types.ts index 3314fef9402a..c06c607b27ed 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/types.ts @@ -1,5 +1,6 @@ import type { ManifestCollection } from './extensions/types.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; import type { UmbPaginationManager } from '@umbraco-cms/backoffice/utils'; export type * from './action/create/types.js'; @@ -17,7 +18,7 @@ export interface UmbCollectionBulkActionPermissions { } export interface UmbCollectionConfiguration { - unique?: string; + unique?: UmbEntityUnique; dataTypeId?: string; /** @deprecated No longer used internally. This will be removed in Umbraco 17. [LK] */ allowedEntityBulkActions?: UmbCollectionBulkActionPermissions; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.context.ts index e1c372487667..73ac95d56386 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.context.ts @@ -1,12 +1,15 @@ +import type { ManifestWorkspaceView } from '../../types.js'; import { UmbWorkspaceViewContext } from './workspace-view.context.js'; import { UMB_WORKSPACE_EDITOR_CONTEXT } from './workspace-editor.context-token.js'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { UmbBasicState, mergeObservables } from '@umbraco-cms/backoffice/observable-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; -import { UmbBasicState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbHintController } from '@umbraco-cms/backoffice/hint'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils'; +import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import { UmbHintController, type UmbVariantHint } from '@umbraco-cms/backoffice/hint'; export class UmbWorkspaceEditorContext extends UmbContextBase { // @@ -14,8 +17,69 @@ export class UmbWorkspaceEditorContext extends UmbContextBase { /** * State holding the permitted Workspace Views as a Workspace View Context */ - #views = new UmbBasicState(>[]); - public readonly views = this.#views.asObservable(); + + #manifests = new UmbBasicState>([]); + #overrides = new UmbBasicState>>([]); + public readonly views = mergeObservables( + [this.#manifests.asObservable(), this.#overrides.asObservable()], + ([manifests, overrides]): Array => { + let contexts = this.#contexts; + + // remove ones that are no longer contained in the workspaceViews (And thereby make the new array): + const contextsToKeep = contexts.filter( + (view) => !manifests.some((manifest) => manifest.alias === view.manifest.alias), + ); + + const hasDiff = contextsToKeep.length !== manifests.length; + if (hasDiff) { + contexts = [...contextsToKeep]; + + // Add ones that are new: + manifests + .filter((manifest) => !contextsToKeep.some((x) => x.manifest.alias === manifest.alias)) + .forEach((manifest) => { + const context = new UmbWorkspaceViewContext(this, manifest); + context.setVariantId(this.#variantId); + context.hints.inheritFrom(this.#hints); + contexts.push(context); + }); + } + + // Apply overrides: + contexts.forEach((context) => { + const override = overrides.find((x) => x.alias === context.manifest.alias); + if (override) { + // Test to see if there is a change, to avoid unnecessary updates, this prevents re-setting the manifest again and again. [NL] + const overrideKeys = Object.keys(override) as Array; + const hasOverrideDiff = overrideKeys.some((key) => context.manifest[key] !== override[key]); + if (hasOverrideDiff) { + context.manifest = { + ...context.manifest, + ...(override as ManifestWorkspaceView), + meta: { ...context.manifest.meta, ...override.meta }, + }; + } + } + }); + + // sort contexts to match manifests weights: + contexts.sort((a, b): number => (b.manifest.weight || 0) - (a.manifest.weight || 0)); + + this.#contexts = contexts; + return contexts; + }, + // Custom memoize method, to check context instance and manifest instance: + (previousValue: Array, currentValue: Array): boolean => { + return ( + previousValue === currentValue && + currentValue.some( + (x) => x.manifest === previousValue.find((y) => y.manifest.alias === x.manifest.alias)?.manifest, + ) + ); + }, + ); + // A storage and cache for the current contexts, to enable communicating to them and to avoid re-initializing them every time there is a change of manifests/overrides. [NL] + #contexts = new Array(); #variantId?: UmbVariantId; #hints = new UmbHintController(this, {}); @@ -31,46 +95,25 @@ export class UmbWorkspaceEditorContext extends UmbContextBase { 'workspaceView', null, (workspaceViews) => { - const oldViews = this.#views.getValue(); - - // remove ones that are no longer contained in the workspaceViews (And thereby make the new array): - const viewsToKeep = oldViews.filter( - (view) => !workspaceViews.some((x) => x.manifest.alias === view.manifest.alias), - ); - - const hasDiff = viewsToKeep.length !== workspaceViews.length; - - if (hasDiff) { - const newViews = [...viewsToKeep]; - - // Add ones that are new: - workspaceViews - .filter((view) => !viewsToKeep.some((x) => x.manifest.alias === view.manifest.alias)) - .forEach((view) => { - const context = new UmbWorkspaceViewContext(this, view.manifest); - context.setVariantId(this.#variantId); - context.hints.inheritFrom(this.#hints); - newViews.push(context); - }); - - this.#views.setValue(newViews); - } + this.#manifests.setValue(workspaceViews.map((controller) => controller.manifest)); }, - 'initViewApis', - {}, ).asPromise(); } setVariantId(variantId: UmbVariantId | undefined): void { this.#variantId = variantId; this.#hints.updateScaffold({ variantId }); - this.#views.getValue().forEach((view) => { + this.#contexts.forEach((view) => { view.hints.updateScaffold({ variantId }); }); } + setOverrides(overrides?: Array>): void { + this.#overrides.setValue(overrides ?? []); + } + async getViewContext(alias: string): Promise { await this.#init; - return this.#views.getValue().find((view) => view.manifest.alias === alias); + return this.#contexts.find((view) => view.manifest.alias === alias); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts index 72276e6ac844..300a8fdc15b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-editor.element.ts @@ -1,12 +1,14 @@ import { UMB_WORKSPACE_VIEW_PATH_PATTERN } from '../../paths.js'; +import type { ManifestWorkspaceView } from '../../types.js'; import { UmbWorkspaceEditorContext } from './workspace-editor.context.js'; import type { UmbWorkspaceViewContext } from './workspace-view.context.js'; import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router'; +import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils'; import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; +import type { UmbRoute, UmbRouterSlotInitEvent, UmbRouterSlotChangeEvent } from '@umbraco-cms/backoffice/router'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbVariantHint } from '@umbraco-cms/backoffice/hint'; @@ -56,6 +58,11 @@ export class UmbWorkspaceEditorElement extends UmbLitElement { } private _variantId?: UmbVariantId | undefined; + @property({ attribute: false }) + public set overrides(value: Array> | undefined) { + this.#navigationContext.setOverrides(value); + } + @state() private _workspaceViews: Array = []; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-view.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-view.context.ts index a268bde27f80..eafe506578e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-view.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-editor/workspace-view.context.ts @@ -4,6 +4,7 @@ import { UmbViewContext } from '@umbraco-cms/backoffice/view'; export class UmbWorkspaceViewContext extends UmbViewContext { public readonly IS_WORKSPACE_VIEW_CONTEXT = true as const; + // Note: manifest can change later, but because we currently only use the alias from it, it's not something we need to handle. [NL] public manifest: ManifestWorkspaceView; constructor(host: UmbControllerHost, manifest: ManifestWorkspaceView) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts index 789994afc5c4..d852da05b3be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts @@ -28,9 +28,6 @@ function ExtensionApiArgsMethod( // TODO: stop naming this something with layout. as its not just an layout. it hooks up with extensions. @customElement('umb-workspace-footer') export class UmbWorkspaceFooterLayoutElement extends UmbLitElement { - @state() - private _withinModal = false; - @state() private _modalContext?: UmbModalContext; @@ -63,13 +60,12 @@ export class UmbWorkspaceFooterLayoutElement extends UmbLitElement { label=${this._isNew ? 'Cancel' : 'Close'} @click=${this.#rejectModal}>` : ''} + - - `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts index 86cf1f63bd41..61b0f317c07f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-split-view/workspace-split-view.element.ts @@ -1,20 +1,22 @@ +import type { ManifestWorkspaceView } from '../../types.js'; import { UmbWorkspaceSplitViewContext } from './workspace-split-view.context.js'; import { css, - html, customElement, - property, + html, ifDefined, + nothing, + property, state, when, - nothing, } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils'; +import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; // import local components import './workspace-split-view-variant-selector.element.js'; -import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; /** * @@ -30,6 +32,9 @@ export class UmbWorkspaceSplitViewElement extends UmbLitElement { @property({ attribute: 'back-path' }) public backPath?: string; + @property({ attribute: false }) + public overrides?: Array>; + @property({ type: Number }) public set splitViewIndex(index: number) { this.splitViewContext.setSplitViewIndex(index); @@ -79,6 +84,7 @@ export class UmbWorkspaceSplitViewElement extends UmbLitElement { back-path=${ifDefined(this.backPath)} .hideNavigation=${!this.displayNavigation} .variantId=${this._variantId} + .overrides=${this.overrides} .enforceNoFooter=${true}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-split-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-split-view.element.ts index bd82b1d110d3..7a1eed8c6ca8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-split-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-split-view.element.ts @@ -1,8 +1,9 @@ import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from './document-workspace.context-token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, nothing, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbActiveVariant } from '@umbraco-cms/backoffice/workspace'; +import { css, customElement, html, ifDefined, nothing, 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 { ManifestWorkspaceView, UmbActiveVariant } from '@umbraco-cms/backoffice/workspace'; +import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils'; import './document-workspace-split-view-variant-selector.element.js'; @@ -17,6 +18,9 @@ export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { @state() private _icon?: string; + @state() + private _overrides?: Array>; + constructor() { super(); @@ -25,6 +29,7 @@ export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { this._workspaceContext = context; this.#observeActiveVariantInfo(); this.#observeIcon(); + this.#observeCollectionOverrides(); }); } @@ -44,6 +49,12 @@ export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { }); } + #observeCollectionOverrides() { + this.observe(this._workspaceContext?.collection.manifestOverrides, (overrides) => { + this._overrides = overrides ? [overrides] : undefined; + }); + } + override render() { return this._variants ? html`
@@ -53,8 +64,9 @@ export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { view.index + '_' + (view.culture ?? '') + '_' + (view.segment ?? '') + '_' + this._variants!.length, (view) => html` + .displayNavigation=${view.index === this._variants!.length - 1} + .overrides=${this._overrides} + .splitViewIndex=${view.index}> 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 c92309e57553..a1b1a2ca4dc0 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 @@ -19,26 +19,23 @@ import { UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT, UmbDocumentPublishingReposit import { UmbDocumentValidationRepository } from '../repository/validation/index.js'; import { UMB_DOCUMENT_CONFIGURATION_CONTEXT } from '../index.js'; import { UMB_DOCUMENT_DETAIL_MODEL_VARIANT_SCAFFOLD, UMB_DOCUMENT_WORKSPACE_ALIAS } from './constants.js'; -import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; +import { ensurePathEndsWithSlash, UmbDeprecation } from '@umbraco-cms/backoffice/utils'; +import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; +import { UmbContentDetailWorkspaceContextBase } from '@umbraco-cms/backoffice/content'; +import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; +import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { - type UmbPublishableWorkspaceContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceIsNewRedirectControllerAlias, } from '@umbraco-cms/backoffice/workspace'; +import { UMB_SERVER_CONTEXT } from '@umbraco-cms/backoffice/server'; +import type { UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UmbDocumentBlueprintDetailRepository } from '@umbraco-cms/backoffice/document-blueprint'; -import { - UmbContentDetailWorkspaceContextBase, - type UmbContentCollectionWorkspaceContext, - type UmbContentWorkspaceContext, -} from '@umbraco-cms/backoffice/content'; import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/document-type'; -import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin'; -import { ensurePathEndsWithSlash, UmbDeprecation } from '@umbraco-cms/backoffice/utils'; -import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_SERVER_CONTEXT } from '@umbraco-cms/backoffice/server'; -import { observeMultiple } from '@umbraco-cms/backoffice/observable-api'; +import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity'; +import type { UmbPublishableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import type { UmbVariantPropertyGuardRule } from '@umbraco-cms/backoffice/property'; type ContentModel = UmbDocumentDetailModel; @@ -52,8 +49,7 @@ export class UmbDocumentWorkspaceContext > implements UmbContentWorkspaceContext, - UmbPublishableWorkspaceContext, - UmbContentCollectionWorkspaceContext + UmbPublishableWorkspaceContext { /** * The publishing repository for the document workspace. @@ -63,10 +59,16 @@ export class UmbDocumentWorkspaceContext public readonly publishingRepository = new UmbDocumentPublishingRepository(this); readonly isTrashed = this._data.createObservablePartOfCurrent((data) => data?.isTrashed); + readonly contentTypeUnique = this._data.createObservablePartOfCurrent((data) => data?.documentType.unique); + + /* + * @deprecated Use `collection.hasCollection` instead, will be removed in v.18 + */ readonly contentTypeHasCollection = this._data.createObservablePartOfCurrent( (data) => !!data?.documentType.collection, ); + readonly contentTypeIcon = this._data.createObservablePartOfCurrent((data) => data?.documentType.icon || null); readonly templateId = this._data.createObservablePartOfCurrent((data) => data?.template?.unique || null); @@ -78,6 +80,7 @@ export class UmbDocumentWorkspaceContext super(host, { entityType: UMB_DOCUMENT_ENTITY_TYPE, workspaceAlias: UMB_DOCUMENT_WORKSPACE_ALIAS, + collectionAlias: UMB_DOCUMENT_COLLECTION_ALIAS, detailRepositoryAlias: UMB_DOCUMENT_DETAIL_REPOSITORY_ALIAS, contentTypeDetailRepository: UmbDocumentTypeDetailRepository, contentValidationRepository: UmbDocumentValidationRepository, @@ -248,6 +251,7 @@ export class UmbDocumentWorkspaceContext }); } + /** @deprecated will be removed in v.18 */ getCollectionAlias() { return UMB_DOCUMENT_COLLECTION_ALIAS; } 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 ccae31b036f6..3c5c1e6506d2 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,14 +1,8 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './constants.js'; import { manifests as actionManifests } from './actions/manifests.js'; -import { - UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION, - UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS, -} from '@umbraco-cms/backoffice/content'; -import { - UMB_WORKSPACE_CONDITION_ALIAS, - UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS, -} from '@umbraco-cms/backoffice/workspace'; +import { UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION } from '@umbraco-cms/backoffice/content'; +import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; export const manifests: Array = [ { @@ -21,30 +15,6 @@ export const manifests: Array = [ entityType: UMB_DOCUMENT_ENTITY_TYPE, }, }, - { - type: 'workspaceView', - kind: 'contentCollection', - alias: 'Umb.WorkspaceView.Document.Collection', - name: 'Document Workspace Collection View', - meta: { - label: 'Collection', - pathname: 'collection', - icon: 'icon-grid', - }, - conditions: [ - { - alias: UMB_WORKSPACE_CONDITION_ALIAS, - match: UMB_DOCUMENT_WORKSPACE_ALIAS, - }, - { - alias: UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS, - }, - { - alias: UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS, - match: false, - }, - ], - }, { type: 'workspaceView', kind: 'contentEditor', 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 d43932d72c04..a4627ec20559 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,14 +1,7 @@ import { UMB_MEDIA_WORKSPACE_ALIAS } from './constants.js'; -import { - UmbSubmitWorkspaceAction, - UMB_WORKSPACE_CONDITION_ALIAS, - UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS, -} from '@umbraco-cms/backoffice/workspace'; +import { UmbSubmitWorkspaceAction, UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace'; +import { UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION } from '@umbraco-cms/backoffice/content'; import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS } from '@umbraco-cms/backoffice/recycle-bin'; -import { - UMB_CONTENT_HAS_PROPERTIES_WORKSPACE_CONDITION, - UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS, -} from '@umbraco-cms/backoffice/content'; export const manifests: Array = [ { @@ -21,30 +14,6 @@ export const manifests: Array = [ entityType: 'media', }, }, - { - type: 'workspaceView', - kind: 'contentCollection', - alias: 'Umb.WorkspaceView.Media.Collection', - name: 'Media Workspace Collection View', - meta: { - label: 'Collection', - pathname: 'collection', - icon: 'icon-grid', - }, - conditions: [ - { - alias: UMB_WORKSPACE_CONDITION_ALIAS, - match: UMB_MEDIA_WORKSPACE_ALIAS, - }, - { - alias: UMB_WORKSPACE_HAS_CONTENT_COLLECTION_CONDITION_ALIAS, - }, - { - alias: UMB_WORKSPACE_ENTITY_IS_NEW_CONDITION_ALIAS, - match: false, - }, - ], - }, { type: 'workspaceView', kind: 'contentEditor', diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-split-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-split-view.element.ts index ba25bcaf1d2d..232288ee41ac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-split-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-split-view.element.ts @@ -1,8 +1,9 @@ import { UMB_MEDIA_WORKSPACE_CONTEXT } from './media-workspace.context-token.js'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, nothing, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import type { UmbActiveVariant } from '@umbraco-cms/backoffice/workspace'; +import { css, customElement, html, ifDefined, nothing, 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 { ManifestWorkspaceView, UmbActiveVariant } from '@umbraco-cms/backoffice/workspace'; +import type { UmbDeepPartialObject } from '@umbraco-cms/backoffice/utils'; @customElement('umb-media-workspace-split-view') export class UmbMediaWorkspaceSplitViewElement extends UmbLitElement { @@ -15,6 +16,9 @@ export class UmbMediaWorkspaceSplitViewElement extends UmbLitElement { @state() private _icon?: string; + @state() + private _overrides?: Array>; + constructor() { super(); @@ -23,6 +27,7 @@ export class UmbMediaWorkspaceSplitViewElement extends UmbLitElement { this._workspaceContext = context; this.#observeActiveVariantInfo(); this.#observeIcon(); + this.#observeCollectionOverrides(); }); } @@ -44,6 +49,12 @@ export class UmbMediaWorkspaceSplitViewElement extends UmbLitElement { }); } + #observeCollectionOverrides() { + this.observe(this._workspaceContext?.collection.manifestOverrides, (overrides) => { + this._overrides = overrides ? [overrides] : undefined; + }); + } + override render() { return this._variants ? html`
@@ -53,8 +64,9 @@ export class UmbMediaWorkspaceSplitViewElement extends UmbLitElement { view.index + '_' + (view.culture ?? '') + '_' + (view.segment ?? '') + '_' + this._variants!.length, (view) => html` + .displayNavigation=${view.index === this._variants!.length - 1} + .overrides=${this._overrides} + .splitViewIndex=${view.index}> `, 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 c8a61f1a461e..4668315e9bd4 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 @@ -8,19 +8,15 @@ import { UmbMediaValidationRepository } from '../repository/validation/media-val import { UMB_MEDIA_COLLECTION_ALIAS } from '../collection/constants.js'; import type { UmbMediaDetailRepository } from '../repository/index.js'; import { UMB_MEDIA_WORKSPACE_ALIAS, UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD } from './constants.js'; -import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; +import { UmbContentDetailWorkspaceContextBase, type UmbContentWorkspaceContext } from '@umbraco-cms/backoffice/content'; +import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin'; import { UmbWorkspaceIsNewRedirectController, UmbWorkspaceIsNewRedirectControllerAlias, } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbMediaTypeDetailModel } from '@umbraco-cms/backoffice/media-type'; -import { - UmbContentDetailWorkspaceContextBase, - type UmbContentCollectionWorkspaceContext, - type UmbContentWorkspaceContext, -} from '@umbraco-cms/backoffice/content'; -import { UmbIsTrashedEntityContext } from '@umbraco-cms/backoffice/recycle-bin'; +import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; type ContentModel = UmbMediaDetailModel; type ContentTypeModel = UmbMediaTypeDetailModel; @@ -32,11 +28,12 @@ export class UmbMediaWorkspaceContext ContentTypeModel, UmbMediaVariantModel > - implements - UmbContentWorkspaceContext, - UmbContentCollectionWorkspaceContext + implements UmbContentWorkspaceContext { readonly contentTypeUnique = this._data.createObservablePartOfCurrent((data) => data?.mediaType.unique); + /* + * @deprecated Use `collection.hasCollection` instead, will be removed in v.18 + */ readonly contentTypeHasCollection = this._data.createObservablePartOfCurrent((data) => !!data?.mediaType.collection); readonly contentTypeIcon = this._data.createObservablePartOfCurrent((data) => data?.mediaType.icon); @@ -51,6 +48,7 @@ export class UmbMediaWorkspaceContext contentValidationRepository: UmbMediaValidationRepository, contentVariantScaffold: UMB_MEMBER_DETAIL_MODEL_VARIANT_SCAFFOLD, contentTypePropertyName: 'mediaType', + collectionAlias: UMB_MEDIA_COLLECTION_ALIAS, }); this.observe( @@ -124,6 +122,9 @@ export class UmbMediaWorkspaceContext }); } + /* + * @deprecated Use `collection.getCollectionAlias()` instead. Will be removed in v.18 + */ public getCollectionAlias() { return UMB_MEDIA_COLLECTION_ALIAS; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/property-editor-ui-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/property-editor-ui-collection.element.ts index ec35f968ec5e..9f2afbf372fb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/property-editor-ui-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/property-editor-ui-collection.element.ts @@ -2,7 +2,7 @@ import type { UmbPropertyEditorConfigCollection, UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; -import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; +import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_DOCUMENT_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/document'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -14,6 +14,9 @@ import type { UmbCollectionConfiguration } from '@umbraco-cms/backoffice/collect */ @customElement('umb-property-editor-ui-collection') export class UmbPropertyEditorUICollectionElement extends UmbLitElement implements UmbPropertyEditorUiElement { + #workspaceContext?: typeof UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT.TYPE; + #propertyContext?: typeof UMB_PROPERTY_CONTEXT.TYPE; + @property() value?: string; @@ -31,22 +34,37 @@ export class UmbPropertyEditorUICollectionElement extends UmbLitElement implemen super(); this.consumeContext(UMB_CONTENT_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => { - this._collectionAlias = workspaceContext?.getCollectionAlias() ?? UMB_DOCUMENT_COLLECTION_ALIAS; - - this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { - this.observe(propertyContext?.alias, async (propertyAlias) => { - if (propertyAlias) { - // Gets the Data Type ID for the current property. - const property = await workspaceContext!.structure.getPropertyStructureByAlias(propertyAlias); - const unique = workspaceContext!.getUnique(); - if (unique && property && this._config) { - this._config.unique = unique; - this._config.dataTypeId = property.dataType.unique; - this.requestUpdate('_config'); - } - } - }); - }); + this.#workspaceContext = workspaceContext; + this._collectionAlias = workspaceContext?.collection.getCollectionAlias() ?? UMB_DOCUMENT_COLLECTION_ALIAS; + this.#gotContexts(); + }); + + this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { + this.#propertyContext = propertyContext; + this.#gotContexts(); + }); + } + + #gotContexts() { + if (!this.#workspaceContext || !this.#propertyContext) return; + + this.observe(this.#propertyContext?.alias, async (propertyAlias) => { + if (this.#workspaceContext && propertyAlias) { + // Gets the Data Type ID for the current property. + const property = await this.#workspaceContext.structure.getPropertyStructureByAlias(propertyAlias); + if (!this.#workspaceContext) { + // We got destroyed in the meantime. + return; + } + + const unique = this.#workspaceContext.getUnique(); + if (unique && property && this._config) { + // TODO: Handle case where config might not be set when this executes during initialization, its not likely but it is fragile to assume this. [NL] + this._config.unique = unique; + this._config.dataTypeId = property.dataType.unique; + this.requestUpdate('_config'); + } + } }); } @@ -67,6 +85,12 @@ export class UmbPropertyEditorUICollectionElement extends UmbLitElement implemen if (!this._config?.unique || !this._config?.dataTypeId) return html``; return html``; } + + override destroy(): void { + super.destroy(); + this.#workspaceContext = undefined; + this.#propertyContext = undefined; + } } export default UmbPropertyEditorUICollectionElement;