diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts index de8480d127..dd7b87b52f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entry/block-grid-entry.element.ts @@ -60,6 +60,9 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper @state() _label = ''; + @state() + _icon?: string; + @state() _workspaceEditContentPath?: string; @@ -105,6 +108,10 @@ export class UmbBlockGridEntryElement extends UmbLitElement implements UmbProper this.#updateBlockViewProps({ label }); this._label = label; }); + this.observe(this.#context.contentElementTypeIcon, (icon) => { + this.#updateBlockViewProps({ icon }); + this._icon = icon; + }); this.observe(this.#context.inlineEditingMode, (mode) => { this._inlineEditingMode = mode; }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index bca25ac505..8ecc6faf3b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -42,6 +42,9 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper @state() _label = ''; + @state() + _icon?: string; + @state() _workspaceEditContentPath?: string; @@ -65,10 +68,14 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper this._hasSettings = !!settingsElementTypeKey; }); this.observe(this.#context.label, (label) => { - const oldValue = this._label; - this._blockViewProps.label = label; this._label = label; - this.requestUpdate('label', oldValue); + this._blockViewProps.label = label; + this.requestUpdate('_blockViewProps'); + }); + this.observe(this.#context.contentElementTypeIcon, (icon) => { + this._icon = icon; + this._blockViewProps.icon = icon; + this.requestUpdate('_blockViewProps'); }); this.observe(this.#context.inlineEditingMode, (inlineEditingMode) => { this._inlineEditingMode = inlineEditingMode; @@ -115,12 +122,12 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper ${this._showContentEdit && this._workspaceEditContentPath ? html` - ` + ` : ''} ${this._hasSettings && this._workspaceEditSettingsPath ? html` - ` + ` : ''} this.#context.requestDelete()}> diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.element.ts index 1769cc53b7..f2e59b38d8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.element.ts @@ -11,7 +11,7 @@ export class UmbBlockRteEntryInlineElement extends UmbBlockRteEntryElement { ...UmbBlockRteEntryElement.styles, css` :host { - display: inline; + display: inline-block; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index d043e02a88..0b7cd42ec6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -1,10 +1,10 @@ import { UmbBlockRteEntryContext } from '../../context/block-rte-entry.context.js'; +import type { UmbBlockRteLayoutModel } from '../../types.js'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { html, css, property, state, customElement } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import '../ref-rte-block/index.js'; import type { UmbBlockViewPropsType } from '@umbraco-cms/backoffice/block'; -import type { UmbBlockListLayoutModel } from '@umbraco-cms/backoffice/block-list'; /** * @element umb-rte-block @@ -34,6 +34,9 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert @state() _label = ''; + @state() + _icon?: string; + @state() _workspaceEditContentPath?: string; @@ -42,7 +45,7 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert // TODO: use this type on the Element Interface for the Manifest. @state() - _blockViewProps: UmbBlockViewPropsType = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render. + _blockViewProps: UmbBlockViewPropsType = { contentUdi: undefined!, urls: {} }; // Set to undefined cause it will be set before we render. constructor() { super(); @@ -57,10 +60,14 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert this._hasSettings = !!settingsElementTypeKey; }); this.observe(this.#context.label, (label) => { - const oldValue = this._label; - this._blockViewProps.label = label; this._label = label; - this.requestUpdate('label', oldValue); + this._blockViewProps.label = label; + this.requestUpdate('_blockViewProps'); + }); + this.observe(this.#context.contentElementTypeIcon, (icon) => { + this._icon = icon; + this._blockViewProps.icon = icon; + this.requestUpdate('_blockViewProps'); }); // Data props: this.observe(this.#context.layout, (layout) => { @@ -84,8 +91,17 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert }); } + connectedCallback() { + super.connectedCallback(); + + // eslint-disable-next-line wc/no-self-class + this.classList.add('uui-font'); + // eslint-disable-next-line wc/no-self-class + this.classList.add('uui-text'); + } + #renderRefBlock() { - return html``; + return html``; } #renderBlock() { @@ -123,6 +139,8 @@ export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropert :host { position: relative; display: block; + user-select: none; + user-drag: auto; } uui-action-bar { position: absolute; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts index f688cbebbd..16fc522e7c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.element.ts @@ -11,6 +11,9 @@ export class UmbRefRteBlockElement extends UmbLitElement { @property({ type: String }) label?: string; + @property({ type: String }) + icon?: string; + @state() _workspaceEditPath?: string; @@ -29,10 +32,9 @@ export class UmbRefRteBlockElement extends UmbLitElement { } render() { - return html``; + return html``; } static styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entry.context.ts index 6e0add4c7a..ce54601d05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-entry.context.ts @@ -19,7 +19,7 @@ export class UmbBlockRteEntryContext extends UmbBlockEntryContext< (x) => !!x?.forceHideContentEditorInOverlay, ); - readonly showContentEdit = this.forceHideContentEditorInOverlay; + readonly showContentEdit = this._blockType.asObservablePart((x) => !x?.forceHideContentEditorInOverlay); constructor(host: UmbControllerHost) { super(host, UMB_BLOCK_RTE_MANAGER_CONTEXT, UMB_BLOCK_RTE_ENTRIES_CONTEXT); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts index 0c06ba5c3e..ad1f02748f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/context/block-rte-manager.context.ts @@ -24,7 +24,19 @@ export class UmbBlockRteManagerContext< partialLayoutEntry?: Omit, modalData?: UmbBlockRteWorkspaceData, ) { - return super.createBlockData(contentElementTypeKey, partialLayoutEntry); + const data = super.createBlockData(contentElementTypeKey, partialLayoutEntry); + + // Find block type. + const blockType = this.getBlockTypes().find((x) => x.contentElementTypeKey === contentElementTypeKey); + if (!blockType) { + throw new Error(`Cannot create block, missing block type for ${contentElementTypeKey}`); + } + + if (blockType.displayInline) { + data.layout.displayInline = true; + } + + return data; } insert( @@ -35,6 +47,10 @@ export class UmbBlockRteManagerContext< ) { if (!this.#editor) return false; + this._layouts.appendOne(layoutEntry); + + this.insertBlockData(layoutEntry, content, settings, modalData); + if (layoutEntry.displayInline) { this.#editor.selection.setContent( ``, @@ -45,9 +61,7 @@ export class UmbBlockRteManagerContext< ); } - this._layouts.appendOne(layoutEntry); - - this.insertBlockData(layoutEntry, content, settings, modalData); + this.#editor.fire('change'); return true; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts index 8087e78717..422391fe62 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts @@ -67,10 +67,11 @@ export abstract class UmbBlockEntryContext< #createAfterPath = new UmbStringState(undefined); readonly createAfterPath = this.#createAfterPath.asObservable(); - #contentElementTypeName = new UmbStringState(undefined); - public readonly contentElementTypeName = this.#contentElementTypeName.asObservable(); - #contentElementTypeAlias = new UmbStringState(undefined); - public readonly contentElementTypeAlias = this.#contentElementTypeAlias.asObservable(); + #contentElementType = new UmbObjectState(undefined); + public readonly contentElementType = this.#contentElementType.asObservable(); + public readonly contentElementTypeName = this.#contentElementType.asObservablePart((x) => x?.name); + public readonly contentElementTypeAlias = this.#contentElementType.asObservablePart((x) => x?.alias); + public readonly contentElementTypeIcon = this.#contentElementType.asObservablePart((x) => x?.icon); _blockType = new UmbObjectState(undefined); public readonly blockType = this._blockType.asObservable(); @@ -301,8 +302,9 @@ export abstract class UmbBlockEntryContext< this.observe( this._manager.contentTypeOf(contentTypeKey), (contentType) => { - this.#contentElementTypeAlias.setValue(contentType?.alias); - this.#contentElementTypeName.setValue(contentType?.name); + //this.#contentElementTypeAlias.setValue(contentType?.alias); + //this.#contentElementTypeName.setValue(contentType?.name); + this.#contentElementType.setValue(contentType); this._gotContentType(contentType); }, 'observeContentElementType', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts index 7f1289a083..a35e8e7038 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts @@ -22,6 +22,7 @@ export interface UmbBlockViewUrlsPropType { export interface UmbBlockViewPropsType { label?: string; + icon?: string; contentUdi: string; layout?: BlockLayoutType; content?: UmbBlockDataType; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts index 788354893c..c9ef4682e4 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/components/input-tiny-mce/input-tiny-mce.defaults.ts @@ -1,5 +1,6 @@ import { UMB_CONTENT_REQUEST_EVENT_TYPE, type UmbContextRequestEvent } from '@umbraco-cms/backoffice/context-api'; import type { RawEditorOptions } from '@umbraco-cms/backoffice/external/tinymce'; +import { UUIIconRequestEvent } from '@umbraco-cms/backoffice/external/uui'; //export const UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH = '/umbraco/backoffice/packages/block/block-rte/index.js'; export const UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH = '@umbraco-cms/backoffice/block-rte'; @@ -13,6 +14,7 @@ export const defaultFallbackConfig: RawEditorOptions = { invalid_elements: 'font', extended_valid_elements: '@[id|class|style],umb-rte-block[!data-content-udi],-umb-rte-block-inline[!data-content-udi],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption', + custom_elements: 'umb-rte-block[!data-content-udi],~umb-rte-block-inline[!data-content-udi]', toolbar: [ 'styles', 'bold', @@ -30,7 +32,7 @@ export const defaultFallbackConfig: RawEditorOptions = { ], init_instance_callback: function (editor) { - // The following code is the context api proxy. + // The following code is the context api proxy. [NL] // It re-dispatches the context api request event to the origin target of this modal, in other words the element that initiated the modal. [NL] editor.dom.doc.addEventListener(UMB_CONTENT_REQUEST_EVENT_TYPE, ((event: UmbContextRequestEvent) => { if (!editor.iframeElement) return; @@ -39,6 +41,19 @@ export const defaultFallbackConfig: RawEditorOptions = { editor.iframeElement.dispatchEvent(event.clone()); }) as EventListener); + // Proxy for retrieving icons from outside the iframe [NL] + editor.dom.doc.addEventListener(UUIIconRequestEvent.ICON_REQUEST, ((event: UUIIconRequestEvent) => { + if (!editor.iframeElement) return; + + const newEvent = new UUIIconRequestEvent(UUIIconRequestEvent.ICON_REQUEST, { + detail: event.detail, + }); + editor.iframeElement.dispatchEvent(newEvent); + if (newEvent.icon !== null) { + event.acceptRequest(newEvent.icon); + } + }) as EventListener); + // Transfer our import-map to the iframe: [NL] const importMapTag = document.head.querySelector('script[type="importmap"]'); if (importMapTag) { @@ -48,8 +63,17 @@ export const defaultFallbackConfig: RawEditorOptions = { editor.dom.doc.head.appendChild(importMap); } + // Transfer our stylesheets to the iframe: [NL] + const stylesheetTags = document.head.querySelectorAll('link[rel="stylesheet"]'); + stylesheetTags.forEach((stylesheetTag) => { + const stylesheet = document.createElement('link'); + stylesheet.rel = 'stylesheet'; + stylesheet.href = stylesheetTag.href; + editor.dom.doc.head.appendChild(stylesheet); + }); + // TODO: Lets use/adapt the router-slot logic so we do not need to add this here [NL] - // TODO: When transfering this code, make sure that we check for target='_parent' or target='top' if its happening within a iframe. [NL] + // TODO: When transferring this code, make sure that we check for target='_parent' or target='top' if its happening within a iframe. [NL] editor.dom.doc.addEventListener('click', (e: MouseEvent) => { // If we try to open link in a new tab, then we want to skip skip: //if ((isWindows && e.ctrlKey) || (!isWindows && e.metaKey)) return; @@ -92,17 +116,14 @@ export const defaultFallbackConfig: RawEditorOptions = { window.history.pushState(null, '', path); }); - function appendScript(path: string) { - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.setAttribute('type', 'module'); - script.text = `import "${path}";`; - editor.dom.doc.head.appendChild(script); - } - - // Load the umb-rte-block component inside the iframe [NL] - appendScript('@umbraco-cms/backoffice/extension-registry'); - appendScript(UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH); + // Load backoffice JS so we can get the umb-rte-block component registered inside the iframe [NL] + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.setAttribute('type', 'module'); + // TODO: Check that we actually get the same extension registry, or find a way so we can make it do so. — It could be some kind of iframe detection? [NL] + script.text = `import "@umbraco-cms/backoffice/extension-registry";`; + script.text = `import "${UMB_BLOCK_ENTRY_WEB_COMPONENTS_ABSOLUTE_PATH}";`; + editor.dom.doc.head.appendChild(script); }, style_formats: [