simple block list editor implementation

This commit is contained in:
Niels Lyngsø
2024-01-16 13:27:45 +01:00
parent 6fbbdb614d
commit cc3310cc0c
7 changed files with 84 additions and 11 deletions

View File

@@ -183,6 +183,7 @@ export const data: Array<DocumentResponseModel> = [
{
udi: '1234',
contentTypeKey: '4f68ba66-6fb2-4778-83b8-6ab4ca3a7c5c',
elementProperty: 'Hello world',
},
],
settingsData: [],

View File

@@ -24,12 +24,24 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
#context = new UmbBlockContext(this);
@state()
_contentUdi?: string;
@state()
_label = '';
@state()
_workspacePath?: string;
constructor() {
super();
this.observe(this.#context.workspacePath, (workspacePath) => {
this._workspacePath = workspacePath;
});
this.observe(this.#context.contentUdi, (contentUdi) => {
this._contentUdi = contentUdi;
});
this.observe(this.#context.label, (label) => {
this._label = label;
});
@@ -65,6 +77,11 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
return html`
${this.#renderRefBlock()}
<uui-action-bar>
${this._workspacePath
? html`<uui-button label="edit" compact href=${this._workspacePath + 'edit/' + this._contentUdi}>
<uui-icon name="icon-edit"></uui-icon>
</uui-button>`
: ''}
<uui-button label="delete" compact @click=${this.#requestDelete}>
<uui-icon name="icon-remove"></uui-icon>
</uui-button>

View File

@@ -15,7 +15,12 @@ import '../../components/block-list-block/index.js';
import { buildUdi } from '@umbraco-cms/backoffice/utils';
import { UmbId } from '@umbraco-cms/backoffice/id';
import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models';
import { UMB_MODAL_MANAGER_CONTEXT_TOKEN, UmbModalManagerContext } from '@umbraco-cms/backoffice/modal';
import {
UMB_MODAL_MANAGER_CONTEXT_TOKEN,
UMB_WORKSPACE_MODAL,
UmbModalManagerContext,
UmbModalRouteRegistrationController,
} from '@umbraco-cms/backoffice/modal';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
export interface UmbBlockListLayoutModel extends UmbBlockLayoutBaseModel {}
@@ -78,6 +83,9 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement
@state()
_layouts: Array<UmbBlockLayoutBaseModel> = [];
@state()
_workspacePath?: string;
#modalContext?: UmbModalManagerContext;
constructor() {

View File

@@ -19,6 +19,9 @@ export class UmbBlockContext<
#label = new UmbStringState('');
public readonly label = this.#label.asObservable();
#workspacePath = new UmbStringState(undefined);
public readonly workspacePath = this.#workspacePath.asObservable();
#blockType = new UmbObjectState<BlockType | undefined>(undefined);
public readonly blockType = this.#blockType.asObservable();
public readonly blockTypeContentElementTypeKey = this.#blockType.asObservablePart((x) => x?.contentElementTypeKey);
@@ -51,6 +54,13 @@ export class UmbBlockContext<
// Consume block manager:
this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (manager) => {
this.#manager = manager;
this.observe(
manager.workspacePath,
(workspacePath) => {
this.#workspacePath.next(workspacePath);
},
'observeWorkspacePath',
);
this.#observeBlockType();
this.#observeData();
});

View File

@@ -2,11 +2,12 @@ import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '..//types.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbArrayState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type';
import { DocumentTypeResponseModel } from '@umbraco-cms/backoffice/backend-api';
import { getKeyFromUdi } from '@umbraco-cms/backoffice/utils';
import { UmbBlockTypeBase } from '@umbraco-cms/backoffice/block';
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
// TODO: We are using backend model here, I think we should get our own model:
type ElementTypeModel = DocumentTypeResponseModel;
@@ -18,6 +19,9 @@ export class UmbBlockManagerContext<
//
#contentTypeRepository = new UmbDocumentTypeDetailRepository(this);
#workspacePath = new UmbStringState(undefined);
workspacePath = this.#workspacePath.asObservable();
#contentTypes = new UmbArrayState(<Array<ElementTypeModel>>[], (x) => x.id);
public readonly contentTypes = this.#contentTypes.asObservable();
@@ -53,6 +57,18 @@ export class UmbBlockManagerContext<
constructor(host: UmbControllerHost) {
super(host, UMB_BLOCK_MANAGER_CONTEXT);
// TODO: Make specific modal token that requires data.
// IDEA: Make a Workspace registration controller that can be used to register a workspace, which does both edit and create?.
new UmbModalRouteRegistrationController(this, UMB_WORKSPACE_MODAL)
.addAdditionalPath('block')
.onSetup(() => {
return { data: { entityType: 'block', preset: {} }, modal: { size: 'medium' } };
})
.observeRouteBuilder((routeBuilder) => {
const newPath = routeBuilder({});
this.#workspacePath.next(newPath);
});
}
async ensureContentType(id?: string) {
@@ -99,14 +115,14 @@ export class UmbBlockManagerContext<
return this.#settings.asObservablePart((source) => source.find((x) => x.udi === udi));
}
updateLayout(contentUdi: string, layoutData: Partial<BlockLayoutType>) {
return this.#layouts.updateOne(contentUdi, layoutData);
setOneLayout(layoutData: BlockLayoutType) {
return this.#layouts.appendOne(layoutData);
}
updateContent(udi: string, contentData: Partial<UmbBlockDataType>) {
return this.#contents.updateOne(udi, contentData);
setOneContent(contentData: UmbBlockDataType) {
this.#contents.appendOne(contentData);
}
updateSettings(udi: string, settingsData: Partial<UmbBlockDataType>) {
return this.#settings.updateOne(udi, settingsData);
setOneSettings(settingsData: UmbBlockDataType) {
this.#settings.appendOne(settingsData);
}
createBlock(layoutEntry: BlockLayoutType, contentElementTypeKey: string) {

View File

@@ -1,3 +1,4 @@
import { manifests as modalManifests } from './modals/manifests.js';
import { manifests as workspaceManifests } from './workspace/manifests.js';
export const manifests = [...modalManifests];
export const manifests = [...modalManifests, ...workspaceManifests];

View File

@@ -21,13 +21,17 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
//
readonly workspaceAlias: string = 'Umb.Workspace.Block';
#blockManager?: typeof UMB_BLOCK_MANAGER_CONTEXT.TYPE;
#entityType: string;
#contentUdi: string;
#isNew = new UmbBooleanState<boolean | undefined>(undefined);
readonly isNew = this.#isNew.asObservable();
#layout = new UmbObjectState<LayoutDataType | undefined>(undefined);
readonly layout = this.#layout.asObservable();
//readonly unique = this.#layout.asObservablePart((x) => x?.contentUdi);
readonly contentUdi = this.#layout.asObservablePart((x) => x?.contentUdi);
readonly content = new UmbBlockElementManager(this);
@@ -37,16 +41,21 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
// TODO: Get the name of the contentElementType..
#label = new UmbStringState<string | undefined>(undefined);
readonly name = this.#label.asObservable();
readonly unique = this.#layout.asObservablePart((data) => data?.contentUdi);
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, 'Umb.Workspace.Block');
this.#entityType = workspaceArgs.manifest.meta?.entityType;
this.observe(this.contentUdi, (contentUdi) => {
this.#contentUdi = contentUdi ?? '';
});
}
async load(unique: string) {
this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (context) => {
this.#blockManager = context;
this.observe(
context.layoutOf(unique),
(layoutData) => {
@@ -146,9 +155,20 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
}
async save() {
if (!this.#layout.value) return;
if (!this.#layout.value || !this.#blockManager) return;
// TODO: Save the block type, but only in non-live-editing mode.
const layoutData = this.#layout.value;
this.#blockManager.setOneLayout(this.#layout.value);
const contentData = this.content.getData();
if (contentData) {
this.#blockManager.setOneContent(contentData);
}
const settingsData = this.settings.getData();
if (settingsData) {
this.#blockManager.setOneSettings(settingsData);
}
this.saveComplete(this.#layout.value);
}