initial scaffolding for editor

This commit is contained in:
Niels Lyngsø
2024-02-05 15:50:19 +01:00
parent d2eb13a28e
commit c9fe3537f8
10 changed files with 265 additions and 143 deletions

View File

@@ -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<UmbBlockGridContext>('UmbBlockContext');

View File

@@ -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');
}
}
}

View File

@@ -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<UmbBlockGridTypeModel, BlockLayoutType> {
//
/*
#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');

View File

@@ -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: {

View File

@@ -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`<div>
inner: ${this.name}
<uui-tab-group slot="navigation">
<uui-tab
label="INNER TAB 1"
href="${this._routerPath}/inner-1"
.active=${this._routerPath + '/inner-1' === this._activePath}></uui-tab>
<uui-tab
label="INNER TAB 2"
href="${this._routerPath}/inner-2"
.active=${this._routerPath + '/inner-2' === this._activePath}></uui-tab>
</uui-tab-group>
<umb-router-slot
id="router-slot"
.routes="${this._routes}"
@init=${(event: UmbRouterSlotInitEvent) => {
this._routerPath = event.target.absoluteRouterPath;
}}
@change=${(event: UmbRouterSlotChangeEvent) => {
this._activePath = event.target.localActiveViewPath;
}}></umb-router-slot>
</div>`;
}
static styles = [UmbTextStyles];
}
export default UmbPropertyEditorUIBlockGridInnerTestElement;
declare global {
interface HTMLElementTagNameMap {
'umb-property-editor-ui-block-grid-inner-test': UmbPropertyEditorUIBlockGridInnerTestElement;
}
}

View File

@@ -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<typeof UMB_BLOCK_CATALOGUE_MODAL.DATA, undefined>;
@state()
private _catalogueRouteBuilder?: UmbModalRouteBuilder;
@state()
private _blocks?: Array<UmbBlockTypeBaseModel>;
@property({ attribute: false })
public set layoutEntries(value: Array<UmbBlockGridLayoutModel>) {
this._layoutEntries = value;
}
public get layoutEntries(): Array<UmbBlockGridLayoutModel> {
return this._layoutEntries;
}
private _layoutEntries: Array<UmbBlockGridLayoutModel> = [];
@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`<uui-button-inline-create
href=${this._catalogueRouteBuilder?.({ view: 'create', index: index }) ?? ''}></uui-button-inline-create>
<umb-property-editor-ui-block-list-block data-udi=${layoutEntry.contentUdi} .layout=${layoutEntry}>
</umb-property-editor-ui-block-list-block> `,
)}
<uui-button-group>
<uui-button
id="add-button"
look="placeholder"
label=${this._createButtonLabel}
href=${createPath ?? ''}></uui-button>
<uui-button
label=${this.localize.term('content_createFromClipboard')}
look="placeholder"
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
<uui-icon name="icon-paste-in"></uui-icon>
</uui-button>
</uui-button-group>`;
//
}
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;
}
}

View File

@@ -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<typeof UMB_BLOCK_CATALOGUE_MODAL.DATA, undefined>;
@property()
value = '';
@@ -35,10 +28,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement
private _blockGroups?: Array<UmbBlockTypeGroup>;
@state()
private _layouts: Array<UmbBlockLayoutBaseModel> = [];
@state()
private _catalogueRouteBuilder?: UmbModalRouteBuilder;
private _rootLayouts: Array<UmbBlockGridLayoutModel> = [];
@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`<uui-button-group>
<uui-button
id="add-button"
look="placeholder"
label=${this._createButtonLabel}
href=${this._directRoute ?? this._catalogueRouteBuilder?.({ view: 'create', index: -1 }) ?? ''}></uui-button>
<uui-button
label=${this.localize.term('content_createFromClipboard')}
look="placeholder"
href=${this._catalogueRouteBuilder?.({ view: 'clipboard', index: -1 }) ?? ''}>
<uui-icon name="icon-paste-in"></uui-icon>
</uui-button>
</uui-button-group>`;
return html`<umb-property-editor-ui-block-grid-entries
.layoutEntries=${this._rootLayouts}></umb-property-editor-ui-block-grid-entries>`;
}
static styles = [

View File

@@ -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<number>;
allowAtRoot: boolean;
allowInAreas: boolean;
@@ -21,3 +22,14 @@ export interface UmbBlockGridGroupType {
export interface UmbBlockGridGroupTypeConfiguration extends Partial<UmbBlockGridGroupType> {
blocks: Array<UmbBlockTypeWithGroupKey>;
}
export interface UmbBlockGridLayoutModel extends UmbBlockLayoutBaseModel {
columnSpan: number;
rowSpan: number;
areas: Array<UmbBlockGridLayoutAreaItemModel>;
}
export interface UmbBlockGridLayoutAreaItemModel {
key: string;
items: Array<UmbBlockGridLayoutModel>;
}

View File

@@ -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<UmbBlockListContext>('UmbBlockContext');

View File

@@ -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