Merge pull request #2423 from umbraco/v15/feature/ufm-as-text

UFM as text
This commit is contained in:
Niels Lyngsø
2024-10-10 14:54:10 +02:00
committed by GitHub
6 changed files with 98 additions and 7 deletions

View File

@@ -13,6 +13,7 @@ interface UmbControllerHostBaseDeclaration extends Omit<UmbControllerHost, 'getH
* This enables controllers to be added to the life cycle of this element.
* @param {object} superClass - superclass to be extended.
* @mixin
* @returns {UmbControllerHost} - A class which extends the given superclass.
*/
export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T) => {
class UmbControllerHostBaseClass extends superClass implements UmbControllerHostBaseDeclaration {
@@ -26,7 +27,8 @@ export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T
/**
* Tests if a controller is assigned to this element.
* @param {UmbController} ctrl
* @param {UmbController} ctrl - The controller to check for.
* @returns {boolean} - true if the controller is assigned
*/
hasUmbController(ctrl: UmbController): boolean {
return this.#controllers.indexOf(ctrl) !== -1;
@@ -34,15 +36,16 @@ export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T
/**
* Retrieve controllers matching a filter of this element.
* @param {method} filterMethod
* @param {Function} filterMethod - filter method
* @returns {Array<UmbController>} - currently assigned controllers passing the filter method.
*/
getUmbControllers(filterMethod: (ctrl: UmbController) => boolean): UmbController[] {
getUmbControllers(filterMethod: (ctrl: UmbController) => boolean): Array<UmbController> {
return this.#controllers.filter(filterMethod);
}
/**
* Append a controller to this element.
* @param {UmbController} ctrl
* @param {UmbController} ctrl - the controller to append to this host.
*/
addUmbController(ctrl: UmbController): void {
// If this specific class is already added, then skip out.
@@ -68,7 +71,7 @@ export const UmbControllerHostMixin = <T extends ClassConstructor>(superClass: T
/**
* Remove a controller from this element.
* Notice this will also destroy the controller.
* @param {UmbController} ctrl
* @param {UmbController} ctrl - The controller to remove and destroy from this host.
*/
removeUmbController(ctrl: UmbController): void {
const index = this.#controllers.indexOf(ctrl);

View File

@@ -23,6 +23,7 @@ import type {
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbBlockTypeBaseModel } from '@umbraco-cms/backoffice/block-type';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UmbUfmVirtualRenderController } from '@umbraco-cms/backoffice/ufm';
export abstract class UmbBlockEntryContext<
BlockManagerContextTokenType extends UmbContextToken<BlockManagerContextType>,
@@ -94,6 +95,8 @@ export abstract class UmbBlockEntryContext<
#label = new UmbStringState('');
public readonly label = this.#label.asObservable();
#labelRender = new UmbUfmVirtualRenderController(this);
#generateWorkspaceEditContentPath = (path?: string, contentKey?: string) =>
path && contentKey ? path + 'edit/' + encodeFilePath(contentKey) + '/view/content' : '';
@@ -244,6 +247,11 @@ export abstract class UmbBlockEntryContext<
) {
super(host, 'UmbBlockEntryContext');
this.observe(this.label, (label) => {
this.#labelRender.markdown = label;
});
this.#watchContentForLabelRender();
// Consume block manager:
this.consumeContext(blockManagerContextToken, (manager) => {
this._manager = manager;
@@ -340,6 +348,12 @@ export abstract class UmbBlockEntryContext<
);
}
async #watchContentForLabelRender() {
this.observe(await this.contentValues(), (content) => {
this.#labelRender.value = content;
});
}
getContentKey() {
return this._layout.value?.contentKey;
}
@@ -361,7 +375,7 @@ export abstract class UmbBlockEntryContext<
* @returns {string} - the value of the label.
*/
getLabel() {
return this.#label.value;
return this.#labelRender.toString();
}
#updateCreatePaths() {

View File

@@ -34,6 +34,10 @@ export class UmbUfmRenderElement extends UmbLitElement {
});
}
override toString(): string {
return this.shadowRoot?.textContent ?? '';
}
override render() {
return until(this.#renderMarkdown());
}

View File

@@ -0,0 +1,68 @@
import { UmbUfmRenderElement } from '../components/index.js';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
/**
* Renders a UFM
*/
export class UmbUfmVirtualRenderController extends UmbControllerBase {
#element: UmbUfmRenderElement;
#getTextFromDescendants(element?: Element | null): string {
if (!element) return '';
const items: Array<string> = [];
items.push(element.shadowRoot?.textContent ?? element.textContent ?? '');
if (element.shadowRoot !== null) {
Array.from(element.shadowRoot.children).forEach((element) => {
items.push(this.#getTextFromDescendants(element));
});
}
if (element.children !== null) {
Array.from(element.children).forEach((element) => {
items.push(this.#getTextFromDescendants(element));
});
}
return items.filter((x) => x).join(' ');
}
set markdown(markdown: string | undefined) {
this.#element.markdown = markdown;
}
get markdown(): string | undefined {
return this.#element.markdown;
}
set value(value: unknown | undefined) {
this.#element.value = value;
}
get value(): unknown | undefined {
return this.#element.value;
}
constructor(host: UmbControllerHost) {
super(host);
const element = new UmbUfmRenderElement();
element.inline = true;
element.style.visibility = 'hidden';
this.getHostElement().appendChild(element);
this.#element = element;
}
override hostConnected(): void {}
override toString(): string {
return this.#getTextFromDescendants(this.#element);
}
override destroy(): void {
super.destroy();
this.#element?.destroy();
(this.#element as any) = undefined;
}
}

View File

@@ -1,6 +1,7 @@
export * from './types.js';
export * from './components/index.js';
export * from './contexts/index.js';
export * from './controllers/ufm-virtual-render.controller.js';
export * from './plugins/index.js';
export * from './types.js';
export * from './ufm-component.extension.js';
export * from './ufm-filter.extension.js';

View File

@@ -1,5 +1,6 @@
import type { UmbUfmFilterApi } from './ufm-filter.extension.js';
// TODO: This is not a type? So it should ideally be move to a different file. [NL]
export abstract class UmbUfmFilterBase implements UmbUfmFilterApi {
abstract filter(...args: Array<unknown>): string | undefined | null;
destroy() {}