Merge branch 'main' into feature/tree-item-ancestors

This commit is contained in:
Mads Rasmussen
2024-03-25 11:21:25 +01:00
146 changed files with 1161 additions and 551 deletions

View File

@@ -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",

View File

@@ -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) => {

View File

@@ -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);
}
}

View File

@@ -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<UmbBlockGridTypeAreaType>
implements UmbInvariantableWorkspaceContextInterface
extends UmbSaveableWorkspaceContextBase<UmbBlockGridTypeAreaType>
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',

View File

@@ -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, UmbBlockTypeWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbBlockTypeWorkspaceContext => (context as any).IS_BLOCK_TYPE_WORKSPACE_CONTEXT,

View File

@@ -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<BlockTypeData extends UmbBlockTypeWithGroupKey = UmbBlockTypeWithGroupKey>
extends UmbEditableWorkspaceContextBase<BlockTypeData>
implements UmbInvariantableWorkspaceContextInterface, UmbRoutableWorkspaceContext
extends UmbSaveableWorkspaceContextBase<BlockTypeData>
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
{
// Just for context token safety:
public readonly IS_BLOCK_TYPE_WORKSPACE_CONTEXT = true;

View File

@@ -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<UmbWorkspaceContextInterface, UmbBlockWorkspaceContext>(
export const UMB_BLOCK_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbBlockWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbBlockWorkspaceContext => (context as any).IS_BLOCK_WORKSPACE_CONTEXT,

View File

@@ -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<LayoutDataType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel>
extends UmbEditableWorkspaceContextBase<LayoutDataType>
extends UmbSaveableWorkspaceContextBase<LayoutDataType>
implements UmbRoutableWorkspaceContext
{
// Just for context token safety:

View File

@@ -1,25 +1,17 @@
import { html, nothing, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { map } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbEntityAction } from '@umbraco-cms/backoffice/entity-action';
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
import { html, nothing, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import type { UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section';
import { UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { ManifestEntityActionDefaultKind } from '@umbraco-cms/backoffice/extension-registry';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { createExtensionApi } from '@umbraco-cms/backoffice/extension-api';
@customElement('umb-entity-actions-bundle')
export class UmbEntityActionsBundleElement extends UmbLitElement {
private _entityType?: string;
@property({ type: String, attribute: 'entity-type' })
public get entityType() {
return this._entityType;
}
public set entityType(value: string | undefined) {
const oldValue = this._entityType;
if (oldValue === value) return;
this._entityType = value;
this.#observeEntityActions();
this.requestUpdate('entityType', oldValue);
}
entityType?: string;
@property({ type: String })
unique?: string | null;
@@ -28,7 +20,13 @@ export class UmbEntityActionsBundleElement extends UmbLitElement {
public label?: string;
@state()
private _hasActions = false;
private _numberOfActions = 0;
@state()
private _firstActionManifest?: ManifestEntityActionDefaultKind;
@state()
private _firstActionApi?: UmbEntityAction<unknown>;
#sectionSidebarContext?: UmbSectionSidebarContext;
@@ -40,36 +38,59 @@ export class UmbEntityActionsBundleElement extends UmbLitElement {
});
}
protected updated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
if (_changedProperties.has('entityType') && _changedProperties.has('unique')) {
this.#observeEntityActions();
}
}
#observeEntityActions() {
this.observe(
umbExtensionsRegistry
.byType('entityAction')
.pipe(map((actions) => actions.some((action) => action.forEntityTypes.includes(this.entityType!)))),
(hasActions) => {
this._hasActions = hasActions;
umbExtensionsRegistry.byType('entityAction'),
async (manifests) => {
const actions = manifests.filter((manifest) => manifest.forEntityTypes.includes(this.entityType!));
this._numberOfActions = actions.length;
this._firstActionManifest =
this._numberOfActions > 0 ? (actions[0] as ManifestEntityActionDefaultKind) : undefined;
if (!this._firstActionManifest) return;
this._firstActionApi = await createExtensionApi(this, this._firstActionManifest, [
{ unique: this.unique, entityType: this.entityType },
]);
},
'umbEntityActionsObserver',
);
}
private _openActions() {
#openContextMenu() {
if (!this.entityType) throw new Error('Entity type is not defined');
if (this.unique === undefined) throw new Error('Unique is not defined');
this.#sectionSidebarContext?.toggleContextMenu(this.entityType, this.unique, this.label);
}
async #onFirstActionClick(event: PointerEvent) {
event.stopPropagation();
await this._firstActionApi?.execute();
}
render() {
return html`
${this._hasActions
? html`
<uui-action-bar slot="actions">
<uui-button @click=${this._openActions} label="Open actions menu">
<uui-symbol-more></uui-symbol-more>
</uui-button>
</uui-action-bar>
`
: nothing}
`;
if (this._numberOfActions === 0) return nothing;
return html`<uui-action-bar slot="actions"> ${this.#renderFirstAction()} ${this.#renderMore()} </uui-action-bar>`;
}
#renderMore() {
if (this._numberOfActions === 1) return nothing;
return html`<uui-button @click=${this.#openContextMenu} label="Open actions menu">
<uui-symbol-more></uui-symbol-more>
</uui-button>`;
}
#renderFirstAction() {
if (!this._firstActionApi) return nothing;
return html`<uui-button
label=${ifDefined(this._firstActionManifest?.meta.label)}
@click=${this.#onFirstActionClick}>
<uui-icon name=${ifDefined(this._firstActionManifest?.meta.icon)}></uui-icon>
</uui-button>`;
}
}

View File

@@ -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<any> | Map<PropertyKey, unknown>): 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`<uui-input
type="number"
required
.value=${this._minValue}
@input=${this._onMinInput}
label=${this.minLabel}></uui-input>

View File

@@ -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<UmbPropertyTypeWorkspaceContext>
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',

View File

@@ -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<ContentTypeType extends UmbContentTypeModel = UmbContentTypeModel>
extends UmbSaveableWorkspaceContextInterface {
extends UmbSaveableWorkspaceContext {
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT: true;
readonly name: Observable<string | undefined>;

View File

@@ -3,13 +3,13 @@ import type { ManifestMenuItem } from '@umbraco-cms/backoffice/extension-registr
const menuItem: ManifestMenuItem = {
type: 'menuItem',
alias: 'Umb.MenuItem.Extensions',
name: 'Extensions Menu Item',
weight: 0,
name: 'Extension Insights Menu Item',
weight: 200,
meta: {
label: 'Extensions',
label: 'Extension Insights',
icon: 'icon-wand',
entityType: 'extension-root',
menus: ['Umb.Menu.Settings'],
menus: ['Umb.Menu.AdvancedSettings'],
},
};

View File

@@ -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<ElementType, ApiType> {
type: 'workspace';
meta: MetaType;

View File

@@ -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

View File

@@ -7,7 +7,10 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
export class UmbExtensionRootWorkspaceElement extends UmbLitElement {
render() {
return html`
<umb-workspace-editor headline="Extensions" alias=${UMB_EXTENSION_ROOT_WORKSPACE_ALIAS} .enforceNoFooter=${true}>
<umb-workspace-editor
headline="Extension Insights"
alias=${UMB_EXTENSION_ROOT_WORKSPACE_ALIAS}
.enforceNoFooter=${true}>
<umb-collection alias=${UMB_EXTENSION_COLLECTION_ALIAS}></umb-collection>
</umb-workspace-editor>
`;

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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');
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -21,21 +21,62 @@ export const manifests = [
},
{
type: 'menu',
alias: 'Umb.Menu.Settings',
alias: 'Umb.Menu.StructureSettings',
name: 'Settings Menu',
meta: {
label: 'Settings',
},
},
{
type: 'sectionSidebarApp',
kind: 'menu',
alias: 'Umb.SectionSidebarMenu.Settings',
name: 'Settings Section Sidebar Menu',
weight: 200,
name: 'Structure Settings Sidebar Menu',
weight: 300,
meta: {
label: 'Settings',
menu: 'Umb.Menu.Settings',
label: 'Structure',
menu: 'Umb.Menu.StructureSettings',
},
conditions: [
{
alias: 'Umb.Condition.SectionAlias',
match: UMB_SETTINGS_SECTION_ALIAS,
},
],
},
{
type: 'menu',
alias: 'Umb.Menu.AdvancedSettings',
name: 'Advanced Settings Menu',
},
{
type: 'sectionSidebarApp',
kind: 'menu',
alias: 'Umb.SectionSidebarMenu.AdvancedSettings',
name: 'Advanced Settings Sidebar Menu',
weight: 100,
meta: {
label: 'Advanced',
menu: 'Umb.Menu.AdvancedSettings',
},
conditions: [
{
alias: 'Umb.Condition.SectionAlias',
match: UMB_SETTINGS_SECTION_ALIAS,
},
],
},
{
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: [
{

View File

@@ -0,0 +1,2 @@
export * from './validation.context.js';
export * from './validation.context-token.js';

View File

@@ -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>('UmbValidationContext');

View File

@@ -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<UmbValidationContext> {
constructor(host: UmbControllerHost) {
super(host, UMB_VALIDATION_CONTEXT);
}
}

View File

@@ -0,0 +1,2 @@
export * from './validation-valid.event.js';
export * from './validation-invalid.event.js';

View File

@@ -0,0 +1,9 @@
import { UmbValidationEvent } from './validation.event.js';
export class UmbValidationInvalidEvent extends UmbValidationEvent {
static readonly TYPE = 'invalid';
public constructor() {
super(UmbValidationInvalidEvent.TYPE);
}
}

View File

@@ -0,0 +1,9 @@
import { UmbValidationEvent } from './validation.event.js';
export class UmbValidationValidEvent extends UmbValidationEvent {
static readonly TYPE = 'valid';
constructor() {
super(UmbValidationValidEvent.TYPE);
}
}

View File

@@ -0,0 +1,5 @@
export class UmbValidationEvent extends Event {
public constructor(type: string) {
super(type, { bubbles: true, composed: true, cancelable: false });
}
}

View File

@@ -0,0 +1,3 @@
export * from './events/index.js';
export * from './mixins/index.js';
export * from './context/index.js';

View File

@@ -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<ValueType, DefaultValueType> 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<ValueType, DefaultValueType>
extends LitElement
implements UmbFormControlMixinInterface<ValueType, DefaultValueType>
{
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<LitElement> = HTMLElementConstructor<LitElement>,
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<string | number | symbol, unknown>) {
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<ValueType, DefaultValueType>
> &
T;
};

View File

@@ -0,0 +1 @@
export * from './form-control.mixin.js';

View File

@@ -0,0 +1,15 @@
type Writeable<T> = { -readonly [P in keyof T]: T[P] };
export class UmbValidityState implements Writeable<ValidityState> {
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;
}

View File

@@ -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 {
<uui-button slot="append" compact id="variant-close" @click=${this.#closeSplitView}>
<uui-icon name="remove"></uui-icon>
</uui-button>
`
`
: ''}
`
`
: nothing
}
</uui-input>
@@ -264,7 +264,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
@click=${() => this.#openSplitView(variant)}>
Split view
</uui-button>
`}
`}
</li>
`,
)}
@@ -272,7 +272,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
</uui-scroll-container>
</div>
</uui-popover-container>
`
`
: nothing
}
</div>

