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 b7f7cc9dcd..3b64fbea4a 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 @@ -16,8 +16,8 @@ export interface UmbClassMixinInterface extends UmbControllerHost, UmbController unique?: string ): UmbObserverController; provideContext(alias: string | UmbContextToken, instance: R): UmbContextProviderController; - consumeContext( - alias: string | UmbContextToken, - callback: UmbContextCallback - ): UmbContextConsumerController; + consumeContext( + alias: string | UmbContextToken, + callback: UmbContextCallback + ): UmbContextConsumerController; } 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 8d8364766d..992bdc968d 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 @@ -27,11 +27,15 @@ declare class UmbClassMixinDeclaration implements UmbClassMixinInterface { callback: (_value: T) => void, controllerAlias?: UmbControllerAlias ): UmbObserverController; - provideContext(alias: string | UmbContextToken, instance: R): UmbContextProviderController; - consumeContext( - alias: string | UmbContextToken, - callback: UmbContextCallback - ): UmbContextConsumerController; + provideContext< + BaseType = unknown, + ResultType extends BaseType = BaseType, + InstanceType extends ResultType = ResultType + >(alias: string | UmbContextToken, instance: InstanceType): UmbContextProviderController; + consumeContext( + alias: string | UmbContextToken, + callback: UmbContextCallback + ): UmbContextConsumerController; hasController(controller: UmbController): boolean; getControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[]; addController(controller: UmbController): void; @@ -82,11 +86,17 @@ export const UmbClassMixin = (superClass: T) => { * @return {UmbContextProviderController} Reference to a Context Provider Controller instance * @memberof UmbElementMixin */ - provideContext( - contextAlias: string | UmbContextToken, - instance: R - ): UmbContextProviderController { - return new UmbContextProviderController(this, contextAlias, instance); + provideContext + < + BaseType = unknown, + ResultType extends BaseType = BaseType, + InstanceType extends ResultType = ResultType + > + ( + contextAlias: string | UmbContextToken, + instance: InstanceType + ): UmbContextProviderController { + return new UmbContextProviderController(this, contextAlias, instance); } /** @@ -96,10 +106,10 @@ export const UmbClassMixin = (superClass: T) => { * @return {UmbContextConsumerController} Reference to a Context Consumer Controller instance * @memberof UmbElementMixin */ - consumeContext( - contextAlias: string | UmbContextToken, - callback: UmbContextCallback - ): UmbContextConsumerController { + consumeContext( + contextAlias: string | UmbContextToken, + callback: UmbContextCallback + ): UmbContextConsumerController { return new UmbContextConsumerController(this, contextAlias, callback); } } diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts index 69d9737250..4a37a0a406 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.controller.ts @@ -3,7 +3,11 @@ import { UmbContextConsumer } from './context-consumer.js'; import { UmbContextCallback } from './context-request.event.js'; import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api'; -export class UmbContextConsumerController extends UmbContextConsumer implements UmbController { + +export class UmbContextConsumerController< + BaseType = unknown, + ResultType extends BaseType = BaseType +> extends UmbContextConsumer implements UmbController { #controllerAlias = Symbol(); #host: UmbControllerHost; @@ -11,7 +15,7 @@ export class UmbContextConsumerController extends UmbContextConsume return this.#controllerAlias; } - constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, callback: UmbContextCallback) { + constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, callback: UmbContextCallback) { super(host.getHostElement(), contextAlias, callback); this.#host = host; host.addController(this); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts index 4a520a3518..e3b82c2da9 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts @@ -1,12 +1,13 @@ import { expect, oneEvent } from '@open-wc/testing'; import { UmbContextProvider } from '../provide/context-provider.js'; +import { UmbContextToken } from '../token/context-token.js'; import { UmbContextConsumer } from './context-consumer.js'; import { UmbContextRequestEventImplementation, umbContextRequestEventType } from './context-request.event.js'; const testContextAlias = 'my-test-context'; class UmbTestContextConsumerClass { - prop = 'value from provider'; + public prop: string = 'value from provider'; } describe('UmbContextConsumer', () => { @@ -18,6 +19,11 @@ describe('UmbContextConsumer', () => { }); describe('Public API', () => { + describe('properties', () => { + it('has a instance property', () => { + expect(consumer).to.have.property('instance').that.is.undefined; + }); + }); describe('methods', () => { it('has a request method', () => { expect(consumer).to.have.property('request').that.is.a('function'); @@ -34,6 +40,7 @@ describe('UmbContextConsumer', () => { expect(event).to.exist; expect(event.type).to.eq(umbContextRequestEventType); expect(event.contextAlias).to.eq(testContextAlias); + consumer.hostDisconnected(); }); }); }); @@ -90,3 +97,78 @@ describe('UmbContextConsumer', () => { }); */ }); + +describe('UmbContextConsumer with discriminator test', () => { + + type A = { prop: string }; + + function discriminator(instance: unknown): instance is A { + return typeof (instance as any).prop === 'string'; + } + + function badDiscriminator(instance: unknown): instance is A { + return typeof (instance as any).notExistingProp === 'string'; + } + + it('discriminator determines the instance type', async () => { + + const localConsumer = new UmbContextConsumer( + document.body, + new UmbContextToken(testContextAlias, discriminator), + (instance: A) => { console.log(instance)} + ); + localConsumer.hostConnected(); + + // This bit of code is not really a test but it serves as a TypeScript type test, making sure the given type is matches the one given from the Discriminator method. + type TestType = Exclude<(typeof localConsumer.instance), undefined> extends A ? true : never; + const test: TestType = true; + expect(test).to.be.true; + + localConsumer.destroy(); + }); + + it('approving discriminator still fires callback', (done) => { + const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); + provider.hostConnected(); + + const element = document.createElement('div'); + document.body.appendChild(element); + + const localConsumer = new UmbContextConsumer( + element, + new UmbContextToken(testContextAlias, discriminator), + (_instance) => { + expect(_instance.prop).to.eq('value from provider'); + done(); + localConsumer.hostDisconnected(); + provider.hostDisconnected(); + } + ); + localConsumer.hostConnected(); + }); + + it('disapproving discriminator does not fire callback', (done) => { + const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); + provider.hostConnected(); + + const element = document.createElement('div'); + document.body.appendChild(element); + + const localConsumer = new UmbContextConsumer( + element, + new UmbContextToken(testContextAlias, badDiscriminator), + (_instance) => { + expect(_instance.prop).to.eq('this must not happen!'); + } + ); + localConsumer.hostConnected(); + + Promise.resolve().then(() => { + done(); + localConsumer.hostDisconnected(); + provider.hostDisconnected(); + }); + }); + + +}); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts index 1c9191de7b..31f75bea1f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts @@ -1,4 +1,4 @@ -import { UmbContextToken } from '../token/context-token.js'; +import { UmbContextDiscriminator, UmbContextToken } from '../token/context-token.js'; import { isUmbContextProvideEventType, //isUmbContextUnprovidedEventType, @@ -11,51 +11,72 @@ import { UmbContextRequestEventImplementation, UmbContextCallback } from './cont * @export * @class UmbContextConsumer */ -export class UmbContextConsumer { - #callback?: UmbContextCallback; - #promise?: Promise; - #promiseResolver?: (instance: T) => void; +export class UmbContextConsumer< +BaseType = unknown, +ResultType extends BaseType = BaseType> { + #callback?: UmbContextCallback; + #promise?: Promise; + #promiseResolver?: (instance: ResultType) => void; - #instance?: T; + #instance?: ResultType; get instance() { return this.#instance; } #contextAlias: string; + #discriminator?: UmbContextDiscriminator; + /** * Creates an instance of UmbContextConsumer. * @param {EventTarget} hostElement * @param {string} contextAlias - * @param {UmbContextCallback} _callback + * @param {UmbContextCallback} callback * @memberof UmbContextConsumer */ constructor( protected hostElement: EventTarget, - contextAlias: string | UmbContextToken, - callback?: UmbContextCallback + contextAlias: string | UmbContextToken, + callback?: UmbContextCallback ) { this.#contextAlias = contextAlias.toString(); this.#callback = callback; + this.#discriminator = (contextAlias as any).getDiscriminator?.(); } - /* Idea: Niels: If we need to filter for specific contexts, we could make the response method return true/false. If false, the event should then then not be stopped. Alternatively parse the event it self on to the response-callback. This will enable the event to continue to bubble up finding a context that matches. The reason for such would be to have some who are more specific than others. For example, some might just need the current workspace-context, others might need the closest handling a certain entityType. As I'm writting this is not relevant, but I wanted to keep the idea as we have had some circumstance that might be solved with this approach.*/ - protected _onResponse = (instance: T) => { + + /* Idea: Niels: If we need to filter for specific contexts, we could make the response method return true/false. If false, the event should then then not be stopped. Alternatively parse the event it self on to the response-callback. + This will enable the event to continue to bubble up finding a context that matches. + The reason for such would be to have some who are more specific than others. For example, some might just need the current workspace-context, others might need the closest handling a certain entityType. + As I'm writing this is not relevant, but I wanted to keep the idea as we have had some circumstance that might be solved with this approach. + */ + protected _onResponse = (instance: BaseType) => { if (this.#instance === instance) { return; } + if(this.#discriminator) { + // Notice if discriminator returns false, we do not want to setInstance. + if(this.#discriminator(instance)) { + this.setInstance(instance as unknown as ResultType); + } + } else { + this.setInstance(instance as ResultType); + } + }; + + protected setInstance(instance: ResultType) { this.#instance = instance; this.#callback?.(instance); if (instance !== undefined) { this.#promiseResolver?.(instance); this.#promise = undefined; } - }; + } public asPromise() { return ( this.#promise ?? - (this.#promise = new Promise((resolve) => { + (this.#promise = new Promise((resolve) => { this.#instance ? resolve(this.#instance) : (this.#promiseResolver = resolve); })) ); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts index 25b473e58c..444a788353 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-request.event.ts @@ -9,9 +9,9 @@ export type UmbContextCallback = (instance: T) => void; * @export * @interface UmbContextRequestEvent */ -export interface UmbContextRequestEvent extends Event { - readonly contextAlias: string | UmbContextToken; - readonly callback: UmbContextCallback; +export interface UmbContextRequestEvent extends Event { + readonly contextAlias: string | UmbContextToken; + readonly callback: UmbContextCallback; } /** @@ -20,10 +20,10 @@ export interface UmbContextRequestEvent extends Event { * @extends {Event} * @implements {UmbContextRequestEvent} */ -export class UmbContextRequestEventImplementation extends Event implements UmbContextRequestEvent { +export class UmbContextRequestEventImplementation extends Event implements UmbContextRequestEvent { public constructor( - public readonly contextAlias: string | UmbContextToken, - public readonly callback: UmbContextCallback + public readonly contextAlias: string | UmbContextToken, + public readonly callback: UmbContextCallback ) { super(umbContextRequestEventType, { bubbles: true, composed: true, cancelable: true }); } diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts index f3db2560b3..9eb834bd44 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.controller.ts @@ -2,14 +2,18 @@ import { UmbContextToken } from '../token/context-token.js'; import { UmbContextProvider } from './context-provider.js'; import type { UmbControllerHost, UmbController } from '@umbraco-cms/backoffice/controller-api'; -export class UmbContextProviderController extends UmbContextProvider implements UmbController { +export class UmbContextProviderController< + BaseType = unknown, + ResultType extends BaseType = BaseType, + InstanceType extends ResultType = ResultType +> extends UmbContextProvider implements UmbController { #host: UmbControllerHost; public get controllerAlias() { return this._contextAlias.toString(); } - constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, instance: T) { + constructor(host: UmbControllerHost, contextAlias: string | UmbContextToken, instance: InstanceType) { super(host.getHostElement(), contextAlias, instance); this.#host = host; diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts index 90418bcab7..e506a5e46f 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/provide/context-provider.ts @@ -13,7 +13,7 @@ import { * @export * @class UmbContextProvider */ -export class UmbContextProvider { +export class UmbContextProvider { protected hostElement: EventTarget; protected _contextAlias: string; @@ -35,7 +35,7 @@ export class UmbContextProvider { * @param {*} instance * @memberof UmbContextProvider */ - constructor(hostElement: EventTarget, contextAlias: string | UmbContextToken, instance: unknown) { + constructor(hostElement: EventTarget, contextAlias: string | UmbContextToken, instance: ResultType) { this.hostElement = hostElement; this._contextAlias = contextAlias.toString(); this.#instance = instance; diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/token/context-token.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/token/context-token.ts index 7ad36b1b4e..0ec2b31786 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/token/context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/token/context-token.ts @@ -1,4 +1,11 @@ -export class UmbContextToken { + +export type UmbContextDiscriminator = (instance: BaseType) => instance is DiscriminatorResult; + +export class UmbContextToken< +BaseType = unknown, +ResultType extends BaseType = BaseType> { + + #discriminator: UmbContextDiscriminator | undefined; /** * Get the type of the token * @@ -8,15 +15,18 @@ export class UmbContextToken { * @example `typeof MyToken.TYPE` * @returns undefined */ - readonly TYPE: T = undefined as never; + readonly TYPE: ResultType = undefined as never; /** - * @param alias Unique identifier for the token, - * @param _desc Description for the token, - * used only for debugging purposes, - * it should but does not need to be unique + * @param alias Unique identifier for the token */ - constructor(protected alias: string, protected _desc?: string) {} + constructor(protected alias: string, discriminator?: UmbContextDiscriminator) { + this.#discriminator = discriminator; + } + + getDiscriminator(): UmbContextDiscriminator | undefined { + return this.#discriminator; + } /** * This method must always return the unique alias of the token since that @@ -27,4 +37,6 @@ export class UmbContextToken { toString(): string { return this.alias; } + + } 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 2dee24bd1c..3c8934cb41 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 @@ -24,11 +24,15 @@ export declare class UmbElement extends UmbControllerHostElement { callback: ObserverCallback, unique?: string ): UmbObserverController; - provideContext(alias: string | UmbContextToken, instance: R): UmbContextProviderController; - consumeContext( - alias: string | UmbContextToken, - callback: UmbContextCallback - ): UmbContextConsumerController; + provideContext< + BaseType = unknown, + ResultType extends BaseType = BaseType, + InstanceType extends ResultType = ResultType + >(alias: string | UmbContextToken, instance: InstanceType): UmbContextProviderController; + consumeContext( + alias: string | UmbContextToken, + callback: UmbContextCallback + ): UmbContextConsumerController; } export const UmbElementMixin = (superClass: T) => { @@ -53,7 +57,12 @@ export const UmbElementMixin = (superClass: T) * @return {UmbContextProviderController} Reference to a Context Provider Controller instance * @memberof UmbElementMixin */ - provideContext(alias: string | UmbContextToken, instance: R): UmbContextProviderController { + provideContext< + BaseType = unknown, + ResultType extends BaseType = BaseType, + InstanceType extends ResultType = ResultType + > + (alias: string | UmbContextToken, instance: InstanceType): UmbContextProviderController { return new UmbContextProviderController(this, alias, instance); } @@ -64,10 +73,10 @@ export const UmbElementMixin = (superClass: T) * @return {UmbContextConsumerController} Reference to a Context Consumer Controller instance * @memberof UmbElementMixin */ - consumeContext( - alias: string | UmbContextToken, - callback: UmbContextCallback - ): UmbContextConsumerController { + consumeContext( + alias: string | UmbContextToken, + callback: UmbContextCallback + ): UmbContextConsumerController { return new UmbContextConsumerController(this, alias, callback); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/property-type-based-property/property-type-based-property.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/property-type-based-property/property-type-based-property.element.ts index 22a215fda2..30ed7b8c57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/property-type-based-property/property-type-based-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/property-type-based-property/property-type-based-property.element.ts @@ -2,12 +2,11 @@ import { UmbDataTypeConfig } from '../../property-editor/index.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbDataTypeRepository } from '@umbraco-cms/backoffice/data-type'; -import { UmbDocumentWorkspaceContext } from '@umbraco-cms/backoffice/document'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/document'; import type { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { DataTypeResponseModel, PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-property-type-based-property') export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement { @@ -57,12 +56,12 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement { } private _propertyVariantId?: UmbVariantId | undefined; - private _workspaceContext?: UmbDocumentWorkspaceContext; + private _workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._workspaceContext = workspaceContext as UmbDocumentWorkspaceContext; + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { + this._workspaceContext = workspaceContext; this._observeProperty(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts index 216bcd0a25..9a332bf0e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-context/workspace-context.ts @@ -1,6 +1,5 @@ import { UmbEntityWorkspaceContextInterface } from './workspace-entity-context.interface.js'; -import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; -import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbBaseController, UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbEntityBase } from '@umbraco-cms/backoffice/models'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @@ -12,6 +11,7 @@ TODO: We need to figure out if we like to keep using same alias for all workspac If so we need to align on a interface that all of these implements. otherwise consumers cant trust the workspace-context. */ export abstract class UmbWorkspaceContext + extends UmbBaseController implements UmbEntityWorkspaceContextInterface { public readonly host: UmbControllerHostElement; @@ -25,11 +25,12 @@ export abstract class UmbWorkspaceContext { + this.provideContext(UMB_WORKSPACE_CONTEXT, this); + this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (context) => { (this.modalContext as UmbModalContext) = context; }); } @@ -59,5 +60,4 @@ export abstract class UmbWorkspaceContext; - abstract destroy(): void; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-variant/workspace-variant.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-variant/workspace-variant.context.ts index ae547cca51..723ef79d03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-variant/workspace-variant.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-variant/workspace-variant.context.ts @@ -1,4 +1,3 @@ -import { UmbDocumentWorkspaceContext } from '../../../documents/documents/workspace/document-workspace.context.js'; import { UmbWorkspaceVariableEntityContextInterface } from '../workspace-context/workspace-variable-entity-context.interface.js'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { @@ -47,7 +46,7 @@ export class UmbWorkspaceVariantContext { // How do we ensure this connects to a document workspace context? and not just any other context? (We could start providing workspace contexts twice, under the general name and under a specific name) // TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken new UmbContextConsumerController(host, UMB_WORKSPACE_CONTEXT, (context) => { - this.#workspaceContext = context as UmbDocumentWorkspaceContext; + this.#workspaceContext = context as UmbWorkspaceVariableEntityContextInterface; this._observeVariant(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace-editor.element.ts index 782c224aec..5e8885353d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace-editor.element.ts @@ -1,20 +1,19 @@ -import { UmbDictionaryWorkspaceContext } from './dictionary-workspace.context.js'; +import { UMB_DICTIONARY_WORKSPACE_CONTEXT } from './dictionary-workspace.context.js'; import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-dictionary-workspace-editor') export class UmbDictionaryWorkspaceEditorElement extends UmbLitElement { @state() private _name?: string | null = ''; - #workspaceContext?: UmbDictionaryWorkspaceContext; + #workspaceContext?: typeof UMB_DICTIONARY_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbDictionaryWorkspaceContext; + this.consumeContext(UMB_DICTIONARY_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeName(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts index 70981cb8d3..65a134993d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/dictionary-workspace.context.ts @@ -3,6 +3,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { DictionaryItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbDictionaryWorkspaceContext extends UmbWorkspaceContext @@ -83,3 +84,9 @@ export class UmbDictionaryWorkspaceContext this.#data.complete(); } } + + +export const UMB_DICTIONARY_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbDictionaryWorkspaceContext => context.getEntityType?.() === 'dictionary-item' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/views/editor/workspace-view-dictionary-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/views/editor/workspace-view-dictionary-editor.element.ts index d099923ff7..c8a800a7be 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/views/editor/workspace-view-dictionary-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/dictionary/workspace/views/editor/workspace-view-dictionary-editor.element.ts @@ -1,10 +1,9 @@ -import { UmbDictionaryWorkspaceContext } from '../../dictionary-workspace.context.js'; +import { UMB_DICTIONARY_WORKSPACE_CONTEXT } from '../../dictionary-workspace.context.js'; import { UmbDictionaryRepository } from '../../../repository/dictionary.repository.js'; import { UUITextStyles, UUITextareaElement, UUITextareaEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { DictionaryItemResponseModel, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-workspace-view-dictionary-editor') export class UmbWorkspaceViewDictionaryEditorElement extends UmbLitElement { @state() @@ -15,7 +14,7 @@ export class UmbWorkspaceViewDictionaryEditorElement extends UmbLitElement { @state() private _languages: Array = []; - #workspaceContext!: UmbDictionaryWorkspaceContext; + #workspaceContext!: typeof UMB_DICTIONARY_WORKSPACE_CONTEXT.TYPE; async connectedCallback() { super.connectedCallback(); @@ -23,8 +22,8 @@ export class UmbWorkspaceViewDictionaryEditorElement extends UmbLitElement { this.#repo = new UmbDictionaryRepository(this); this._languages = await this.#repo.getLanguages(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (_instance) => { - this.#workspaceContext = _instance as UmbDictionaryWorkspaceContext; + this.consumeContext(UMB_DICTIONARY_WORKSPACE_CONTEXT, (_instance) => { + this.#workspaceContext = _instance; this.#observeDictionary(); }); } 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 edaee609fa..28582fa254 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 @@ -7,6 +7,7 @@ import type { DocumentTypeResponseModel, } from '@umbraco-cms/backoffice/backend-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = DocumentTypeResponseModel; export class UmbDocumentTypeWorkspaceContext @@ -151,5 +152,13 @@ export class UmbDocumentTypeWorkspaceContext public destroy(): void { this.structure.destroy(); + super.destroy(); } } + + +export const UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbDocumentTypeWorkspaceContext => context.getEntityType?.() === 'document-type' +); + 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 c3315d124d..0523fe1716 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 @@ -19,7 +19,6 @@ export class UmbDocumentTypeWorkspaceViewEditTabElement extends UmbLitElement { } public set ownerTabId(value: string | null | undefined) { if (value === this._ownerTabId) return; - console.log('ownerTabId', value); const oldValue = this._ownerTabId; this._ownerTabId = value; this._groupStructureHelper.setOwnerId(value); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-editor.element.ts index bbc79d3022..03a0a78f2c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace-editor.element.ts @@ -1,11 +1,11 @@ import { UmbDocumentWorkspaceSplitViewElement } from './document-workspace-split-view.element.js'; -import { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from './document-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { customElement, state, css, html } from '@umbraco-cms/backoffice/external/lit'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbRoute, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router'; import { VariantModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT, ActiveVariant } from '@umbraco-cms/backoffice/workspace'; +import { ActiveVariant } from '@umbraco-cms/backoffice/workspace'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-document-workspace-editor') export class UmbDocumentWorkspaceEditorElement extends UmbLitElement { @@ -21,13 +21,13 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement { @state() _workspaceSplitViews: Array = []; - #workspaceContext?: UmbDocumentWorkspaceContext; + #workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbDocumentWorkspaceContext; + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeVariants(); this.#observeSplitViews(); }); 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 2e9969575a..3b53f112e6 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,11 +1,11 @@ -import { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from './document-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; -import { ActiveVariant, UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { ActiveVariant } from '@umbraco-cms/backoffice/workspace'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-document-workspace-split-view') export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { - private _workspaceContext?: UmbDocumentWorkspaceContext; + private _workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE; @state() _unique?: string; @@ -16,8 +16,8 @@ export class UmbDocumentWorkspaceSplitViewElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { - this._workspaceContext = context as UmbDocumentWorkspaceContext; + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (context) => { + this._workspaceContext = context; this._observeActiveVariantInfo(); }); } 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 b2dae9cbe6..f893bbc8e9 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 { UmbDocumentTypeRepository } from '../../document-types/repository/docum import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type'; import { + UmbEntityWorkspaceContextInterface, UmbWorkspaceContext, UmbWorkspaceSplitViewManager, UmbWorkspaceVariableEntityContextInterface, @@ -15,6 +16,7 @@ import { UmbObserverController, } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; // TODO: should this context be called DocumentDraft instead of workspace? or should the draft be part of this? // TODO: Should we have a DocumentStructureContext and maybe even a DocumentDraftContext? @@ -200,7 +202,14 @@ export class UmbDocumentWorkspaceContext public destroy(): void { this.#draft.complete(); this.structure.destroy(); + super.destroy(); } } export default UmbDocumentWorkspaceContext; + + +export const UMB_DOCUMENT_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbDocumentWorkspaceContext => context.getEntityType?.() === 'document' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/index.ts index 2571e17006..e9022a2bcb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/index.ts @@ -1 +1 @@ -export { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; +export * from './document-workspace.context.js'; 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 f10b171c2c..86779efde4 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 @@ -1,10 +1,9 @@ -import { UmbDocumentWorkspaceContext } from '../../document-workspace.context.js'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../../document-workspace.context.js'; import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { UmbContentTypePropertyStructureHelper, PropertyContainerTypes } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { PropertyTypeModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-document-workspace-view-edit-properties') export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement { @property({ type: String, attribute: 'container-name', reflect: false }) @@ -31,8 +30,8 @@ export class UmbDocumentWorkspaceViewEditPropertiesElement extends UmbLitElement constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._propertyStructureHelper.setStructureManager((workspaceContext as UmbDocumentWorkspaceContext).structure); + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { + this._propertyStructureHelper.setStructureManager(workspaceContext.structure); }); this.observe(this._propertyStructureHelper.propertyStructure, (propertyStructure) => { this._propertyStructure = propertyStructure; 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 edd197e53c..d20ab42f14 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 @@ -1,10 +1,9 @@ -import { UmbDocumentWorkspaceContext } from '../../document-workspace.context.js'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../../document-workspace.context.js'; import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { UmbContentTypeContainerStructureHelper } from '@umbraco-cms/backoffice/content-type'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import './document-workspace-view-edit-properties.element.js'; @customElement('umb-document-workspace-view-edit-tab') @@ -53,8 +52,8 @@ export class UmbDocumentWorkspaceViewEditTabElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this._groupStructureHelper.setStructureManager((workspaceContext as UmbDocumentWorkspaceContext).structure); + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { + this._groupStructureHelper.setStructureManager(workspaceContext.structure); }); this.observe(this._groupStructureHelper.containers, (groups) => { this._groups = 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 5f3d398cc3..7ff9ef424c 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 @@ -1,4 +1,4 @@ -import { UmbDocumentWorkspaceContext } from '../../document-workspace.context.js'; +import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from '../../document-workspace.context.js'; import type { UmbDocumentWorkspaceViewEditTabElement } from './document-workspace-view-edit-tab.element.js'; import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; @@ -11,7 +11,6 @@ import { } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { PropertyTypeContainerModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-document-workspace-view-edit') @@ -34,7 +33,7 @@ export class UmbDocumentWorkspaceViewEditElement @state() private _activePath = ''; - private _workspaceContext?: UmbDocumentWorkspaceContext; + private _workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE; private _tabsStructureHelper = new UmbContentTypeContainerStructureHelper(this); @@ -50,9 +49,9 @@ export class UmbDocumentWorkspaceViewEditElement // _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 UmbDocumentWorkspaceContext; - this._tabsStructureHelper.setStructureManager((workspaceContext as UmbDocumentWorkspaceContext).structure); + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (workspaceContext) => { + this._workspaceContext = workspaceContext; + this._tabsStructureHelper.setStructureManager(workspaceContext.structure); this._observeRootGroups(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace-editor.element.ts index ef0a7d0cef..66ccbc9080 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace-editor.element.ts @@ -1,19 +1,18 @@ -import { UmbWorkspaceMediaTypeContext } from './media-type-workspace.context.js'; +import { UMB_MEDIA_TYPE_WORKSPACE_CONTEXT } from './media-type-workspace.context.js'; import { UUITextStyles, UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-media-type-workspace-editor') export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement { @state() private _mediaTypeName?: string | null = ''; - #workspaceContext?: UmbWorkspaceMediaTypeContext; + #workspaceContext?: typeof UMB_MEDIA_TYPE_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbWorkspaceMediaTypeContext; + this.consumeContext(UMB_MEDIA_TYPE_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeName(); }); } 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 7094e6321e..af3dc52196 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 @@ -3,9 +3,10 @@ import type { MediaTypeDetails } from '../types.js'; import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = MediaTypeDetails; -export class UmbWorkspaceMediaTypeContext +export class UmbMediaTypeWorkspaceContext extends UmbWorkspaceContext implements UmbEntityWorkspaceContextInterface { @@ -61,3 +62,9 @@ export class UmbWorkspaceMediaTypeContext this.#data.complete(); } } + + +export const UMB_MEDIA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbMediaTypeWorkspaceContext => context.getEntityType?.() === 'media-type' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.element.ts index e52824cf62..f7526539cf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.element.ts @@ -1,4 +1,4 @@ -import { UmbWorkspaceMediaTypeContext } from './media-type-workspace.context.js'; +import { UmbMediaTypeWorkspaceContext } from './media-type-workspace.context.js'; import { UmbMediaTypeWorkspaceEditorElement } from './media-type-workspace-editor.element.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; @@ -7,7 +7,7 @@ import type { UmbRoute } from '@umbraco-cms/backoffice/router'; @customElement('umb-media-type-workspace') export class UmbMediaTypeWorkspaceElement extends UmbLitElement { - #workspaceContext = new UmbWorkspaceMediaTypeContext(this); + #workspaceContext = new UmbMediaTypeWorkspaceContext(this); #element = new UmbMediaTypeWorkspaceEditorElement(); @state() diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-editor.element.ts index bd019acd8a..a73f300ae3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace-editor.element.ts @@ -1,20 +1,19 @@ -import { UmbMediaWorkspaceContext } from './media-workspace.context.js'; +import { UMB_MEDIA_WORKSPACE_CONTEXT } from './media-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-media-workspace-editor') export class UmbMediaWorkspaceEditorElement extends UmbLitElement { @state() _id?: string; - #umbWorkspaceContext?: UmbMediaWorkspaceContext; + #umbWorkspaceContext?: typeof UMB_MEDIA_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#umbWorkspaceContext = instance as UmbMediaWorkspaceContext; + this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (instance) => { + this.#umbWorkspaceContext = instance; this.#observeId(); }); } 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 6385dabbf3..be6dc2a30c 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 @@ -3,6 +3,7 @@ import type { MediaDetails } from '../index.js'; import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { appendToFrozenArray, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = MediaDetails; export class UmbMediaWorkspaceContext @@ -81,3 +82,8 @@ export class UmbMediaWorkspaceContext this.#data.complete(); } } + +export const UMB_MEDIA_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbMediaWorkspaceContext => context.getEntityType?.() === 'media' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace-editor.element.ts index 68326fa8ab..f179034f91 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace-editor.element.ts @@ -1,16 +1,15 @@ import type { MemberGroupDetails } from '../types.js'; -import { UmbWorkspaceMemberGroupContext } from './member-group-workspace.context.js'; +import { UMB_MEMBER_GROUP_WORKSPACE_CONTEXT } from './member-group-workspace.context.js'; import { UUITextStyles, UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; /** * @element umb-member-group-edit-workspace * @description - Element for displaying a Member Group Workspace */ @customElement('umb-member-group-workspace-editor') export class UmbMemberGroupWorkspaceEditorElement extends UmbLitElement { - #workspaceContext?: UmbWorkspaceMemberGroupContext; + #workspaceContext?: typeof UMB_MEMBER_GROUP_WORKSPACE_CONTEXT.TYPE; @state() private _memberGroup?: MemberGroupDetails; @@ -18,8 +17,8 @@ export class UmbMemberGroupWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbWorkspaceMemberGroupContext; + this.consumeContext(UMB_MEMBER_GROUP_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeMemberGroup(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts index e62fd10d22..3de2301159 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.context.ts @@ -3,9 +3,10 @@ import type { MemberGroupDetails } from '../types.js'; import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; type EntityType = MemberGroupDetails; -export class UmbWorkspaceMemberGroupContext +export class UmbMemberGroupWorkspaceContext extends UmbWorkspaceContext implements UmbEntityWorkspaceContextInterface { @@ -63,3 +64,10 @@ export class UmbWorkspaceMemberGroupContext this.#data.complete(); } } + + + +export const UMB_MEMBER_GROUP_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbMemberGroupWorkspaceContext => context.getEntityType?.() === 'member-group' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.element.ts index a3f7dd6275..a7b397d6b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/member-group-workspace.element.ts @@ -1,4 +1,4 @@ -import { UmbWorkspaceMemberGroupContext } from './member-group-workspace.context.js'; +import { UmbMemberGroupWorkspaceContext } from './member-group-workspace.context.js'; import { UmbMemberGroupWorkspaceEditorElement } from './member-group-workspace-editor.element.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; @@ -11,7 +11,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; */ @customElement('umb-member-group-workspace') export class UmbMemberGroupWorkspaceElement extends UmbLitElement { - #workspaceContext = new UmbWorkspaceMemberGroupContext(this); + #workspaceContext = new UmbMemberGroupWorkspaceContext(this); #element = new UmbMemberGroupWorkspaceEditorElement(); @state() diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/views/info/workspace-view-member-group-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/views/info/workspace-view-member-group-info.element.ts index 668e27a38b..f0ecd0c4c7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/views/info/workspace-view-member-group-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-groups/workspace/views/info/workspace-view-member-group-info.element.ts @@ -1,22 +1,20 @@ -import { UmbWorkspaceMemberGroupContext } from '../../member-group-workspace.context.js'; +import { UMB_MEMBER_GROUP_WORKSPACE_CONTEXT } from '../../member-group-workspace.context.js'; import type { MemberGroupDetails } from '../../../types.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-workspace-view-member-group-info') export class UmbWorkspaceViewMemberGroupInfoElement extends UmbLitElement { @state() private _memberGroup?: MemberGroupDetails; - #workspaceContext?: UmbWorkspaceMemberGroupContext; + #workspaceContext?: typeof UMB_MEMBER_GROUP_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - // TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbWorkspaceMemberGroupContext; + this.consumeContext(UMB_MEMBER_GROUP_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeMemberGroup(); }); } @@ -26,10 +24,7 @@ export class UmbWorkspaceViewMemberGroupInfoElement extends UmbLitElement { this.observe(this.#workspaceContext.data, (memberGroup) => { if (!memberGroup) return; - - // TODO: handle if model is not of the type wanted. - // TODO: Make method to identify wether data is of type MemberGroupDetails - this._memberGroup = memberGroup as MemberGroupDetails; + this._memberGroup = memberGroup; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts index 0f23872a49..89d557f61c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-types/workspace/member-type-workspace.context.ts @@ -2,6 +2,7 @@ import { UmbMemberTypeRepository } from '../repository/member-type.repository.js import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; // TODO => use correct tpye type EntityType = any; @@ -73,3 +74,8 @@ export class UmbMemberTypeWorkspaceContext this.#data.complete(); } } + +export const UMB_MEMBER_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbMemberTypeWorkspaceContext => context.getEntityType?.() === 'member-type' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts index 05ff73ad53..20aecf8e28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/members/workspace/member-workspace.context.ts @@ -2,6 +2,7 @@ import { UmbMemberRepository } from '../repository/member.repository.js'; import type { MemberDetails } from '../types.js'; import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbMemberWorkspaceContext extends UmbWorkspaceContext @@ -35,3 +36,8 @@ export class UmbMemberWorkspaceContext console.log('destroy'); } } + +export const UMB_MEMBER_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbMemberWorkspaceContext => context.getEntityType?.() === 'member' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace-editor.element.ts index 2df92c319c..c78e61f20b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace-editor.element.ts @@ -1,28 +1,28 @@ -import { UmbDataTypeWorkspaceContext } from './data-type-workspace.context.js'; +import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from './data-type-workspace.context.js'; import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; /** * @element umb-data-type-workspace-editor * @description - Element for displaying the Data Type Workspace edit route. */ @customElement('umb-data-type-workspace-editor') export class UmbDataTypeWorkspaceEditorElement extends UmbLitElement { - @property() + + @property({attribute: false}) manifest?: ManifestWorkspace; @state() private _dataTypeName = ''; - #workspaceContext?: UmbDataTypeWorkspaceContext; + #workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#workspaceContext = workspaceContext as UmbDataTypeWorkspaceContext; + this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => { + this.#workspaceContext = workspaceContext; this.#observeIsNew(); this.#observeName(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace.context.ts index 4c2e250cb5..4019f99469 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/data-type-workspace.context.ts @@ -3,6 +3,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import type { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { appendToFrozenArray, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbDataTypeWorkspaceContext extends UmbWorkspaceContext @@ -95,3 +96,8 @@ export class UmbDataTypeWorkspaceContext this.#data.complete(); } } + +export const UMB_DATA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbDataTypeWorkspaceContext => context.getEntityType?.() === 'data-type' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts index d8ef6db802..3b6543ae28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/details/data-type-details-workspace-view.element.ts @@ -1,7 +1,6 @@ -import { UmbDataTypeWorkspaceContext } from '../../data-type-workspace.context.js'; +import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '../../data-type-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbModalManagerContext, UMB_MODAL_MANAGER_CONTEXT_TOKEN, @@ -37,7 +36,7 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement @state() private _data: Array = []; - private _workspaceContext?: UmbDataTypeWorkspaceContext; + private _workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE; private _modalContext?: UmbModalManagerContext; constructor() { @@ -47,9 +46,8 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement this._modalContext = instance; }); - // TODO: Figure out if this is the best way to consume a context or if it could be strongly typed using UmbContextToken - this.consumeContext(UMB_WORKSPACE_CONTEXT, (_instance) => { - this._workspaceContext = _instance as UmbDataTypeWorkspaceContext; + this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (_instance) => { + this._workspaceContext = _instance; this._observeDataType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts index 82df25cb71..9a4cf225e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/data-types/workspace/views/info/workspace-view-data-type-info.element.ts @@ -1,7 +1,6 @@ -import { UmbDataTypeWorkspaceContext } from '../../data-type-workspace.context.js'; +import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '../../data-type-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry'; @@ -14,14 +13,13 @@ export class UmbWorkspaceViewDataTypeInfoElement @state() _dataType?: DataTypeResponseModel; - private _workspaceContext?: UmbDataTypeWorkspaceContext; + private _workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - // TODO: Figure out if this is the best way to consume the context or if it can be strongly typed with an UmbContextToken - this.consumeContext(UMB_WORKSPACE_CONTEXT, (dataTypeContext) => { - this._workspaceContext = dataTypeContext as UmbDataTypeWorkspaceContext; + this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (dataTypeContext) => { + this._workspaceContext = dataTypeContext; this._observeDataType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace-editor.element.ts index 302bb99f90..81d95a8b76 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace-editor.element.ts @@ -1,12 +1,11 @@ -import { UmbLanguageWorkspaceContext } from './language-workspace.context.js'; +import { UMB_LANGUAGE_WORKSPACE_CONTEXT } from './language-workspace.context.js'; import { UUITextStyles, UUIInputElement, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-language-workspace-editor') export class UmbLanguageWorkspaceEditorElement extends UmbLitElement { - #workspaceContext?: UmbLanguageWorkspaceContext; + #workspaceContext?: typeof UMB_LANGUAGE_WORKSPACE_CONTEXT.TYPE; @state() _language?: LanguageResponseModel; @@ -17,8 +16,8 @@ export class UmbLanguageWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { - this.#workspaceContext = context as UmbLanguageWorkspaceContext; + this.consumeContext(UMB_LANGUAGE_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this.#observeData(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts index 06c3895234..1b8609c9b1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/language-workspace.context.ts @@ -3,6 +3,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import { ApiError, LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbLanguageWorkspaceContext extends UmbWorkspaceContext @@ -100,3 +101,9 @@ export class UmbLanguageWorkspaceContext this.#data.complete(); } } + + +export const UMB_LANGUAGE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbLanguageWorkspaceContext => context.getEntityType?.() === 'language' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/views/details/language-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/views/details/language-details-workspace-view.element.ts index 39e7490849..c9237b6767 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/views/details/language-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/languages/workspace/language/views/details/language-details-workspace-view.element.ts @@ -1,13 +1,11 @@ -import { UmbLanguageWorkspaceContext } from '../../language-workspace.context.js'; +import { UMB_LANGUAGE_WORKSPACE_CONTEXT } from '../../language-workspace.context.js'; import type { UmbInputCultureSelectElement } from '../../../../../cultures/components/input-culture-select/input-culture-select.element.js'; import type { UmbInputLanguagePickerElement } from '../../../../components/input-language-picker/input-language-picker.element.js'; import { UUIBooleanInputEvent, UUIToggleElement, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -// TODO: set up import alias for these modules import { UmbChangeEvent } from '@umbraco-cms/backoffice/events'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-language-details-workspace-view') @@ -27,7 +25,7 @@ export class UmbLanguageDetailsWorkspaceViewElement @state() _validationErrors?: { [key: string]: Array }; - #languageWorkspaceContext?: UmbLanguageWorkspaceContext; + #languageWorkspaceContext?: typeof UMB_LANGUAGE_WORKSPACE_CONTEXT.TYPE; constructor() { super(); @@ -36,8 +34,8 @@ export class UmbLanguageDetailsWorkspaceViewElement In the language workspace we want to clear a default language change warning and reset the initial state after a save action has been executed. */ let initialStateSet = false; - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#languageWorkspaceContext = instance as UmbLanguageWorkspaceContext; + this.consumeContext(UMB_LANGUAGE_WORKSPACE_CONTEXT, (instance) => { + this.#languageWorkspaceContext = instance; this.observe(this.#languageWorkspaceContext.data, (language) => { this._language = language; diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace-editor.element.ts index a5c702b781..c240f5074e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace-editor.element.ts @@ -1,16 +1,15 @@ -import { UmbRelationTypeWorkspaceContext } from './relation-type-workspace.context.js'; +import { UMB_RELATION_TYPE_WORKSPACE_CONTEXT } from './relation-type-workspace.context.js'; import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; /** * @element umb-relation-type-workspace-editor * @description - Element for displaying a Relation Type Workspace */ @customElement('umb-relation-type-workspace-editor') export class UmbRelationTypeWorkspaceEditorElement extends UmbLitElement { - #workspaceContext?: UmbRelationTypeWorkspaceContext; + #workspaceContext?: typeof UMB_RELATION_TYPE_WORKSPACE_CONTEXT.TYPE; @state() private _relationType?: RelationTypeResponseModel; @@ -18,8 +17,8 @@ export class UmbRelationTypeWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbRelationTypeWorkspaceContext; + this.consumeContext(UMB_RELATION_TYPE_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.#observeRelationType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts index e4ca17e164..d8e9eb97d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/relation-type-workspace.context.ts @@ -3,6 +3,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import type { RelationTypeBaseModel, RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbRelationTypeWorkspaceContext extends UmbWorkspaceContext @@ -75,3 +76,10 @@ export class UmbRelationTypeWorkspaceContext this.#data.complete(); } } + + + +export const UMB_RELATION_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbRelationTypeWorkspaceContext => context.getEntityType?.() === 'relation-type' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/views/relation-type/relation-type-workspace-view-relation-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/views/relation-type/relation-type-workspace-view-relation-type.element.ts index 38a5646f66..2cbde2b269 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/views/relation-type/relation-type-workspace-view-relation-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/settings/relation-types/workspace/views/relation-type/relation-type-workspace-view-relation-type.element.ts @@ -1,4 +1,4 @@ -import { UmbRelationTypeWorkspaceContext } from '../../relation-type-workspace.context.js'; +import { UMB_RELATION_TYPE_WORKSPACE_CONTEXT } from '../../relation-type-workspace.context.js'; import { UUITextStyles, UUIBooleanInputEvent, @@ -9,7 +9,6 @@ import { import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { RelationTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbWorkspaceEditorViewExtensionElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-relation-type-workspace-view-relation-type') @@ -20,13 +19,13 @@ export class UmbRelationTypeWorkspaceViewRelationTypeElement @state() private _relationType?: RelationTypeResponseModel; - #workspaceContext?: UmbRelationTypeWorkspaceContext; + #workspaceContext?: typeof UMB_RELATION_TYPE_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbRelationTypeWorkspaceContext; + this.consumeContext(UMB_RELATION_TYPE_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this._observeRelationType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts index 1765d62c51..bcc1996c93 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/manifests.ts @@ -5,7 +5,7 @@ const workspace: ManifestWorkspace = { type: 'workspace', alias: 'Umb.Workspace.PartialView', name: 'Partial View Workspace', - loader: () => import('./partial-views-workspace.element.js'), + loader: () => import('./partial-view-workspace.element.js'), meta: { entityType: 'partial-view', }, diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace-edit.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-edit.element.ts similarity index 79% rename from src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace-edit.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-edit.element.ts index f376caaa93..c5bc85cde3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-edit.element.ts @@ -1,17 +1,16 @@ import { UmbTemplatingInsertMenuElement } from '../../components/index.js'; import { UMB_TEMPLATE_QUERY_BUILDER_MODAL } from '../../templates/modals/modal-tokens.js'; import { getQuerySnippet } from '../../utils.js'; -import { UmbPartialViewsWorkspaceContext } from './partial-views-workspace.context.js'; +import { UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT } from './partial-view-workspace.context.js'; import type { UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; import { UUITextStyles, UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, query, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; import { Subject, debounceTime } from '@umbraco-cms/backoffice/external/rxjs'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -@customElement('umb-partial-views-workspace-edit') -export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement { +@customElement('umb-partial-view-workspace-edit') +export class UmbPartialViewWorkspaceEditElement extends UmbLitElement { #name: string | undefined = ''; @state() private get _name() { @@ -35,11 +34,9 @@ export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement { @query('umb-code-editor') private _codeEditor?: UmbCodeEditorElement; - #partialViewsWorkspaceContext?: UmbPartialViewsWorkspaceContext; + #partialViewWorkspaceContext?: typeof UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT.TYPE; private _modalContext?: UmbModalManagerContext; - #isNew = false; - private inputQuery$ = new Subject(); constructor() { @@ -49,31 +46,26 @@ export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement { this._modalContext = instance; }); - //tODO: should this be called something else here? - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#partialViewsWorkspaceContext = workspaceContext as UmbPartialViewsWorkspaceContext; - this.observe(this.#partialViewsWorkspaceContext.name, (name) => { + this.consumeContext(UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT, (workspaceContext) => { + this.#partialViewWorkspaceContext = workspaceContext; + this.observe(this.#partialViewWorkspaceContext.name, (name) => { this._name = name; }); - this.observe(this.#partialViewsWorkspaceContext.content, (content) => { + this.observe(this.#partialViewWorkspaceContext.content, (content) => { this._content = content; }); - this.observe(this.#partialViewsWorkspaceContext.path, (path) => { + this.observe(this.#partialViewWorkspaceContext.path, (path) => { this._path = path; }); - this.observe(this.#partialViewsWorkspaceContext.isNew, (isNew) => { - this.#isNew = !!isNew; - }); - - this.observe(this.#partialViewsWorkspaceContext.isCodeEditorReady, (isReady) => { + this.observe(this.#partialViewWorkspaceContext.isCodeEditorReady, (isReady) => { this._ready = isReady; }); this.inputQuery$.pipe(debounceTime(250)).subscribe((nameInputValue: string) => { - this.#partialViewsWorkspaceContext?.setName(`${nameInputValue}.cshtml`); + this.#partialViewWorkspaceContext?.setName(`${nameInputValue}.cshtml`); }); }); } @@ -87,7 +79,7 @@ export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement { #onCodeEditorInput(event: Event) { const target = event.target as UmbCodeEditorElement; const value = target.code as string; - this.#partialViewsWorkspaceContext?.setContent(value); + this.#partialViewWorkspaceContext?.setContent(value); } #insertSnippet(event: Event) { @@ -206,10 +198,10 @@ export class UmbPartialViewsWorkspaceEditElement extends UmbLitElement { ]; } -export default UmbPartialViewsWorkspaceEditElement; +export default UmbPartialViewWorkspaceEditElement; declare global { interface HTMLElementTagNameMap { - 'umb-partial-views-workspace-edit': UmbPartialViewsWorkspaceEditElement; + 'umb-partial-view-workspace-edit': UmbPartialViewWorkspaceEditElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts similarity index 80% rename from src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.context.ts rename to src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts index 797750451f..89792790cc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts @@ -2,20 +2,20 @@ import { UmbPartialViewsRepository } from '../repository/partial-views.repositor import { PartialViewDetails } from '../config.js'; import { createObservablePart, UmbBooleanState, UmbDeepState } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; -import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; import { UpdatePartialViewRequestModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -// TODO: I think this should be named PartialViewWorkspace... not with an 's' -export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext< +export class UmbPartialViewWorkspaceContext extends UmbWorkspaceContext< UmbPartialViewsRepository, PartialViewDetails -> { +> implements UmbEntityWorkspaceContextInterface { getEntityId(): string | undefined { return this.getData()?.path; } getEntityType(): string { - return 'partial-views'; + return 'partial-view'; } save(): Promise { const partialView = this.getData(); @@ -41,9 +41,7 @@ export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext< this.repository.save(partialView.path, updateRequestBody); return Promise.resolve(); } - destroy(): void { - throw new Error('Method not implemented.'); - } + #data = new UmbDeepState(undefined); data = this.#data.asObservable(); name = createObservablePart(this.#data, (data) => data?.name); @@ -54,7 +52,7 @@ export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext< isCodeEditorReady = this.#isCodeEditorReady.asObservable(); constructor(host: UmbControllerHostElement) { - super(host, 'Umb.Workspace.PartialViews', new UmbPartialViewsRepository(host)); + super(host, 'Umb.Workspace.PartialView', new UmbPartialViewsRepository(host)); this.#loadCodeEditor(); } @@ -99,3 +97,10 @@ export class UmbPartialViewsWorkspaceContext extends UmbWorkspaceContext< this.#data.next(newPartial); } } + + + +export const UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbPartialViewWorkspaceContext => context.getEntityType?.() === 'partial-view' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.element.ts similarity index 66% rename from src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.element.ts index fd4e506f4b..68ffef8742 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-views-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.element.ts @@ -1,19 +1,19 @@ -import { UmbPartialViewsWorkspaceContext } from './partial-views-workspace.context.js'; +import { UmbPartialViewWorkspaceContext } from './partial-view-workspace.context.js'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbRoute, IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; -import './partial-views-workspace-edit.element.js'; +import './partial-view-workspace-edit.element.js'; import '../../components/insert-menu/templating-insert-menu.element.js'; import { UmbWorkspaceIsNewRedirectController } from '@umbraco-cms/backoffice/workspace'; -@customElement('umb-partial-views-workspace') -export class UmbPartialViewsWorkspaceElement extends UmbLitElement { - #partialViewsWorkspaceContext = new UmbPartialViewsWorkspaceContext(this); +@customElement('umb-partial-view-workspace') +export class UmbPartialViewWorkspaceElement extends UmbLitElement { + #partialViewWorkspaceContext = new UmbPartialViewWorkspaceContext(this); - #element = document.createElement('umb-partial-views-workspace-edit'); + #element = document.createElement('umb-partial-view-workspace-edit'); @state() _routes: UmbRoute[] = [ @@ -24,11 +24,11 @@ export class UmbPartialViewsWorkspaceElement extends UmbLitElement { const parentKey = info.match.params.parentKey; const decodePath = decodeURIComponent(parentKey); const snippetName = info.match.params.snippetName; - this.#partialViewsWorkspaceContext.create(decodePath === 'null' ? null : parentKey, snippetName); + this.#partialViewWorkspaceContext.create(decodePath === 'null' ? null : parentKey, snippetName); new UmbWorkspaceIsNewRedirectController( this, - this.#partialViewsWorkspaceContext, + this.#partialViewWorkspaceContext, this.shadowRoot!.querySelector('umb-router-slot')! ); }, @@ -39,7 +39,7 @@ export class UmbPartialViewsWorkspaceElement extends UmbLitElement { setup: (component: PageComponent, info: IRoutingInfo) => { const key = info.match.params.key; const decodePath = decodeURIComponent(key).replace('-cshtml', '.cshtml'); - this.#partialViewsWorkspaceContext.load(decodePath); + this.#partialViewWorkspaceContext.load(decodePath); }, }, ]; @@ -51,10 +51,10 @@ export class UmbPartialViewsWorkspaceElement extends UmbLitElement { static styles = [UUITextStyles, css``]; } -export default UmbPartialViewsWorkspaceElement; +export default UmbPartialViewWorkspaceElement; declare global { interface HTMLElementTagNameMap { - 'umb-partial-views-workspace': UmbPartialViewsWorkspaceElement; + 'umb-partial-view-workspace': UmbPartialViewWorkspaceElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts index 8ee2e24e01..84ab0d335a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context.ts @@ -1,14 +1,15 @@ import { UmbStylesheetRepository } from '../repository/stylesheet.repository.js'; import { StylesheetDetails } from '../index.js'; -import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, UmbBooleanState, UmbObjectState, createObservablePart } from '@umbraco-cms/backoffice/observable-api'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; import { RichTextRuleModel, UpdateStylesheetRequestModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export type RichTextRuleModelSortable = RichTextRuleModel & { sortOrder?: number }; -export class UmbStylesheetWorkspaceContext extends UmbWorkspaceContext { +export class UmbStylesheetWorkspaceContext extends UmbWorkspaceContext implements UmbEntityWorkspaceContextInterface { #data = new UmbObjectState(undefined); #rules = new UmbArrayState([], (rule) => rule.name); data = this.#data.asObservable(); @@ -158,3 +159,8 @@ export class UmbStylesheetWorkspaceContext extends UmbWorkspaceContext( + 'UmbWorkspaceContext', + (context): context is UmbStylesheetWorkspaceContext => context.getEntityType?.() === 'stylesheet' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts index d42558c8f8..e205153628 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace-editor.element.ts @@ -2,7 +2,7 @@ import type { UmbTemplatingInsertMenuElement } from '../../components/insert-men import { UMB_MODAL_TEMPLATING_INSERT_SECTION_MODAL } from '../../modals/insert-section-modal/insert-section-modal.element.js'; import { UMB_TEMPLATE_QUERY_BUILDER_MODAL } from '../modals/modal-tokens.js'; import { getQuerySnippet } from '../../utils.js'; -import type { UmbTemplateWorkspaceContext } from './template-workspace.context.js'; +import { UMB_TEMPLATE_WORKSPACE_CONTEXT } from './template-workspace.context.js'; import type { UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; import { camelCase } from '@umbraco-cms/backoffice/external/lodash'; import { UUITextStyles, UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; @@ -14,7 +14,6 @@ import { } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { Subject, debounceTime } from '@umbraco-cms/backoffice/external/rxjs'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-template-workspace-editor') export class UmbTemplateWorkspaceEditorElement extends UmbLitElement { @@ -36,7 +35,7 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement { @query('umb-code-editor') private _codeEditor?: UmbCodeEditorElement; - #templateWorkspaceContext?: UmbTemplateWorkspaceContext; + #templateWorkspaceContext?: typeof UMB_TEMPLATE_WORKSPACE_CONTEXT.TYPE; #isNew = false; #masterTemplateId: string | null = null; @@ -50,8 +49,8 @@ export class UmbTemplateWorkspaceEditorElement extends UmbLitElement { this._modalContext = instance; }); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#templateWorkspaceContext = workspaceContext as UmbTemplateWorkspaceContext; + this.consumeContext(UMB_TEMPLATE_WORKSPACE_CONTEXT, (workspaceContext) => { + this.#templateWorkspaceContext = workspaceContext; this.observe(this.#templateWorkspaceContext.name, (name) => { this._name = name; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts index 7c0d3b12b4..eb16956d22 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context.ts @@ -1,6 +1,6 @@ import { UmbTemplateRepository } from '../repository/template.repository.js'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; -import { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { createObservablePart, UmbBooleanState, @@ -9,8 +9,9 @@ import { } from '@umbraco-cms/backoffice/observable-api'; import type { TemplateItemResponseModel, TemplateResponseModel } from '@umbraco-cms/backoffice/backend-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext { +export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext implements UmbEntityWorkspaceContextInterface { #data = new UmbDeepState(undefined); data = this.#data.asObservable(); #masterTemplate = new UmbObjectState(null); @@ -163,5 +164,13 @@ ${currentContent}`; public destroy() { this.#data.complete(); + super.destroy(); } } + + + +export const UMB_TEMPLATE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbTemplateWorkspaceContext => context.getEntityType?.() === 'template' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/actions/workspace-action-user-group-save.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/actions/workspace-action-user-group-save.element.ts index 6dcb3840f1..82e73a3bfd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/actions/workspace-action-user-group-save.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/actions/workspace-action-user-group-save.element.ts @@ -1,21 +1,20 @@ -import { UmbUserWorkspaceContext } from '../../../users/workspace/user-workspace.context.js'; +import { UMB_USER_WORKSPACE_CONTEXT } from '../../../users/workspace/user-workspace.context.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-workspace-action-user-group-save') export class UmbWorkspaceActionUserGroupSaveElement extends UmbLitElement { @state() private _saveButtonState?: UUIButtonState; - private _workspaceContext?: UmbUserWorkspaceContext; + private _workspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this._workspaceContext = instance as UmbUserWorkspaceContext; + this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => { + this._workspaceContext = instance; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts index f31fdc2cb4..ff25993a7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace-editor.element.ts @@ -1,11 +1,10 @@ -import { UmbUserGroupWorkspaceContext } from './user-group-workspace.context.js'; +import { UMB_USER_GROUP_WORKSPACE_CONTEXT } from './user-group-workspace.context.js'; import { UUIInputElement, UUIInputEvent, UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; // TODO: import from package when available //import { UmbUserInputElement } from '../../users/components/user-input/user-input.element.js'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UserGroupResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UMB_CONFIRM_MODAL, @@ -21,14 +20,14 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { @state() private _userKeys?: Array; - #workspaceContext?: UmbUserGroupWorkspaceContext; + #workspaceContext?: typeof UMB_USER_GROUP_WORKSPACE_CONTEXT.TYPE; #modalContext?: UmbModalManagerContext; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbUserGroupWorkspaceContext; + this.consumeContext(UMB_USER_GROUP_WORKSPACE_CONTEXT, (instance) => { + this.#workspaceContext = instance; this.observe(this.#workspaceContext.data, (userGroup) => (this._userGroup = userGroup)); this.observe(this.#workspaceContext.userIds, (userKeys) => (this._userKeys = userKeys)); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace.context.ts index 22c49bbfc8..b9ce1e2fe3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/user-groups/workspace/user-group-workspace.context.ts @@ -4,6 +4,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import type { UserGroupResponseModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbUserGroupWorkspaceContext extends UmbWorkspaceContext @@ -102,3 +103,9 @@ export class UmbUserGroupWorkspaceContext this.#userIds.next(keys); } } + + +export const UMB_USER_GROUP_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbUserGroupWorkspaceContext => context.getEntityType?.() === 'user-group' +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/actions/user-workspace-action-save.element.ts b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/actions/user-workspace-action-save.element.ts index cb2668d241..3f13e9538a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/actions/user-workspace-action-save.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/actions/user-workspace-action-save.element.ts @@ -1,21 +1,20 @@ -import { UmbUserWorkspaceContext } from '../user-workspace.context.js'; +import { UMB_USER_WORKSPACE_CONTEXT } from '../user-workspace.context.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UUITextStyles } from '@umbraco-cms/backoffice/external/uui'; import type { UUIButtonState } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-user-workspace-action-save') export class UmbUserWorkspaceActionSaveElement extends UmbLitElement { @state() private _saveButtonState?: UUIButtonState; - private _workspaceContext?: UmbUserWorkspaceContext; + private _workspaceContext?: typeof UMB_USER_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this._workspaceContext = instance as UmbUserWorkspaceContext; + this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (instance) => { + this._workspaceContext = instance; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace.context.ts index 7a5c8bea0c..b9c4567217 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/users/users/workspace/user-workspace.context.ts @@ -4,7 +4,7 @@ import { UmbEntityWorkspaceContextInterface, UmbWorkspaceContext } from '@umbrac import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import type { UpdateUserRequestModel } from '@umbraco-cms/backoffice/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; -import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; +import { UmbContextConsumerController, UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UMB_AUTH } from '@umbraco-cms/backoffice/auth'; import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; @@ -81,3 +81,8 @@ export class UmbUserWorkspaceContext this.#data.complete(); } } + +export const UMB_USER_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + (context): context is UmbUserWorkspaceContext => context.getEntityType?.() === 'user' +); diff --git a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts index 36a5862575..831ead587c 100644 --- a/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts +++ b/src/Umbraco.Web.UI.Client/src/shared/auth/auth.token.ts @@ -2,6 +2,5 @@ import { IUmbAuth } from './auth.interface.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export const UMB_AUTH = new UmbContextToken( - 'UmbAuth', - 'An instance of UmbAuthFlow that should be shared across the app.' + 'UmbAuth' ); diff --git a/src/Umbraco.Web.UI.Client/storybook/stories/context-api.mdx b/src/Umbraco.Web.UI.Client/storybook/stories/context-api.mdx index 8b7b5c8fb5..1dfa36306a 100644 --- a/src/Umbraco.Web.UI.Client/storybook/stories/context-api.mdx +++ b/src/Umbraco.Web.UI.Client/storybook/stories/context-api.mdx @@ -5,7 +5,7 @@ import { Meta } from '@storybook/blocks'; # Context API The Context API enables connections between Elements and APIs. -DOM structure defines the context of which an API is exposed for. APIs are provided via an element and can then be consumed by any decending element wthin. +DOM structure defines the context of which an API is exposed for. APIs are provided via an element and can then be consumed by any decending element. ### Consume a Context API. @@ -18,7 +18,7 @@ this.consumeContext('requestThisContextAlias', (context) => { }); ``` -Or with a Controller using a 'host' reference to Controller Host(Umbraco Element/Controller): +Or with a Controller using a 'host' reference to Controller Host(Thats either a Umbraco Element or just another Controller): ```ts new UmbContextConsumerController(host, 'requestThisContextAlias', (context) => { @@ -27,6 +27,91 @@ new UmbContextConsumerController(host, 'requestThisContextAlias', (context) => { }); ``` +#### Context Token + +Using a Context Token gives you a typed context: + +```ts +import { UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; + +this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (context) => { + // Notice this is a subscription, as context might change or a new one appears, but the value is strongly typed + console.log("I've got the context of the right type", context); +}); +``` + +#### Write your own Context Token + +A Context Token is generally just a string matched with a type. In this way users of the token can be sure to get the right type of context. + +```ts +import { ContextToken } from '@umbraco-cms/backoffice/context'; + +type MyContext = { + foo: string; + bar: number; +}; + +const MY_CONTEXT_TOKEN = new ContextToken('My.Context.Token'); +``` + +#### Context Token with discriminator. + +Notice this is only relevant if you are going to make multiple context API for the same context. + +In some cases we need to have different APIs for the same context. Our Workspace Contexts is a good example of this. + +If someone wants the workspace name, they might not care about the specific API of the Workspace Context. These implementations can use a standard Context Token with a type of a generic Workspace Context. + +Our Document Workspace Context, has features around Publishing. We do not want a new Context for these features, as we want to make sure when we are in a Workspace, we do not accidentally retrieve workspace context of a parent workspace. So we need to provide a workspace context in each workspace, the one we retrieve is the one we will be using. +But since publishing is not part of the generic Workspace Context, we need to identify if the context is a Document Workspace Context and then recast it. + +To avoid each implementation taking care of this, Context Tokens can be extended with a type discriminator. +This will dicard the given api if it does not live up to the needs, and when it is the decired type, it will cast the api to the desired type. + +This example, shows how to create a discriminator Context Token, that will discard the api if it is not a Publishable Context: + +Context token example: + +```ts +import { ContextToken } from '@umbraco-cms/backoffice/context'; + +interface MyBaseContext { + foo: string; + bar: number; +}; + +interface MyPublishableContext extends MyBaseContext { + publish() +}; + +const MY_PUBLISHABLE_CONTEXT_TOKEN = new ContextToken('My.Context.Token', (context): context is MyPublishableContext => { + return 'publish' in context; +}); +``` + +Implementation of context token example: + +```ts + +const contextElement = new UmbLitElement(); +contextElement.provideContext(MY_PUBLISHABLE_CONTEXT_TOKEN, new MyPublishableContext()); + + +const consumerElement = new UmbLitElement(); +contextElement.appendChild(contextElement); +consumerElement.consumeContext(MY_PUBLISHABLE_CONTEXT_TOKEN, (context) => { + // context is of type 'MyPublishableContext' + console.log("I've got the context of the right type", context); +}); + +``` + +This enables implementors to request a publishable context, without the knowledge about how do identify such, neither they need to know about the Type. + +In details, the Context API will find the first API matching alias 'My.Context.Token', and never look furhter. If that API does live up to the type discriminator, it will be returned. If not the consumer will never reply. + + ### Provide a Context API. From a Umbraco Element or Umbraco Controller: diff --git a/src/Umbraco.Web.UI.Client/storybook/stories/umb-controller.mdx b/src/Umbraco.Web.UI.Client/storybook/stories/umb-controller.mdx index 9c919807b1..c0787a2a02 100644 --- a/src/Umbraco.Web.UI.Client/storybook/stories/umb-controller.mdx +++ b/src/Umbraco.Web.UI.Client/storybook/stories/umb-controller.mdx @@ -15,22 +15,6 @@ provideContext(alias: string | UmbContextToken, instance: R): Um consumeContext(alias: string | UmbContextToken, callback: UmbContextCallback): UmbContextConsumerController ``` -Use these for an smooth consumption, like this request for a Context API using a simple string context, where the callback value is of an unknown type: +Read about the 'observe' method in the [Store-API](?path=/docs/guides-store--docs). -```ts -this.consumeContext('requestThisContextAlias', (context) => { - // Notice this is a subscription, as context might change or a new one appears. - console.log("I've got the context", context); -}); -``` - -Or use the a Context Token to get a typed context: - -```ts -import { UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; - -this.consumeContext(UMB_NOTIFICATION_CONTEXT_TOKEN, (context) => { - // Notice this is a subscription, as context might change or a new one appears, but the value is strongly typed - console.log("I've got the context", context); -}); -``` +Read about the 'provideContext' and 'consumeContext' methods in the [Context-API](?path=/docs/guides-context-api--docs).