diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/string-state.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/string-state.ts index d893e5b8ce..03c38f7a33 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/string-state.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/states/string-state.ts @@ -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 extends UmbBasicState { constructor(initialData: T | string) { super(initialData); } + + asObservablePart( + mappingFunction: MappingFunction, + memoizationFunction?: MemoizationFunction, + ) { + return createObservablePart(this._subject, mappingFunction, memoizationFunction); + } } diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type.data.ts index 74e7646d41..e4d46ca5bf 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/data-type.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/data-type.data.ts @@ -500,7 +500,6 @@ export const data: Array = editorSize: 'medium', icon: 'icon-coffee', }, - { label: 'Headline', contentElementTypeKey: 'headline-umbraco-demo-block-id', @@ -528,6 +527,14 @@ export const data: Array = }, ], }, + { + alias: 'useInlineEditingAsDefault', + value: true, + }, + { + alias: 'useLiveEditing', + value: true, + }, ], }, { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-block/block-list-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-block/block-list-block.element.ts index 2e0aa2c7c1..58663957fc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-block/block-list-block.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-block/block-list-block.element.ts @@ -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` `; + return html``; } - /*#renderInlineBlock() { - return html``; - }*/ + #renderInlineBlock() { + return html``; + } #renderBlock() { return html` - ${this.#renderRefBlock()} + ${this._inlineEditingMode ? this.#renderInlineBlock() : this.#renderRefBlock()} - ${this._workspacePath - ? html` + ${this._workspaceEditPath + ? html` ` : ''} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts index 06e0fa5b0b..7dc6d20276 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/ref-list-block/ref-list-block.element.ts @@ -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``; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context-token.ts new file mode 100644 index 0000000000..f5ac6af1ec --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context-token.ts @@ -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('UmbBlockContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context.ts new file mode 100644 index 0000000000..60b2c3b4c3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list.context.ts @@ -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'); + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/index.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/index.ts index 60f06132a7..06bff7c73b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/index.ts @@ -1 +1,2 @@ export * from './workspace/index.js'; +export * from './context/block-list.context-token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/manager/block-list-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/manager/block-list-manager.context.ts index 8049c9c48a..4f32598ee1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/manager/block-list-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/manager/block-list-manager.context.ts @@ -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 { + // + #inlineEditingMode = new UmbBooleanState(undefined); + inlineEditingMode = this.#inlineEditingMode.asObservable(); + + setInlineEditingMode(inlineEditingMode: boolean | undefined) { + this.#inlineEditingMode.setValue(inlineEditingMode ?? false); + } + createBlock(layoutEntry: Omit, contentElementTypeKey: string) { // Here is room to append some extra layout properties if needed for this type. diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index b9ef84cf8e..c897630ce7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -79,6 +79,9 @@ export class UmbPropertyEditorUIBlockListElement extends UmbLitElement implement const blocks = config.getValueByAlias>('blocks') ?? []; this.#context.setBlockTypes(blocks); + + const useInlineEditingAsDefault = config.getValueByAlias('useInlineEditingAsDefault'); + this.#context.setInlineEditingMode(useInlineEditingAsDefault); //config.useSingleBlockMode //config.useLiveEditing //config.useInlineEditingAsDefault diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts index 2871fdd639..b949ca70f7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/types.ts @@ -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; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block.context.ts index 10fe739b4b..e091007682 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block.context.ts @@ -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 extends UmbBlockManagerContext, BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel, BlockLayoutType extends UmbBlockLayoutBaseModel = UmbBlockLayoutBaseModel, -> extends UmbContextBase { +> extends UmbContextBase< + UmbBlockContext +> { // - #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(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(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(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'); +export const UMB_BLOCK_CONTEXT = new UmbContextToken< + UmbBlockContext +>('UmbBlockContext');