From c9fe3537f8cef8a9a1ca74064706b3c16a5df80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 5 Feb 2024 15:50:19 +0100 Subject: [PATCH] initial scaffolding for editor --- .../context/block-grid.context-token.ts | 5 + .../block-grid/context/block-grid.context.ts | 36 +++++ .../manager/block-grid-manager.context.ts | 37 +++++ .../block-grid-editor/manifests.ts | 4 +- ...editor-ui-block-grid-inner-test.element.ts | 82 ----------- ...-ui-block-grid-layout-container.element.ts | 137 ++++++++++++++++++ .../property-editor-ui-block-grid.element.ts | 89 ++++-------- .../src/packages/block/block-grid/types.ts | 14 +- .../context/block-list.context-token.ts | 1 + .../manager/block-list-manager.context.ts | 3 +- 10 files changed, 265 insertions(+), 143 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context-token.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/manager/block-grid-manager.context.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-inner-test.element.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-layout-container.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context-token.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context-token.ts new file mode 100644 index 0000000000..a8c429f977 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context-token.ts @@ -0,0 +1,5 @@ +import type { UmbBlockGridContext } from './block-grid.context.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +// TODO: Make discriminator method for this: +export const UMB_BLOCK_GRID_CONTEXT = new UmbContextToken('UmbBlockContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context.ts new file mode 100644 index 0000000000..49a95d7aca --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid.context.ts @@ -0,0 +1,36 @@ +import { UMB_BLOCK_GRID_MANAGER_CONTEXT } from '../manager/block-grid-manager.context.js'; +import { + UmbBlockContext, + type UmbBlockGridTypeModel, + type UmbBlockGridLayoutModel, +} from '@umbraco-cms/backoffice/block'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api'; +export class UmbBlockGridContext extends UmbBlockContext< + typeof UMB_BLOCK_GRID_MANAGER_CONTEXT, + typeof UMB_BLOCK_GRID_MANAGER_CONTEXT.TYPE, + UmbBlockGridTypeModel, + UmbBlockGridLayoutModel +> { + #inlineEditingMode = new UmbBooleanState(undefined); + inlineEditingMode = this.#inlineEditingMode.asObservable(); + + constructor(host: UmbControllerHost) { + super(host, UMB_BLOCK_GRID_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-grid/manager/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/manager/block-grid-manager.context.ts new file mode 100644 index 0000000000..f0adca8e20 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/manager/block-grid-manager.context.ts @@ -0,0 +1,37 @@ +import type { UmbBlockGridLayoutModel, UmbBlockGridTypeModel } from '../types.js'; +import { UmbBlockManagerContext } from '../../block/manager/block-manager.context.js'; +import type { UmbBlockGridWorkspaceData } from '../index.js'; +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; + +/** + * A implementation of the Block Manager specifically for the Block Grid Editor. + */ +export class UmbBlockGridManagerContext< + BlockLayoutType extends UmbBlockGridLayoutModel = UmbBlockGridLayoutModel, +> extends UmbBlockManagerContext { + // + /* + #inlineEditingMode = new UmbBooleanState(undefined); + inlineEditingMode = this.#inlineEditingMode.asObservable(); + + setInlineEditingMode(inlineEditingMode: boolean | undefined) { + this.#inlineEditingMode.setValue(inlineEditingMode ?? false); + } + */ + + _createBlock(modalData: UmbBlockGridWorkspaceData, layoutEntry: BlockLayoutType, contentElementTypeKey: string) { + // Here is room to append some extra layout properties if needed for this type. + + this._layouts.appendOneAt(layoutEntry, modalData.originData.index ?? -1); + + // TODO: Ability to add at a specific Area. + + return true; + } +} + +// TODO: Make discriminator method for this: +export const UMB_BLOCK_GRID_MANAGER_CONTEXT = new UmbContextToken< + UmbBlockGridManagerContext, + UmbBlockGridManagerContext +>('UmbBlockManagerContext'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts index 1f722f3d75..aee0e499fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/manifests.ts @@ -1,5 +1,7 @@ import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry'; +export const UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS = 'Umbraco.BlockGrid'; + export const manifest: ManifestPropertyEditorUi = { type: 'propertyEditorUi', alias: 'Umb.PropertyEditorUi.BlockGrid', @@ -7,7 +9,7 @@ export const manifest: ManifestPropertyEditorUi = { js: () => import('./property-editor-ui-block-grid.element.js'), meta: { label: 'Block Grid', - propertyEditorSchemaAlias: 'Umbraco.BlockGrid', + propertyEditorSchemaAlias: UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS, icon: 'icon-layout', group: 'richContent', settings: { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-inner-test.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-inner-test.element.ts deleted file mode 100644 index 91b3cb1c3a..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-inner-test.element.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; -import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import type { UmbRouterSlotChangeEvent, UmbRouterSlotInitEvent, UmbRoute } from '@umbraco-cms/backoffice/router'; - -/** - * @element umb-property-editor-ui-block-grid-inner-test - */ -@customElement('umb-property-editor-ui-block-grid-inner-test') -export class UmbPropertyEditorUIBlockGridInnerTestElement extends UmbLitElement { - @property({ type: String }) - public name = ''; - - @state() - private _routerPath: string | undefined; - - @state() - private _activePath: string | undefined; - - @state() - private _routes: UmbRoute[] = [ - { - path: 'inner-1', - component: () => { - return import('./property-editor-ui-block-grid-inner-test.element.js'); - }, - setup: (component) => { - if (component instanceof HTMLElement) { - (component as any).name = 'inner-1'; - } - }, - }, - { - path: 'inner-2', - component: () => { - return import('./property-editor-ui-block-grid-inner-test.element.js'); - }, - setup: (component) => { - if (component instanceof HTMLElement) { - (component as any).name = 'inner-2'; - } - }, - }, - ]; - - render() { - return html`
- inner: ${this.name} - - - - - - - { - this._routerPath = event.target.absoluteRouterPath; - }} - @change=${(event: UmbRouterSlotChangeEvent) => { - this._activePath = event.target.localActiveViewPath; - }}> -
`; - } - - static styles = [UmbTextStyles]; -} - -export default UmbPropertyEditorUIBlockGridInnerTestElement; - -declare global { - interface HTMLElementTagNameMap { - 'umb-property-editor-ui-block-grid-inner-test': UmbPropertyEditorUIBlockGridInnerTestElement; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-layout-container.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-layout-container.element.ts new file mode 100644 index 0000000000..afc0146d2e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid-layout-container.element.ts @@ -0,0 +1,137 @@ +import { + UMB_BLOCK_CATALOGUE_MODAL, + type UmbBlockGridLayoutModel, + type UmbBlockTypeBaseModel, +} from '@umbraco-cms/backoffice/block'; +import { html, customElement, state, repeat, css, property } from '@umbraco-cms/backoffice/external/lit'; +import { type UmbModalRouteBuilder, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; +import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; + +/** + * @element umb-property-editor-ui-block-grid-entries + */ +@customElement('umb-property-editor-ui-block-grid-entries') +export class UmbPropertyEditorUIBlockGridEntriesElement extends UmbLitElement { + // TODO: Make sure Sorter handles columnSpan when retrieving a new entry. + #catalogueModal: UmbModalRouteRegistrationController; + + @state() + private _catalogueRouteBuilder?: UmbModalRouteBuilder; + + @state() + private _blocks?: Array; + + @property({ attribute: false }) + public set layoutEntries(value: Array) { + this._layoutEntries = value; + } + public get layoutEntries(): Array { + return this._layoutEntries; + } + private _layoutEntries: Array = []; + + @state() + private _createButtonLabel = this.localize.term('content_createEmpty'); + + constructor() { + super(); + + // TODO: Observe Blocks of the layout entries of this component. + + // TODO: Could this become part of the Block Manager Context? + this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { + this.observe( + propertyContext?.alias, + (alias) => { + this.#catalogueModal.setUniquePathValue('propertyAlias', alias); + }, + 'observePropertyAlias', + ); + }); + + // Maybe this can be moved to the Block Manager Context? As I don't want to know about groups here. Maybe just part of the onSetup method? + this.#catalogueModal = new UmbModalRouteRegistrationController(this, UMB_BLOCK_CATALOGUE_MODAL) + .addUniquePaths(['propertyAlias', 'parentUnique']) + .addAdditionalPath(':view/:index') + .onSetup((routingInfo) => { + const index = routingInfo.index ? parseInt(routingInfo.index) : -1; + return { + data: { + blocks: this._blocks ?? [], + //blockGroups: this._blockGroups ?? [], + blockGroups: [], + openClipboard: routingInfo.view === 'clipboard', + blockOriginData: { index: index }, + }, + }; + }) + .observeRouteBuilder((routeBuilder) => { + this._catalogueRouteBuilder = routeBuilder; + }); + } + + render() { + let createPath: string | undefined; + if (this._blocks?.length === 1) { + const elementKey = this._blocks[0].contentElementTypeKey; + createPath = + this._catalogueRouteBuilder?.({ view: 'create', index: -1 }) + 'modal/umb-modal-workspace/create/' + elementKey; + } else { + createPath = this._catalogueRouteBuilder?.({ view: 'create', index: -1 }); + } + return html` ${repeat( + this._layoutEntries, + (x) => x.contentUdi, + (layoutEntry, index) => + html` + + `, + )} + + + + + + `; + // + } + + static styles = [ + UmbTextStyles, + css` + :host { + display: grid; + gap: 1px; + } + > div { + display: flex; + flex-direction: column; + align-items: stretch; + } + + uui-button-group { + padding-top: 1px; + display: grid; + grid-template-columns: 1fr auto; + } + `, + ]; +} + +export default UmbPropertyEditorUIBlockGridEntriesElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-property-editor-ui-block-grid-entries': UmbPropertyEditorUIBlockGridEntriesElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts index fc7af7e81f..66114e53fe 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/property-editors/block-grid-editor/property-editor-ui-block-grid.element.ts @@ -3,23 +3,16 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; -import { - UMB_BLOCK_CATALOGUE_MODAL, - type UmbBlockLayoutBaseModel, - type UmbBlockTypeBaseModel, - type UmbBlockTypeGroup, -} from '@umbraco-cms/backoffice/block'; -import { type UmbModalRouteBuilder, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal'; +import type { UmbBlockGridLayoutModel, UmbBlockTypeBaseModel, UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block'; import type { NumberRangeValueType } from '@umbraco-cms/backoffice/models'; -import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; +import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS } from './manifests'; /** * @element umb-property-editor-ui-block-grid */ @customElement('umb-property-editor-ui-block-grid') export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implements UmbPropertyEditorUiElement { - #catalogueModal: UmbModalRouteRegistrationController; - @property() value = ''; @@ -35,10 +28,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement private _blockGroups?: Array; @state() - private _layouts: Array = []; - - @state() - private _catalogueRouteBuilder?: UmbModalRouteBuilder; + private _rootLayouts: Array = []; @state() private _directRoute?: string; @@ -76,57 +66,40 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement //this.#context.setEditorConfiguration(config); } + #context = new UmbBlockGridManagerContext(this); + constructor() { super(); - this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext) => { - this.observe( - propertyContext?.alias, - (alias) => { - this.#catalogueModal.setUniquePathValue('propertyAlias', alias); - }, - 'observePropertyAlias', - ); + // TODO: Prevent initial notification from these observes: + this.observe(this.#context.layouts, (layouts) => { + this._value = { ...this._value, layout: { [UMB_BLOCK_GRID_PROPERTY_EDITOR_ALIAS]: layouts } }; + // Notify that the value has changed. + //console.log('layout changed', this._value); + // TODO: idea: consider inserting an await here, so other changes could appear first? Maybe some mechanism to only fire change event onces? + this._rootLayouts = layouts; + this.dispatchEvent(new UmbChangeEvent()); + }); + this.observe(this.#context.contents, (contents) => { + this._value = { ...this._value, contentData: contents }; + // Notify that the value has changed. + //console.log('content changed', this._value); + this.dispatchEvent(new UmbChangeEvent()); + }); + this.observe(this.#context.settings, (settings) => { + this._value = { ...this._value, settingsData: settings }; + // Notify that the value has changed. + //console.log('settings changed', this._value); + this.dispatchEvent(new UmbChangeEvent()); + }); + this.observe(this.#context.blockTypes, (blockTypes) => { + this._blocks = blockTypes; }); - - this.#catalogueModal = new UmbModalRouteRegistrationController(this, UMB_BLOCK_CATALOGUE_MODAL) - .addUniquePaths(['propertyAlias']) - .addAdditionalPath(':view/:index') - .onSetup((routingInfo) => { - const index = routingInfo.index ? parseInt(routingInfo.index) : -1; - return { - data: { - blocks: this._blocks ?? [], - blockGroups: this._blockGroups ?? [], - openClipboard: routingInfo.view === 'clipboard', - blockOriginData: { index: index }, - }, - }; - }) - .observeRouteBuilder((routeBuilder) => { - this._catalogueRouteBuilder = routeBuilder; - }); } render() { - if (this._blocks?.length === 1) { - const elementKey = this._blocks[0].contentElementTypeKey; - this._directRoute = - this._catalogueRouteBuilder?.({ view: 'create', index: -1 }) + 'modal/umb-modal-workspace/create/' + elementKey; - } - return html` - - - - - `; + return html``; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts index 3a51638811..b70d6f54a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/types.ts @@ -1,8 +1,9 @@ import type { UmbBlockTypeBaseModel, UmbBlockTypeWithGroupKey } from '../block-type/index.js'; +import type { UmbBlockLayoutBaseModel } from '../index.js'; export const UMB_BLOCK_GRID_TYPE = 'block-grid-type'; -export interface UmbBlockGridType extends UmbBlockTypeBaseModel { +export interface UmbBlockGridTypeModel extends UmbBlockTypeBaseModel { columnSpanOptions: Array; allowAtRoot: boolean; allowInAreas: boolean; @@ -21,3 +22,14 @@ export interface UmbBlockGridGroupType { export interface UmbBlockGridGroupTypeConfiguration extends Partial { blocks: Array; } + +export interface UmbBlockGridLayoutModel extends UmbBlockLayoutBaseModel { + columnSpan: number; + rowSpan: number; + areas: Array; +} + +export interface UmbBlockGridLayoutAreaItemModel { + key: string; + items: Array; +} 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 index f5ac6af1ec..fb4f9c63fe 100644 --- 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 @@ -1,4 +1,5 @@ import type { UmbBlockListContext } from './block-list.context.js'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +// TODO: Make discriminator method for this: export const UMB_BLOCK_LIST_CONTEXT = new UmbContextToken('UmbBlockContext'); 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 265645fe82..96d18238e8 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 @@ -5,7 +5,7 @@ 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. + * A implementation of the Block Manager specifically for the Block List Editor. */ export class UmbBlockListManagerContext< BlockLayoutType extends UmbBlockListLayoutModel = UmbBlockListLayoutModel, @@ -27,6 +27,7 @@ export class UmbBlockListManagerContext< } } +// TODO: Make discriminator method for this: export const UMB_BLOCK_LIST_MANAGER_CONTEXT = new UmbContextToken< UmbBlockListManagerContext, UmbBlockListManagerContext