View File

@@ -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<UmbSaveableWorkspaceContextInterface> {
constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs<UmbSaveableWorkspaceContextInterface>) {
export class UmbSaveWorkspaceAction extends UmbWorkspaceActionBase<UmbSaveableWorkspaceContext> {
constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs<UmbSaveableWorkspaceContext>) {
super(host, args);
// TODO: Could we make change label depending on the state?

View File

@@ -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();
});

View File

@@ -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';

View File

@@ -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<WorkspaceAliasConditionConfig>) {
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) {

View File

@@ -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<WorkspaceHasCollectionConditionConfig>) {
super(host, args);
this.consumeContext(UMB_WORKSPACE_COLLECTION_CONTEXT, (context) => {
this.consumeContext(UMB_COLLECTION_WORKSPACE_CONTEXT, (context) => {
this.observe(
context.contentTypeHasCollection,
(hasCollection) => {

View File

@@ -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<UmbDefaultWorkspaceContext>
implements UmbWorkspaceContextInterface
implements UmbWorkspaceContext
{
public workspaceAlias!: string;
#entityType!: string;

View File

@@ -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';

View File

@@ -1,7 +0,0 @@
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
export interface UmbPublishableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface {
saveAndPublish(): Promise<void>;
publish(): Promise<void>;
unpublish(): Promise<void>;
}

View File

@@ -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,
);

View File

@@ -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;
}

View File

@@ -1,5 +1,6 @@
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';
@@ -7,18 +8,22 @@ 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<WorkspaceDataModelType>
extends UmbContextBase<UmbEditableWorkspaceContextBase<WorkspaceDataModelType>>
implements UmbSaveableWorkspaceContextInterface
export abstract class UmbSaveableWorkspaceContextBase<WorkspaceDataModelType>
extends UmbContextBase<UmbSaveableWorkspaceContextBase<WorkspaceDataModelType>>
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<string | null | undefined>;
#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.
@@ -63,3 +68,11 @@ export abstract class UmbEditableWorkspaceContextBase<WorkspaceDataModelType>
abstract save(): Promise<void>;
abstract unique: Observable<string | undefined>;
}
/*
* @deprecated Use UmbSaveableWorkspaceContextBase instead Will be removed before RC.
* TODO: Delete before RC.
*/
export abstract class UmbEditableWorkspaceContextBase<
WorkspaceDataModelType,
> extends UmbSaveableWorkspaceContextBase<WorkspaceDataModelType> {}

View File

@@ -1,11 +0,0 @@
import type { UmbWorkspaceContextInterface } from './workspace-context.interface.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
export interface UmbSaveableWorkspaceContextInterface extends UmbWorkspaceContextInterface {
unique: Observable<string | undefined>;
isNew: Observable<boolean | undefined>;
getIsNew(): boolean | undefined;
save(): Promise<void>;
setValidationErrors?(errorMap: any): void; // TODO: temp solution to bubble validation errors to the UI
destroy(): void;
}

View File

@@ -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,
);

View File

@@ -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<T extends UmbContentTypeModel>
extends UmbWorkspaceContextInterface {
export interface UmbCollectionWorkspaceContext<T extends UmbContentTypeModel> extends UmbEntityWorkspaceContext {
contentTypeHasCollection: Observable<boolean>;
getCollectionAlias(): string;
structure: UmbContentTypeStructureManager<T>;
}
/**
* @deprecated Use UmbCollectionWorkspaceContextInterface instead Will be removed before RC.
* TODO: Delete before RC.
*/
export interface UmbWorkspaceCollectionContextInterface<T extends UmbContentTypeModel>
extends UmbCollectionWorkspaceContext<T> {}

View File

@@ -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<UmbContentTypeModel>
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbCollectionWorkspaceContext<UmbContentTypeModel> =>
(context as UmbCollectionWorkspaceContext<UmbContentTypeModel>).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;

View File

@@ -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<UmbWorkspaceUniqueType | undefined>;
getUnique(): UmbWorkspaceUniqueType | undefined;
}

View File

@@ -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, UmbEntityWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbEntityWorkspaceContext => (context as any).getUnique !== undefined,
);

View File

@@ -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';

View File

@@ -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<string | undefined>;
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 {}

View File

@@ -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<Observable<ValueModelBaseModel | undefined>>;
}

View File

@@ -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,
);

View File

@@ -0,0 +1,7 @@
import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js';
export interface UmbPublishableWorkspaceContext extends UmbSaveableWorkspaceContext {
saveAndPublish(): Promise<void>;
publish(): Promise<void>;
unpublish(): Promise<void>;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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, UmbRoutableWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbRoutableWorkspaceContext => 'routes' in context,
);

View File

@@ -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<boolean | undefined>;
getIsNew(): boolean | undefined;
save(): Promise<void>;
destroy(): void;
}
/**
* @deprecated Use `UmbSaveableWorkspaceContext` instead. This token will be removed in the RC version.
* TODO: Remove in RC
*/
export interface UmbSaveableWorkspaceContextInterface extends UmbSaveableWorkspaceContext {}

