better data binding + inline mode

This commit is contained in:
Niels Lyngsø
2024-01-19 10:59:54 +01:00
parent 4db3c1d1d7
commit 43070aca04
11 changed files with 169 additions and 60 deletions

View File

@@ -1,3 +1,4 @@
import { createObservablePart, type MappingFunction, type MemoizationFunction } from '../index.js';
import { UmbBasicState } from './basic-state.js';
/**
@@ -10,4 +11,11 @@ export class UmbStringState<T> extends UmbBasicState<T | string> {
constructor(initialData: T | string) {
super(initialData);
}
asObservablePart<ReturnType>(
mappingFunction: MappingFunction<T | string, ReturnType>,
memoizationFunction?: MemoizationFunction<ReturnType>,
) {
return createObservablePart(this._subject, mappingFunction, memoizationFunction);
}
}

View File

@@ -500,7 +500,6 @@ export const data: Array<DataTypeResponseModel | FolderTreeItemResponseModel> =
editorSize: 'medium',
icon: 'icon-coffee',
},
{
label: 'Headline',
contentElementTypeKey: 'headline-umbraco-demo-block-id',
@@ -528,6 +527,14 @@ export const data: Array<DataTypeResponseModel | FolderTreeItemResponseModel> =
},
],
},
{
alias: 'useInlineEditingAsDefault',
value: true,
},
{
alias: 'useLiveEditing',
value: true,
},
],
},
{

View File

@@ -1,9 +1,9 @@
import { UmbBlockListContext } from '../../context/block-list.context.js';
import { html, css, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { type UmbBlockLayoutBaseModel } from '@umbraco-cms/backoffice/block';
import '../ref-list-block/index.js';
import { UmbBlockContext } from '@umbraco-cms/backoffice/block';
import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/modal';
import { encodeFilePath } from '@umbraco-cms/backoffice/utils';
@@ -23,7 +23,7 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
}
private _layout?: UmbBlockLayoutBaseModel | undefined;
#context = new UmbBlockContext(this);
#context = new UmbBlockListContext(this);
@state()
_contentUdi?: string;
@@ -32,13 +32,16 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
_label = '';
@state()
_workspacePath?: string;
_workspaceEditPath?: string;
@state()
_inlineEditingMode?: boolean;
constructor() {
super();
this.observe(this.#context.workspacePath, (workspacePath) => {
this._workspacePath = workspacePath;
this.observe(this.#context.workspaceEditPath, (workspaceEditPath) => {
this._workspaceEditPath = workspaceEditPath;
});
this.observe(this.#context.contentUdi, (contentUdi) => {
this._contentUdi = contentUdi;
@@ -46,9 +49,9 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
this.observe(this.#context.label, (label) => {
this._label = label;
});
/*this.observe(this.#context.layout, (layout) => {
console.log('layout', layout);
});*/
this.observe(this.#context.inlineEditingMode, (inlineEditingMode) => {
this._inlineEditingMode = inlineEditingMode;
});
}
#requestDelete() {
@@ -67,22 +70,19 @@ export class UmbPropertyEditorUIBlockListBlockElement extends UmbLitElement impl
}
#renderRefBlock() {
return html`<umb-ref-list-block .name=${this._label}> </umb-ref-list-block>`;
return html`<umb-ref-list-block .name=${this._label}></umb-ref-list-block>`;
}
/*#renderInlineBlock() {
return html`<umb-inline-list-block name="block" }}></umb-inline-list-block>`;
}*/
#renderInlineBlock() {
return html`<umb-inline-list-block .name=${this._label}></umb-inline-list-block>`;
}
#renderBlock() {
return html`
${this.#renderRefBlock()}
${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()}
<uui-action-bar>
${this._workspacePath
? html`<uui-button
label="edit"
compact
href=${this._workspacePath + 'edit/' + encodeFilePath(this._contentUdi!)}>
${this._workspaceEditPath
? html`<uui-button label="edit" compact href=${this._workspaceEditPath}>
<uui-icon name="icon-edit"></uui-icon>
</uui-button>`
: ''}

View File

