initial scaffolding for editor
This commit is contained in:
@@ -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');
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user