View File

@@ -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, UmbSaveableWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbSaveableWorkspaceContext => 'getIsNew' in context,
);

View File

@@ -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<VariantType extends UmbVariantModel = UmbVariantModel>
extends UmbSaveableWorkspaceContextInterface {
export interface UmbVariantDatasetWorkspaceContext<VariantType extends UmbVariantModel = UmbVariantModel>
extends UmbSaveableWorkspaceContext {
// Name:
getName(variantId?: UmbVariantId): string | undefined;
setName(name: string, variantId?: UmbVariantId): void;
@@ -19,14 +19,19 @@ export interface UmbVariantableWorkspaceContextInterface<VariantType extends Umb
getVariant(variantId: UmbVariantId): UmbVariantModel | undefined;
// Property:
// This one is async cause it needs to structure to provide this data:
// This one is async cause it needs to structure to provide this data: [NL]
propertyValueByAlias<ReturnValue = unknown>(
alias: string,
variantId?: UmbVariantId,
): Promise<Observable<ReturnValue | undefined> | undefined>;
getPropertyValue<ReturnValue = unknown>(alias: string, variantId?: UmbVariantId): ReturnValue | undefined;
setPropertyValue(alias: string, value: unknown, variantId?: UmbVariantId): Promise<void>;
//propertyDataByAlias(alias: string, variantId?: UmbVariantId): Observable<ValueModelBaseModel | undefined>;
createPropertyDatasetContext(host: UmbControllerHost, variantId?: UmbVariantId): UmbPropertyDatasetContext;
}
/**
* @deprecated Use UmbVariantWorkspaceContextInterface instead Will be removed before RC.
* TODO: Delete before RC.
*/
export interface UmbVariantableWorkspaceContextInterface extends UmbVariantDatasetWorkspaceContext {}

View File

@@ -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);

