diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 760ddaa09c..2a30c862e8 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -83,6 +83,7 @@ "./user-permission": "./dist-cms/packages/user/user-permission/index.js", "./user": "./dist-cms/packages/user/user/index.js", "./utils": "./dist-cms/packages/core/utils/index.js", + "./validation": "./dist-cms/packages/core/validation/index.js", "./variant": "./dist-cms/packages/core/variant/index.js", "./webhook": "./dist-cms/packages/webhook/index.js", "./workspace": "./dist-cms/packages/core/workspace/index.js", diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts index 51c2146779..4a5456b67f 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.element.ts @@ -55,7 +55,7 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbBackofficeContext(this); new UmbBundleExtensionInitializer(this, umbExtensionsRegistry); new UmbEntryPointExtensionInitializer(this, umbExtensionsRegistry); - new UmbServerExtensionRegistrator(this, umbExtensionsRegistry); + new UmbServerExtensionRegistrator(this, umbExtensionsRegistry).registerAllExtensions(); // So far local packages are this simple to registerer, so no need for a manager to do that: CORE_PACKAGES.forEach(async (packageImport) => { diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts index 0f13864082..58226d9dea 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/server-extension-registrator.controller.ts @@ -1,4 +1,8 @@ -import { PackageResource, OpenAPI } from '@umbraco-cms/backoffice/external/backend-api'; +import { + PackageResource, + OpenAPI, + type PackageManifestResponseModel, +} from '@umbraco-cms/backoffice/external/backend-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbBackofficeExtensionRegistry } from '@umbraco-cms/backoffice/extension-registry'; @@ -14,56 +18,66 @@ export class UmbServerExtensionRegistrator extends UmbControllerBase { constructor(host: UmbControllerHost, extensionRegistry: UmbBackofficeExtensionRegistry) { super(host, UmbServerExtensionRegistrator.name); this.#extensionRegistry = extensionRegistry; - this.#loadServerPackages(); } - async #loadServerPackages() { - /* TODO: we need a new endpoint here, to remove the dependency on the package repository, to get the modules available for the backoffice scope - / we will need a similar endpoint for the login, installer etc at some point. - We should expose more information about the packages when not authorized so the end point should only return a list of modules from the manifest with - with the correct scope. - - This code is copy pasted from the package repository. We probably don't need this is the package repository anymore. - */ + /** + * Registers all extensions from the server. + * This is used to register all extensions that are available to the user (including private extensions). + * @remark Users must have the BACKOFFICE_ACCESS permission to access this method. + */ + public async registerAllExtensions() { const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifest()); - if (packages) { - // Append packages to the store but only if they have a name - //store.appendItems(packages.filter((p) => p.name?.length)); - const extensions: ManifestBase[] = []; - - packages.forEach((p) => { - p.extensions?.forEach((e) => { - // Crudely validate that the extension at least follows a basic manifest structure - // Idea: Use `Zod` to validate the manifest - if (isManifestBaseType(e)) { - /** - * Crude check to see if extension is of type "js" since it is safe to assume we do not - * need to load any other types of extensions in the backoffice (we need a js file to load) - */ - - // TODO: add helper to check for relative paths - // Add base url if the js path is relative - if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) { - e.js = `${this.#apiBaseUrl}${e.js}`; - } - - // Add base url if the element path is relative - if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) { - e.element = `${this.#apiBaseUrl}${e.element}`; - } - - // Add base url if the element path api relative - if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) { - e.api = `${this.#apiBaseUrl}${e.api}`; - } - - extensions.push(e); - } - }); - }); - - this.#extensionRegistry.registerMany(extensions); + await this.#loadServerPackages(packages); } } + + /** + * Registers all public extensions from the server. + * This is used to register all extensions that are available to the user (excluding private extensions) such as login extensions. + * @remark Any user can access this method without any permissions. + */ + public async registerPublicExtensions() { + const { data: packages } = await tryExecuteAndNotify(this, PackageResource.getPackageManifestPublic()); + if (packages) { + await this.#loadServerPackages(packages); + } + } + + async #loadServerPackages(packages: PackageManifestResponseModel[]) { + const extensions: ManifestBase[] = []; + + packages.forEach((p) => { + p.extensions?.forEach((e) => { + // Crudely validate that the extension at least follows a basic manifest structure + // Idea: Use `Zod` to validate the manifest + if (isManifestBaseType(e)) { + /** + * Crude check to see if extension is of type "js" since it is safe to assume we do not + * need to load any other types of extensions in the backoffice (we need a js file to load) + */ + + // TODO: add helper to check for relative paths + // Add base url if the js path is relative + if ('js' in e && typeof e.js === 'string' && !e.js.startsWith('http')) { + e.js = `${this.#apiBaseUrl}${e.js}`; + } + + // Add base url if the element path is relative + if ('element' in e && typeof e.element === 'string' && !e.element.startsWith('http')) { + e.element = `${this.#apiBaseUrl}${e.element}`; + } + + // Add base url if the element path api relative + if ('api' in e && typeof e.api === 'string' && !e.api.startsWith('http')) { + e.api = `${this.#apiBaseUrl}${e.api}`; + } + + extensions.push(e); + } + }); + }); + + this.#extensionRegistry.registerMany(extensions); + } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts index 3dfbff5f95..7354fc4d2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts @@ -1,12 +1,9 @@ import type { UmbBlockGridTypeAreaType } from '../../../types.js'; import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; -import type { - UmbInvariantableWorkspaceContextInterface, - UmbWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; +import type { UmbInvariantDatasetWorkspaceContext, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbInvariantWorkspacePropertyDatasetContext, } from '@umbraco-cms/backoffice/workspace'; import { UmbArrayState, UmbObjectState, appendToFrozenArray } from '@umbraco-cms/backoffice/observable-api'; @@ -15,8 +12,8 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import type { ManifestWorkspace, PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry'; export class UmbBlockGridAreaTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbInvariantableWorkspaceContextInterface + extends UmbSaveableWorkspaceContextBase + implements UmbInvariantDatasetWorkspaceContext { // Just for context token safety: public readonly IS_BLOCK_GRID_AREA_TYPE_WORKSPACE_CONTEXT = true; @@ -132,7 +129,7 @@ export class UmbBlockGridAreaTypeWorkspaceContext export default UmbBlockGridAreaTypeWorkspaceContext; export const UMB_BLOCK_GRID_AREA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, + UmbWorkspaceContext, UmbBlockGridAreaTypeWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context-token.ts index 6817e2d795..01b8191323 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context-token.ts @@ -1,11 +1,8 @@ import type { UmbBlockTypeWorkspaceContext } from './block-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_BLOCK_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, - UmbBlockTypeWorkspaceContext ->( +export const UMB_BLOCK_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbBlockTypeWorkspaceContext => (context as any).IS_BLOCK_TYPE_WORKSPACE_CONTEXT, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts index 74b691f62e..5c74f82147 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts @@ -3,11 +3,11 @@ import { UmbBlockTypeWorkspaceEditorElement } from './block-type-workspace-edito import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; import type { - UmbInvariantableWorkspaceContextInterface, + UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext, } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbInvariantWorkspacePropertyDatasetContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, @@ -17,8 +17,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { ManifestWorkspace, PropertyEditorSettingsProperty } from '@umbraco-cms/backoffice/extension-registry'; export class UmbBlockTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbInvariantableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext { // Just for context token safety: public readonly IS_BLOCK_TYPE_WORKSPACE_CONTEXT = true; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context-token.ts index 696d6c70a4..a2147870e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context-token.ts @@ -1,8 +1,8 @@ import type { UmbBlockWorkspaceContext } from './block-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_BLOCK_WORKSPACE_CONTEXT = new UmbContextToken( +export const UMB_BLOCK_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbBlockWorkspaceContext => (context as any).IS_BLOCK_WORKSPACE_CONTEXT, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index b9ebead2b4..c379a4ce51 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -2,7 +2,7 @@ import type { UmbBlockDataType, UmbBlockLayoutBaseModel } from '../types.js'; import { UmbBlockElementManager } from './block-element-manager.js'; import { UmbBlockWorkspaceEditorElement } from './block-workspace-editor.element.js'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager, type UmbRoutableWorkspaceContext, UmbWorkspaceIsNewRedirectController, @@ -20,7 +20,7 @@ import { decodeFilePath } from '@umbraco-cms/backoffice/utils'; export type UmbBlockWorkspaceElementManagerNames = 'content' | 'settings'; export class UmbBlockWorkspaceContext - extends UmbEditableWorkspaceContextBase + extends UmbSaveableWorkspaceContextBase implements UmbRoutableWorkspaceContext { // Just for context token safety: @@ -48,7 +48,7 @@ export class UmbBlockWorkspaceContext(undefined); readonly layout = this.#layout.asObservable(); - //readonly unique = this.#layout.asObservablePart((x) => x?.contentUdi); + readonly unique = this.#layout.asObservablePart((x) => x?.contentUdi); readonly contentUdi = this.#layout.asObservablePart((x) => x?.contentUdi); readonly content = new UmbBlockElementManager(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts index ca4f086e89..9b2c008fb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-number-range/input-number-range.element.ts @@ -1,5 +1,5 @@ -import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { FormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import { html, customElement, property, state, type PropertyValueMap } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; function getNumberOrUndefined(value: string) { @@ -8,7 +8,7 @@ function getNumberOrUndefined(value: string) { } @customElement('umb-input-number-range') -export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) { +export class UmbInputNumberRangeElement extends UmbFormControlMixin(UmbLitElement, undefined) { @property({ type: String, attribute: 'min-label' }) minLabel = 'Low value'; @@ -40,7 +40,8 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) } private updateValue() { - const newValue = this._minValue || this._maxValue ? (this._minValue || '') + ',' + (this._maxValue || '') : ''; + const newValue = + this._minValue || this._maxValue ? (this._minValue ?? '') + ',' + (this._maxValue ?? '') : undefined; if (super.value !== newValue) { super.value = newValue; } @@ -48,7 +49,7 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) @property() public set value(valueString: string) { - if (valueString !== this._value) { + if (valueString !== this.value) { const splittedValue = valueString.split(/[ ,]+/); this.minValue = getNumberOrUndefined(splittedValue[0]); this.maxValue = getNumberOrUndefined(splittedValue[1]); @@ -62,19 +63,27 @@ export class UmbInputNumberRangeElement extends FormControlMixin(UmbLitElement) return this; } + protected firstUpdated(_changedProperties: PropertyValueMap | Map): void { + super.firstUpdated(_changedProperties); + this.shadowRoot + ?.querySelectorAll('uui-input') + .forEach((x) => this.addFormControlElement(x as unknown as HTMLInputElement)); + } + private _onMinInput(e: InputEvent) { this.minValue = Number((e.target as HTMLInputElement).value); - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('change', { bubbles: true })); } private _onMaxInput(e: InputEvent) { this.maxValue = Number((e.target as HTMLInputElement).value); - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('change', { bubbles: true })); } render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts index e22f3f0aa2..76c94f8483 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/modals/property-type-settings/property-type-settings-modal.context.ts @@ -1,4 +1,4 @@ -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; @@ -10,7 +10,7 @@ export const UMB_PROPERTY_TYPE_WORKSPACE_ALIAS = 'Umb.Workspace.PropertyType'; */ export class UmbPropertyTypeWorkspaceContext extends UmbContextBase - implements UmbWorkspaceContextInterface + implements UmbWorkspaceContext { constructor(host: UmbControllerHost) { super(host, UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT); @@ -32,7 +32,7 @@ export class UmbPropertyTypeWorkspaceContext export default UmbPropertyTypeWorkspaceContext; export const UMB_PROPERTY_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, + UmbWorkspaceContext, UmbPropertyTypeWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts index 743abd5e8e..959d8a438b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context.interface.ts @@ -1,10 +1,10 @@ import type { UmbContentTypeCompositionModel, UmbContentTypeModel, UmbContentTypeSortModel } from '../types.js'; import type { UmbContentTypeStructureManager } from '../structure/index.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export interface UmbContentTypeWorkspaceContext - extends UmbSaveableWorkspaceContextInterface { + extends UmbSaveableWorkspaceContext { readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT: true; readonly name: Observable; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts index e4d674f53f..d53e829441 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/workspace.model.ts @@ -1,5 +1,5 @@ -import type { UmbRoutableWorkspaceContext } from '../../workspace/contexts/routable-workspace-context.interface.js'; -import type { UmbWorkspaceContextInterface } from '../../workspace/contexts/workspace-context.interface.js'; +import type { UmbRoutableWorkspaceContext } from '../../workspace/contexts/tokens/routable-workspace-context.interface.js'; +import type { UmbWorkspaceContext } from '../../workspace/contexts/tokens/workspace-context.interface.js'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; @@ -7,7 +7,7 @@ import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-ap export interface ManifestWorkspace< MetaType extends MetaWorkspace = MetaWorkspace, ElementType extends UmbControllerHostElement = UmbControllerHostElement, - ApiType extends UmbWorkspaceContextInterface = UmbWorkspaceContextInterface, + ApiType extends UmbWorkspaceContext = UmbWorkspaceContext, > extends ManifestElementAndApi { type: 'workspace'; meta: MetaType; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/umbraco-package.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/umbraco-package.ts index 7663b53c2c..3a05a2d7f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/umbraco-package.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/umbraco-package.ts @@ -27,6 +27,12 @@ export interface UmbracoPackage { */ allowTelemetry?: boolean; + /** + * @title Decides if the package is allowed to be accessed by the public, e.g. on the login screen + * @default false + */ + allowPublicAccess?: boolean; + /** * @title An array of Umbraco package manifest types that will be installed * @required diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/localization/localize-date.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/localization/localize-date.element.ts index fbddbaeccb..9d8238aec2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/localization/localize-date.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/localization/localize-date.element.ts @@ -13,20 +13,20 @@ export class UmbLocalizeDateElement extends UmbLitElement { * @attr * @example date="Sep 22 2023" */ - @property() - date!: string | Date; + @property({ type: String }) + date?: string | Date; /** * Formatting options * @attr * @example options={ dateStyle: 'full', timeStyle: 'long', timeZone: 'Australia/Sydney' } */ - @property() + @property({ type: Object }) options?: Intl.DateTimeFormatOptions; @state() protected get text(): string { - return this.localize.date(this.date, this.options); + return this.localize.date(this.date!, this.options); } protected render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts index 443a993919..e546f7df28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/models/index.ts @@ -1,3 +1,5 @@ +export type UmbEntityUnique = string | null; + /** Tried to find a common base of our entities — used by Entity Workspace Context */ export type UmbEntityBase = { id?: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts index b423bf9ebb..3d7838c4ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/collection-view/property-editor-ui-collection-view.element.ts @@ -3,7 +3,7 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/ex import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_DOCUMENT_COLLECTION_ALIAS } from '@umbraco-cms/backoffice/document'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; -import { UMB_WORKSPACE_COLLECTION_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { UMB_COLLECTION_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbCollectionBulkActionPermissions, @@ -31,7 +31,7 @@ export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement impl constructor() { super(); - this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (workspaceContext) => { + this.consumeContext(UMB_COLLECTION_WORKSPACE_CONTEXT, (workspaceContext) => { this._collectionAlias = workspaceContext.getCollectionAlias(); this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { @@ -39,8 +39,9 @@ export class UmbPropertyEditorUICollectionViewElement extends UmbLitElement impl if (propertyAlias) { // Gets the Data Type ID for the current property. const property = await workspaceContext.structure.getPropertyStructureByAlias(propertyAlias); - if (property && this._config) { - this._config.unique = workspaceContext.getUnique(); + const unique = workspaceContext.getUnique(); + if (unique && property && this._config) { + this._config.unique = unique; this._config.dataTypeId = property.dataType.unique; this.requestUpdate('_config'); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts index a8e13e3fbd..490b0e6a1f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-editor/uis/tree-picker/property-editor-ui-tree-picker.element.ts @@ -2,7 +2,7 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/ex import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor'; import { UmbDynamicRootRepository } from '@umbraco-cms/backoffice/dynamic-root'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { UMB_ENTITY_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import type { UmbInputTreeElement } from '@umbraco-cms/backoffice/tree'; @@ -69,7 +69,7 @@ export class UmbPropertyEditorUITreePickerElement extends UmbLitElement implemen // TODO: Awaiting the workspace context to have a parent entity ID value. [LK] // e.g. const parentEntityId = this.#workspaceContext?.getParentEntityId(); - const workspaceContext = await this.getContext(UMB_WORKSPACE_CONTEXT); + const workspaceContext = await this.getContext(UMB_ENTITY_WORKSPACE_CONTEXT); const unique = workspaceContext.getUnique(); if (unique && this.#dynamicRoot) { const result = await this.#dynamicRootRepository.postDynamicRootQuery(this.#dynamicRoot, unique); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset/property-dataset-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset/property-dataset-context.interface.ts index e6523ce269..f4f41bda12 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset/property-dataset-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-dataset/property-dataset-context.interface.ts @@ -1,6 +1,7 @@ import type { UmbVariantId } from '../../variant/variant-id.class.js'; import type { UmbContext } from '@umbraco-cms/backoffice/class-api'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; +import type { UmbEntityUnique } from '@umbraco-cms/backoffice/models'; /** * A property dataset context, represents the data of a set of properties. @@ -18,7 +19,7 @@ import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; */ export interface UmbPropertyDatasetContext extends UmbContext { getEntityType(): string; - getUnique(): string | undefined; + getUnique(): UmbEntityUnique | undefined; getVariantId: () => UmbVariantId; getName(): string | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts index 73f0a117b6..aaa710df79 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/settings/manifests.ts @@ -63,5 +63,27 @@ export const manifests = [ }, ], }, + { + type: 'menu', + alias: 'Umb.Menu.AdvancedSettings', + name: 'Advanced Settings Menu', + }, + { + type: 'sectionSidebarApp', + kind: 'menu', + alias: 'Umb.SectionSidebarMenu.AdvancedSettings', + name: 'Advanced Settings Section Sidebar Menu', + weight: 100, + meta: { + label: 'Advanced', + menu: 'Umb.Menu.AdvancedSettings', + }, + conditions: [ + { + alias: 'Umb.Condition.SectionAlias', + match: UMB_SETTINGS_SECTION_ALIAS, + }, + ], + }, ...welcomeDashboardManifests, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts new file mode 100644 index 0000000000..99311cb3cf --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/index.ts @@ -0,0 +1,2 @@ +export * from './validation.context.js'; +export * from './validation.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context-token.ts new file mode 100644 index 0000000000..3223a775dc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context-token.ts @@ -0,0 +1,4 @@ +import type { UmbValidationContext } from './validation.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_VALIDATION_CONTEXT = new UmbContextToken('UmbValidationContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts new file mode 100644 index 0000000000..e5d1eaeaca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/validation.context.ts @@ -0,0 +1,9 @@ +import { UMB_VALIDATION_CONTEXT } from './validation.context-token.js'; +import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbValidationContext extends UmbContextBase { + constructor(host: UmbControllerHost) { + super(host, UMB_VALIDATION_CONTEXT); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/index.ts new file mode 100644 index 0000000000..b3f00f0bd3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/index.ts @@ -0,0 +1,2 @@ +export * from './validation-valid.event.js'; +export * from './validation-invalid.event.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-invalid.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-invalid.event.ts new file mode 100644 index 0000000000..2fe6ad0c81 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-invalid.event.ts @@ -0,0 +1,9 @@ +import { UmbValidationEvent } from './validation.event.js'; + +export class UmbValidationInvalidEvent extends UmbValidationEvent { + static readonly TYPE = 'invalid'; + + public constructor() { + super(UmbValidationInvalidEvent.TYPE); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-valid.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-valid.event.ts new file mode 100644 index 0000000000..5d4d29a031 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation-valid.event.ts @@ -0,0 +1,9 @@ +import { UmbValidationEvent } from './validation.event.js'; + +export class UmbValidationValidEvent extends UmbValidationEvent { + static readonly TYPE = 'valid'; + + constructor() { + super(UmbValidationValidEvent.TYPE); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation.event.ts new file mode 100644 index 0000000000..62d03f34da --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/events/validation.event.ts @@ -0,0 +1,5 @@ +export class UmbValidationEvent extends Event { + public constructor(type: string) { + super(type, { bubbles: true, composed: true, cancelable: false }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts new file mode 100644 index 0000000000..807bd98e24 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts @@ -0,0 +1,3 @@ +export * from './events/index.js'; +export * from './mixins/index.js'; +export * from './context/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts new file mode 100644 index 0000000000..dd9368a7f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -0,0 +1,306 @@ +import { UmbValidationInvalidEvent } from '../events/validation-invalid.event.js'; +import { UmbValidationValidEvent } from '../events/validation-valid.event.js'; +import { UmbValidityState } from './validity-state.class.js'; +import { property, type LitElement } from '@umbraco-cms/backoffice/external/lit'; +import type { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api'; + +type NativeFormControlElement = HTMLInputElement | HTMLTextAreaElement; // Eventually use a specific interface or list multiple options like appending these types: ... | HTMLTextAreaElement | HTMLSelectElement + +/* FlagTypes type options originate from: + * https://developer.mozilla.org/en-US/docs/Web/API/ValidityState + * */ +type FlagTypes = + | 'badInput' + | 'customError' + | 'patternMismatch' + | 'rangeOverflow' + | 'rangeUnderflow' + | 'stepMismatch' + | 'tooLong' + | 'tooShort' + | 'typeMismatch' + | 'valueMissing' + | 'badInput' + | 'valid'; + +// Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public class in a separate file. +interface Validator { + flagKey: FlagTypes; + getMessageMethod: () => string; + checkMethod: () => boolean; +} + +export interface UmbFormControlMixinInterface extends HTMLElement { + formAssociated: boolean; + get value(): ValueType | DefaultValueType; + set value(newValue: ValueType | DefaultValueType); + formResetCallback(): void; + checkValidity(): boolean; + get validationMessage(): string; + get validity(): ValidityState; + setCustomValidity(error: string): void; + submit(): void; + pristine: boolean; + required: boolean; + requiredMessage: string; + error: boolean; + errorMessage: string; +} + +export declare abstract class UmbFormControlMixinElement + extends LitElement + implements UmbFormControlMixinInterface +{ + protected _internals: ElementInternals; + protected _runValidators(): void; + protected abstract getFormElement(): HTMLElement | undefined; + protected addValidator: (flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean) => void; + protected addFormControlElement(element: NativeFormControlElement): void; + + formAssociated: boolean; + get value(): ValueType | DefaultValueType; + set value(newValue: ValueType | DefaultValueType); + formResetCallback(): void; + checkValidity(): boolean; + get validationMessage(): string; + get validity(): ValidityState; + setCustomValidity(error: string): void; + submit(): void; + pristine: boolean; + required: boolean; + requiredMessage: string; + error: boolean; + errorMessage: string; +} + +/** + * The mixin allows a custom element to participate in HTML forms. + * + * @param {Object} superClass - superclass to be extended. + * @mixin + */ +export const UmbFormControlMixin = < + ValueType = FormDataEntryValue | FormData, + T extends HTMLElementConstructor = HTMLElementConstructor, + DefaultValueType = unknown, +>( + superClass: T, + defaultValue: DefaultValueType, +) => { + abstract class UmbFormControlMixinClass extends superClass { + /** + * This is a static class field indicating that the element is can be used inside a native form and participate in its events. + * It may require a polyfill, check support here https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals. + * Read more about form controls here https://web.dev/more-capable-form-controls/ + * @type {boolean} + */ + static readonly formAssociated = true; + + /** + * Value of this form control. + * If you dont want the setFormValue to be called on the ElementInternals, then prevent calling this method, by not calling super.value = newValue in your implementation of the value setter method. + * @type {string} + * @attr value + * @default '' + */ + @property({ reflect: false }) // Do not 'reflect' as the attribute is used as fallback. + get value(): ValueType | DefaultValueType { + return this.#value; + } + set value(newValue: ValueType | DefaultValueType) { + this.#value = newValue; + this._runValidators(); + } + + // Validation + private _validityState = new UmbValidityState(); + + /** + * Determines wether the form control has been touched or interacted with, this determines wether the validation-status of this form control should be made visible. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + pristine: boolean = true; + + #value: ValueType | DefaultValueType = defaultValue; + protected _internals: ElementInternals; + #form: HTMLFormElement | null = null; + #validators: Validator[] = []; + #formCtrlElements: NativeFormControlElement[] = []; + + constructor(...args: any[]) { + super(...args); + this._internals = this.attachInternals(); + + this.addEventListener('blur', () => { + this.pristine = false; + this.checkValidity(); + }); + } + + /** + * Get internal form element. + * This has to be implemented to provide a FormControl Element of choice for the given context. The element is used as anchor for validation-messages. + * @abstract + * @method getFormElement + * @returns {HTMLElement | undefined} + */ + protected abstract getFormElement(): HTMLElement | undefined; + + disconnectedCallback(): void { + super.disconnectedCallback(); + this.#removeFormListeners(); + } + #removeFormListeners() { + if (this.#form) { + this.#form.removeEventListener('submit', this.#onFormSubmit); + } + } + + /** + * Add validator, to validate this Form Control. + * See https://developer.mozilla.org/en-US/docs/Web/API/ValidityState for available Validator FlagTypes. + * + * @example + * this.addValidator( + * 'tooLong', + * () => 'This input contains too many characters', + * () => this._value.length > 10 + * ); + * @method addValidator + * @param {FlagTypes} flagKey the type of validation. + * @param {method} getMessageMethod method to retrieve relevant message. Is executed every time the validator is re-executed. + * @param {method} checkMethod method to determine if this validator should invalidate this form control. Return true if this should prevent submission. + */ + protected addValidator(flagKey: FlagTypes, getMessageMethod: () => string, checkMethod: () => boolean): Validator { + const obj = { + flagKey: flagKey, + getMessageMethod: getMessageMethod, + checkMethod: checkMethod, + }; + this.#validators.push(obj); + return obj; + } + + protected removeValidator(validator: Validator) { + const index = this.#validators.indexOf(validator); + if (index !== -1) { + this.#validators.splice(index, 1); + } + } + + /** + * @method addFormControlElement + * @description Important notice if adding a native form control then ensure that its value and thereby validity is updated when value is changed from the outside. + * @param element {NativeFormControlElement} - element to validate and include as part of this form association. + */ + protected addFormControlElement(element: NativeFormControlElement) { + this.#formCtrlElements.push(element); + element.addEventListener(UmbValidationInvalidEvent.TYPE, () => { + this._runValidators(); + }); + element.addEventListener(UmbValidationValidEvent.TYPE, () => { + this._runValidators(); + }); + } + + /** + * @method _runValidators + * @description Run all validators and set the validityState of this form control. + * Run this method when you want to re-run all validators. + * This can be relevant if you have a validators that is using values that is not triggering the Lit Updated Callback. + * Such are mainly properties that are not declared as a Lit state and or Lit property. + */ + protected _runValidators() { + this._validityState = new UmbValidityState(); + + // Loop through inner native form controls to adapt their validityState. + this.#formCtrlElements.forEach((formCtrlEl) => { + let key: keyof ValidityState; + for (key in formCtrlEl.validity) { + if (key !== 'valid' && formCtrlEl.validity[key]) { + this._validityState[key] = true; + this._internals.setValidity(this._validityState, formCtrlEl.validationMessage, formCtrlEl); + } + } + }); + + // Loop through custom validators, currently its intentional to have them overwritten native validity. but might need to be reconsidered (This current way enables to overwrite with custom messages) [NL] + this.#validators.forEach((validator) => { + if (validator.checkMethod()) { + this._validityState[validator.flagKey] = true; + this._internals.setValidity(this._validityState, validator.getMessageMethod(), this.getFormElement()); + } + }); + + const hasError = Object.values(this._validityState).includes(true); + + // https://developer.mozilla.org/en-US/docs/Web/API/ValidityState#valid + this._validityState.valid = !hasError; + + if (hasError) { + this.dispatchEvent(new UmbValidationInvalidEvent()); + } else { + this._internals.setValidity({}); + this.dispatchEvent(new UmbValidationValidEvent()); + } + } + + updated(changedProperties: Map) { + super.updated(changedProperties); + this._runValidators(); + } + + #onFormSubmit = () => { + this.pristine = false; + }; + + public formAssociatedCallback() { + this.#removeFormListeners(); + this.#form = this._internals.form; + if (this.#form) { + // This relies on the form begin a 'uui-form': [NL] + if (this.#form.hasAttribute('submit-invalid')) { + this.pristine = false; + } + this.#form.addEventListener('submit', this.#onFormSubmit); + } + } + public formResetCallback() { + this.pristine = true; + this.value = this.getInitialValue() ?? this.getDefaultValue(); + } + + protected getDefaultValue(): DefaultValueType { + return defaultValue; + } + protected getInitialValue(): ValueType | DefaultValueType { + return this.getAttribute('value') as ValueType | DefaultValueType; + } + + public checkValidity() { + for (const key in this.#formCtrlElements) { + if (this.#formCtrlElements[key].checkValidity() === false) { + return false; + } + } + + return this._internals?.checkValidity(); + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/validity + public get validity(): ValidityState { + return this._validityState; + } + + get validationMessage() { + return this._internals?.validationMessage; + } + } + return UmbFormControlMixinClass as unknown as HTMLElementConstructor< + UmbFormControlMixinElement + > & + T; +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/index.ts new file mode 100644 index 0000000000..0635d528ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/index.ts @@ -0,0 +1 @@ +export * from './form-control.mixin.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/validity-state.class.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/validity-state.class.ts new file mode 100644 index 0000000000..d62b087a0a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/validity-state.class.ts @@ -0,0 +1,15 @@ +type Writeable = { -readonly [P in keyof T]: T[P] }; + +export class UmbValidityState implements Writeable { + badInput: boolean = false; + customError: boolean = false; + patternMismatch: boolean = false; + rangeOverflow: boolean = false; + rangeUnderflow: boolean = false; + stepMismatch: boolean = false; + tooLong: boolean = false; + tooShort: boolean = false; + typeMismatch: boolean = false; + valid: boolean = false; + valueMissing: boolean = false; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts index ce0148af9d..fe361e65e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/variant-selector/variant-selector.element.ts @@ -99,7 +99,7 @@ export class UmbVariantSelectorElement extends UmbLitElement { async #observeActiveVariants() { if (!this.#splitViewContext) return; - const workspaceContext = this.#splitViewContext.getWorkspaceContext(); + const workspaceContext = this.#splitViewContext.getWorkspaceContext() as UmbDocumentWorkspaceContext; if (workspaceContext) { this.observe( workspaceContext.splitView.activeVariantsInfo, @@ -223,9 +223,9 @@ export class UmbVariantSelectorElement extends UmbLitElement { - ` + ` : ''} - ` + ` : nothing } @@ -264,7 +264,7 @@ export class UmbVariantSelectorElement extends UmbLitElement { @click=${() => this.#openSplitView(variant)}> Split view - `} + `} `, )} @@ -272,7 +272,7 @@ export class UmbVariantSelectorElement extends UmbLitElement { - ` + ` : nothing } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/save/save.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/save/save.action.ts index d106cae3e5..c7f4c1f985 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/save/save.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action/common/save/save.action.ts @@ -1,10 +1,10 @@ -import type { UmbSaveableWorkspaceContextInterface } from '../../../../contexts/saveable-workspace-context.interface.js'; +import type { UmbSaveableWorkspaceContext } from '../../../../contexts/tokens/saveable-workspace-context.interface.js'; import { UmbWorkspaceActionBase } from '../../workspace-action-base.controller.js'; import { UMB_SAVEABLE_WORKSPACE_CONTEXT, type UmbWorkspaceActionArgs } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export class UmbSaveWorkspaceAction extends UmbWorkspaceActionBase { - constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs) { +export class UmbSaveWorkspaceAction extends UmbWorkspaceActionBase { + constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs) { super(host, args); // TODO: Could we make change label depending on the state? diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-entity-action-menu/workspace-entity-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-entity-action-menu/workspace-entity-action-menu.element.ts index 3cab2661db..c0d6c00d1c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-entity-action-menu/workspace-entity-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-entity-action-menu/workspace-entity-action-menu.element.ts @@ -2,14 +2,14 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state, nothing, query } from '@umbraco-cms/backoffice/external/lit'; import type { UmbActionExecutedEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; +import { UMB_ENTITY_WORKSPACE_CONTEXT, type UmbWorkspaceUniqueType } from '@umbraco-cms/backoffice/workspace'; import type { UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-workspace-entity-action-menu') export class UmbWorkspaceEntityActionMenuElement extends UmbLitElement { - private _workspaceContext?: typeof UMB_WORKSPACE_CONTEXT.TYPE; + private _workspaceContext?: typeof UMB_ENTITY_WORKSPACE_CONTEXT.TYPE; @state() - private _unique?: string; + private _unique?: UmbWorkspaceUniqueType; @state() private _entityType?: string; @@ -23,7 +23,7 @@ export class UmbWorkspaceEntityActionMenuElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (context) => { + this.consumeContext(UMB_ENTITY_WORKSPACE_CONTEXT, (context) => { this._workspaceContext = context; this._observeInfo(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts index f91a2858f4..a97ab40327 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-footer/workspace-footer.element.ts @@ -1,4 +1,4 @@ -import { UMB_SAVEABLE_WORKSPACE_CONTEXT } from '../../contexts/saveable-workspace.context-token.js'; +import { UMB_SAVEABLE_WORKSPACE_CONTEXT } from '../../contexts/tokens/saveable-workspace.context-token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-alias.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-alias.condition.ts index 46b33dc39d..db6308554b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-alias.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-alias.condition.ts @@ -1,4 +1,4 @@ -import { UMB_WORKSPACE_CONTEXT, type UmbWorkspaceContextInterface } from '../contexts/index.js'; +import { UMB_WORKSPACE_CONTEXT, type UmbWorkspaceContext } from '../contexts/index.js'; import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; import type { ManifestCondition, @@ -15,12 +15,11 @@ export class UmbWorkspaceAliasCondition constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { super(host, args); - let permissionCheck: ((context: UmbWorkspaceContextInterface) => boolean) | undefined = undefined; + let permissionCheck: ((context: UmbWorkspaceContext) => boolean) | undefined = undefined; if (this.config.match) { - permissionCheck = (context: UmbWorkspaceContextInterface) => context.workspaceAlias === this.config.match; + permissionCheck = (context: UmbWorkspaceContext) => context.workspaceAlias === this.config.match; } else if (this.config.oneOf) { - permissionCheck = (context: UmbWorkspaceContextInterface) => - this.config.oneOf!.indexOf(context.workspaceAlias) !== -1; + permissionCheck = (context: UmbWorkspaceContext) => this.config.oneOf!.indexOf(context.workspaceAlias) !== -1; } if (permissionCheck !== undefined) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-has-collection.condition.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-has-collection.condition.ts index d30c8ea65e..ef1ef8c203 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-has-collection.condition.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/conditions/workspace-has-collection.condition.ts @@ -1,4 +1,4 @@ -import { UMB_WORKSPACE_COLLECTION_CONTEXT } from '../contexts/workspace-collection-context.token.js'; +import { UMB_COLLECTION_WORKSPACE_CONTEXT } from '../contexts/tokens/collection-workspace.context-token.js'; import { UmbConditionBase } from '@umbraco-cms/backoffice/extension-registry'; import type { ManifestCondition, @@ -15,7 +15,7 @@ export class UmbWorkspaceHasCollectionCondition constructor(host: UmbControllerHost, args: UmbConditionControllerArguments) { super(host, args); - this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (context) => { + this.consumeContext(UMB_COLLECTION_WORKSPACE_CONTEXT, (context) => { this.observe( context.contentTypeHasCollection, (hasCollection) => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/default-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/default-workspace.context.ts index d6b3439584..04c90380b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/default-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/default-workspace.context.ts @@ -1,12 +1,12 @@ -import { UMB_WORKSPACE_CONTEXT } from './workspace-context.token.js'; -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; +import { UMB_WORKSPACE_CONTEXT } from './tokens/workspace.context-token.js'; +import type { UmbWorkspaceContext } from './tokens/workspace-context.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import type { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry'; export abstract class UmbDefaultWorkspaceContext extends UmbContextBase - implements UmbWorkspaceContextInterface + implements UmbWorkspaceContext { public workspaceAlias!: string; #entityType!: string; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/index.ts index 6c3697d84e..ebf0204607 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/index.ts @@ -1,15 +1,3 @@ export * from './default-workspace.context.js'; -export * from './editable-workspace-context-base.js'; -export * from './property-structure-workspace-context.interface.js'; -export * from './publishable-workspace-context.interface.js'; -export * from './publishable-workspace.context-token.js'; -export * from './routable-workspace-context.interface.js'; -export * from './saveable-workspace-context.interface.js'; -export * from './saveable-workspace.context-token.js'; -export * from './variant-workspace-context.token.js'; -export * from './workspace-collection-context.interface.js'; -export * from './workspace-collection-context.token.js'; -export * from './workspace-context.interface.js'; -export * from './workspace-context.token.js'; -export * from './workspace-invariantable-context.interface.js'; -export * from './workspace-variantable-context.interface.js'; +export * from './saveable-workspace-context-base.js'; +export * from './tokens/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace-context.interface.ts deleted file mode 100644 index d3dc964eec..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace-context.interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js'; - -export interface UmbPublishableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface { - saveAndPublish(): Promise; - publish(): Promise; - unpublish(): Promise; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace.context-token.ts deleted file mode 100644 index be1cd00336..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/publishable-workspace.context-token.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { - UmbPublishableWorkspaceContextInterface, - UmbSaveableWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; - -export const UMB_PUBLISHABLE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, - UmbPublishableWorkspaceContextInterface ->( - 'UmbWorkspaceContext', - undefined, - (context): context is UmbPublishableWorkspaceContextInterface => (context as any).publish !== undefined, -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/routable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/routable-workspace-context.interface.ts deleted file mode 100644 index c6a0a16e36..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/routable-workspace-context.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { UmbWorkspaceRouteManager } from '../index.js'; -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; - -export interface UmbRoutableWorkspaceContext extends UmbWorkspaceContextInterface { - readonly routes: UmbWorkspaceRouteManager; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/editable-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context-base.ts similarity index 69% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/editable-workspace-context-base.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context-base.ts index 3aab9c5b0d..c734354b8c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/editable-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context-base.ts @@ -1,23 +1,29 @@ -import { UMB_WORKSPACE_CONTEXT } from './workspace-context.token.js'; -import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js'; +import { UmbWorkspaceRouteManager } from '../controllers/workspace-route-manager.controller.js'; +import { UMB_WORKSPACE_CONTEXT } from './tokens/workspace.context-token.js'; +import type { UmbSaveableWorkspaceContext } from './tokens/saveable-workspace-context.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -export abstract class UmbEditableWorkspaceContextBase - extends UmbContextBase> - implements UmbSaveableWorkspaceContextInterface +export abstract class UmbSaveableWorkspaceContextBase + extends UmbContextBase> + implements UmbSaveableWorkspaceContext { public readonly workspaceAlias: string; // TODO: We could make a base type for workspace modal data, and use this here: As well as a base for the result, to make sure we always include the unique (instead of the object type) public readonly modalContext?: UmbModalContext<{ preset: object }>; + abstract readonly unique: Observable; + #isNew = new UmbBooleanState(undefined); isNew = this.#isNew.asObservable(); + readonly routes = new UmbWorkspaceRouteManager(this); + /* Concept notes: [NL] Considerations are, if we bring a dirty state (observable) we need to maintain it all the time. @@ -61,3 +67,11 @@ export abstract class UmbEditableWorkspaceContextBase abstract getData(): WorkspaceDataModelType | undefined; abstract save(): Promise; } + +/* + * @deprecated Use UmbSaveableWorkspaceContextBase instead — Will be removed before RC. + * TODO: Delete before RC. + */ +export abstract class UmbEditableWorkspaceContextBase< + WorkspaceDataModelType, +> extends UmbSaveableWorkspaceContextBase {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context.interface.ts deleted file mode 100644 index 1c3d095a31..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace-context.interface.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; -import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; - -export interface UmbSaveableWorkspaceContextInterface extends UmbWorkspaceContextInterface { - isNew: Observable; - getIsNew(): boolean | undefined; - save(): Promise; - setValidationErrors?(errorMap: any): void; // TODO: temp solution to bubble validation errors to the UI - destroy(): void; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace.context-token.ts deleted file mode 100644 index 3daf803e7b..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/saveable-workspace.context-token.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbWorkspaceContextInterface, UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; - -export const UMB_SAVEABLE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, - UmbSaveableWorkspaceContextInterface ->( - 'UmbWorkspaceContext', - undefined, - (context): context is UmbSaveableWorkspaceContextInterface => - (context as UmbSaveableWorkspaceContextInterface).getIsNew !== undefined, -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace-context.interface.ts similarity index 51% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace-context.interface.ts index e60933172a..d4d4e54a63 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace-context.interface.ts @@ -1,10 +1,16 @@ -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; +import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { UmbContentTypeModel, UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; -export interface UmbWorkspaceCollectionContextInterface - extends UmbWorkspaceContextInterface { +export interface UmbCollectionWorkspaceContext extends UmbEntityWorkspaceContext { contentTypeHasCollection: Observable; getCollectionAlias(): string; structure: UmbContentTypeStructureManager; } + +/** + * @deprecated Use UmbCollectionWorkspaceContextInterface instead — Will be removed before RC. + * TODO: Delete before RC. + */ +export interface UmbWorkspaceCollectionContextInterface + extends UmbCollectionWorkspaceContext {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace.context-token.ts new file mode 100644 index 0000000000..c39ef48779 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/collection-workspace.context-token.ts @@ -0,0 +1,19 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; +import type { UmbWorkspaceContext, UmbCollectionWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; + +export const UMB_COLLECTION_WORKSPACE_CONTEXT = new UmbContextToken< + UmbWorkspaceContext, + UmbCollectionWorkspaceContext +>( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbCollectionWorkspaceContext => + (context as UmbCollectionWorkspaceContext).contentTypeHasCollection !== undefined, +); + +/** + * @deprecated Use `UMB_COLLECTION_WORKSPACE_CONTEXT` instead. This token will be removed in the RC version. + * TODO: Remove in RC + */ +export const UMB_WORKSPACE_COLLECTION_CONTEXT = UMB_COLLECTION_WORKSPACE_CONTEXT; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace-context.interface.ts new file mode 100644 index 0000000000..16f335e73e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace-context.interface.ts @@ -0,0 +1,8 @@ +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; +import type { UmbWorkspaceUniqueType } from './../../types.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; + +export interface UmbEntityWorkspaceContext extends UmbWorkspaceContext { + unique: Observable; + getUnique(): UmbWorkspaceUniqueType | undefined; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace.context-token.ts new file mode 100644 index 0000000000..6305c04de2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/entity-workspace.context-token.ts @@ -0,0 +1,9 @@ +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; +import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_ENTITY_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbEntityWorkspaceContext => (context as any).getUnique !== undefined, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/index.ts new file mode 100644 index 0000000000..d68f5d9945 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/index.ts @@ -0,0 +1,16 @@ +export * from './collection-workspace.context-token.js'; +export * from './entity-workspace.context-token.js'; +export * from './publishable-workspace.context-token.js'; +export * from './routable-workspace.context-token.js'; +export * from './saveable-workspace.context-token.js'; +export * from './variant-workspace.context-token.js'; +export * from './workspace.context-token.js'; +export type * from './collection-workspace-context.interface.js'; +export type * from './entity-workspace-context.interface.js'; +export type * from './invariant-dataset-workspace-context.interface.js'; +export type * from './property-structure-workspace-context.interface.js'; +export type * from './publishable-workspace-context.interface.js'; +export type * from './routable-workspace-context.interface.js'; +export type * from './saveable-workspace-context.interface.js'; +export type * from './variant-dataset-workspace-context.interface.js'; +export type * from './workspace-context.interface.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-invariantable-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/invariant-dataset-workspace-context.interface.ts similarity index 55% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-invariantable-context.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/invariant-dataset-workspace-context.interface.ts index b9585fd07c..1fdf54d75c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-invariantable-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/invariant-dataset-workspace-context.interface.ts @@ -1,10 +1,10 @@ -import type { UmbVariantId } from '../../variant/variant-id.class.js'; -import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js'; -import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js'; +import type { UmbVariantId } from '../../../variant/variant-id.class.js'; +import type { UmbPropertyDatasetContext } from '../../../property/property-dataset/property-dataset-context.interface.js'; +import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; -export interface UmbInvariantableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface { +export interface UmbInvariantDatasetWorkspaceContext extends UmbSaveableWorkspaceContext { // Name: name: Observable; getName(): string | undefined; @@ -17,3 +17,9 @@ export interface UmbInvariantableWorkspaceContextInterface extends UmbSaveableWo createPropertyDatasetContext(host: UmbControllerHost, variantId?: UmbVariantId): UmbPropertyDatasetContext; } + +/** + * @deprecated Use UmbInvariantWorkspaceContextInterface instead — Will be removed before RC. + * TODO: Delete before RC. + */ +export interface UmbInvariantableWorkspaceContextInterface extends UmbInvariantDatasetWorkspaceContext {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/property-structure-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace-context.interface.ts similarity index 57% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/property-structure-workspace-context.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace-context.interface.ts index 7e8722c7cb..da9d3467e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/property-structure-workspace-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace-context.interface.ts @@ -1,7 +1,7 @@ -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; +import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { ValueModelBaseModel } from '@umbraco-cms/backoffice/external/backend-api'; -export interface UmbPropertyStructureWorkspaceContextInterface extends UmbWorkspaceContextInterface { +export interface UmbPropertyStructureWorkspaceContext extends UmbEntityWorkspaceContext { propertyStructureById(id: string): Promise>; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace.context-token.ts new file mode 100644 index 0000000000..6b4f95d8c3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/property-structure-workspace.context-token.ts @@ -0,0 +1,12 @@ +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; +import type { UmbPropertyStructureWorkspaceContext } from './property-structure-workspace-context.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_PROPERTY_STRUCTURE_WORKSPACE_CONTEXT = new UmbContextToken< + UmbWorkspaceContext, + UmbPropertyStructureWorkspaceContext +>( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbPropertyStructureWorkspaceContext => 'propertyStructureById' in context, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace-context.interface.ts new file mode 100644 index 0000000000..c5cf6ca39c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace-context.interface.ts @@ -0,0 +1,7 @@ +import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js'; + +export interface UmbPublishableWorkspaceContext extends UmbSaveableWorkspaceContext { + saveAndPublish(): Promise; + publish(): Promise; + unpublish(): Promise; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace.context-token.ts new file mode 100644 index 0000000000..616b772a8f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/publishable-workspace.context-token.ts @@ -0,0 +1,7 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbPublishableWorkspaceContext, UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; + +export const UMB_PUBLISHABLE_WORKSPACE_CONTEXT = new UmbContextToken< + UmbWorkspaceContext, + UmbPublishableWorkspaceContext +>('UmbWorkspaceContext', undefined, (context): context is UmbPublishableWorkspaceContext => 'publish' in context); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace-context.interface.ts new file mode 100644 index 0000000000..c268b6c4d7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace-context.interface.ts @@ -0,0 +1,6 @@ +import type { UmbWorkspaceRouteManager } from '../../index.js'; +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; + +export interface UmbRoutableWorkspaceContext extends UmbWorkspaceContext { + readonly routes: UmbWorkspaceRouteManager; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace.context-token.ts new file mode 100644 index 0000000000..36deeebcfe --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/routable-workspace.context-token.ts @@ -0,0 +1,9 @@ +import type { UmbRoutableWorkspaceContext } from './routable-workspace-context.interface.js'; +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_ROUTABLE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbRoutableWorkspaceContext => 'routes' in context, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace-context.interface.ts new file mode 100644 index 0000000000..b9a50d8673 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace-context.interface.ts @@ -0,0 +1,15 @@ +import type { UmbEntityWorkspaceContext } from './entity-workspace-context.interface.js'; +import type { UmbRoutableWorkspaceContext } from './routable-workspace-context.interface.js'; +import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; + +export interface UmbSaveableWorkspaceContext extends UmbEntityWorkspaceContext, UmbRoutableWorkspaceContext { + isNew: Observable; + getIsNew(): boolean | undefined; + save(): Promise; + destroy(): void; +} +/** + * @deprecated Use `UmbSaveableWorkspaceContext` instead. This token will be removed in the RC version. + * TODO: Remove in RC + */ +export interface UmbSaveableWorkspaceContextInterface extends UmbSaveableWorkspaceContext {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace.context-token.ts new file mode 100644 index 0000000000..7e537194ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/saveable-workspace.context-token.ts @@ -0,0 +1,8 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbWorkspaceContext, UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; + +export const UMB_SAVEABLE_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbSaveableWorkspaceContext => 'getIsNew' in context, +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-variantable-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-dataset-workspace-context.interface.ts similarity index 61% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-variantable-context.interface.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-dataset-workspace-context.interface.ts index 4f488e9d22..717c8e5eac 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-variantable-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-dataset-workspace-context.interface.ts @@ -1,12 +1,12 @@ -import type { UmbWorkspaceSplitViewManager } from '../controllers/workspace-split-view-manager.controller.js'; -import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js'; -import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js'; +import type { UmbWorkspaceSplitViewManager } from '../../controllers/workspace-split-view-manager.controller.js'; +import type { UmbPropertyDatasetContext } from '../../../property/property-dataset/property-dataset-context.interface.js'; +import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { UmbVariantId, UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -export interface UmbVariantableWorkspaceContextInterface - extends UmbSaveableWorkspaceContextInterface { +export interface UmbVariantDatasetWorkspaceContext + extends UmbSaveableWorkspaceContext { // Name: getName(variantId?: UmbVariantId): string | undefined; setName(name: string, variantId?: UmbVariantId): void; @@ -18,14 +18,19 @@ export interface UmbVariantableWorkspaceContextInterface( alias: string, variantId?: UmbVariantId, ): Promise | undefined>; getPropertyValue(alias: string, variantId?: UmbVariantId): ReturnValue | undefined; setPropertyValue(alias: string, value: unknown, variantId?: UmbVariantId): Promise; - //propertyDataByAlias(alias: string, variantId?: UmbVariantId): Observable; createPropertyDatasetContext(host: UmbControllerHost, variantId?: UmbVariantId): UmbPropertyDatasetContext; } + +/** + * @deprecated Use UmbVariantWorkspaceContextInterface instead — Will be removed before RC. + * TODO: Delete before RC. + */ +export interface UmbVariantableWorkspaceContextInterface extends UmbVariantDatasetWorkspaceContext {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-workspace.context-token.ts new file mode 100644 index 0000000000..0141a6c978 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/variant-workspace.context-token.ts @@ -0,0 +1,8 @@ +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; +import type { UmbVariantDatasetWorkspaceContext } from './variant-dataset-workspace-context.interface.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +export const UMB_VARIANT_WORKSPACE_CONTEXT = new UmbContextToken< + UmbWorkspaceContext, + UmbVariantDatasetWorkspaceContext +>('UmbWorkspaceContext', undefined, (context): context is UmbVariantDatasetWorkspaceContext => 'variants' in context); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace-context.interface.ts new file mode 100644 index 0000000000..de4c7e1535 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace-context.interface.ts @@ -0,0 +1,13 @@ +import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; + +export interface UmbWorkspaceContext extends UmbApi { + readonly workspaceAlias: string; + getEntityType(): string; +} + +/** + * @deprecated Use UmbWorkspaceContext instead — Will be removed before RC. + * Notice if you have complains on missing `getUnique()` then you need to use UmbEntityWorkspaceContext instead. + * TODO: Delete before RC. + */ +export interface UmbWorkspaceContextInterface extends UmbWorkspaceContext {} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace.context-token.ts similarity index 50% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.token.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace.context-token.ts index e1a8855cee..2b393c54b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/tokens/workspace.context-token.ts @@ -1,4 +1,4 @@ -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; +import type { UmbWorkspaceContext } from './workspace-context.interface.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -export const UMB_WORKSPACE_CONTEXT = new UmbContextToken('UmbWorkspaceContext'); +export const UMB_WORKSPACE_CONTEXT = new UmbContextToken('UmbWorkspaceContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/variant-workspace-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/variant-workspace-context.token.ts deleted file mode 100644 index 562eabe48f..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/variant-workspace-context.token.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js'; -import type { UmbVariantableWorkspaceContextInterface } from './workspace-variantable-context.interface.js'; -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; - -export const UMB_VARIANT_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, - UmbVariantableWorkspaceContextInterface ->( - 'UmbWorkspaceContext', - undefined, - (context): context is UmbVariantableWorkspaceContextInterface => 'variants' in context, -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.token.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.token.ts deleted file mode 100644 index 4cc4ba1460..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-collection-context.token.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbContentTypeModel } from '@umbraco-cms/backoffice/content-type'; -import type { - UmbWorkspaceContextInterface, - UmbWorkspaceCollectionContextInterface, -} from '@umbraco-cms/backoffice/workspace'; - -export const UMB_WORKSPACE_COLLECTION_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, - UmbWorkspaceCollectionContextInterface ->( - 'UmbWorkspaceContext', - undefined, - (context): context is UmbWorkspaceCollectionContextInterface => - (context as UmbWorkspaceCollectionContextInterface).contentTypeHasCollection !== undefined, -); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.interface.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.interface.ts deleted file mode 100644 index 272e945062..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/contexts/workspace-context.interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; - -export interface UmbWorkspaceContextInterface extends UmbApi { - readonly workspaceAlias: string; - getEntityType(): string; - getUnique(): string | undefined; -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/controllers/workspace-is-new-redirect.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/controllers/workspace-is-new-redirect.controller.ts index 1157df8c8e..5bb0d8b2f3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/controllers/workspace-is-new-redirect.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/controllers/workspace-is-new-redirect.controller.ts @@ -1,4 +1,4 @@ -import type { UmbEditableWorkspaceContextBase } from '../contexts/index.js'; +import type { UmbSaveableWorkspaceContextBase } from '../contexts/index.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { createRoutePathBuilder, type UmbRouterSlotElement } from '@umbraco-cms/backoffice/router'; @@ -16,7 +16,7 @@ import { ensurePathEndsWithSlash } from '@umbraco-cms/backoffice/utils'; export class UmbWorkspaceIsNewRedirectController extends UmbControllerBase { constructor( host: UmbControllerHost, - workspaceContext: UmbEditableWorkspaceContextBase, + workspaceContext: UmbSaveableWorkspaceContextBase, router: UmbRouterSlotElement, ) { super(host, 'isNewRedirectController'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/index.ts index e31957d8fb..1babbbae1f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/index.ts @@ -1,6 +1,7 @@ export * from './components/index.js'; export * from './contexts/index.js'; export * from './controllers/index.js'; -export type * from './conditions/index.js'; export * from './modals/index.js'; export * from './workspace-property-dataset/index.js'; +export type * from './conditions/index.js'; +export type * from './types.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.element.ts new file mode 100644 index 0000000000..85b8dbf8ab --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.element.ts @@ -0,0 +1,34 @@ +import type { UmbSaveableWorkspaceContext } from '../../contexts/tokens/saveable-workspace-context.interface.js'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; +import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { UmbRoute } from '@umbraco-cms/backoffice/router'; +import { UmbExtensionsApiInitializer } from '@umbraco-cms/backoffice/extension-api'; + +@customElement('umb-editable-workspace') +export class UmbEditableWorkspaceElement extends UmbLitElement { + @state() + _routes: UmbRoute[] = []; + + public set api(api: UmbSaveableWorkspaceContext) { + this.observe(api.routes.routes, (routes) => (this._routes = routes)); + + new UmbExtensionsApiInitializer(this, umbExtensionsRegistry, 'workspaceContext', [api]); + } + + render() { + return html` +
+ +
+
`; + } +} + +export default UmbEditableWorkspaceElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-editable-workspace': UmbEditableWorkspaceElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.kind.ts new file mode 100644 index 0000000000..81ddb39fda --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.kind.ts @@ -0,0 +1,13 @@ +import type { UmbBackofficeManifestKind } from '@umbraco-cms/backoffice/extension-registry'; + +export const manifest: UmbBackofficeManifestKind = { + type: 'kind', + alias: 'Umb.Kind.Workspace.Editable', + matchKind: 'editable', + matchType: 'workspace', + manifest: { + type: 'workspace', + kind: 'editable', + element: () => import('./editable-workspace.element.js'), + }, +}; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.test.ts new file mode 100644 index 0000000000..e0f2d541ca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/editable/editable-workspace.test.ts @@ -0,0 +1,22 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { UmbEditableWorkspaceElement } from './editable-workspace.element.js'; +import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; + +describe('UmbEditableWorkspaceElement', () => { + let element: UmbEditableWorkspaceElement; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('is defined with its own instance', () => { + expect(element).to.be.instanceOf(UmbEditableWorkspaceElement); + }); + + if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { + it('passes the a11y audit', async () => { + // TODO: should we use shadowDom here? + await expect(element).to.be.accessible(defaultA11yConfig); + }); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/manifests.ts index 0c129d12fc..fb50387ddd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/manifests.ts @@ -1,3 +1,3 @@ -import { manifest as routableKindManifest } from './routable-workspace.kind.js'; +import { manifest as routableKindManifest } from './routable/routable-workspace.kind.js'; export const manifests = [routableKindManifest]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.element.ts similarity index 89% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.element.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.element.ts index b108b86b50..f87e29a9d4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.element.ts @@ -1,4 +1,4 @@ -import type { UmbRoutableWorkspaceContext } from '../contexts/routable-workspace-context.interface.js'; +import type { UmbRoutableWorkspaceContext } from '../../contexts/tokens/routable-workspace-context.interface.js'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.kind.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.kind.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.kind.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.kind.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.test.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable-workspace.test.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/workspace/kinds/routable/routable-workspace.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/types.ts new file mode 100644 index 0000000000..738a9fdb2d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/types.ts @@ -0,0 +1 @@ +export type UmbWorkspaceUniqueType = string | null; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property-dataset/invariant-workspace-property-dataset-context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property-dataset/invariant-workspace-property-dataset-context.ts index 44ca0b1bc4..5187570626 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property-dataset/invariant-workspace-property-dataset-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/workspace-property-dataset/invariant-workspace-property-dataset-context.ts @@ -3,13 +3,13 @@ import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import type { UmbInvariantableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbInvariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; /** * A property dataset context that hooks directly into the workspace context. */ export class UmbInvariantWorkspacePropertyDatasetContext< - WorkspaceType extends UmbInvariantableWorkspaceContextInterface = UmbInvariantableWorkspaceContextInterface, + WorkspaceType extends UmbInvariantDatasetWorkspaceContext = UmbInvariantDatasetWorkspaceContext, > extends UmbContextBase implements UmbPropertyDatasetContext, UmbNameablePropertyDatasetContext diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts index c6dd9ce6f5..558131b3e8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.server.data-source.ts @@ -5,6 +5,7 @@ import { DataTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbCollectionDataSource } from '@umbraco-cms/backoffice/collection'; import type { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; /** * A data source that fetches the data-type collection data from the server. @@ -14,6 +15,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; */ export class UmbDataTypeCollectionServerDataSource implements UmbCollectionDataSource { #host: UmbControllerHost; + #manifestPropertyEditorUis: Array = []; /** * Creates an instance of UmbDataTypeCollectionServerDataSource. @@ -22,6 +24,12 @@ export class UmbDataTypeCollectionServerDataSource implements UmbCollectionDataS */ constructor(host: UmbControllerHost) { this.#host = host; + umbExtensionsRegistry + .byType('propertyEditorUi') + .subscribe((manifestPropertyEditorUIs) => { + this.#manifestPropertyEditorUis = manifestPropertyEditorUIs; + }) + .unsubscribe(); } /** @@ -48,6 +56,7 @@ export class UmbDataTypeCollectionServerDataSource implements UmbCollectionDataS unique: item.id, name: item.name, propertyEditorUiAlias: item.editorUiAlias!, + icon: this.#manifestPropertyEditorUis.find((ui) => ui.alias === item.editorUiAlias!)?.meta.icon, }; return dataTypeDetail; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts index 2f888c4a5b..d89712a6da 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/data-type-flow-input/data-type-flow-input.element.ts @@ -78,8 +78,6 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) { this.#editDataTypeModal?.open({}, 'edit/' + this._ids![0]); }} standalone> - - diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/ref-data-type/ref-data-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/ref-data-type/ref-data-type.element.ts index 4af4380ec5..e144ad4b2e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/components/ref-data-type/ref-data-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/components/ref-data-type/ref-data-type.element.ts @@ -1,7 +1,8 @@ import { UmbDataTypeDetailRepository } from '../../repository/detail/data-type-detail.repository.js'; -import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui'; +import { UUIIconRequestEvent, UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui'; import { html, customElement, property, state, css } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; /** * @element umb-ref-data-type @@ -10,8 +11,10 @@ import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; */ @customElement('umb-ref-data-type') export class UmbRefDataTypeElement extends UmbElementMixin(UUIRefNodeElement) { + @state() protected fallbackIcon = - ''; + ``; + //icon-circle-dotted.svg @property({ type: String, attribute: 'data-type-id' }) public get dataTypeId(): string | undefined { @@ -28,8 +31,11 @@ export class UmbRefDataTypeElement extends UmbElementMixin(UUIRefNodeElement) { (dataType) => { if (dataType) { this.name = dataType.name ?? ''; - this.propertyEditorUiAlias = dataType.editorUiAlias ?? ''; this.propertyEditorSchemaAlias = dataType.editorAlias ?? ''; + if (dataType.editorUiAlias ?? '' !== this.propertyEditorUiAlias) { + this.propertyEditorUiAlias = dataType.editorUiAlias ?? ''; + this.#getIconFromUiAlias(); + } } }, 'dataType', @@ -53,6 +59,37 @@ export class UmbRefDataTypeElement extends UmbElementMixin(UUIRefNodeElement) { @state() propertyEditorSchemaAlias = ''; + async #getIconFromUiAlias() { + if (!this.propertyEditorUiAlias) return; + this.observe( + umbExtensionsRegistry.byTypeAndAlias('propertyEditorUi', this.propertyEditorUiAlias), + async (manifestPropertyEditorUi) => { + const icon = manifestPropertyEditorUi?.meta.icon; + /** [LI] We have the icon name now, but because this element extends from uui-ref-node, it wants the icon via the icon slot. + * From what I can see, this is not possible via this file, but this is the file that have the datatype data.... + * Instead, overwriting the fallbackIcon property which requires a SVG... */ + if (icon) { + this.#requestIconSVG(icon); + } + }, + ), + '_observeIcon'; + } + + #requestIconSVG(iconName: string) { + if (iconName !== '' && iconName !== null) { + const event = new UUIIconRequestEvent(UUIIconRequestEvent.ICON_REQUEST, { + detail: { iconName: iconName }, + }); + this.dispatchEvent(event); + if (event.icon !== null) { + event.icon.then((iconSvg: string) => { + this.fallbackIcon = iconSvg; + }); + } + } + } + protected renderDetail() { const details: string[] = []; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts index 9df268dfcf..23d0012b14 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-data-type-picker-modal.element.ts @@ -86,7 +86,7 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas ? html`
  • - + ${dataType.name}
    diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index af03a97473..d0d634e4e0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -1,4 +1,3 @@ -import { UmbDataTypeTreeRepository } from '../../tree/data-type-tree.repository.js'; import { UMB_DATATYPE_WORKSPACE_MODAL } from '../../workspace/data-type-workspace.modal-token.js'; import { UMB_DATA_TYPE_ENTITY_TYPE } from '../../entity.js'; import { UmbDataTypeCollectionRepository } from '../../collection/index.js'; @@ -275,10 +274,10 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< dataTypes, (dataType) => dataType.unique, (dataType) => - html`
  • + html`
  • - + ${dataType.name}
    diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.server.data-source.ts index d2963a59c1..6e5faabc94 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/data-type-item.server.data-source.ts @@ -3,6 +3,9 @@ import { UmbItemServerDataSourceBase } from '@umbraco-cms/backoffice/repository' import type { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DataTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +let manifestPropertyEditorUis: Array = []; /** * A server data source for Data Type items @@ -19,11 +22,19 @@ export class UmbDataTypeItemServerDataSource extends UmbItemServerDataSourceBase * @param {UmbControllerHost} host * @memberof UmbDataTypeItemServerDataSource */ + constructor(host: UmbControllerHost) { super(host, { getItems, mapper, }); + + umbExtensionsRegistry + .byType('propertyEditorUi') + .subscribe((manifestPropertyEditorUIs) => { + manifestPropertyEditorUis = manifestPropertyEditorUIs; + }) + .unsubscribe(); } } @@ -35,5 +46,6 @@ const mapper = (item: DataTypeItemResponseModel): UmbDataTypeItemModel => { unique: item.id, name: item.name, propertyEditorUiAlias: item.editorUiAlias || '', // TODO: why can this be undefined or null on the server? + icon: manifestPropertyEditorUis.find((ui) => ui.alias === item.editorUiAlias)?.meta.icon, }; }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/types.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/types.ts index d798ac80cc..185aa5cba4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/item/types.ts @@ -2,4 +2,5 @@ export interface UmbDataTypeItemModel { unique: string; name: string; propertyEditorUiAlias: string; + icon?: string; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts index d4fda7664a..7f0af01905 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/tree/data-type-tree.server.data-source.ts @@ -4,6 +4,9 @@ import { UmbTreeServerDataSourceBase } from '@umbraco-cms/backoffice/tree'; import type { DataTypeTreeItemResponseModel } from '@umbraco-cms/backoffice/external/backend-api'; import { DataTypeResource } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { type ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; + +let manifestPropertyEditorUis: Array = []; /** * A data source for a data type tree that fetches data from the server @@ -26,12 +29,19 @@ export class UmbDataTypeTreeServerDataSource extends UmbTreeServerDataSourceBase getChildrenOf, mapper, }); + umbExtensionsRegistry + .byType('propertyEditorUi') + .subscribe((manifestPropertyEditorUIs) => { + manifestPropertyEditorUis = manifestPropertyEditorUIs; + }) + .unsubscribe(); } } -const getRootItems = (args: UmbTreeRootItemsRequestArgs) => +const getRootItems = async (args: UmbTreeRootItemsRequestArgs) => { // eslint-disable-next-line local-rules/no-direct-api-import - DataTypeResource.getTreeDataTypeRoot({ skip: args.skip, take: args.take }); + return DataTypeResource.getTreeDataTypeRoot({ skip: args.skip, take: args.take }); +}; const getChildrenOf = (args: UmbTreeChildrenOfRequestArgs) => { if (args.parentUnique === null) { @@ -48,6 +58,7 @@ const mapper = (item: DataTypeTreeItemResponseModel): UmbDataTypeTreeItemModel = return { unique: item.id, parentUnique: item.parent?.id || null, + icon: manifestPropertyEditorUis.find((ui) => ui.alias === item.editorUiAlias)?.meta.icon, name: item.name, entityType: item.isFolder ? 'data-type-folder' : 'data-type', isFolder: item.isFolder, diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts index 62896e5616..49e488c69f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace-editor.element.ts @@ -23,7 +23,7 @@ export class UmbDataTypeWorkspaceEditorElement extends UmbLitElement { this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => { this.#workspaceContext = workspaceContext; - this.#workspaceContext?.createPropertyDatasetContext(this); + this.#workspaceContext.createPropertyDatasetContext(this); this.#observeIsNew(); this.#observeName(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context-token.ts index 0604febd3f..f3f71750d2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context-token.ts @@ -1,11 +1,8 @@ import type { UmbDataTypeWorkspaceContext } from './data-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_DATA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbWorkspaceContextInterface, - UmbDataTypeWorkspaceContext ->( +export const UMB_DATA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbDataTypeWorkspaceContext => context.getEntityType?.() === 'data-type', diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts index 63c6a5f1a2..446c91ab20 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/data-type-workspace.context.ts @@ -3,11 +3,11 @@ import type { UmbDataTypeDetailModel } from '../types.js'; import { UmbDataTypeWorkspaceEditorElement } from './data-type-workspace-editor.element.js'; import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property'; import type { - UmbInvariantableWorkspaceContextInterface, + UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext, } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbInvariantWorkspacePropertyDatasetContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, @@ -32,8 +32,8 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbDataTypeDetailModel; export class UmbDataTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbInvariantableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext { // public readonly repository: UmbDataTypeDetailRepository = new UmbDataTypeDetailRepository(this); @@ -345,6 +345,11 @@ export class UmbDataTypeWorkspaceContext this.workspaceComplete(this.#currentData.value); } + protected workspaceComplete(data: EntityType | undefined) { + this.dispatchEvent(new CustomEvent('workspace-complete')); + super.workspaceComplete(data); + } + async delete(unique: string) { await this.repository.delete(unique); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context-token.ts index 06413a52f6..d5597f3763 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbDictionaryWorkspaceContext } from './dictionary-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_DICTIONARY_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbDictionaryWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts index 83440198b3..813a15c8b3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/workspace/dictionary-workspace.context.ts @@ -2,8 +2,8 @@ import { UmbDictionaryDetailRepository } from '../repository/index.js'; import type { UmbDictionaryDetailModel } from '../types.js'; import { UmbDictionaryWorkspaceEditorElement } from './dictionary-workspace-editor.element.js'; import { - type UmbSaveableWorkspaceContextInterface, - UmbEditableWorkspaceContextBase, + type UmbSaveableWorkspaceContext, + UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, type UmbRoutableWorkspaceContext, @@ -15,8 +15,8 @@ import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/event'; export class UmbDictionaryWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { // public readonly detailRepository = new UmbDictionaryDetailRepository(this); @@ -26,6 +26,7 @@ export class UmbDictionaryWorkspaceContext #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.unique); readonly name = this.#data.asObservablePart((data) => data?.name); readonly dictionary = this.#data.asObservablePart((data) => data); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context-token.ts index 6fc579e6e0..712e436906 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbDocumentTypeWorkspaceContext } from './document-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbDocumentTypeWorkspaceContext >( 'UmbWorkspaceContext', 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 fc3265ddd1..55724005fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/document-type-workspace.context.ts @@ -4,7 +4,7 @@ import type { UmbDocumentTypeDetailModel } from '../types.js'; import { UmbDocumentTypeWorkspaceEditorElement } from './document-type-workspace-editor.element.js'; import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, type UmbRoutableWorkspaceContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, @@ -23,7 +23,7 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbDocumentTypeDetailModel; export class UmbDocumentTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase + extends UmbSaveableWorkspaceContextBase implements UmbContentTypeWorkspaceContext, UmbRoutableWorkspaceContext { readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true; @@ -36,6 +36,7 @@ export class UmbDocumentTypeWorkspaceContext // General for content types: //readonly data; + readonly unique; readonly name; readonly alias; readonly description; @@ -62,6 +63,7 @@ export class UmbDocumentTypeWorkspaceContext // General for content types: //this.data = this.structure.ownerContentType; + this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique); this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name); this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias); this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/settings/document-type-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/settings/document-type-workspace-view-settings.element.ts index 5e5e55f6be..58aef22912 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/settings/document-type-workspace-view-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/settings/document-type-workspace-view-settings.element.ts @@ -1,14 +1,13 @@ -import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; +import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '../../document-type-workspace.context-token.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UUIToggleElement } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; @customElement('umb-document-type-workspace-view-settings') export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement implements UmbWorkspaceViewElement { - #workspaceContext?: UmbDocumentTypeWorkspaceContext; + #workspaceContext?: typeof UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT.TYPE; @state() private _variesByCulture?: boolean; @@ -21,8 +20,8 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i 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, (documentTypeContext) => { - this.#workspaceContext = documentTypeContext as UmbDocumentTypeWorkspaceContext; + this.consumeContext(UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT, (documentTypeContext) => { + this.#workspaceContext = documentTypeContext; this._observeDocumentType(); }); } @@ -47,7 +46,7 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
    Allow editors to create content of different languages.
    { this.#workspaceContext?.setVariesByCulture((e.target as UUIToggleElement).checked); }} @@ -58,7 +57,7 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
    Allow editors to segment their content.
    { this.#workspaceContext?.setVariesBySegment((e.target as UUIToggleElement).checked); }} @@ -71,7 +70,7 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
    { this.#workspaceContext?.setIsElement((e.target as UUIToggleElement).checked); }} diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts index 62a4719935..71a89c2ed2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/structure/document-type-workspace-view-structure.element.ts @@ -1,9 +1,9 @@ import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; import type { UmbInputDocumentTypeElement } from '../../../components/input-document-type/input-document-type.element.js'; +import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '../../document-type-workspace.context-token.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbContentTypeSortModel } from '@umbraco-cms/backoffice/content-type'; import type { UmbInputCollectionConfigurationElement } from '@umbraco-cms/backoffice/components'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; @@ -26,7 +26,7 @@ export class UmbDocumentTypeWorkspaceViewStructureElement extends UmbLitElement 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, (documentTypeContext) => { + this.consumeContext(UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT, (documentTypeContext) => { this.#workspaceContext = documentTypeContext as UmbDocumentTypeWorkspaceContext; this._observeDocumentType(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts index e66d2cb0a4..081ba0069d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts @@ -1,9 +1,9 @@ import type { UmbDocumentTypeWorkspaceContext } from '../../document-type-workspace.context.js'; +import { UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT } from '../../document-type-workspace.context-token.js'; import type { UmbInputTemplateElement } from '@umbraco-cms/backoffice/template'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; import '@umbraco-cms/backoffice/template'; // TODO: This is needed to register the element, but it should be done in a better way without importing the whole module. @@ -20,7 +20,7 @@ export class UmbDocumentTypeWorkspaceViewTemplatesElement extends UmbLitElement constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (documentTypeContext) => { + this.consumeContext(UMB_DOCUMENT_TYPE_WORKSPACE_CONTEXT, (documentTypeContext) => { this.#workspaceContext = documentTypeContext as UmbDocumentTypeWorkspaceContext; this._observeDocumentType(); }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context-token.ts index 65cb0d1ec3..47f40626cb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context-token.ts @@ -1,10 +1,10 @@ import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js'; import type { UmbDocumentWorkspaceContext } from './document-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_DOCUMENT_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbDocumentWorkspaceContext >( 'UmbWorkspaceContext', 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 50abd6196a..451046c4ba 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 @@ -22,15 +22,15 @@ import { UmbDocumentWorkspaceEditorElement } from './document-workspace-editor.e import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, UmbWorkspaceSplitViewManager, } from '@umbraco-cms/backoffice/workspace'; import type { - UmbWorkspaceCollectionContextInterface, - UmbVariantableWorkspaceContextInterface, - UmbPublishableWorkspaceContextInterface, + UmbCollectionWorkspaceContext, + UmbVariantDatasetWorkspaceContext, + UmbPublishableWorkspaceContext, UmbRoutableWorkspaceContext, } from '@umbraco-cms/backoffice/workspace'; import { @@ -51,12 +51,12 @@ import type { UmbDocumentTypeDetailModel } from '@umbraco-cms/backoffice/documen type EntityType = UmbDocumentDetailModel; export class UmbDocumentWorkspaceContext - extends UmbEditableWorkspaceContextBase + extends UmbSaveableWorkspaceContextBase implements UmbRoutableWorkspaceContext, - UmbVariantableWorkspaceContextInterface, - UmbPublishableWorkspaceContextInterface, - UmbWorkspaceCollectionContextInterface + UmbVariantDatasetWorkspaceContext, + UmbPublishableWorkspaceContext, + UmbCollectionWorkspaceContext { public readonly repository = new UmbDocumentDetailRepository(this); public readonly publishingRepository = new UmbDocumentPublishingRepository(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts index 031aed1397..851b75d8cb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/views/info/document-workspace-view-info.element.ts @@ -2,7 +2,6 @@ import { TimeOptions } from './utils.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state, repeat, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_MODAL_MANAGER_CONTEXT, UMB_WORKSPACE_MODAL, @@ -10,7 +9,11 @@ import { } from '@umbraco-cms/backoffice/modal'; import './document-workspace-view-info-history.element.js'; import './document-workspace-view-info-reference.element.js'; -import type { UmbDocumentVariantModel, UmbDocumentWorkspaceContext } from '@umbraco-cms/backoffice/document'; +import { + UMB_DOCUMENT_WORKSPACE_CONTEXT, + type UmbDocumentVariantModel, + type UmbDocumentWorkspaceContext, +} from '@umbraco-cms/backoffice/document'; import { DocumentVariantStateModel, type DocumentUrlInfoModel } from '@umbraco-cms/backoffice/external/backend-api'; import { type UmbDocumentTypeDetailModel, @@ -78,8 +81,8 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement { this._editDocumentTypePath = routeBuilder({}); }); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (nodeContext) => { - this.#workspaceContext = nodeContext as UmbDocumentWorkspaceContext; + this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this._documentTypeUnique = this.#workspaceContext.getContentTypeId()!; this.#getData(); this._observeContent(); @@ -258,7 +261,7 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement { - ` + ` : html``} diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context-token.ts index dccc8de0cf..fd6f1c74df 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbLanguageWorkspaceContext } from './language-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_LANGUAGE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbLanguageWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context.ts index c25d16beba..815845eb6a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/language-workspace.context.ts @@ -2,24 +2,24 @@ import { UmbLanguageDetailRepository } from '../../repository/index.js'; import type { UmbLanguageDetailModel } from '../../types.js'; import { UmbLanguageWorkspaceEditorElement } from './language-workspace-editor.element.js'; import { - type UmbSaveableWorkspaceContextInterface, - UmbEditableWorkspaceContextBase, + type UmbSaveableWorkspaceContext, + UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, type UmbRoutableWorkspaceContext, } from '@umbraco-cms/backoffice/workspace'; -import { ApiError } from '@umbraco-cms/backoffice/external/backend-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbLanguageWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository: UmbLanguageDetailRepository = new UmbLanguageDetailRepository(this); #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((x) => x?.unique); // TODO: this is a temp solution to bubble validation errors to the UI #validationErrors = new UmbObjectState(undefined); @@ -111,30 +111,17 @@ export class UmbLanguageWorkspaceContext this.#data.update({ fallbackIsoCode: unique }); } - // TODO: this is a temp solution to bubble validation errors to the UI - setValidationErrors(errorMap: any) { - // TODO: I can't use the update method to set the value to undefined - this.#validationErrors.setValue(errorMap); - } - async save() { - const data = this.getData(); - if (!data) return; + const newData = this.getData(); + if (!newData) return; if (this.getIsNew()) { - const { error } = await this.repository.create(data); - // TODO: this is temp solution to bubble validation errors to the UI - if (error) { - if (error instanceof ApiError && error.body.type === 'validation') { - this.setValidationErrors?.(error.body.errors); - } - } else { - this.setValidationErrors?.(undefined); - // TODO: do not make it the buttons responsibility to set the workspace to not new. + const { data } = await this.repository.create(newData); + if (data) { this.setIsNew(false); } } else { - await this.repository.save(data); + await this.repository.save(newData); // TODO: Show validation errors as warnings? } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/views/language-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/views/language-details-workspace-view.element.ts index 1645e22d54..259a7751b4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/views/language-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/language/workspace/language/views/language-details-workspace-view.element.ts @@ -22,9 +22,6 @@ export class UmbLanguageDetailsWorkspaceViewElement extends UmbLitElement implem @state() _isNew?: boolean; - @state() - _validationErrors?: { [key: string]: Array }; - #languageWorkspaceContext?: typeof UMB_LANGUAGE_WORKSPACE_CONTEXT.TYPE; constructor() { @@ -52,11 +49,6 @@ export class UmbLanguageDetailsWorkspaceViewElement extends UmbLitElement implem this.observe(this.#languageWorkspaceContext.isNew, (isNew) => { this._isNew = isNew; }); - - this.observe(this.#languageWorkspaceContext.validationErrors, (value) => { - this._validationErrors = value; - this.requestUpdate('_validationErrors'); - }); }); } @@ -123,9 +115,6 @@ export class UmbLanguageDetailsWorkspaceViewElement extends UmbLitElement implem value=${ifDefined(this._language?.unique)} @change=${this.#handleCultureChange} ?readonly=${this._isNew === false}> - - - ${this._validationErrors?.isoCode.map((error) => html`
    ${error}
    `)}
    diff --git a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer-workspace.context.ts index d37ee3f8b1..73af896920 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/log-viewer/workspace/logviewer-workspace.context.ts @@ -13,7 +13,7 @@ import { DirectionModel, LogLevelModel } from '@umbraco-cms/backoffice/external/ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { query } from '@umbraco-cms/backoffice/router'; -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; export type PoolingInterval = 0 | 2000 | 5000 | 10000 | 20000 | 30000; @@ -27,7 +27,7 @@ export interface LogViewerDateRange { } // TODO: Revisit usage of workspace for this case... -export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements UmbWorkspaceContextInterface { +export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements UmbWorkspaceContext { public readonly workspaceAlias: string = 'Umb.Workspace.LogViewer'; #repository: UmbLogViewerRepository; @@ -39,10 +39,6 @@ export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements U return 'Log Viewer'; } - getUnique() { - return undefined; - } - get today() { const today = new Date(); const dd = String(today.getDate()).padStart(2, '0'); @@ -109,7 +105,6 @@ export class UmbLogViewerWorkspaceContext extends UmbControllerBase implements U constructor(host: UmbControllerHost) { super(host); - console.log('log-viewer context'); this.provideContext(UMB_WORKSPACE_CONTEXT, this); // TODO: Revisit usage of workspace for this case... currently no other workspace context provides them self with their own token. this.provideContext(UMB_APP_LOG_VIEWER_CONTEXT, this); 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 93abbbb22b..e276337937 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,10 +1,10 @@ import type { UmbMediaTypeWorkspaceContext } from './media-type-workspace.context.js'; +import { UMB_MEDIA_TYPE_WORKSPACE_CONTEXT } from './media-type-workspace.context-token.js'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UMB_ICON_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { generateAlias } from '@umbraco-cms/backoffice/utils'; @customElement('umb-media-type-workspace-editor') @@ -26,8 +26,8 @@ export class UmbMediaTypeWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbMediaTypeWorkspaceContext; + this.consumeContext(UMB_MEDIA_TYPE_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this.#observeMediaType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context-token.ts index c0e20a1550..74e55cb85e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/media-type-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbMediaTypeWorkspaceContext } from './media-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_MEDIA_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbMediaTypeWorkspaceContext >( 'UmbWorkspaceContext', 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 f28eb8b625..7f066464d4 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,7 +3,7 @@ import { UMB_MEDIA_TYPE_ENTITY_TYPE } from '../entity.js'; import type { UmbMediaTypeDetailModel } from '../types.js'; import { UmbMediaTypeWorkspaceEditorElement } from './media-type-workspace-editor.element.js'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, type UmbRoutableWorkspaceContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, @@ -23,7 +23,7 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbMediaTypeDetailModel; export class UmbMediaTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase + extends UmbSaveableWorkspaceContextBase implements UmbContentTypeWorkspaceContext, UmbRoutableWorkspaceContext { readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true; @@ -36,6 +36,7 @@ export class UmbMediaTypeWorkspaceContext // General for content types: readonly data; + readonly unique; readonly name; readonly alias; readonly description; @@ -56,6 +57,7 @@ export class UmbMediaTypeWorkspaceContext // General for content types: this.data = this.structure.ownerContentType; + this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique); this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name); this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias); this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/structure/media-type-workspace-view-structure.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/structure/media-type-workspace-view-structure.element.ts index 02db30bb48..04a8e22e52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/structure/media-type-workspace-view-structure.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/workspace/views/structure/media-type-workspace-view-structure.element.ts @@ -1,9 +1,9 @@ import type { UmbMediaTypeWorkspaceContext } from '../../media-type-workspace.context.js'; import type { UmbInputMediaTypeElement } from '../../../components/input-media-type/input-media-type.element.js'; +import { UMB_MEDIA_TYPE_WORKSPACE_CONTEXT } from '../../media-type-workspace.context-token.js'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import type { UmbContentTypeSortModel } from '@umbraco-cms/backoffice/content-type'; import type { UmbInputCollectionConfigurationElement } from '@umbraco-cms/backoffice/components'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; @@ -26,8 +26,8 @@ export class UmbMediaTypeWorkspaceViewStructureElement extends UmbLitElement imp 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, (mediaTypeContext) => { - this.#workspaceContext = mediaTypeContext as UmbMediaTypeWorkspaceContext; + this.consumeContext(UMB_MEDIA_TYPE_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this._observeMediaType(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context-token.ts index a467048308..24ffa52a23 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/media-workspace.context-token.ts @@ -1,12 +1,9 @@ import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js'; import type { UmbMediaWorkspaceContext } from './media-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_MEDIA_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, - UmbMediaWorkspaceContext ->( +export const UMB_MEDIA_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbMediaWorkspaceContext => context.getEntityType?.() === UMB_MEDIA_ENTITY_TYPE, 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 b2ec225c93..0c9ec4e81f 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 @@ -6,11 +6,11 @@ import type { UmbMediaDetailModel, UmbMediaVariantModel, UmbMediaVariantOptionMo import { UMB_INVARIANT_CULTURE, UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbContentTypeStructureManager } from '@umbraco-cms/backoffice/content-type'; import type { - UmbWorkspaceCollectionContextInterface, - UmbVariantableWorkspaceContextInterface, + UmbCollectionWorkspaceContext, + UmbVariantDatasetWorkspaceContext, } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, UmbWorkspaceSplitViewManager, @@ -33,8 +33,8 @@ import UmbMediaWorkspaceEditorElement from './media-workspace-editor.element.js' type EntityType = UmbMediaDetailModel; export class UmbMediaWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbVariantableWorkspaceContextInterface, UmbWorkspaceCollectionContextInterface + extends UmbSaveableWorkspaceContextBase + implements UmbVariantDatasetWorkspaceContext, UmbCollectionWorkspaceContext { // public readonly repository = new UmbMediaDetailRepository(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info.element.ts index 7f2c56d9bd..d6c8eaf1da 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/workspace/views/info/media-workspace-view-info.element.ts @@ -1,12 +1,11 @@ import { TimeOptions } from './utils.js'; import { css, html, customElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import './media-workspace-view-info-history.element.js'; import './media-workspace-view-info-reference.element.js'; -import type { UmbMediaWorkspaceContext } from '@umbraco-cms/backoffice/media'; +import { UMB_MEDIA_WORKSPACE_CONTEXT, type UmbMediaWorkspaceContext } from '@umbraco-cms/backoffice/media'; import type { MediaUrlInfoModel } from '@umbraco-cms/backoffice/external/backend-api'; @customElement('umb-media-workspace-view-info') @@ -20,7 +19,7 @@ export class UmbMediaWorkspaceViewInfoElement extends UmbLitElement { @state() private _mediaUnique = ''; - private _workspaceContext?: typeof UMB_WORKSPACE_CONTEXT.TYPE; + private _workspaceContext?: typeof UMB_MEDIA_WORKSPACE_CONTEXT.TYPE; @state() private _editMediaTypePath = ''; @@ -43,8 +42,8 @@ export class UmbMediaWorkspaceViewInfoElement extends UmbLitElement { this._editMediaTypePath = routeBuilder({}); }); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (nodeContext) => { - this._workspaceContext = nodeContext; + this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (context) => { + this._workspaceContext = context; this._observeContent(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context-token.ts index e8dc975186..45835e08e7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbMemberGroupWorkspaceContext } from './member-group-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_MEMBER_GROUP_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbMemberGroupWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts index 1d69d6a74b..493df687f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/member-group-workspace.context.ts @@ -3,8 +3,8 @@ import type { UmbMemberGroupDetailModel } from '../types.js'; import { UMB_MEMBER_GROUP_WORKSPACE_ALIAS } from './manifests.js'; import { UmbMemberGroupWorkspaceEditorElement } from './member-group-workspace-editor.element.js'; import { - type UmbSaveableWorkspaceContextInterface, - UmbEditableWorkspaceContextBase, + type UmbSaveableWorkspaceContext, + UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, type UmbRoutableWorkspaceContext, @@ -13,8 +13,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; export class UmbMemberGroupWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository = new UmbMemberGroupDetailRepository(this); #getDataPromise?: Promise; diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/views/info/member-type-workspace-view-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/views/info/member-type-workspace-view-info.element.ts index fe7267c904..d38e509e02 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/views/info/member-type-workspace-view-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/workspace/views/info/member-type-workspace-view-info.element.ts @@ -1,14 +1,13 @@ // import { UMB_COMPOSITION_PICKER_MODAL, type UmbCompositionPickerModalData } from '../../../modals/index.js'; -import type { UmbMemberGroupWorkspaceContext } from '../../member-group-workspace.context.js'; +import { UMB_MEMBER_GROUP_WORKSPACE_CONTEXT } from '../../member-group-workspace.context-token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; @customElement('umb-member-type-workspace-view-member-info') export class UmbMemberTypeWorkspaceViewMemberInfoElement extends UmbLitElement implements UmbWorkspaceViewElement { - private _workspaceContext?: UmbMemberGroupWorkspaceContext; + private _workspaceContext?: typeof UMB_MEMBER_GROUP_WORKSPACE_CONTEXT.TYPE; @state() private _unique = ''; @@ -16,8 +15,8 @@ export class UmbMemberTypeWorkspaceViewMemberInfoElement extends UmbLitElement i constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, async (context) => { - this._workspaceContext = context as UmbMemberGroupWorkspaceContext; + this.consumeContext(UMB_MEMBER_GROUP_WORKSPACE_CONTEXT, async (context) => { + this._workspaceContext = context; this._unique = this._workspaceContext.getUnique() ?? ''; }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context-token.ts index 3307c71b19..fe736c7d78 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbMemberTypeWorkspaceContext } from './member-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_MEMBER_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbMemberTypeWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts index cc01f5917a..a587aebd59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/workspace/member-type-workspace.context.ts @@ -3,7 +3,7 @@ import type { UmbMemberTypeDetailModel } from '../types.js'; import { UMB_MEMBER_TYPE_ENTITY_TYPE } from '../index.js'; import { UmbMemberTypeWorkspaceEditorElement } from './member-type-workspace-editor.element.js'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, type UmbRoutableWorkspaceContext, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, @@ -21,7 +21,7 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice type EntityType = UmbMemberTypeDetailModel; export class UmbMemberTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase + extends UmbSaveableWorkspaceContextBase implements UmbContentTypeWorkspaceContext, UmbRoutableWorkspaceContext { readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true; @@ -32,6 +32,7 @@ export class UmbMemberTypeWorkspaceContext // General for content types: readonly data; + readonly unique; readonly name; readonly alias; readonly description; @@ -52,6 +53,7 @@ export class UmbMemberTypeWorkspaceContext // General for content types: this.data = this.structure.ownerContentType; + this.unique = this.structure.ownerContentTypeObservablePart((data) => data?.unique); this.name = this.structure.ownerContentTypeObservablePart((data) => data?.name); this.alias = this.structure.ownerContentTypeObservablePart((data) => data?.alias); this.description = this.structure.ownerContentTypeObservablePart((data) => data?.description); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context-token.ts index a70bdcd274..4cbc630994 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context-token.ts @@ -1,11 +1,8 @@ import type { UmbMemberWorkspaceContext } from './member-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_MEMBER_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, - UmbMemberWorkspaceContext ->( +export const UMB_MEMBER_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbMemberWorkspaceContext => context.getEntityType?.() === 'member', diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts index 5afc5fff60..d63808ed04 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/member-workspace.context.ts @@ -5,16 +5,12 @@ import { UMB_MEMBER_WORKSPACE_ALIAS } from './manifests.js'; import { UmbMemberWorkspaceEditorElement } from './member-workspace-editor.element.js'; import { UmbMemberTypeDetailRepository } from '@umbraco-cms/backoffice/member-type'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, UmbWorkspaceSplitViewManager, } from '@umbraco-cms/backoffice/workspace'; -import type { - UmbRoutableWorkspaceContext, - UmbVariantableWorkspaceContextInterface, - UmbWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; +import type { UmbRoutableWorkspaceContext, UmbVariantDatasetWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbArrayState, @@ -30,11 +26,8 @@ import type { UmbDataSourceResponse } from '@umbraco-cms/backoffice/repository'; type EntityType = UmbMemberDetailModel; export class UmbMemberWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements - UmbVariantableWorkspaceContextInterface, - UmbRoutableWorkspaceContext, - UmbWorkspaceContextInterface + extends UmbSaveableWorkspaceContextBase + implements UmbVariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository = new UmbMemberDetailRepository(this); @@ -52,6 +45,7 @@ export class UmbMemberWorkspaceContext } readonly data = this.#currentData.asObservable(); + readonly unique = this.#currentData.asObservablePart((data) => data?.unique); readonly name = this.#currentData.asObservablePart((data) => data?.variants[0].name); readonly createDate = this.#currentData.asObservablePart((data) => data?.variants[0].createDate); readonly updateDate = this.#currentData.asObservablePart((data) => data?.variants[0].updateDate); @@ -65,8 +59,6 @@ export class UmbMemberWorkspaceContext readonly variants = this.#currentData.asObservablePart((data) => data?.variants ?? []); - readonly unique = this.#currentData.asObservablePart((data) => data?.unique); - readonly routes = new UmbWorkspaceRouteManager(this); readonly splitView = new UmbWorkspaceSplitViewManager(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts index bd1c6d3fde..48025bc6a3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/workspace/views/member/member-workspace-view-member-info.element.ts @@ -1,10 +1,9 @@ // import { UMB_COMPOSITION_PICKER_MODAL, type UmbCompositionPickerModalData } from '../../../modals/index.js'; -import type { UmbMemberWorkspaceContext } from '../../member-workspace.context.js'; +import { UMB_MEMBER_WORKSPACE_CONTEXT } from '../../member-workspace.context-token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/extension-registry'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; import { UmbMemberTypeItemRepository } from '@umbraco-cms/backoffice/member-type'; @@ -17,7 +16,7 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple @state() private _memberTypeIcon = ''; - private _workspaceContext?: UmbMemberWorkspaceContext; + private _workspaceContext?: typeof UMB_MEMBER_WORKSPACE_CONTEXT.TYPE; private _memberTypeItemRepository: UmbMemberTypeItemRepository = new UmbMemberTypeItemRepository(this); @state() @@ -40,8 +39,8 @@ export class UmbMemberWorkspaceViewMemberInfoElement extends UmbLitElement imple this._editMemberTypePath = routeBuilder({}); }); - this.consumeContext(UMB_WORKSPACE_CONTEXT, async (nodeContext) => { - this._workspaceContext = nodeContext as UmbMemberWorkspaceContext; + this.consumeContext(UMB_MEMBER_WORKSPACE_CONTEXT, async (context) => { + this._workspaceContext = context; this.observe(this._workspaceContext.contentTypeUnique, (unique) => (this._memberTypeUnique = unique || '')); this.observe(this._workspaceContext.createDate, (date) => (this._createDate = date || 'Unknown')); this.observe(this._workspaceContext.updateDate, (date) => (this._updateDate = date || 'Unknown')); diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context-token.ts index a9c560302b..2fcb61a826 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbRelationTypeWorkspaceContext } from './relation-type-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_RELATION_TYPE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbRelationTypeWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context.ts index 93784537b1..ff08db2841 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/workspace/relation-type-workspace.context.ts @@ -1,8 +1,8 @@ import { UmbRelationTypeRepository } from '../repository/relation-type.repository.js'; import { UmbRelationTypeWorkspaceEditorElement } from './relation-type-workspace-editor.element.js'; import { - type UmbSaveableWorkspaceContextInterface, - UmbEditableWorkspaceContextBase, + type UmbSaveableWorkspaceContext, + UmbSaveableWorkspaceContextBase, type UmbRoutableWorkspaceContext, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, @@ -12,8 +12,8 @@ import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbRelationTypeWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { // public readonly repository: UmbRelationTypeRepository = new UmbRelationTypeRepository(this); @@ -22,6 +22,7 @@ export class UmbRelationTypeWorkspaceContext #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.id); readonly name = this.#data.asObservablePart((data) => data?.name); readonly id = this.#data.asObservablePart((data) => data?.id); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.stories.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.stories.ts index cb94bb5bfc..df07650c41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/code-editor/code-editor.stories.ts @@ -113,7 +113,7 @@ const codeSnippets: Record = { "Smartypants, double quotes" and 'single quotes'`, typescript: `import { UmbTemplateRepository } from '../repository/template.repository.js'; - import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context.js'; + import { UmbWorkspaceContextBase } from '../../../shared/components/workspace/workspace-context/workspace-context.js'; import { UmbObjectState } from '@umbraco-cms/observable-api'; import { TemplateModel } from '@umbraco-cms/backend-api'; import { UmbControllerHostElement } from '@umbraco-cms/controller'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context-token.ts index cd524599bf..d17a5ae6f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context-token.ts @@ -1,10 +1,10 @@ import { UMB_PARTIAL_VIEW_ENTITY_TYPE } from '../entity.js'; import type { UmbPartialViewWorkspaceContext } from './partial-view-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbPartialViewWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts index 764f7623d9..52e112a76a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace.context.ts @@ -4,12 +4,9 @@ import { UMB_PARTIAL_VIEW_ENTITY_TYPE } from '../entity.js'; import { UmbPartialViewWorkspaceEditorElement } from './partial-view-workspace-editor.element.js'; import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { - UmbRoutableWorkspaceContext, - UmbSaveableWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; +import type { UmbRoutableWorkspaceContext, UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, } from '@umbraco-cms/backoffice/workspace'; @@ -22,8 +19,8 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice import type { IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; export class UmbPartialViewWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository = new UmbPartialViewDetailRepository(this); @@ -31,6 +28,7 @@ export class UmbPartialViewWorkspaceContext #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.unique); readonly name = this.#data.asObservablePart((data) => data?.name); readonly content = this.#data.asObservablePart((data) => data?.content); readonly path = this.#data.asObservablePart((data) => data?.path); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/index.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/index.ts index a545c0ae31..bc053ba3af 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/index.ts @@ -1,4 +1,5 @@ export * from './repository/index.js'; +export * from './workspace/script-workspace.context-token.js'; export * from './tree/index.js'; export * from './types.js'; export * from './entity.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace-editor.element.ts index 9142f47d06..5a0c17ec3d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace-editor.element.ts @@ -1,9 +1,8 @@ -import type { UmbScriptWorkspaceContext } from './script-workspace.context.js'; +import { UMB_SCRIPT_WORKSPACE_CONTEXT } from './script-workspace.context-token.js'; import type { UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @customElement('umb-script-workspace-editor') @@ -23,31 +22,31 @@ export class UmbScriptWorkspaceEditorElement extends UmbLitElement { @state() private _isNew?: boolean = false; - #scriptsWorkspaceContext?: UmbScriptWorkspaceContext; + #context?: typeof UMB_SCRIPT_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#scriptsWorkspaceContext = workspaceContext as UmbScriptWorkspaceContext; + this.consumeContext(UMB_SCRIPT_WORKSPACE_CONTEXT, (context) => { + this.#context = context; - this.observe(this.#scriptsWorkspaceContext.name, (name) => { + this.observe(this.#context.name, (name) => { this._name = name; }); - this.observe(this.#scriptsWorkspaceContext.content, (content) => { + this.observe(this.#context.content, (content) => { this._content = content; }); - this.observe(this.#scriptsWorkspaceContext.path, (path) => { + this.observe(this.#context.path, (path) => { this._path = path; }); - this.observe(this.#scriptsWorkspaceContext.isCodeEditorReady, (isReady) => { + this.observe(this.#context.isCodeEditorReady, (isReady) => { this._ready = isReady; }); - this.observe(this.#scriptsWorkspaceContext.isNew, (isNew) => { + this.observe(this.#context.isNew, (isNew) => { this._isNew = isNew; }); }); @@ -56,13 +55,13 @@ export class UmbScriptWorkspaceEditorElement extends UmbLitElement { #onNameInput(event: Event) { const target = event.target as UUIInputElement; const value = target.value as string; - this.#scriptsWorkspaceContext?.setName(value); + this.#context?.setName(value); } #onCodeEditorInput(event: Event) { const target = event.target as UmbCodeEditorElement; const value = target.code as string; - this.#scriptsWorkspaceContext?.setContent(value); + this.#context?.setContent(value); } #renderCodeEditor() { @@ -91,7 +90,7 @@ export class UmbScriptWorkspaceEditorElement extends UmbLitElement { ? this.#renderCodeEditor() : html`
    -
    `} +
    `} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context-token.ts new file mode 100644 index 0000000000..c5dd0234b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context-token.ts @@ -0,0 +1,9 @@ +import type { UmbScriptWorkspaceContext } from './script-workspace.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; + +export const UMB_SCRIPT_WORKSPACE_CONTEXT = new UmbContextToken( + 'UmbWorkspaceContext', + undefined, + (context): context is UmbScriptWorkspaceContext => context.getEntityType?.() === 'script', +); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts index ea0c25b340..07104330f9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/workspace/script-workspace.context.ts @@ -6,9 +6,9 @@ import { UmbScriptWorkspaceEditorElement } from './script-workspace-editor.eleme import { UmbBooleanState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, type UmbRoutableWorkspaceContext, - type UmbSaveableWorkspaceContextInterface, + type UmbSaveableWorkspaceContext, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, } from '@umbraco-cms/backoffice/workspace'; @@ -19,8 +19,8 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice import type { IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; export class UmbScriptWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository = new UmbScriptDetailRepository(this); @@ -28,6 +28,7 @@ export class UmbScriptWorkspaceContext #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.unique); readonly name = this.#data.asObservablePart((data) => data?.name); readonly content = this.#data.asObservablePart((data) => data?.content); readonly path = this.#data.asObservablePart((data) => data?.path); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts index ad7e644241..31e8a93c1e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace-editor.element.ts @@ -1,7 +1,6 @@ -import type { UmbStylesheetWorkspaceContext } from './stylesheet-workspace.context.js'; +import { UMB_STYLESHEET_WORKSPACE_CONTEXT } from './stylesheet-workspace.context-token.js'; import type { UUIInputElement, UUIInputEvent } 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/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; @@ -16,13 +15,13 @@ export class UmbStylesheetWorkspaceEditorElement extends UmbLitElement { @state() private _name?: string; - #workspaceContext?: UmbStylesheetWorkspaceContext; + #workspaceContext?: typeof UMB_STYLESHEET_WORKSPACE_CONTEXT.TYPE; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (instance) => { - this.#workspaceContext = instance as UmbStylesheetWorkspaceContext; + this.consumeContext(UMB_STYLESHEET_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this.observe(this.#workspaceContext.path, (path) => (this._path = path), '_observeStylesheetPath'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context-token.ts index cb0c5e25af..71b9cda4f1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/stylesheet-workspace.context-token.ts @@ -1,10 +1,10 @@ import { UMB_STYLESHEET_ENTITY_TYPE } from '../entity.js'; import type { UmbStylesheetWorkspaceContext } from './stylesheet-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_STYLESHEET_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbStylesheetWorkspaceContext >( 'UmbWorkspaceContext', 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 db162fac11..c38d908461 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 @@ -4,8 +4,8 @@ import { UMB_STYLESHEET_ENTITY_TYPE } from '../entity.js'; import { UMB_STYLESHEET_WORKSPACE_ALIAS } from './manifests.js'; import { UmbStylesheetWorkspaceEditorElement } from './stylesheet-workspace-editor.element.js'; import { - type UmbSaveableWorkspaceContextInterface, - UmbEditableWorkspaceContextBase, + type UmbSaveableWorkspaceContext, + UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager, UmbWorkspaceIsNewRedirectController, type UmbRoutableWorkspaceContext, @@ -19,8 +19,8 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice import type { IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; export class UmbStylesheetWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly repository = new UmbStylesheetDetailRepository(this); @@ -28,6 +28,7 @@ export class UmbStylesheetWorkspaceContext #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.unique); readonly name = this.#data.asObservablePart((data) => data?.name); readonly content = this.#data.asObservablePart((data) => data?.content); readonly path = this.#data.asObservablePart((data) => data?.path); diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-rule/stylesheet-rich-text-rule-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-rule/stylesheet-rich-text-rule-workspace-view.element.ts index f55d91bf51..047069c5db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-rule/stylesheet-rich-text-rule-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/workspace/views/rich-text-rule/stylesheet-rich-text-rule-workspace-view.element.ts @@ -1,10 +1,9 @@ -import type { UmbStylesheetWorkspaceContext } from '../../stylesheet-workspace.context.js'; import type { UmbStylesheetRule } from '../../../types.js'; import type { UmbStylesheetRuleInputElement } from '../../../components/index.js'; import { UmbStylesheetRuleManager } from '../../../utils/index.js'; +import { UMB_STYLESHEET_WORKSPACE_CONTEXT } from '../../stylesheet-workspace.context-token.js'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; @@ -13,15 +12,15 @@ export class UmbStylesheetRichTextRuleWorkspaceViewElement extends UmbLitElement @state() _rules: UmbStylesheetRule[] = []; - #context?: UmbStylesheetWorkspaceContext; + #context?: typeof UMB_STYLESHEET_WORKSPACE_CONTEXT.TYPE; #stylesheetRuleManager = new UmbStylesheetRuleManager(); #stylesheetContent = ''; constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#context = workspaceContext as UmbStylesheetWorkspaceContext; + this.consumeContext(UMB_STYLESHEET_WORKSPACE_CONTEXT, (context) => { + this.#context = context; this.#observeContent(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context-token.ts index 808766310c..34029fdd7d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/workspace/template-workspace.context-token.ts @@ -1,9 +1,9 @@ import type { UmbTemplateWorkspaceContext } from './template-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_TEMPLATE_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbTemplateWorkspaceContext >( 'UmbWorkspaceContext', 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 2461875df8..0e36439986 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 @@ -4,12 +4,9 @@ import { UmbTemplateDetailRepository, UmbTemplateItemRepository } from '../repos import { UMB_TEMPLATE_WORKSPACE_ALIAS } from './manifests.js'; import { UmbTemplateWorkspaceEditorElement } from './template-workspace-editor.element.js'; import { loadCodeEditor } from '@umbraco-cms/backoffice/code-editor'; -import type { - UmbRoutableWorkspaceContext, - UmbSaveableWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; +import type { UmbRoutableWorkspaceContext, UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, } from '@umbraco-cms/backoffice/workspace'; @@ -21,8 +18,8 @@ import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice import type { IRoutingInfo, PageComponent } from '@umbraco-cms/backoffice/router'; export class UmbTemplateWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { public readonly detailRepository = new UmbTemplateDetailRepository(this); public readonly itemRepository = new UmbTemplateItemRepository(this); @@ -36,7 +33,7 @@ export class UmbTemplateWorkspaceContext name = this.#data.asObservablePart((data) => data?.name); alias = this.#data.asObservablePart((data) => data?.alias); content = this.#data.asObservablePart((data) => data?.content); - unique = this.#data.asObservablePart((data) => data?.unique); + readonly unique = this.#data.asObservablePart((data) => data?.unique); masterTemplateUnique = this.#data.asObservablePart((data) => data?.masterTemplate?.unique); #isCodeEditorReady = new UmbBooleanState(false); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context-token.ts index 3303651a97..d9f70a906b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context-token.ts @@ -1,10 +1,10 @@ import { UMB_USER_GROUP_ENTITY_TYPE } from '../index.js'; import type { UmbUserGroupWorkspaceContext } from './user-group-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; export const UMB_USER_GROUP_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, + UmbSaveableWorkspaceContext, UmbUserGroupWorkspaceContext >( 'UmbWorkspaceContext', diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts index 19cfa3752e..54b7390b03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group-workspace.context.ts @@ -2,12 +2,9 @@ import { UmbUserGroupDetailRepository } from '../repository/detail/index.js'; import type { UmbUserGroupDetailModel } from '../types.js'; import { UmbUserGroupWorkspaceEditorElement } from './user-group-workspace-editor.element.js'; import type { UmbUserPermissionModel } from '@umbraco-cms/backoffice/user-permission'; -import type { - UmbRoutableWorkspaceContext, - UmbSaveableWorkspaceContextInterface, -} from '@umbraco-cms/backoffice/workspace'; +import type { UmbRoutableWorkspaceContext, UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { - UmbEditableWorkspaceContextBase, + UmbSaveableWorkspaceContextBase, UmbWorkspaceIsNewRedirectController, UmbWorkspaceRouteManager, } from '@umbraco-cms/backoffice/workspace'; @@ -15,8 +12,8 @@ import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbUserGroupWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext { // public readonly repository: UmbUserGroupDetailRepository = new UmbUserGroupDetailRepository(this); @@ -24,6 +21,7 @@ export class UmbUserGroupWorkspaceContext #data = new UmbObjectState(undefined); data = this.#data.asObservable(); + readonly unique = this.#data.asObservablePart((data) => data?.unique); readonly name = this.#data.asObservablePart((data) => data?.name || ''); readonly icon = this.#data.asObservablePart((data) => data?.icon || null); readonly sections = this.#data.asObservablePart((data) => data?.sections || []); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts index 7445d002ae..d3a169032e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace-editor.element.ts @@ -1,11 +1,11 @@ import type { UmbUserDetailModel } from '../index.js'; import { UMB_USER_ENTITY_TYPE } from '../entity.js'; import type { UmbUserWorkspaceContext } from './user-workspace.context.js'; +import { UMB_USER_WORKSPACE_CONTEXT } from './user-workspace.context-token.js'; import type { UUIInputElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; import { css, html, nothing, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; // import local components. Theses are not meant to be used outside of this component. @@ -24,8 +24,8 @@ export class UmbUserWorkspaceEditorElement extends UmbLitElement { constructor() { super(); - this.consumeContext(UMB_WORKSPACE_CONTEXT, (workspaceContext) => { - this.#workspaceContext = workspaceContext as UmbUserWorkspaceContext; + this.consumeContext(UMB_USER_WORKSPACE_CONTEXT, (context) => { + this.#workspaceContext = context; this.#observeUser(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context-token.ts index fdf725a359..bd3fc158b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context-token.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context-token.ts @@ -1,12 +1,9 @@ import { UMB_USER_ENTITY_TYPE } from '../entity.js'; import type { UmbUserWorkspaceContext } from './user-workspace.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; -export const UMB_USER_WORKSPACE_CONTEXT = new UmbContextToken< - UmbSaveableWorkspaceContextInterface, - UmbUserWorkspaceContext ->( +export const UMB_USER_WORKSPACE_CONTEXT = new UmbContextToken( 'UmbWorkspaceContext', undefined, (context): context is UmbUserWorkspaceContext => context.getEntityType?.() === UMB_USER_ENTITY_TYPE, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts index 744a84767e..c83f21c2f2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/workspace/user-workspace.context.ts @@ -4,16 +4,16 @@ import { UmbUserDetailRepository } from '../repository/index.js'; import { UmbUserAvatarRepository } from '../repository/avatar/index.js'; import { UMB_USER_WORKSPACE_ALIAS } from './manifests.js'; import { UmbUserWorkspaceEditorElement } from './user-workspace-editor.element.js'; -import type { UmbSaveableWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; -import { UmbEditableWorkspaceContextBase, UmbWorkspaceRouteManager } from '@umbraco-cms/backoffice/workspace'; +import type { UmbSaveableWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; +import { UmbSaveableWorkspaceContextBase, UmbWorkspaceRouteManager } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; type EntityType = UmbUserDetailModel; export class UmbUserWorkspaceContext - extends UmbEditableWorkspaceContextBase - implements UmbSaveableWorkspaceContextInterface + extends UmbSaveableWorkspaceContextBase + implements UmbSaveableWorkspaceContext { public readonly detailRepository: UmbUserDetailRepository = new UmbUserDetailRepository(this); public readonly avatarRepository: UmbUserAvatarRepository = new UmbUserAvatarRepository(this); diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.context.ts index a98faea18c..7b1a61f4c9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/workspace/webhook/webhook-workspace.context.ts @@ -2,20 +2,16 @@ import { UMB_WEBHOOK_ENTITY_TYPE, UMB_WEBHOOK_WORKSPACE } from '../../entity.js' import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; -import type { UmbWorkspaceContextInterface } from '@umbraco-cms/backoffice/workspace'; +import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace'; import { UMB_WORKSPACE_CONTEXT } from '@umbraco-cms/backoffice/workspace'; -export class UmbWebhookWorkspaceContext extends UmbControllerBase implements UmbWorkspaceContextInterface { +export class UmbWebhookWorkspaceContext extends UmbControllerBase implements UmbWorkspaceContext { public readonly workspaceAlias = UMB_WEBHOOK_WORKSPACE; getEntityType() { return UMB_WEBHOOK_ENTITY_TYPE; } - getUnique() { - return undefined; - } - constructor(host: UmbControllerHost) { super(host); this.provideContext(UMB_WORKSPACE_CONTEXT, this); diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 364077e447..33bf53145e 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -101,6 +101,7 @@ "@umbraco-cms/backoffice/user-permission": ["./src/packages/user/user-permission/index.ts"], "@umbraco-cms/backoffice/user": ["./src/packages/user/user/index.ts"], "@umbraco-cms/backoffice/utils": ["./src/packages/core/utils/index.ts"], + "@umbraco-cms/backoffice/validation": ["./src/packages/core/validation/index.ts"], "@umbraco-cms/backoffice/variant": ["./src/packages/core/variant/index.ts"], "@umbraco-cms/backoffice/webhook": ["./src/packages/webhook/index.ts"], "@umbraco-cms/backoffice/workspace": ["./src/packages/core/workspace/index.ts"],