From 9323d8b0e75b5df2288a79ca2f871a873428d81c Mon Sep 17 00:00:00 2001 From: Lee Kelleher Date: Fri, 31 Jan 2025 12:24:12 +0000 Subject: [PATCH] Introduces `getStyles()` for Tiptap extensions (#18075) --- .../input-tiptap/input-tiptap.element.ts | 176 +++++------------- .../src/packages/tiptap/extensions/base.ts | 8 + .../core/embedded-media.tiptap-api.ts | 29 +++ .../extensions/core/image.tiptap-api.ts | 26 ++- .../extensions/core/table.tiptap-api.ts | 63 +++++++ .../src/packages/tiptap/extensions/types.ts | 6 + 6 files changed, 177 insertions(+), 131 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts index 60a5819869..8ad7178036 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -1,12 +1,14 @@ import type { UmbTiptapExtensionApi } from '../../extensions/types.js'; import type { UmbTiptapToolbarValue } from '../types.js'; -import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { css, customElement, html, property, state, unsafeCSS, when } from '@umbraco-cms/backoffice/external/lit'; import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; +import type { Extensions } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import './tiptap-hover-menu.element.js'; @@ -45,6 +47,9 @@ export class UmbInputTiptapElement extends UmbFormControlMixin = []; + @state() + private _styles: Array = []; + @state() _toolbar: UmbTiptapToolbarValue = [[[]]]; @@ -100,14 +105,24 @@ export class UmbInputTiptapElement extends UmbFormControlMixin('toolbar') ?? [[[]]]; - const extensions = this._extensions - .map((ext) => ext.getTiptapExtensions({ configuration: this.configuration })) - .flat(); + const tiptapExtensions: Extensions = []; + + this._extensions.forEach((ext) => { + const tiptapExt = ext.getTiptapExtensions({ configuration: this.configuration }); + if (tiptapExt?.length) { + tiptapExtensions.push(...tiptapExt); + } + + const styles = ext.getStyles(); + if (styles) { + this._styles.push(styles); + } + }); this._editor = new Editor({ element: element, editable: !this.readonly, - extensions: extensions, + extensions: tiptapExtensions, content: this.#value, onBeforeCreate: ({ editor }) => { this._extensions.forEach((ext) => ext.setEditor(editor)); @@ -125,6 +140,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin html`
`, () => html` + ${this.#renderStyles()} + ${this._styles.map((style) => unsafeCSS(style))} + + `; + } + static override readonly styles = [ css` :host { @@ -158,23 +183,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin p, - img { - pointer-events: none; - margin: 0; - padding: 0; - } - - &.ProseMirror-selectednode { - outline: 3px solid var(--uui-color-focus); - } - } - - img { - &.ProseMirror-selectednode { - outline: 3px solid var(--uui-color-focus); - } - } - li { > p { margin: 0; padding: 0; } } - - .umb-embed-holder { - display: inline-block; - position: relative; - } - - .umb-embed-holder > * { - user-select: none; - pointer-events: none; - } - - .umb-embed-holder.ProseMirror-selectednode { - outline: 2px solid var(--uui-palette-spanish-pink-light); - } - - .umb-embed-holder::before { - z-index: 1000; - width: 100%; - height: 100%; - position: absolute; - content: ' '; - } - - .umb-embed-holder.ProseMirror-selectednode::before { - background: rgba(0, 0, 0, 0.025); - } - - /* Table-specific styling */ - .tableWrapper { - margin: 1.5rem 0; - overflow-x: auto; - - table { - border-collapse: collapse; - margin: 0; - overflow: hidden; - table-layout: fixed; - width: 100%; - - td, - th { - border: 1px solid var(--uui-color-border); - box-sizing: border-box; - min-width: 1em; - padding: 6px 8px; - position: relative; - vertical-align: top; - - > * { - margin-bottom: 0; - } - } - - th { - background-color: var(--uui-color-background); - font-weight: bold; - text-align: left; - } - - .selectedCell:after { - background: var(--uui-color-surface-emphasis); - content: ''; - left: 0; - right: 0; - top: 0; - bottom: 0; - pointer-events: none; - position: absolute; - z-index: 2; - } - - .column-resize-handle { - background-color: var(--uui-color-default); - bottom: -2px; - pointer-events: none; - position: absolute; - right: -2px; - top: 0; - width: 3px; - } - } - - .resize-cursor { - cursor: ew-resize; - cursor: col-resize; - } - } } `, ]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/base.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/base.ts index 5911ae8bc0..7a2b218b3e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/base.ts @@ -6,6 +6,7 @@ import type { UmbTiptapToolbarElementApi, } from './types.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -27,6 +28,13 @@ export abstract class UmbTiptapExtensionApiBase extends UmbControllerBase implem this._editor = editor; } + /** + * @inheritdoc + */ + getStyles(): CSSResultGroup | null | undefined { + return null; + } + /** * @inheritdoc */ diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/embedded-media.tiptap-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/embedded-media.tiptap-api.ts index 4cbc0188f2..cffceb3d74 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/embedded-media.tiptap-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/embedded-media.tiptap-api.ts @@ -1,6 +1,35 @@ import { UmbTiptapExtensionApiBase } from '../base.js'; +import { css } from '@umbraco-cms/backoffice/external/lit'; import { umbEmbeddedMedia } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapEmbeddedMediaExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [umbEmbeddedMedia.configure({ inline: true })]; + + override getStyles = () => css` + .umb-embed-holder { + display: inline-block; + position: relative; + } + + .umb-embed-holder > * { + user-select: none; + pointer-events: none; + } + + .umb-embed-holder.ProseMirror-selectednode { + outline: 2px solid var(--uui-palette-spanish-pink-light); + } + + .umb-embed-holder::before { + z-index: 1000; + width: 100%; + height: 100%; + position: absolute; + content: ' '; + } + + .umb-embed-holder.ProseMirror-selectednode::before { + background: rgba(0, 0, 0, 0.025); + } + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/image.tiptap-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/image.tiptap-api.ts index 7bb79bf2c1..1d7a2da449 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/image.tiptap-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/image.tiptap-api.ts @@ -1,8 +1,28 @@ import { UmbTiptapExtensionApiBase } from '../base.js'; +import { css } from '@umbraco-cms/backoffice/external/lit'; import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapImageExtensionApi extends UmbTiptapExtensionApiBase { - getTiptapExtensions() { - return [UmbImage.configure({ inline: true })]; - } + getTiptapExtensions = () => [UmbImage.configure({ inline: true })]; + + override getStyles = () => css` + figure { + > p, + img { + pointer-events: none; + margin: 0; + padding: 0; + } + + &.ProseMirror-selectednode { + outline: 3px solid var(--uui-color-focus); + } + } + + img { + &.ProseMirror-selectednode { + outline: 3px solid var(--uui-color-focus); + } + } + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/table.tiptap-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/table.tiptap-api.ts index d3acf8c647..6f2ec47cf5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/table.tiptap-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/core/table.tiptap-api.ts @@ -1,6 +1,69 @@ import { UmbTiptapExtensionApiBase } from '../base.js'; +import { css } from '@umbraco-cms/backoffice/external/lit'; import { Table, TableHeader, TableRow, TableCell } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapTableExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [Table.configure({ resizable: true }), TableHeader, TableRow, TableCell]; + + override getStyles = () => css` + .tableWrapper { + margin: 1.5rem 0; + overflow-x: auto; + + table { + border-collapse: collapse; + margin: 0; + overflow: hidden; + table-layout: fixed; + width: 100%; + + td, + th { + border: 1px solid var(--uui-color-border); + box-sizing: border-box; + min-width: 1em; + padding: 6px 8px; + position: relative; + vertical-align: top; + + > * { + margin-bottom: 0; + } + } + + th { + background-color: var(--uui-color-background); + font-weight: bold; + text-align: left; + } + + .selectedCell:after { + background: var(--uui-color-surface-emphasis); + content: ''; + left: 0; + right: 0; + top: 0; + bottom: 0; + pointer-events: none; + position: absolute; + z-index: 2; + } + + .column-resize-handle { + background-color: var(--uui-color-default); + bottom: -2px; + pointer-events: none; + position: absolute; + right: -2px; + top: 0; + width: 3px; + } + } + + .resize-cursor { + cursor: ew-resize; + cursor: col-resize; + } + } + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts index 0791bf6b2a..b172f1dae1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/types.ts @@ -1,5 +1,6 @@ import type { ManifestTiptapExtension } from './tiptap.extension.js'; import type { ManifestTiptapToolbarExtension } from './tiptap-toolbar.extension.js'; +import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import type { Editor, Extension, Mark, Node } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; @@ -18,6 +19,11 @@ export interface UmbTiptapExtensionApi extends UmbApi { */ setEditor(editor: Editor): void; + /** + * Gets the styles for the extension + */ + getStyles(): CSSResultGroup | null | undefined; + /** * Gets the Tiptap extensions for the editor. */