@@ -1,15 +1,37 @@
import { css, customElement } from '@umbraco-cms/backoffice/external/lit';
import { UMB_BLOCK_LIST_CONTEXT } from '../../context/block-list.context-token.js';
import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
/**
* @element umb-ref-list-block
*/
@customElement('umb-ref-list-block')
export class UmbRefListBlockElement extends UUIRefNodeElement {
export class UmbRefListBlockElement extends UmbLitElement {
//
connectedCallback(): void {
super.connectedCallback();
this.setAttribute('border', '');
@property({ type: String })
name?: string;
@state()
_workspaceEditPath?: string;
constructor() {
super();
this.consumeContext(UMB_BLOCK_LIST_CONTEXT, (context) => {
this.observe(
context.workspaceEditPath,
(workspaceEditPath) => {
this._workspaceEditPath = workspaceEditPath;
},
'observeWorkspaceEditPath',
);
});
}
render() {
// href=${this._workspaceEditPath ?? '#'}
return html`<uui-ref-node border .name=${this.name ?? ''}></uui-ref-node>`;
}
static styles = [

View File

@@ -0,0 +1,4 @@
import type { UmbBlockListContext } from './block-list.context.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_BLOCK_LIST_CONTEXT = new UmbContextToken<UmbBlockListContext>('UmbBlockContext');

View File

@@ -0,0 +1,30 @@
import { UMB_BLOCK_LIST_MANAGER_CONTEXT } from '../manager/block-list-manager.context.js';
import { UmbBlockContext } from '@umbraco-cms/backoffice/block';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
export class UmbBlockListContext extends UmbBlockContext<
typeof UMB_BLOCK_LIST_MANAGER_CONTEXT,
typeof UMB_BLOCK_LIST_MANAGER_CONTEXT.TYPE
> {
#inlineEditingMode = new UmbBooleanState(undefined);
inlineEditingMode = this.#inlineEditingMode.asObservable();
constructor(host: UmbControllerHost) {
super(host, UMB_BLOCK_LIST_MANAGER_CONTEXT);
}
_gotManager() {
super._gotManager();
if (this._manager) {
this.observe(
this._manager.inlineEditingMode,
(inlineEditingMode) => {
this.#inlineEditingMode.setValue(inlineEditingMode);
},
'observeInlineEditingMode',
);
} else {
this.removeControllerByAlias('observeInlineEditingMode');
}
}
}

View File

@@ -1 +1,2 @@
export * from './workspace/index.js';
export * from './context/block-list.context-token.js';

View File

@@ -1,6 +1,7 @@
import type { UmbBlockListLayoutModel, UmbBlockListTypeModel } from '../types.js';
import { UmbBlockManagerContext } from '@umbraco-cms/backoffice/block';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
/**
* A implementation of the Block Manager specifically for the Block List.
@@ -8,6 +9,14 @@ import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export class UmbBlockListManagerContext<
BlockLayoutType extends UmbBlockListLayoutModel = UmbBlockListLayoutModel,
> extends UmbBlockManagerContext<UmbBlockListTypeModel, BlockLayoutType> {
//
#inlineEditingMode = new UmbBooleanState(undefined);
inlineEditingMode = this.#inlineEditingMode.asObservable();
setInlineEditingMode(inlineEditingMode: boolean | undefined) {
this.#inlineEditingMode.setValue(inlineEditingMode ?? false);
}
createBlock(layoutEntry: Omit<BlockLayoutType, 'contentUdi'>, contentElementTypeKey: string) {
// Here is room to append some extra layout properties if needed for this type.

View File

@@ -79,6 +79,9 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement
const blocks = config.getValueByAlias<Array<UmbBlockTypeBaseModel>>('blocks') ?? [];
this.#context.setBlockTypes(blocks);
const useInlineEditingAsDefault = config.getValueByAlias<boolean>('useInlineEditingAsDefault');
this.#context.setInlineEditingMode(useInlineEditingAsDefault);
//config.useSingleBlockMode
//config.useLiveEditing
//config.useInlineEditingAsDefault

View File

@@ -4,8 +4,8 @@ export interface UmbBlockTypeBaseModel {
contentElementTypeKey: string;
settingsElementTypeKey?: string;
label?: string;
view?: string;
stylesheet?: string;
view?: string; // TODO: remove/replace with custom element manifest type for block list.
stylesheet?: string; // TODO: remove/replace with custom element manifest type for block list.
iconColor?: string;
backgroundColor?: string;
editorSize?: UUIModalSidebarSize;

View File

@@ -1,17 +1,22 @@
import type { UmbBlockTypeBaseModel } from '../../block-type/types.js';
import type { UmbBlockLayoutBaseModel, UmbBlockDataType } from '../types.js';
import { UMB_BLOCK_MANAGER_CONTEXT } from '../manager/index.js';
import { UMB_BLOCK_MANAGER_CONTEXT, type UmbBlockManagerContext } from '../manager/index.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 { UmbObjectState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { encodeFilePath } from '@umbraco-cms/backoffice/utils';
export class UmbBlockContext<
export abstract class UmbBlockContext<
BlockManagerContextTokenType extends UmbContextToken<BlockManagerContextType, BlockManagerContextType>,
BlockManagerContextType extends UmbBlockManagerContext<BlockType, BlockLayoutType>,
BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel,
BlockLayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel,
> extends UmbContextBase<UmbBlockContext> {
> extends UmbContextBase<
UmbBlockContext<BlockManagerContextTokenType, BlockManagerContextType, BlockType, BlockLayoutType>
> {
//
#manager?: typeof UMB_BLOCK_MANAGER_CONTEXT.TYPE;
_manager?: BlockManagerContextType;
#blockTypeName = new UmbStringState(undefined);
public readonly blockTypeName = this.#blockTypeName.asObservable();
@@ -21,11 +26,17 @@ export class UmbBlockContext<
#workspacePath = new UmbStringState(undefined);
public readonly workspacePath = this.#workspacePath.asObservable();
public readonly workspaceEditPath = this.#workspacePath.asObservablePart(
(path) => path + 'edit/' + encodeFilePath(this.getContentUdi() ?? ''),
);
public readonly workspaceEditSettingsPath = this.#workspacePath.asObservablePart(
(path) => path + 'edit/' + encodeFilePath(this.getContentUdi() ?? '') + '/view/settings',
);
#blockType = new UmbObjectState<BlockType | undefined>(undefined);
public readonly blockType = this.#blockType.asObservable();
public readonly blockTypeContentElementTypeKey = this.#blockType.asObservablePart((x) => x?.contentElementTypeKey);
public readonly blockTypeSettingsElementTypeKey = this.#blockType.asObservablePart((x) => x?.settingsElementTypeKey);
_blockType = new UmbObjectState<BlockType | undefined>(undefined);
public readonly blockType = this._blockType.asObservable();
public readonly blockTypeContentElementTypeKey = this._blockType.asObservablePart((x) => x?.contentElementTypeKey);
public readonly blockTypeSettingsElementTypeKey = this._blockType.asObservablePart((x) => x?.settingsElementTypeKey);
#layout = new UmbObjectState<BlockLayoutType | undefined>(undefined);
public readonly layout = this.#layout.asObservable();
@@ -48,21 +59,13 @@ export class UmbBlockContext<
this.#layout.setValue(layout);
}
constructor(host: UmbControllerHost) {
constructor(host: UmbControllerHost, blockManagerContextToken: BlockManagerContextTokenType) {
super(host, UMB_BLOCK_CONTEXT);
// Consume block manager:
this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (manager) => {
this.#manager = manager;
this.observe(
manager.workspacePath,
(workspacePath) => {
this.#workspacePath.setValue(workspacePath);
},
'observeWorkspacePath',
);
this.#observeBlockType();
this.#observeData();
this.consumeContext(blockManagerContextToken, (manager) => {
this._manager = manager;
this._gotManager();
});
// Observe UDI:
@@ -85,14 +88,34 @@ export class UmbBlockContext<
});
}
getContentUdi() {
return this.#layout.value?.contentUdi;
}
_gotManager() {
if (this._manager) {
this.observe(
this._manager.workspacePath,
(workspacePath) => {
this.#workspacePath.setValue(workspacePath);
},
'observeWorkspacePath',
);
} else {
this.removeControllerByAlias('observeWorkspacePath');
}
this.#observeBlockType();
this.#observeData();
}
#observeData() {
if (!this.#manager) return;
if (!this._manager) return;
const contentUdi = this.#layout.value?.contentUdi;
if (!contentUdi) return;
// observe content:
this.observe(
this.#manager.contentOf(contentUdi),
this._manager.contentOf(contentUdi),
(content) => {
this.#content.setValue(content);
},
@@ -103,7 +126,7 @@ export class UmbBlockContext<
const settingsUdi = this.#layout.value?.settingsUdi;
if (settingsUdi) {
this.observe(
this.#manager.contentOf(settingsUdi),
this._manager.contentOf(settingsUdi),
(content) => {
this.#settings.setValue(content);
},
@@ -113,28 +136,28 @@ export class UmbBlockContext<
}
#observeBlockType() {
if (!this.#manager) return;
if (!this._manager) return;
const contentTypeKey = this.#content.value?.contentTypeKey;
if (!contentTypeKey) return;
// observe blockType:
this.observe(
this.#manager.blockTypeOf(contentTypeKey),
this._manager.blockTypeOf(contentTypeKey),
(blockType) => {
this.#blockType.setValue(blockType as BlockType);
this._blockType.setValue(blockType as BlockType);
},
'observeBlockType',
);
}
#observeBlockTypeContentElementName() {
if (!this.#manager) return;
const contentElementTypeKey = this.#blockType.value?.contentElementTypeKey;
if (!this._manager) return;
const contentElementTypeKey = this._blockType.value?.contentElementTypeKey;
if (!contentElementTypeKey) return;
// observe blockType:
this.observe(
this.#manager.contentTypeNameOf(contentElementTypeKey),
this._manager.contentTypeNameOf(contentElementTypeKey),
(contentTypeName) => {
this.#blockTypeName.setValue(contentTypeName);
},
@@ -143,8 +166,8 @@ export class UmbBlockContext<
}
#observeBlockTypeLabel() {
if (!this.#manager) return;
const blockType = this.#blockType.value;
if (!this._manager) return;
const blockType = this._blockType.value;
if (!blockType) return;
if (blockType.label) {
@@ -168,11 +191,13 @@ export class UmbBlockContext<
// Public methods:
public delete() {
if (!this.#manager) return;
if (!this._manager) return;
const contentUdi = this.#layout.value?.contentUdi;
if (!contentUdi) return;
this.#manager.deleteBlock(contentUdi);
this._manager.deleteBlock(contentUdi);
}
}
export const UMB_BLOCK_CONTEXT = new UmbContextToken<UmbBlockContext, UmbBlockContext>('UmbBlockContext');
export const UMB_BLOCK_CONTEXT = new UmbContextToken<
UmbBlockContext<typeof UMB_BLOCK_MANAGER_CONTEXT, typeof UMB_BLOCK_MANAGER_CONTEXT.TYPE>
>('UmbBlockContext');