Block Element Manager

This commit is contained in:
Niels Lyngsø
2024-01-16 08:49:08 +01:00
parent 4cfc635d49
commit ea5d8347c3
9 changed files with 207 additions and 36 deletions

View File

@@ -6,6 +6,7 @@ export interface UmbBlockLayoutBaseModel {
export interface UmbBlockDataType {
udi: string;
contentTypeKey: string;
[key: string]: unknown;
}
export interface UmbBlockValueType<BlockLayoutType extends UmbBlockLayoutBaseModel> {

View File

@@ -0,0 +1,81 @@
import { UmbBlockDataType } from '../types.js';
import { UmbBlockElementPropertyDatasetContext } from './block-element-property-dataset.context.js';
import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type';
import { UmbObjectState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type';
export class UmbBlockElementManager extends UmbBaseController {
//
#data = new UmbObjectState<UmbBlockDataType | undefined>(undefined);
#getDataPromise = new Promise<void>((resolve) => {
this.#getDataResolver = resolve;
});
#getDataResolver!: () => void;
readonly unique = this.#data.asObservablePart((data) => data?.udi);
readonly contentTypeId = this.#data.asObservablePart((data) => data?.contentTypeKey);
readonly structure;
constructor(host: UmbControllerHost) {
// TODO: Get Workspace Alias via Manifest.
super(host);
this.structure = new UmbContentTypePropertyStructureManager(this, new UmbDocumentTypeDetailRepository(this));
new UmbObserverController(this, this.contentTypeId, (id) => this.structure.loadType(id));
}
setData(data: UmbBlockDataType) {
this.#data.next(data);
this.#getDataResolver();
}
getData() {
return this.#data.getValue();
}
getEntityId() {
return this.getData()?.udi;
}
getEntityType() {
return 'element';
}
getContentTypeId() {
return this.getData()?.contentTypeKey;
}
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
await this.#getDataPromise;
return this.#data.asObservablePart((data) => data?.[propertyAlias] as ReturnType);
}
async getPropertyValue<ReturnType = unknown>(propertyAlias: string) {
await this.#getDataPromise;
return this.#data.getValue()?.[propertyAlias] as ReturnType;
}
async setPropertyValue(alias: string, value: unknown) {
await this.#getDataPromise;
this.#data.update({ [alias]: value });
}
public createPropertyDatasetContext(host: UmbControllerHost) {
return new UmbBlockElementPropertyDatasetContext(host, this);
}
public destroy(): void {
this.#data.destroy();
this.structure.destroy();
super.destroy();
}
}
export default UmbBlockElementManager;

View File

@@ -0,0 +1,6 @@
import { UmbBlockElementPropertyDatasetContext } from './block-element-property-dataset.context.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT = new UmbContextToken<UmbBlockElementPropertyDatasetContext>(
'UmbPropertyDatasetContext',
);

View File

@@ -0,0 +1,50 @@
import { UmbBlockElementManager } from './block-element-manager.js';
import { UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT } from './block-element-property-dataset.context-token.js';
import { UMB_PROPERTY_DATASET_CONTEXT, UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { type Observable } from '@umbraco-cms/backoffice/external/rxjs';
export class UmbBlockElementPropertyDatasetContext extends UmbBaseController implements UmbPropertyDatasetContext {
#elementManager: UmbBlockElementManager;
// default data:
getVariantId() {
return UmbVariantId.CreateInvariant();
}
getEntityType() {
return this.#elementManager.getEntityType();
}
getUnique() {
return this.#elementManager.getEntityId();
}
getName(): string | undefined {
return 'TODO: get label';
}
readonly name: Observable<string | undefined> = 'TODO: get label observable' as any;
constructor(host: UmbControllerHost, elementManager: UmbBlockElementManager) {
// The controller alias, is a very generic name cause we want only one of these for this controller host.
super(host, UMB_PROPERTY_DATASET_CONTEXT.toString());
this.#elementManager = elementManager;
this.provideContext(UMB_BLOCK_ELEMENT_PROPERTY_DATASET_CONTEXT, this);
}
/**
* TODO: Write proper JSDocs here.
*/
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
return await this.#elementManager.propertyValueByAlias<ReturnType>(propertyAlias);
}
/**
* TODO: Write proper JSDocs here.
*/
async setPropertyValue(propertyAlias: string, value: unknown) {
return this.#elementManager.setPropertyValue(propertyAlias, value);
}
}

