Introduces getStyles() for Tiptap extensions (#18075)

This commit is contained in:
Lee Kelleher
2025-01-31 12:24:12 +00:00
committed by GitHub
parent 85a81b3d6f
commit 9323d8b0e7
6 changed files with 177 additions and 131 deletions

View File

@@ -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<string, typeof Um
@state()
private readonly _extensions: Array<UmbTiptapExtensionApi> = [];
@state()
private _styles: Array<CSSResultGroup> = [];
@state()
_toolbar: UmbTiptapToolbarValue = [[[]]];
@@ -100,14 +105,24 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
this._toolbar = this.configuration?.getValueByAlias<UmbTiptapToolbarValue>('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<string, typeof Um
!this._editor && !this._extensions?.length,
() => html`<div id="loader"><uui-loader></uui-loader></div>`,
() => html`
${this.#renderStyles()}
<umb-tiptap-toolbar
.toolbar=${this._toolbar}
.editor=${this._editor}
@@ -136,6 +152,15 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
`;
}
#renderStyles() {
if (!this._styles?.length) return;
return html`
<style>
${this._styles.map((style) => unsafeCSS(style))}
</style>
`;
}
static override readonly styles = [
css`
:host {
@@ -158,23 +183,6 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
justify-content: center;
}
.tiptap {
height: 100%;
width: 100%;
outline: none;
white-space: pre-wrap;
min-width: 0;
}
.tiptap .is-editor-empty:first-child::before {
color: var(--uui-color-text);
opacity: 0.55;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
#editor {
/* Required as overflow is set to auto, so that the scrollbars don't appear. */
display: flex;
@@ -189,6 +197,24 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
height: 100%;
width: 100%;
.tiptap {
height: 100%;
width: 100%;
outline: none;
white-space: pre-wrap;
min-width: 0;
.is-editor-empty:first-child::before {
color: var(--uui-color-text);
opacity: 0.55;
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
}
/* The following styles are required for the "StarterKit" extension. */
pre {
background-color: var(--uui-color-surface-alt);
padding: var(--uui-size-space-2) var(--uui-size-space-4);
@@ -220,118 +246,12 @@ export class UmbInputTiptapElement extends UmbFormControlMixin<string, typeof Um
margin-bottom: 0.5em;
}
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);
}
}
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;
}
}
}
`,
];

View File

@@ -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
*/

View File

@@ -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);
}
`;
}

View File

@@ -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);
}
}
`;
}

View File

@@ -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;
}
}
`;
}

View File

@@ -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.
*/