View File

@@ -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 {}

View File

@@ -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<UmbWorkspaceContextInterface>('UmbWorkspaceContext');
export const UMB_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext>('UmbWorkspaceContext');

View File

@@ -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,
);

View File

@@ -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<UmbContentTypeModel>
>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbWorkspaceCollectionContextInterface<UmbContentTypeModel> =>
(context as UmbWorkspaceCollectionContextInterface<UmbContentTypeModel>).contentTypeHasCollection !== undefined,
);

View File

@@ -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;
}

View File

@@ -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<unknown>,
workspaceContext: UmbSaveableWorkspaceContextBase<unknown>,
router: UmbRouterSlotElement,
) {
super(host, 'isNewRedirectController');

View File

@@ -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';

View File

@@ -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`<uui-form>
<form>
<umb-router-slot .routes="${this._routes}"></umb-router-slot>
</form>
</uui-form>`;
}
}
export default UmbEditableWorkspaceElement;
declare global {
interface HTMLElementTagNameMap {
'umb-editable-workspace': UmbEditableWorkspaceElement;
}
}

View File

@@ -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'),
},
};

View File

@@ -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`<umb-editable-workspace></umb-editable-workspace>`);
});
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);
});
}
});

View File

@@ -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];

View File

@@ -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';

View File