View File

@@ -1,12 +1,11 @@
import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js';
import { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { UmbBlockElementManager } from './block-element-manager.js';
import {
UmbInvariantableWorkspaceContextInterface,
UmbEditableWorkspaceContextBase,
UmbSaveableWorkspaceContextInterface,
UmbWorkspaceContextInterface,
UmbInvariantWorkspacePropertyDatasetContext,
} from '@umbraco-cms/backoffice/workspace';
import { UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbBooleanState, UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { ManifestWorkspace } from '@umbraco-cms/backoffice/extension-registry';
@@ -14,22 +13,32 @@ import { UmbId } from '@umbraco-cms/backoffice/id';
import { UMB_BLOCK_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/block';
export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel>
extends UmbEditableWorkspaceContextBase<never, LayoutDataType>
implements UmbInvariantableWorkspaceContextInterface
extends UmbEditableWorkspaceContextBase<UmbBlockWorkspaceContext>
implements UmbSaveableWorkspaceContextInterface
{
// Just for context token safety:
public readonly IS_BLOCK_WORKSPACE_CONTEXT = true;
//
readonly workspaceAlias: string = 'Umb.Workspace.Block';
#entityType: string;
#isNew = new UmbBooleanState<boolean | undefined>(undefined);
readonly isNew = this.#isNew.asObservable();
#layout = new UmbObjectState<LayoutDataType | undefined>(undefined);
readonly layout = this.#layout.asObservable();
#content = new UmbObjectState<UmbBlockDataType | undefined>(undefined);
readonly content = this.#content.asObservable();
// Consider not storing this here:
//#content = new UmbObjectState<UmbBlockDataType | undefined>(undefined);
//readonly content = this.#content.asObservable();
#settings = new UmbObjectState<UmbBlockDataType | undefined>(undefined);
readonly settings = this.#settings.asObservable();
// Consider not storing this here:
//#settings = new UmbObjectState<UmbBlockDataType | undefined>(undefined);
//readonly settings = this.#settings.asObservable();
readonly content = new UmbBlockElementManager(this);
readonly settings = new UmbBlockElementManager(this);
// TODO: Get the name of the contentElementType..
#label = new UmbStringState<string | undefined>(undefined);
@@ -38,14 +47,10 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
constructor(host: UmbControllerHost, workspaceArgs: { manifest: ManifestWorkspace }) {
// TODO: We don't need a repo here, so maybe we should not require this of the UmbEditableWorkspaceContextBase
super(host, workspaceArgs.manifest.alias, undefined as never);
super(host, UMB_BLOCK_WORKSPACE_CONTEXT);
this.#entityType = workspaceArgs.manifest.meta?.entityType;
}
createPropertyDatasetContext(host: UmbControllerHost): UmbPropertyDatasetContext {
return new UmbInvariantWorkspacePropertyDatasetContext(host, this);
}
async load(unique: string) {
this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (context) => {
this.observe(context.value, (value) => {
@@ -64,6 +69,8 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
}
async create(contentElementTypeId: string) {
//
// TODO: Condense this into some kind of create method?
const key = UmbId.new();
const contentUdi = `umb://block/${key}`;
const layout: UmbBlockLayoutBaseModel = {
@@ -73,11 +80,21 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
udi: contentUdi,
contentTypeKey: contentElementTypeId,
};
this.content.setData(content);
// TODO: If we have Settings dedicated to this block type, we initiate them here:
this.setIsNew(true);
this.#layout.next(layout as LayoutDataType);
}
getIsNew() {
return this.#isNew.value;
}
setIsNew(value: boolean): void {
this.#isNew.next(value);
}
getData() {
return this.#layout.getValue();
}
@@ -93,17 +110,18 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
getName() {
return 'block name content element type here...';
}
setName(name: string | undefined) {
alert('You cannot set a name of a block-type.');
// NOTICE currently the property methods are for layout, but this could be seen as wrong, we might need to dedicate a data manager for the layout as well.
async propertyValueByAlias<propertyAliasType extends keyof LayoutDataType>(propertyAlias: propertyAliasType) {
return this.#layout.asObservablePart(
(layout) => layout?.[propertyAlias as keyof LayoutDataType] as LayoutDataType[propertyAliasType],
);
}
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
return this.#layout.asObservablePart((data) => data?.[propertyAlias as keyof BlockTypeData] as ReturnType);
}
getPropertyValue<ReturnType = unknown>(propertyAlias: string) {
getPropertyValue<propertyAliasType extends keyof LayoutDataType>(propertyAlias: propertyAliasType) {
// TODO: Should be using Content, then we need a toggle or another method for getting settings.
return this.#layout.getValue()?.[propertyAlias as keyof BlockTypeData] as ReturnType;
return this.#layout.getValue()?.[propertyAlias as keyof LayoutDataType] as LayoutDataType[propertyAliasType];
}
async setPropertyValue(alias: string, value: unknown) {

View File

@@ -0,0 +1,4 @@
export * from './block-element-property-dataset.context-token.js';
export * from './block-workspace.context.js';
export const UMB_BLOCK_WORKSPACE_ALIAS = 'Umb.Workspace.Block';

View File

@@ -1,13 +1,11 @@
import { UMB_BLOCK_GRID_TYPE_WORKSPACE_ALIAS } from '../../block-grid/workspace/index.js';
import { UMB_BLOCK_LIST_TYPE_WORKSPACE_ALIAS } from '../../block-list/workspace/index.js';
import { UMB_BLOCK_RTE_TYPE_WORKSPACE_ALIAS } from '../../block-rte/workspace/index.js';
import { UMB_BLOCK_WORKSPACE_ALIAS } from './index.js';
import { UmbSaveWorkspaceAction } from '@umbraco-cms/backoffice/workspace';
import type { ManifestWorkspaceAction } from '@umbraco-cms/backoffice/extension-registry';
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestWorkspaceAction> = [
export const manifests: Array<ManifestTypes> = [
{
type: 'workspaceAction',
alias: 'Umb.WorkspaceAction.BlockType.Save',
alias: 'Umb.WorkspaceAction.Block.Save',
name: 'Save Block Type Workspace Action',
api: UmbSaveWorkspaceAction,
meta: {
@@ -18,12 +16,19 @@ export const manifests: Array<ManifestWorkspaceAction> = [
conditions: [
{
alias: 'Umb.Condition.WorkspaceAlias',
oneOf: [
UMB_BLOCK_GRID_TYPE_WORKSPACE_ALIAS,
UMB_BLOCK_LIST_TYPE_WORKSPACE_ALIAS,
UMB_BLOCK_RTE_TYPE_WORKSPACE_ALIAS,
],
oneOf: [UMB_BLOCK_WORKSPACE_ALIAS],
},
],
},
{
type: 'workspace',
name: 'Block List Type Workspace',
alias: UMB_BLOCK_WORKSPACE_ALIAS,
element: () => import('./block-workspace.element.js'),
api: () => import('./block-workspace.context.js'),
weight: 900,
meta: {
entityType: 'block',
},
},
];

View File

@@ -11,6 +11,12 @@ export abstract class UmbEditableWorkspaceContextBase<RepositoryType, WorkspaceD
{
public readonly host: UmbControllerHost;
public readonly workspaceAlias: string;
/*
*
* HER SKAL DU FORTSÆTTE I MORGEN, Det vil kræve ændringer i mange workspace contexts.
*
*/
// TODO: Get rid of the repository, as it prevents flexibility needed for Blocks and other workspaces that like to borrow the features of EditableWorkspace but not using one repository.
// TODO: I think we should get rid of the repository from this one.
public readonly repository: RepositoryType;

View File

@@ -41,7 +41,7 @@ export class UmbDocumentWorkspaceContext
}
readonly unique = this.#currentData.asObservablePart((data) => data?.id);
readonly documentTypeKey = this.#currentData.asObservablePart((data) => data?.contentTypeId);
readonly contentTypeId = this.#currentData.asObservablePart((data) => data?.contentTypeId);
readonly variants = this.#currentData.asObservablePart((data) => data?.variants || []);
readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []);
@@ -57,7 +57,7 @@ export class UmbDocumentWorkspaceContext
this.structure = new UmbContentTypePropertyStructureManager(this, new UmbDocumentTypeDetailRepository(this));
this.splitView = new UmbWorkspaceSplitViewManager();
new UmbObserverController(this.host, this.documentTypeKey, (id) => this.structure.loadType(id));
new UmbObserverController(this.host, this.contentTypeId, (id) => this.structure.loadType(id));
/*
TODO: Make something to ensure all variants are present in data? Seems like a good idea?.