implement manager for rte property editor
This commit is contained in:
@@ -1 +1,2 @@
|
||||
export * from './block-list-entries.context-token.js';
|
||||
export * from './block-list-entry.context-token.js';
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { UmbBlockRteEntriesContext } from './block-rte-entries.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
// TODO: Make discriminator method for this:
|
||||
export const UMB_BLOCK_RTE_ENTRIES_CONTEXT = new UmbContextToken<UmbBlockRteEntriesContext>('UmbBlockEntriesContext');
|
||||
@@ -0,0 +1,121 @@
|
||||
import type { UmbBlockDataType } from '../../block/index.js';
|
||||
import { UMB_BLOCK_CATALOGUE_MODAL, UmbBlockEntriesContext } from '../../block/index.js';
|
||||
import type { UmbBlockRteWorkspaceData } from '../index.js';
|
||||
import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '../types.js';
|
||||
import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from './block-rte-manager.context.js';
|
||||
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
|
||||
|
||||
export class UmbBlockRteEntriesContext extends UmbBlockEntriesContext<
|
||||
typeof UMB_BLOCK_RTE_MANAGER_CONTEXT,
|
||||
typeof UMB_BLOCK_RTE_MANAGER_CONTEXT.TYPE,
|
||||
UmbBlockRteTypeModel,
|
||||
UmbBlockRteLayoutModel
|
||||
> {
|
||||
//
|
||||
#catalogueModal: UmbModalRouteRegistrationController<typeof UMB_BLOCK_CATALOGUE_MODAL.DATA, undefined>;
|
||||
|
||||
// We will just say its always allowed for RTE for now: [NL]
|
||||
public readonly canCreate = new UmbBooleanState(true).asObservable();
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_BLOCK_RTE_MANAGER_CONTEXT);
|
||||
|
||||
this.#catalogueModal = new UmbModalRouteRegistrationController(this, UMB_BLOCK_CATALOGUE_MODAL)
|
||||
.addUniquePaths(['propertyAlias', 'variantId'])
|
||||
.addAdditionalPath(':view/:index')
|
||||
.onSetup((routingInfo) => {
|
||||
// Idea: Maybe on setup should be async, so it can retrieve the values when needed? [NL]
|
||||
const index = routingInfo.index ? parseInt(routingInfo.index) : -1;
|
||||
return {
|
||||
data: {
|
||||
blocks: this._manager?.getBlockTypes() ?? [],
|
||||
blockGroups: [],
|
||||
openClipboard: routingInfo.view === 'clipboard',
|
||||
blockOriginData: { index: index },
|
||||
},
|
||||
};
|
||||
})
|
||||
.observeRouteBuilder((routeBuilder) => {
|
||||
this._catalogueRouteBuilderState.setValue(routeBuilder);
|
||||
});
|
||||
}
|
||||
|
||||
protected _gotBlockManager() {
|
||||
if (!this._manager) return;
|
||||
|
||||
this.observe(
|
||||
this._manager.layouts,
|
||||
(layouts) => {
|
||||
this._layoutEntries.setValue(layouts);
|
||||
},
|
||||
'observeParentLayouts',
|
||||
);
|
||||
this.observe(
|
||||
this.layoutEntries,
|
||||
(layouts) => {
|
||||
this._manager?.setLayouts(layouts);
|
||||
},
|
||||
'observeThisLayouts',
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this._manager.propertyAlias,
|
||||
(alias) => {
|
||||
this.#catalogueModal.setUniquePathValue('propertyAlias', alias ?? 'null');
|
||||
},
|
||||
'observePropertyAlias',
|
||||
);
|
||||
|
||||
this.observe(
|
||||
this._manager.variantId,
|
||||
(variantId) => {
|
||||
if (variantId) {
|
||||
this.#catalogueModal.setUniquePathValue('variantId', variantId.toString());
|
||||
}
|
||||
},
|
||||
'observePropertyAlias',
|
||||
);
|
||||
}
|
||||
|
||||
getPathForCreateBlock(index: number) {
|
||||
return this._catalogueRouteBuilderState.getValue()?.({ view: 'create', index: index });
|
||||
}
|
||||
|
||||
getPathForClipboard(index: number) {
|
||||
return this._catalogueRouteBuilderState.getValue()?.({ view: 'clipboard', index: index });
|
||||
}
|
||||
|
||||
async setLayouts(layouts: Array<UmbBlockRteLayoutModel>) {
|
||||
await this._retrieveManager;
|
||||
this._manager?.setLayouts(layouts);
|
||||
}
|
||||
|
||||
async create(
|
||||
contentElementTypeKey: string,
|
||||
partialLayoutEntry?: Omit<UmbBlockRteLayoutModel, 'contentUdi'>,
|
||||
modalData?: UmbBlockRteWorkspaceData,
|
||||
) {
|
||||
await this._retrieveManager;
|
||||
return this._manager?.create(contentElementTypeKey, partialLayoutEntry, modalData);
|
||||
}
|
||||
|
||||
// insert Block?
|
||||
|
||||
async insert(
|
||||
layoutEntry: UmbBlockRteLayoutModel,
|
||||
content: UmbBlockDataType,
|
||||
settings: UmbBlockDataType | undefined,
|
||||
modalData: UmbBlockRteWorkspaceData,
|
||||
) {
|
||||
await this._retrieveManager;
|
||||
return this._manager?.insert(layoutEntry, content, settings, modalData) ?? false;
|
||||
}
|
||||
|
||||
// create Block?
|
||||
async delete(contentUdi: string) {
|
||||
// TODO: Loop through children and delete them as well?
|
||||
await super.delete(contentUdi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { UmbBlockRteEntryContext } from './block-rte-entry.context.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
|
||||
// TODO: Make discriminator method for this:
|
||||
export const UMB_BLOCK_RTE_ENTRY_CONTEXT = new UmbContextToken<UmbBlockRteEntryContext>('UmbBlockEntryContext');
|
||||
@@ -0,0 +1,26 @@
|
||||
import { UMB_BLOCK_RTE_MANAGER_CONTEXT } from './block-rte-manager.context.js';
|
||||
import { UMB_BLOCK_RTE_ENTRIES_CONTEXT } from './block-rte-entries.context-token.js';
|
||||
import { UmbBlockEntryContext } from '@umbraco-cms/backoffice/block';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
export class UmbBlockRteEntryContext extends UmbBlockEntryContext<
|
||||
typeof UMB_BLOCK_RTE_MANAGER_CONTEXT,
|
||||
typeof UMB_BLOCK_RTE_MANAGER_CONTEXT.TYPE,
|
||||
typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT,
|
||||
typeof UMB_BLOCK_RTE_ENTRIES_CONTEXT.TYPE
|
||||
> {
|
||||
readonly forceHideContentEditorInOverlay = this._blockType.asObservablePart(
|
||||
(x) => !!x?.forceHideContentEditorInOverlay,
|
||||
);
|
||||
|
||||
readonly showContentEdit = this.forceHideContentEditorInOverlay;
|
||||
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UMB_BLOCK_RTE_MANAGER_CONTEXT, UMB_BLOCK_RTE_ENTRIES_CONTEXT);
|
||||
}
|
||||
|
||||
_gotManager() {}
|
||||
|
||||
_gotEntries() {}
|
||||
|
||||
_gotContentType() {}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { UmbBlockRteLayoutModel, UmbBlockRteTypeModel } from '../types.js';
|
||||
import type { UmbBlockRteWorkspaceData } from '../index.js';
|
||||
import type { UmbBlockDataType } from '../../block/types.js';
|
||||
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbBooleanState } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbBlockManagerContext } from '@umbraco-cms/backoffice/block';
|
||||
|
||||
/**
|
||||
* A implementation of the Block Manager specifically for the Rich Text Editor.
|
||||
*/
|
||||
export class UmbBlockRteManagerContext<
|
||||
BlockLayoutType extends UmbBlockRteLayoutModel = UmbBlockRteLayoutModel,
|
||||
> extends UmbBlockManagerContext<UmbBlockRteTypeModel, BlockLayoutType> {
|
||||
//
|
||||
#inlineEditingMode = new UmbBooleanState(undefined);
|
||||
readonly inlineEditingMode = this.#inlineEditingMode.asObservable();
|
||||
|
||||
setInlineEditingMode(inlineEditingMode: boolean | undefined) {
|
||||
this.#inlineEditingMode.setValue(inlineEditingMode ?? false);
|
||||
}
|
||||
|
||||
create(
|
||||
contentElementTypeKey: string,
|
||||
partialLayoutEntry?: Omit<BlockLayoutType, 'contentUdi'>,
|
||||
modalData?: UmbBlockRteWorkspaceData,
|
||||
) {
|
||||
return super.createBlockData(contentElementTypeKey, partialLayoutEntry);
|
||||
}
|
||||
|
||||
insert(
|
||||
layoutEntry: BlockLayoutType,
|
||||
content: UmbBlockDataType,
|
||||
settings: UmbBlockDataType | undefined,
|
||||
modalData: UmbBlockRteWorkspaceData,
|
||||
) {
|
||||
this._layouts.appendOneAt(layoutEntry, modalData.originData.index ?? -1);
|
||||
|
||||
this.insertBlockData(layoutEntry, content, settings, modalData);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make discriminator method for this:
|
||||
export const UMB_BLOCK_RTE_MANAGER_CONTEXT = new UmbContextToken<UmbBlockRteManagerContext, UmbBlockRteManagerContext>(
|
||||
'UmbBlockManagerContext',
|
||||
);
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './block-rte-entries.context-token.js';
|
||||
export * from './block-rte-entries.context.js';
|
||||
export * from './block-rte-entry.context-token.js';
|
||||
export * from './block-rte-entry.context.js';
|
||||
export * from './block-rte-manager.context.js';
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './context/index.js';
|
||||
export * from './workspace/index.js';
|
||||
export * from './types.js';
|
||||
|
||||
@@ -40,16 +40,20 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase
|
||||
|
||||
async showDialog() {
|
||||
const blockEl = this.editor.selection.getNode();
|
||||
let blockUdi: string | undefined;
|
||||
|
||||
if (blockEl.nodeName === 'UMB-RTE-BLOCK' || blockEl.nodeName === 'UMB-RTE-BLOCK-INLINE') {
|
||||
blockUdi = blockEl.getAttribute('data-content-udi') ?? undefined;
|
||||
const blockUdi = blockEl.getAttribute('data-content-udi') ?? undefined;
|
||||
if (blockUdi) {
|
||||
this.#editBlock(blockUdi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.#openBlockPicker(blockUdi);
|
||||
// If no block is selected, open the block picker:
|
||||
this.#createBlock();
|
||||
}
|
||||
|
||||
async #openBlockPicker(blockUdi?: string) {
|
||||
async #editBlock(blockUdi?: string) {
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modalHandler = modalManager.open(this, UMB_BLOCK_RTE_WORKSPACE_MODAL, {
|
||||
data: {
|
||||
@@ -69,6 +73,8 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase
|
||||
}
|
||||
}
|
||||
|
||||
#createBlock() {}
|
||||
|
||||
#insertBlockInEditor(blockContentUdi: string, displayInline = false) {
|
||||
if (!blockContentUdi) {
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { UmbBlockTypeBaseModel } from '../block-type/index.js';
|
||||
import type { UmbBlockLayoutBaseModel, UmbBlockValueType } from '@umbraco-cms/backoffice/block';
|
||||
|
||||
export const UMB_BLOCK_RTE_TYPE = 'block-rte-type';
|
||||
|
||||
export interface UmbBlockRteTypeModel extends UmbBlockTypeBaseModel {}
|
||||
export interface UmbBlockRteLayoutModel extends UmbBlockLayoutBaseModel {}
|
||||
|
||||
export interface UmbBlockRteValueModel extends UmbBlockValueType<UmbBlockRteLayoutModel> {}
|
||||
|
||||
@@ -6,14 +6,14 @@ import type {
|
||||
import type { UmbWorkspaceModalData } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
|
||||
|
||||
export interface UmbBlockRTEWorkspaceData
|
||||
export interface UmbBlockRteWorkspaceData
|
||||
extends UmbBlockWorkspaceData<{
|
||||
index: number;
|
||||
}> {}
|
||||
|
||||
export type UmbBlockRTEWorkspaceValue = Array<UmbBlockViewPropsType<UmbBlockLayoutBaseModel>>;
|
||||
export type UmbBlockRteWorkspaceValue = Array<UmbBlockViewPropsType<UmbBlockLayoutBaseModel>>;
|
||||
|
||||
export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRTEWorkspaceData, UmbBlockRTEWorkspaceValue>(
|
||||
export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRteWorkspaceData, UmbBlockRteWorkspaceValue>(
|
||||
'Umb.Modal.Workspace',
|
||||
{
|
||||
modal: {
|
||||
@@ -23,4 +23,4 @@ export const UMB_BLOCK_RTE_WORKSPACE_MODAL = new UmbModalToken<UmbBlockRTEWorksp
|
||||
data: { entityType: 'block', preset: {}, originData: { index: -1 } },
|
||||
// Recast the type, so the entityType data prop is not required:
|
||||
},
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbBlockRTEWorkspaceValue>;
|
||||
) as UmbModalToken<Omit<UmbWorkspaceModalData, 'entityType'>, UmbBlockRteWorkspaceValue>;
|
||||
|
||||
@@ -9,12 +9,15 @@ export interface UmbBlockDataType {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface UmbBlockValueType<BlockLayoutType extends UmbBlockLayoutBaseModel> {
|
||||
layout: { [key: string]: Array<BlockLayoutType> | undefined };
|
||||
export interface UmbBlockDataBaseValueType {
|
||||
contentData: Array<UmbBlockDataType>;
|
||||
settingsData: Array<UmbBlockDataType>;
|
||||
}
|
||||
|
||||
export interface UmbBlockValueType<BlockLayoutType extends UmbBlockLayoutBaseModel> extends UmbBlockDataBaseValueType {
|
||||
layout: { [key: string]: Array<BlockLayoutType> | undefined };
|
||||
}
|
||||
|
||||
export interface UmbBlockViewUrlsPropType {
|
||||
editContent?: string;
|
||||
editSettings?: string;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
|
||||
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
|
||||
|
||||
import '../../components/input-tiny-mce/input-tiny-mce.element.js';
|
||||
import { UmbBlockRteEntriesContext, UmbBlockRteManagerContext } from '@umbraco-cms/backoffice/block-rte';
|
||||
import type { UmbBlockDataBaseValueType } from '@umbraco-cms/backoffice/block';
|
||||
|
||||
type RichTextEditorValue = {
|
||||
blocks: object;
|
||||
export interface UmbRichTextEditorValueType extends UmbBlockDataBaseValueType {
|
||||
markup: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-tiny-mce
|
||||
@@ -18,11 +19,20 @@ type RichTextEditorValue = {
|
||||
export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
#configuration?: UmbPropertyEditorConfigCollection;
|
||||
|
||||
@property({ type: Object })
|
||||
value?: RichTextEditorValue = {
|
||||
blocks: {},
|
||||
markup: '',
|
||||
};
|
||||
@property({ attribute: false })
|
||||
public set value(value: UmbRichTextEditorValueType | undefined) {
|
||||
const buildUpValue: Partial<UmbRichTextEditorValueType> = value ? { ...value } : {};
|
||||
buildUpValue.markup ??= '';
|
||||
buildUpValue.contentData ??= [];
|
||||
buildUpValue.settingsData ??= [];
|
||||
this._value = buildUpValue as UmbRichTextEditorValueType;
|
||||
|
||||
this.#managerContext.setContents(buildUpValue.contentData);
|
||||
this.#managerContext.setSettings(buildUpValue.settingsData);
|
||||
}
|
||||
public get value(): UmbRichTextEditorValueType {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
@@ -32,9 +42,41 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements
|
||||
return this.#configuration;
|
||||
}
|
||||
|
||||
@state()
|
||||
private _value: UmbRichTextEditorValueType = {
|
||||
markup: '',
|
||||
contentData: [],
|
||||
settingsData: [],
|
||||
};
|
||||
|
||||
#managerContext = new UmbBlockRteManagerContext(this);
|
||||
#entriesContext = new UmbBlockRteEntriesContext(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.observe(this.#entriesContext.layoutEntries, (layouts) => {
|
||||
// Update manager:
|
||||
this.#managerContext.setLayouts(layouts);
|
||||
});
|
||||
|
||||
this.observe(this.#managerContext.contents, (contents) => {
|
||||
this._value = { ...this._value, contentData: contents };
|
||||
this.#fireChangeEvent();
|
||||
});
|
||||
this.observe(this.#managerContext.settings, (settings) => {
|
||||
this._value = { ...this._value, settingsData: settings };
|
||||
this.#fireChangeEvent();
|
||||
});
|
||||
}
|
||||
|
||||
#fireChangeEvent() {
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
#onChange(event: InputEvent & { target: HTMLInputElement }) {
|
||||
this.value = {
|
||||
blocks: {},
|
||||
...this._value,
|
||||
markup: event.target.value,
|
||||
};
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
@@ -44,7 +86,7 @@ export class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements
|
||||
return html`
|
||||
<umb-input-tiny-mce
|
||||
.configuration=${this.#configuration}
|
||||
.value=${this.value?.markup ?? ''}
|
||||
.value=${this._value?.markup ?? ''}
|
||||
@change=${this.#onChange}>
|
||||
</umb-input-tiny-mce>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user