@@ -0,0 +1 @@
export type UmbWorkspaceUniqueType = string | null;

View File

@@ -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<UmbPropertyDatasetContext>
implements UmbPropertyDatasetContext, UmbNameablePropertyDatasetContext

View File

@@ -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<UmbDataTypeItemModel> {
#host: UmbControllerHost;
#manifestPropertyEditorUis: Array<ManifestPropertyEditorUi> = [];
/**
* 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;

View File

@@ -78,8 +78,6 @@ export class UmbInputDataTypeElement extends FormControlMixin(UmbLitElement) {
this.#editDataTypeModal?.open({}, 'edit/' + this._ids![0]);
}}
standalone>
<!-- TODO: Get the icon from property editor UI -->
<uui-icon name="${'document'}" slot="icon"></uui-icon>
<uui-action-bar slot="actions">
<uui-button label="Change" .href=${this._createRoute}></uui-button>
</uui-action-bar>

View File

@@ -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 =
'<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M142.212 397.267l106.052-48.024L398.479 199.03l-26.405-26.442-90.519 90.517-15.843-15.891 90.484-90.486-16.204-16.217-150.246 150.243-47.534 106.513zm74.904-100.739l23.285-23.283 3.353 22.221 22.008 3.124-23.283 23.313-46.176 20.991 20.813-46.366zm257.6-173.71L416.188 64.3l-49.755 49.785 58.504 58.503 49.779-49.77zM357.357 300.227h82.826v116.445H68.929V300.227h88.719v-30.648H38.288v177.733h432.537V269.578H357.357v30.649z"></path></svg>';
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor"><path d="M255.981 440.734c-4.422 0-8.895-.159-13.293-.471l1.682-23.62c15.395 1.095 31.076-.014 46.053-3.277l5.039 23.137a185.943 185.943 0 0 1-39.481 4.231zm-43.253-5.094a183.61 183.61 0 0 1-49.174-19.657l11.864-20.49a159.927 159.927 0 0 0 42.833 17.123l-5.523 23.024zm111.734-8.02l-8.781-21.991a160.553 160.553 0 0 0 39.949-23.097l14.666 18.593a184.376 184.376 0 0 1-45.834 26.495zm-185.815-28.926a185.575 185.575 0 0 1-35.652-39.114l19.596-13.293a161.956 161.956 0 0 0 31.105 34.125l-15.049 18.282zm253.834-18.216l-17.492-15.96a161.321 161.321 0 0 0 25.924-38.192l21.297 10.353a184.986 184.986 0 0 1-29.729 43.799zM88.097 333.183a183.381 183.381 0 0 1-14.977-50.791l23.438-3.355a159.869 159.869 0 0 0 13.047 44.243l-21.508 9.903zm345.082-24.798l-22.711-6.705c4.355-14.761 6.566-30.131 6.566-45.679h23.678c0 17.818-2.533 35.444-7.533 52.384zM94.96 252.634l-23.672-.483c.365-17.809 3.266-35.378 8.625-52.224l22.566 7.181c-4.671 14.677-7.203 29.996-7.519 45.526zm320.881-16.346a159.854 159.854 0 0 0-12.115-44.503l21.713-9.45a183.696 183.696 0 0 1 13.908 51.088l-23.506 2.865zM112.546 182.67l-21.072-10.798a184.915 184.915 0 0 1 30.633-43.168l17.154 16.319a161.599 161.599 0 0 0-26.715 37.647zm278.68-14.155a161.801 161.801 0 0 0-30.389-34.763l15.426-17.966a185.512 185.512 0 0 1 34.832 39.846l-19.869 12.883zm-232.239-41.101l-14.273-18.894a184.318 184.318 0 0 1 46.375-25.533l8.322 22.169a160.705 160.705 0 0 0-40.424 22.258zm180.444-9.19a160.053 160.053 0 0 0-42.466-18.02l6.009-22.903a183.633 183.633 0 0 1 48.748 20.684l-12.291 20.239zM224.825 97.956l-4.553-23.239a186.147 186.147 0 0 1 35.705-3.45h.004c5.711 0 11.473.266 17.129.786l-2.174 23.58c-15.306-1.414-31.072-.624-46.111 2.323z"/></svg>`;
//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[] = [];

View File

@@ -86,7 +86,7 @@ export class UmbDataTypePickerFlowDataTypePickerModalElement extends UmbModalBas
? html` <li class="item">
<uui-button label="dataType.name" type="button" @click="${() => this._handleClick(dataType)}">
<div class="item-content">
<uui-icon name="${'icon-bug'}" class="icon"></uui-icon>
<umb-icon name=${dataType.icon ?? 'icon-circle-dotted'} class="icon"></umb-icon>
${dataType.name}
</div>
</uui-button>

View File

@@ -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`<li class="item" ?selected=${this.value.selection.includes(dataType.unique)}>
html` <li class="item" ?selected=${this.value.selection.includes(dataType.unique)}>
<uui-button .label=${dataType.name} type="button" @click="${() => this._handleDataTypeClick(dataType)}">
<div class="item-content">
<uui-icon name="${'icon-bug'}" class="icon"></uui-icon>
<umb-icon name=${dataType.icon ?? 'icon-circle-dotted'} class="icon"></umb-icon>
${dataType.name}
</div>
</uui-button>

View File

@@ -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<ManifestPropertyEditorUi> = [];
/**
* 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,
};
};

View File

@@ -2,4 +2,5 @@ export interface UmbDataTypeItemModel {
unique: string;
name: string;
propertyEditorUiAlias: string;
icon?: string;
}

View File

@@ -8,6 +8,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<ManifestPropertyEditorUi> = [];
/**
* A data source for a data type tree that fetches data from the server
@@ -31,12 +34,19 @@ export class UmbDataTypeTreeServerDataSource extends UmbTreeServerDataSourceBase
getAncestorsOf,
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) {
@@ -59,6 +69,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,

View File

@@ -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();
});

View File

@@ -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, UmbDataTypeWorkspaceContext>(
'UmbWorkspaceContext',
undefined,
(context): context is UmbDataTypeWorkspaceContext => context.getEntityType?.() === 'data-type',

View File

@@ -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<EntityType>
implements UmbInvariantableWorkspaceContextInterface, UmbRoutableWorkspaceContext
extends UmbSaveableWorkspaceContextBase<EntityType>
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
{
//
public readonly repository: UmbDataTypeDetailRepository = new UmbDataTypeDetailRepository(this);
@@ -348,6 +348,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);
}

View File

@@ -8,9 +8,6 @@ export const manifests = [
type: 'menu',
alias: UMB_DICTIONARY_MENU_ALIAS,
name: 'Dictionary Menu',
meta: {
label: 'Dictionary',
},
},
{
type: 'menuItem',

View File

@@ -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',

View File

@@ -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<UmbDictionaryDetailModel>
implements UmbSaveableWorkspaceContextInterface, UmbRoutableWorkspaceContext
extends UmbSaveableWorkspaceContextBase<UmbDictionaryDetailModel>
implements UmbSaveableWorkspaceContext, UmbRoutableWorkspaceContext
{
//
public readonly detailRepository = new UmbDictionaryDetailRepository(this);
@@ -26,6 +26,7 @@ export class UmbDictionaryWorkspaceContext
#data = new UmbObjectState<UmbDictionaryDetailModel | undefined>(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);

View File

@@ -9,7 +9,7 @@ const menuItem: ManifestMenuItem = {
label: 'Document Blueprints',
icon: 'icon-blueprint',
entityType: 'document-blueprint-root',
menus: ['Umb.Menu.Settings'],
menus: ['Umb.Menu.StructureSettings'],
},
};

View File

@@ -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',

View File

@@ -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<EntityType>
extends UmbSaveableWorkspaceContextBase<EntityType>
implements UmbContentTypeWorkspaceContext<EntityType>, UmbRoutableWorkspaceContext
{
readonly IS_CONTENT_TYPE_WORKSPACE_CONTEXT = true;

View File

@@ -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
<div slot="description">Allow editors to create content of different languages.</div>
<div slot="editor">
<uui-toggle
.checked=${this._variesByCulture}
?checked=${this._variesByCulture}
@change=${(e: CustomEvent) => {
this.#workspaceContext?.setVariesByCulture((e.target as UUIToggleElement).checked);
}}
@@ -58,7 +57,7 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
<div slot="description">Allow editors to segment their content.</div>
<div slot="editor">
<uui-toggle
.checked=${this._variesBySegment}
?checked=${this._variesBySegment}
@change=${(e: CustomEvent) => {
this.#workspaceContext?.setVariesBySegment((e.target as UUIToggleElement).checked);
}}
@@ -71,7 +70,7 @@ export class UmbDocumentTypeWorkspaceViewSettingsElement extends UmbLitElement i
</div>
<div slot="editor">
<uui-toggle
.checked=${this._isElement}
?checked=${this._isElement}
@change=${(e: CustomEvent) => {
this.#workspaceContext?.setIsElement((e.target as UUIToggleElement).checked);
}}

View File

@@ -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();
});

View File

@@ -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 <umb-input-template> 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();
});

View File

@@ -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',

Some files were not shown because too many files have changed in this diff Show More