Saveable workspace interface + token (#19220)
Co-authored-by: Mads Rasmussen <madsr@hey.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
UmbWorkspaceSplitViewManager,
|
UmbWorkspaceSplitViewManager,
|
||||||
type UmbEntityDetailWorkspaceContextArgs,
|
type UmbEntityDetailWorkspaceContextArgs,
|
||||||
type UmbEntityDetailWorkspaceContextCreateArgs,
|
type UmbEntityDetailWorkspaceContextCreateArgs,
|
||||||
|
type UmbSaveableWorkspaceContext,
|
||||||
} from '@umbraco-cms/backoffice/workspace';
|
} from '@umbraco-cms/backoffice/workspace';
|
||||||
import {
|
import {
|
||||||
UmbContentTypeStructureManager,
|
UmbContentTypeStructureManager,
|
||||||
@@ -97,7 +98,9 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType> = UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType>,
|
UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType> = UmbEntityDetailWorkspaceContextCreateArgs<DetailModelType>,
|
||||||
>
|
>
|
||||||
extends UmbEntityDetailWorkspaceContextBase<DetailModelType, DetailRepositoryType, CreateArgsType>
|
extends UmbEntityDetailWorkspaceContextBase<DetailModelType, DetailRepositoryType, CreateArgsType>
|
||||||
implements UmbContentWorkspaceContext<DetailModelType, ContentTypeDetailModelType, VariantModelType>
|
implements
|
||||||
|
UmbContentWorkspaceContext<DetailModelType, ContentTypeDetailModelType, VariantModelType>,
|
||||||
|
UmbSaveableWorkspaceContext
|
||||||
{
|
{
|
||||||
public readonly IS_CONTENT_WORKSPACE_CONTEXT = true as const;
|
public readonly IS_CONTENT_WORKSPACE_CONTEXT = true as const;
|
||||||
|
|
||||||
@@ -778,6 +781,14 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
return this._handleSubmit();
|
return this._handleSubmit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a save of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be saved.
|
||||||
|
* @returns {Promise<void>} a promise which resolves once it has been completed.
|
||||||
|
*/
|
||||||
|
public requestSave() {
|
||||||
|
return this._handleSave();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the data to save
|
* Get the data to save
|
||||||
* @param {Array<UmbVariantId>} variantIds - The variant ids to save
|
* @param {Array<UmbVariantId>} variantIds - The variant ids to save
|
||||||
@@ -789,6 +800,10 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async _handleSubmit() {
|
protected async _handleSubmit() {
|
||||||
|
await this._handleSave();
|
||||||
|
this._closeModal();
|
||||||
|
}
|
||||||
|
protected async _handleSave() {
|
||||||
const data = this.getData();
|
const data = this.getData();
|
||||||
if (!data) {
|
if (!data) {
|
||||||
throw new Error('Data is missing');
|
throw new Error('Data is missing');
|
||||||
@@ -818,8 +833,8 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
|
|
||||||
variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? [];
|
variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? [];
|
||||||
} else {
|
} else {
|
||||||
/* If there are multiple variants but no modal token is set
|
/* If there are multiple variants but no modal token is set
|
||||||
we will save the variants that would have been preselected in the modal.
|
we will save the variants that would have been preselected in the modal.
|
||||||
These are based on the variants that have been edited */
|
These are based on the variants that have been edited */
|
||||||
variantIds = selected.map((x) => UmbVariantId.FromString(x));
|
variantIds = selected.map((x) => UmbVariantId.FromString(x));
|
||||||
}
|
}
|
||||||
@@ -829,18 +844,13 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
await this.runMandatoryValidationForSaveData(saveData, variantIds);
|
await this.runMandatoryValidationForSaveData(saveData, variantIds);
|
||||||
if (this.#validateOnSubmit) {
|
if (this.#validateOnSubmit) {
|
||||||
await this.askServerToValidate(saveData, variantIds);
|
await this.askServerToValidate(saveData, variantIds);
|
||||||
return this.validateAndSubmit(
|
const valid = await this._validateAndLog().then(
|
||||||
async () => {
|
() => true,
|
||||||
return this.performCreateOrUpdate(variantIds, saveData);
|
() => false,
|
||||||
},
|
|
||||||
async (reason?: any) => {
|
|
||||||
if (this.#ignoreValidationResultOnSubmit) {
|
|
||||||
return this.performCreateOrUpdate(variantIds, saveData);
|
|
||||||
} else {
|
|
||||||
return this.invalidSubmit(reason);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
if (valid || this.#ignoreValidationResultOnSubmit) {
|
||||||
|
return this.performCreateOrUpdate(variantIds, saveData);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.performCreateOrUpdate(variantIds, saveData);
|
await this.performCreateOrUpdate(variantIds, saveData);
|
||||||
}
|
}
|
||||||
@@ -915,8 +925,6 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
});
|
});
|
||||||
eventContext.dispatchEvent(event);
|
eventContext.dispatchEvent(event);
|
||||||
this.setIsNew(false);
|
this.setIsNew(false);
|
||||||
|
|
||||||
this._closeModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async #update(variantIds: Array<UmbVariantId>, saveData: DetailModelType) {
|
async #update(variantIds: Array<UmbVariantId>, saveData: DetailModelType) {
|
||||||
@@ -966,8 +974,6 @@ export abstract class UmbContentDetailWorkspaceContextBase<
|
|||||||
});
|
});
|
||||||
|
|
||||||
eventContext.dispatchEvent(updatedEvent);
|
eventContext.dispatchEvent(updatedEvent);
|
||||||
|
|
||||||
this._closeModal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override resetState() {
|
override resetState() {
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
export * from './save/index.js';
|
||||||
export * from './submit/index.js';
|
export * from './submit/index.js';
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './save.action.js';
|
||||||
|
export type * from './types.js';
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import type { MetaWorkspaceAction } from '../../../../types.js';
|
||||||
|
import { UMB_SAVEABLE_WORKSPACE_CONTEXT } from '../../../../contexts/tokens/index.js';
|
||||||
|
import type { UmbSaveableWorkspaceContext } from '../../../../contexts/tokens/index.js';
|
||||||
|
import { UmbWorkspaceActionBase } from '../../workspace-action-base.controller.js';
|
||||||
|
import type { UmbSaveWorkspaceActionArgs } from './types.js';
|
||||||
|
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||||
|
|
||||||
|
export class UmbSaveWorkspaceAction<
|
||||||
|
ArgsMetaType extends MetaWorkspaceAction = MetaWorkspaceAction,
|
||||||
|
WorkspaceContextType extends UmbSaveableWorkspaceContext = UmbSaveableWorkspaceContext,
|
||||||
|
> extends UmbWorkspaceActionBase<ArgsMetaType> {
|
||||||
|
protected _retrieveWorkspaceContext: Promise<unknown>;
|
||||||
|
protected _workspaceContext?: WorkspaceContextType;
|
||||||
|
|
||||||
|
constructor(host: UmbControllerHost, args: UmbSaveWorkspaceActionArgs<ArgsMetaType, WorkspaceContextType>) {
|
||||||
|
super(host, args);
|
||||||
|
|
||||||
|
this._retrieveWorkspaceContext = this.consumeContext(
|
||||||
|
args.workspaceContextToken ?? UMB_SAVEABLE_WORKSPACE_CONTEXT,
|
||||||
|
(context) => {
|
||||||
|
this._workspaceContext = context as WorkspaceContextType | undefined;
|
||||||
|
this.#observeUnique();
|
||||||
|
this._gotWorkspaceContext();
|
||||||
|
},
|
||||||
|
).asPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
#observeUnique() {
|
||||||
|
this.observe(
|
||||||
|
this._workspaceContext?.unique,
|
||||||
|
(unique) => {
|
||||||
|
// We can't save if we don't have a unique
|
||||||
|
if (unique === undefined) {
|
||||||
|
this.disable();
|
||||||
|
} else {
|
||||||
|
// Dangerous, cause this could enable despite a class extension decided to disable it?. [NL]
|
||||||
|
this.enable();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'saveWorkspaceActionUniqueObserver',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _gotWorkspaceContext() {
|
||||||
|
// Override in subclass
|
||||||
|
}
|
||||||
|
|
||||||
|
override async execute() {
|
||||||
|
await this._retrieveWorkspaceContext;
|
||||||
|
return await this._workspaceContext?.requestSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { UmbSaveableWorkspaceContext, UmbWorkspaceContext } from '../../../../contexts/index.js';
|
||||||
|
import type { UmbWorkspaceActionArgs } from '../../types.js';
|
||||||
|
import type { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||||
|
|
||||||
|
export interface UmbSaveWorkspaceActionArgs<MetaArgsType, WorkspaceContextType extends UmbSaveableWorkspaceContext>
|
||||||
|
extends UmbWorkspaceActionArgs<MetaArgsType> {
|
||||||
|
workspaceContextToken?: string | UmbContextToken<UmbWorkspaceContext, WorkspaceContextType>;
|
||||||
|
}
|
||||||
@@ -51,8 +51,3 @@ export class UmbSubmitWorkspaceAction<
|
|||||||
return await this._workspaceContext!.requestSubmit();
|
return await this._workspaceContext!.requestSubmit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* @deprecated Use UmbSubmitWorkspaceAction instead
|
|
||||||
*/
|
|
||||||
export { UmbSubmitWorkspaceAction as UmbSaveWorkspaceAction };
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ export * from './entity-workspace.context-token.js';
|
|||||||
export * from './publishable-workspace.context-token.js';
|
export * from './publishable-workspace.context-token.js';
|
||||||
export * from './routable-workspace.context-token.js';
|
export * from './routable-workspace.context-token.js';
|
||||||
export * from './submittable-workspace.context-token.js';
|
export * from './submittable-workspace.context-token.js';
|
||||||
|
export * from './saveable-workspace.context-token.js';
|
||||||
export * from './variant-workspace.context-token.js';
|
export * from './variant-workspace.context-token.js';
|
||||||
export type * from './entity-workspace-context.interface.js';
|
export type * from './entity-workspace-context.interface.js';
|
||||||
export type * from './invariant-dataset-workspace-context.interface.js';
|
export type * from './invariant-dataset-workspace-context.interface.js';
|
||||||
export type * from './publishable-workspace-context.interface.js';
|
export type * from './publishable-workspace-context.interface.js';
|
||||||
export type * from './routable-workspace-context.interface.js';
|
export type * from './routable-workspace-context.interface.js';
|
||||||
export type * from './submittable-workspace-context.interface.js';
|
export type * from './submittable-workspace-context.interface.js';
|
||||||
|
export type * from './saveable-workspace-context.interface.js';
|
||||||
export type * from './variant-dataset-workspace-context.interface.js';
|
export type * from './variant-dataset-workspace-context.interface.js';
|
||||||
export type * from '../../workspace-context.interface.js';
|
export type * from '../../workspace-context.interface.js';
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import type { UmbSubmittableWorkspaceContext } from './submittable-workspace-context.interface.js';
|
||||||
|
|
||||||
|
export interface UmbSaveableWorkspaceContext extends UmbSubmittableWorkspaceContext {
|
||||||
|
requestSave(): Promise<void>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { UmbWorkspaceContext } from '../../types.js';
|
||||||
|
import type { UmbSaveableWorkspaceContext } from './saveable-workspace-context.interface.js';
|
||||||
|
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||||
|
|
||||||
|
export const UMB_SAVEABLE_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbSaveableWorkspaceContext>(
|
||||||
|
'UmbWorkspaceContext',
|
||||||
|
undefined,
|
||||||
|
(context): context is UmbSaveableWorkspaceContext => 'requestSave' in context,
|
||||||
|
);
|
||||||
@@ -84,6 +84,17 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async _validateAndLog(): Promise<void> {
|
||||||
|
await this.validate().catch(async () => {
|
||||||
|
// TODO: Implement developer-mode logging here. [NL]
|
||||||
|
console.warn(
|
||||||
|
'Validation failed because of these validation messages still begin present: ',
|
||||||
|
this.#validationContexts.flatMap((x) => x.messages.getMessages()),
|
||||||
|
);
|
||||||
|
return Promise.reject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async validateAndSubmit(
|
public async validateAndSubmit(
|
||||||
onValid: () => Promise<void>,
|
onValid: () => Promise<void>,
|
||||||
onInvalid: (reason?: any) => Promise<void>,
|
onInvalid: (reason?: any) => Promise<void>,
|
||||||
@@ -95,16 +106,11 @@ export abstract class UmbSubmittableWorkspaceContextBase<WorkspaceDataModelType>
|
|||||||
this.#submitResolve = resolve;
|
this.#submitResolve = resolve;
|
||||||
this.#submitReject = reject;
|
this.#submitReject = reject;
|
||||||
});
|
});
|
||||||
this.validate().then(
|
this._validateAndLog().then(
|
||||||
async () => {
|
async () => {
|
||||||
onValid().then(this.#completeSubmit, this.#rejectSubmit);
|
onValid().then(this.#completeSubmit, this.#rejectSubmit);
|
||||||
},
|
},
|
||||||
async (error) => {
|
async (error) => {
|
||||||
// TODO: Implement developer-mode logging here. [NL]
|
|
||||||
console.warn(
|
|
||||||
'Validation failed because of these validation messages still begin present: ',
|
|
||||||
this.#validationContexts.flatMap((x) => x.messages.getMessages()),
|
|
||||||
);
|
|
||||||
onInvalid(error).then(this.#resolveSubmit, this.#rejectSubmit);
|
onInvalid(error).then(this.#resolveSubmit, this.#rejectSubmit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,17 +3,20 @@ import type UmbDocumentWorkspaceContext from '../document-workspace.context.js';
|
|||||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||||
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
|
||||||
import {
|
import {
|
||||||
UmbSubmitWorkspaceAction,
|
UmbSaveWorkspaceAction,
|
||||||
type MetaWorkspaceAction,
|
type MetaWorkspaceAction,
|
||||||
type UmbSubmitWorkspaceActionArgs,
|
type UmbSaveWorkspaceActionArgs,
|
||||||
type UmbWorkspaceActionDefaultKind,
|
type UmbWorkspaceActionDefaultKind,
|
||||||
} from '@umbraco-cms/backoffice/workspace';
|
} from '@umbraco-cms/backoffice/workspace';
|
||||||
|
|
||||||
export class UmbDocumentSaveWorkspaceAction
|
export class UmbDocumentSaveWorkspaceAction
|
||||||
extends UmbSubmitWorkspaceAction<MetaWorkspaceAction, UmbDocumentWorkspaceContext>
|
extends UmbSaveWorkspaceAction<MetaWorkspaceAction, UmbDocumentWorkspaceContext>
|
||||||
implements UmbWorkspaceActionDefaultKind<MetaWorkspaceAction>
|
implements UmbWorkspaceActionDefaultKind<MetaWorkspaceAction>
|
||||||
{
|
{
|
||||||
constructor(host: UmbControllerHost, args: UmbSubmitWorkspaceActionArgs<MetaWorkspaceAction>) {
|
constructor(
|
||||||
|
host: UmbControllerHost,
|
||||||
|
args: UmbSaveWorkspaceActionArgs<MetaWorkspaceAction, UmbDocumentWorkspaceContext>,
|
||||||
|
) {
|
||||||
super(host, { workspaceContextToken: UMB_DOCUMENT_WORKSPACE_CONTEXT, ...args });
|
super(host, { workspaceContextToken: UMB_DOCUMENT_WORKSPACE_CONTEXT, ...args });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
|
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
|
||||||
import type { UmbDocumentWorkspaceContext } from './document-workspace.context.js';
|
import type { UmbDocumentWorkspaceContext } from './document-workspace.context.js';
|
||||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||||
import type { UmbSubmittableWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
import type { UmbWorkspaceContext } from '@umbraco-cms/backoffice/workspace';
|
||||||
|
|
||||||
export const UMB_DOCUMENT_WORKSPACE_CONTEXT = new UmbContextToken<
|
export const UMB_DOCUMENT_WORKSPACE_CONTEXT = new UmbContextToken<UmbWorkspaceContext, UmbDocumentWorkspaceContext>(
|
||||||
UmbSubmittableWorkspaceContext,
|
|
||||||
UmbDocumentWorkspaceContext
|
|
||||||
>(
|
|
||||||
'UmbWorkspaceContext',
|
'UmbWorkspaceContext',
|
||||||
undefined,
|
undefined,
|
||||||
(context): context is UmbDocumentWorkspaceContext => context.getEntityType?.() === UMB_DOCUMENT_ENTITY_TYPE,
|
(context): context is UmbDocumentWorkspaceContext => context.getEntityType?.() === UMB_DOCUMENT_ENTITY_TYPE,
|
||||||
|
|||||||
@@ -341,17 +341,13 @@ export class UmbDocumentWorkspaceContext
|
|||||||
this._data.updateCurrent({ template: { unique: templateUnique } });
|
this._data.updateCurrent({ template: { unique: templateUnique } });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected override async _handleSave() {
|
||||||
* Request a submit of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be submitted.
|
|
||||||
* @returns {Promise<void>} a promise which resolves once it has been completed.
|
|
||||||
*/
|
|
||||||
public override requestSubmit() {
|
|
||||||
const elementStyle = (this.getHostElement() as HTMLElement).style;
|
const elementStyle = (this.getHostElement() as HTMLElement).style;
|
||||||
elementStyle.setProperty('--uui-color-invalid', 'var(--uui-color-warning)');
|
elementStyle.setProperty('--uui-color-invalid', 'var(--uui-color-warning)');
|
||||||
elementStyle.setProperty('--uui-color-invalid-emphasis', 'var(--uui-color-warning-emphasis)');
|
elementStyle.setProperty('--uui-color-invalid-emphasis', 'var(--uui-color-warning-emphasis)');
|
||||||
elementStyle.setProperty('--uui-color-invalid-standalone', 'var(--uui-color-warning-standalone)');
|
elementStyle.setProperty('--uui-color-invalid-standalone', 'var(--uui-color-warning-standalone)');
|
||||||
elementStyle.setProperty('--uui-color-invalid-contrast', 'var(--uui-color-warning-contrast)');
|
elementStyle.setProperty('--uui-color-invalid-contrast', 'var(--uui-color-warning-contrast)');
|
||||||
return this._handleSubmit();
|
await super._handleSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async saveAndPreview(): Promise<void> {
|
public async saveAndPreview(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user