From 9813131c0f5fe67a4829469eafbdce94e97aaa34 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 17 Mar 2025 09:16:01 +0000 Subject: [PATCH 1/5] Code formatting --- .../src/packages/tiptap/extensions/base.ts | 1 - .../tiptap/extensions/table/table.tiptap-toolbar-api.ts | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) 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 002422080156..7a2b218b3e0a 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 @@ -59,7 +59,6 @@ export abstract class UmbTiptapToolbarElementApiBase extends UmbControllerBase i * @see {ManifestTiptapToolbarExtension} * @param {Editor} editor The editor instance. */ - public abstract execute(editor?: Editor): void; /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts index 79b5da40a517..4fb036f5ba06 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-toolbar-api.ts @@ -6,18 +6,25 @@ import './components/table-insert.element.js'; export class UmbTiptapToolbarTableExtensionApi extends UmbTiptapToolbarElementApiBase { #commands: Record void> = { + // Cells mergeCells: (editor) => editor?.chain().focus().mergeCells().run(), splitCell: (editor) => editor?.chain().focus().splitCell().run(), mergeOrSplit: (editor) => editor?.chain().focus().mergeOrSplit().run(), toggleHeaderCell: (editor) => editor?.chain().focus().toggleHeaderCell().run(), + + // Rows addRowBefore: (editor) => editor?.chain().focus().addRowBefore().run(), addRowAfter: (editor) => editor?.chain().focus().addRowAfter().run(), deleteRow: (editor) => editor?.chain().focus().deleteRow().run(), toggleHeaderRow: (editor) => editor?.chain().focus().toggleHeaderRow().run(), + + // Columns addColumnBefore: (editor) => editor?.chain().focus().addColumnBefore().run(), addColumnAfter: (editor) => editor?.chain().focus().addColumnAfter().run(), deleteColumn: (editor) => editor?.chain().focus().deleteColumn().run(), toggleHeaderColumn: (editor) => editor?.chain().focus().toggleHeaderColumn().run(), + + // Table deleteTable: (editor) => editor?.chain().focus().deleteTable().run(), }; From 7a5f9f8c61b7d6be5ce2d638ec648372415318a8 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 17 Mar 2025 09:16:16 +0000 Subject: [PATCH 2/5] Icons for Tiptap table row/column actions --- .../src/packages/tiptap/extensions/table/manifests.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/manifests.ts index 68263c0e0727..fa9fbf51feb3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/manifests.ts @@ -47,8 +47,8 @@ const toolbarExtensions: Array = [ { label: 'Row', items: [ - { label: 'Add row before', data: 'addRowBefore' }, - { label: 'Add row after', data: 'addRowAfter' }, + { label: 'Add row before', icon: 'icon-page-up', data: 'addRowBefore' }, + { label: 'Add row after', icon: 'icon-page-down', data: 'addRowAfter' }, { label: 'Delete row', icon: 'icon-trash', data: 'deleteRow' }, { label: 'Toggle header row', data: 'toggleHeaderRow' }, ], @@ -56,8 +56,8 @@ const toolbarExtensions: Array = [ { label: 'Column', items: [ - { label: 'Add column before', data: 'addColumnBefore' }, - { label: 'Add column after', data: 'addColumnAfter' }, + { label: 'Add column before', icon: 'icon-navigation-first', data: 'addColumnBefore' }, + { label: 'Add column after', icon: 'icon-tab-key', data: 'addColumnAfter' }, { label: 'Delete column', icon: 'icon-trash', data: 'deleteColumn' }, { label: 'Toggle header column', data: 'toggleHeaderColumn' }, ], From 3be89d33af0eb808d88d255a69aa6dec4e9d3f5c Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 17 Mar 2025 09:16:49 +0000 Subject: [PATCH 3/5] Combined Tiptap exports Removed extra constant --- .../src/packages/tiptap/extensions/manifests.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/manifests.ts index 28c2baf3e070..57b67924fea1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/manifests.ts @@ -645,12 +645,11 @@ const toolbarExtensions: Array = [ }, ]; -const extensions = [ +export const manifests = [ + ...kinds, ...coreExtensions, ...toolbarExtensions, ...blockExtensions, ...styleSelectExtensions, ...tableExtensions, ]; - -export const manifests = [...kinds, ...extensions]; From c6254f535784ec3a432b92018f5e00300ea37a7f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 17 Mar 2025 17:42:27 +0000 Subject: [PATCH 4/5] Added Table Column Menu --- .../extensions/tiptap-umb-table.extension.ts | 63 ++++++++++++++++--- .../cascading-menu-popover.element.ts | 2 +- .../components/table-column-menu.element.ts | 50 +++++++++++++++ .../extensions/table/table.tiptap-api.ts | 2 + 4 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts index 86794bc96ad0..81b9d035e645 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts @@ -1,16 +1,15 @@ import { CellSelection, TableMap } from '@tiptap/pm/tables'; -import { Decoration, DecorationSet } from '@tiptap/pm/view'; -import { EditorState } from '@tiptap/pm/state'; -import { EditorView } from '@tiptap/pm/view'; -import { findParentNode, mergeAttributes, Editor, Node } from '@tiptap/core'; +import { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view'; +import { EditorState, Plugin, Selection, Transaction } from '@tiptap/pm/state'; +import { findParentNode, Editor } from '@tiptap/core'; import { Node as PMNode, ResolvedPos } from '@tiptap/pm/model'; -import { Plugin } from '@tiptap/pm/state'; -import { Selection, Transaction } from '@tiptap/pm/state'; import { Table } from '@tiptap/extension-table'; import { TableCell } from '@tiptap/extension-table-cell'; import { TableHeader } from '@tiptap/extension-table-header'; import { TableRow } from '@tiptap/extension-table-row'; +import type { PluginView } from '@tiptap/pm/state'; import type { Rect } from '@tiptap/pm/tables'; +import type { UUIPopoverContainerElement } from '@umbraco-ui/uui'; export const UmbTable = Table.configure({ resizable: true }); @@ -43,7 +42,13 @@ export const UmbTableHeader = TableHeader.extend({ }, addProseMirrorPlugins() { + const { editor } = this; return [ + new Plugin({ + view(editorView) { + return new UmbTableColumnMenuPlugin(editor, editorView); + }, + }), new Plugin({ props: { decorations: (state) => { @@ -64,14 +69,18 @@ export const UmbTableHeader = TableHeader.extend({ const colSelected = isColumnSelected(index)(selection); const className = colSelected ? 'grip-column selected' : 'grip-column'; + const menu = document.createElement('h4'); + menu.textContent = 'Column Menu'; + const grip = document.createElement('a'); grip.appendChild(document.createElement('uui-symbol-more')); grip.className = className; + grip.setAttribute('popovertarget', colSelected ? 'table-column-menu' : ''); + grip.addEventListener('mousedown', (event) => { event.preventDefault(); event.stopImmediatePropagation(); - this.editor.view.dispatch(selectColumn(index)(this.editor.state.tr)); }); @@ -89,6 +98,44 @@ export const UmbTableHeader = TableHeader.extend({ }, }); +class UmbTableColumnMenuPlugin implements PluginView { + editor: Editor; + tooltip: UUIPopoverContainerElement; + + constructor(editor: Editor, view: EditorView) { + this.editor = editor; + + this.tooltip = document.createElement('uui-popover-container') as UUIPopoverContainerElement; + this.tooltip.id = 'table-column-menu'; + this.tooltip.setAttribute('placement', 'top'); + this.tooltip.setAttribute('popover', 'manual'); + + const menu = document.createElement('umb-tiptap-table-column-menu'); + menu.editor = editor; + this.tooltip.appendChild(menu); + + view.dom.parentNode?.appendChild(this.tooltip); + + this.update(view, null); + } + + update(view: EditorView, prevState: EditorState | null) { + const editor = this.editor; + const { state } = view; + const { from } = state.selection; + + if (isColumnGripSelected({ editor, view, state, from })) { + this.tooltip.showPopover(); + } else { + this.tooltip.hidePopover(); + } + } + + destroy() { + this.tooltip.remove(); + } +} + export const UmbTableCell = TableCell.extend({ addAttributes() { return { @@ -449,7 +496,7 @@ const isColumnGripSelected = ({ return !!gripColumn; }; -export const isRowGripSelected = ({ +const isRowGripSelected = ({ editor, view, state, diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts index 9a0f518bb378..3cae4c23acc6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/components/cascading-menu-popover/cascading-menu-popover.element.ts @@ -100,7 +100,7 @@ export class UmbCascadingMenuPopoverElement extends UUIPopoverContainerElement { :host { --uui-menu-item-flat-structure: 1; - background: var(--uui-color-surface); + background-color: var(--uui-color-surface); border-radius: var(--uui-border-radius); box-shadow: var(--uui-shadow-depth-3); padding: var(--uui-size-space-1); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts new file mode 100644 index 000000000000..7d0e955a0bf8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts @@ -0,0 +1,50 @@ +import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +@customElement('umb-tiptap-table-column-menu') +export class UmbTableColumnMenuElement extends UmbLitElement { + @property({ attribute: false }) + editor?: Editor; + + #onAddColumnBefore = () => this.editor?.chain().focus().addColumnBefore().run(); + #onAddColumnAfter = () => this.editor?.chain().focus().addColumnAfter().run(); + #onDeleteColumn = () => this.editor?.chain().focus().deleteColumn().run(); + + override render() { + return html` + + + + + + + + + + `; + } + + static override readonly styles = [ + css` + :host { + --uui-menu-item-flat-structure: 1; + + display: flex; + flex-direction: column; + + background-color: var(--uui-color-surface); + border-radius: var(--uui-border-radius); + box-shadow: var(--uui-shadow-depth-3); + } + `, + ]; +} + +export { UmbTableColumnMenuElement as element }; + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-table-column-menu': UmbTableColumnMenuElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts index 74180c14fda4..e6cf3ecb7955 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts @@ -2,6 +2,8 @@ import { UmbTiptapExtensionApiBase } from '../base.js'; import { css } from '@umbraco-cms/backoffice/external/lit'; import { UmbTable, UmbTableHeader, UmbTableRow, UmbTableCell } from '@umbraco-cms/backoffice/external/tiptap'; +import './components/table-column-menu.element.js'; + export default class UmbTiptapTableExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [UmbTable, UmbTableHeader, UmbTableRow, UmbTableCell]; From c60f2aa58ada5ac9f2412401d2990ac49995132e Mon Sep 17 00:00:00 2001 From: leekelleher Date: Tue, 18 Mar 2025 09:48:20 +0000 Subject: [PATCH 5/5] Added Table Row Menu Made the bubble menu reusable --- .../tiptap-umb-bubble-menu.extension.ts | 109 ++++++++++++++++++ .../extensions/tiptap-umb-table.extension.ts | 70 ++++------- .../src/external/tiptap/index.ts | 1 + .../components/table-column-menu.element.ts | 8 +- .../components/table-row-menu.element.ts | 50 ++++++++ .../extensions/table/table.tiptap-api.ts | 1 + 6 files changed, 185 insertions(+), 54 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-row-menu.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts new file mode 100644 index 000000000000..aab466bf6a29 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-bubble-menu.extension.ts @@ -0,0 +1,109 @@ +import type { UUIPopoverContainerElement } from '../../uui/index.js'; +import { Extension } from '@tiptap/core'; +import { Editor } from '@tiptap/core'; +import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state'; +import { EditorView } from '@tiptap/pm/view'; +import type { PluginView } from '@tiptap/pm/state'; + +export interface UmbTiptapBubbleMenuElement extends HTMLElement { + editor?: Editor; +} + +export type UmbBubbleMenuPluginProps = { + unique: string; + placement?: UUIPopoverContainerElement['placement']; + elementName?: string | null; + shouldShow?: + | ((props: { editor: Editor; view: EditorView; state: EditorState; from: number; to: number }) => boolean) + | null; +}; + +export type UmbBubbleMenuOptions = UmbBubbleMenuPluginProps; + +export const UmbBubbleMenu = Extension.create({ + name: 'umbBubbleMenu', + + addOptions() { + return { + unique: 'umb-tiptap-menu', + placement: 'top', + elementName: null, + shouldShow: null, + }; + }, + + addProseMirrorPlugins() { + if (!this.options.unique || !this.options.elementName) { + return []; + } + + return [ + UmbBubbleMenuPlugin(this.editor, { + unique: this.options.unique, + placement: this.options.placement, + elementName: this.options.elementName, + shouldShow: this.options.shouldShow, + }), + ]; + }, +}); + +class UmbBubbleMenuPluginView implements PluginView { + #editor: Editor; + + #popover: UUIPopoverContainerElement; + + #shouldShow: UmbBubbleMenuPluginProps['shouldShow']; + + constructor(editor: Editor, view: EditorView, props: UmbBubbleMenuPluginProps) { + this.#editor = editor; + + this.#shouldShow = props.shouldShow ?? null; + + this.#popover = document.createElement('uui-popover-container') as UUIPopoverContainerElement; + this.#popover.id = props.unique; + this.#popover.setAttribute('placement', props.placement ?? 'top'); + this.#popover.setAttribute('popover', 'manual'); + + if (props.elementName) { + const menu = document.createElement(props.elementName) as UmbTiptapBubbleMenuElement; + menu.editor = editor; + this.#popover.appendChild(menu); + } + + view.dom.parentNode?.appendChild(this.#popover); + + this.update(view, null); + } + + update(view: EditorView, prevState: EditorState | null) { + const editor = this.#editor; + + const { state } = view; + const { selection } = state; + + const { ranges } = selection; + const from = Math.min(...ranges.map((range) => range.$from.pos)); + const to = Math.max(...ranges.map((range) => range.$to.pos)); + + const shouldShow = this.#shouldShow?.({ editor, view, state, from, to }); + + if (!shouldShow) { + this.#popover.hidePopover(); + } else { + this.#popover.showPopover(); + } + } + + destroy() { + this.#popover.remove(); + } +} + +export const UmbBubbleMenuPlugin = (editor: Editor, props: UmbBubbleMenuPluginProps) => { + return new Plugin({ + view(editorView) { + return new UmbBubbleMenuPluginView(editor, editorView, props); + }, + }); +}; diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts index 81b9d035e645..e015be74e252 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/extensions/tiptap-umb-table.extension.ts @@ -1,3 +1,4 @@ +import { UmbBubbleMenuPlugin } from './tiptap-umb-bubble-menu.extension.js'; import { CellSelection, TableMap } from '@tiptap/pm/tables'; import { Decoration, DecorationSet, EditorView } from '@tiptap/pm/view'; import { EditorState, Plugin, Selection, Transaction } from '@tiptap/pm/state'; @@ -7,9 +8,7 @@ import { Table } from '@tiptap/extension-table'; import { TableCell } from '@tiptap/extension-table-cell'; import { TableHeader } from '@tiptap/extension-table-header'; import { TableRow } from '@tiptap/extension-table-row'; -import type { PluginView } from '@tiptap/pm/state'; import type { Rect } from '@tiptap/pm/tables'; -import type { UUIPopoverContainerElement } from '@umbraco-ui/uui'; export const UmbTable = Table.configure({ resizable: true }); @@ -44,9 +43,12 @@ export const UmbTableHeader = TableHeader.extend({ addProseMirrorPlugins() { const { editor } = this; return [ - new Plugin({ - view(editorView) { - return new UmbTableColumnMenuPlugin(editor, editorView); + UmbBubbleMenuPlugin(this.editor, { + unique: 'table-column-menu', + placement: 'top', + elementName: 'umb-tiptap-table-column-menu', + shouldShow(props) { + return isColumnGripSelected(props); }, }), new Plugin({ @@ -67,15 +69,11 @@ export const UmbTableHeader = TableHeader.extend({ decorations.push( Decoration.widget(pos + 1, () => { const colSelected = isColumnSelected(index)(selection); - const className = colSelected ? 'grip-column selected' : 'grip-column'; - - const menu = document.createElement('h4'); - menu.textContent = 'Column Menu'; const grip = document.createElement('a'); grip.appendChild(document.createElement('uui-symbol-more')); - grip.className = className; + grip.className = colSelected ? 'grip-column selected' : 'grip-column'; grip.setAttribute('popovertarget', colSelected ? 'table-column-menu' : ''); grip.addEventListener('mousedown', (event) => { @@ -98,44 +96,6 @@ export const UmbTableHeader = TableHeader.extend({ }, }); -class UmbTableColumnMenuPlugin implements PluginView { - editor: Editor; - tooltip: UUIPopoverContainerElement; - - constructor(editor: Editor, view: EditorView) { - this.editor = editor; - - this.tooltip = document.createElement('uui-popover-container') as UUIPopoverContainerElement; - this.tooltip.id = 'table-column-menu'; - this.tooltip.setAttribute('placement', 'top'); - this.tooltip.setAttribute('popover', 'manual'); - - const menu = document.createElement('umb-tiptap-table-column-menu'); - menu.editor = editor; - this.tooltip.appendChild(menu); - - view.dom.parentNode?.appendChild(this.tooltip); - - this.update(view, null); - } - - update(view: EditorView, prevState: EditorState | null) { - const editor = this.editor; - const { state } = view; - const { from } = state.selection; - - if (isColumnGripSelected({ editor, view, state, from })) { - this.tooltip.showPopover(); - } else { - this.tooltip.hidePopover(); - } - } - - destroy() { - this.tooltip.remove(); - } -} - export const UmbTableCell = TableCell.extend({ addAttributes() { return { @@ -173,7 +133,16 @@ export const UmbTableCell = TableCell.extend({ }, addProseMirrorPlugins() { + const { editor } = this; return [ + UmbBubbleMenuPlugin(this.editor, { + unique: 'table-row-menu', + placement: 'left', + elementName: 'umb-tiptap-table-row-menu', + shouldShow(props) { + return isRowGripSelected(props); + }, + }), new Plugin({ props: { decorations: (state) => { @@ -192,12 +161,13 @@ export const UmbTableCell = TableCell.extend({ decorations.push( Decoration.widget(pos + 1, () => { const rowSelected = isRowSelected(index)(selection); - const className = rowSelected ? 'grip-row selected' : 'grip-row'; const grip = document.createElement('a'); grip.appendChild(document.createElement('uui-symbol-more')); - grip.className = className; + grip.className = rowSelected ? 'grip-row selected' : 'grip-row'; + grip.setAttribute('popovertarget', rowSelected ? 'table-row-menu' : ''); + grip.addEventListener('mousedown', (event) => { event.preventDefault(); event.stopImmediatePropagation(); diff --git a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts index 16f06c87da1e..607acb91f89d 100644 --- a/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts +++ b/src/Umbraco.Web.UI.Client/src/external/tiptap/index.ts @@ -37,6 +37,7 @@ export * from './extensions/tiptap-html-global-attributes.extension.js'; export * from './extensions/tiptap-text-direction-extension.js'; export * from './extensions/tiptap-text-indent-extension.js'; export * from './extensions/tiptap-trailing-node.extension.js'; +export * from './extensions/tiptap-umb-bubble-menu.extension.js'; export * from './extensions/tiptap-umb-embedded-media.extension.js'; export * from './extensions/tiptap-umb-image.extension.js'; export * from './extensions/tiptap-umb-link.extension.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts index 7d0e955a0bf8..77075faf1389 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-column-menu.element.ts @@ -1,9 +1,9 @@ import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor, UmbTiptapBubbleMenuElement } from '@umbraco-cms/backoffice/external/tiptap'; @customElement('umb-tiptap-table-column-menu') -export class UmbTableColumnMenuElement extends UmbLitElement { +export class UmbTiptapTableColumnMenuElement extends UmbLitElement implements UmbTiptapBubbleMenuElement { @property({ attribute: false }) editor?: Editor; @@ -41,10 +41,10 @@ export class UmbTableColumnMenuElement extends UmbLitElement { ]; } -export { UmbTableColumnMenuElement as element }; +export default UmbTiptapTableColumnMenuElement; declare global { interface HTMLElementTagNameMap { - 'umb-tiptap-table-column-menu': UmbTableColumnMenuElement; + 'umb-tiptap-table-column-menu': UmbTiptapTableColumnMenuElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-row-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-row-menu.element.ts new file mode 100644 index 000000000000..7678c77810fd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/components/table-row-menu.element.ts @@ -0,0 +1,50 @@ +import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; +import type { Editor, UmbTiptapBubbleMenuElement } from '@umbraco-cms/backoffice/external/tiptap'; + +@customElement('umb-tiptap-table-row-menu') +export class UmbTiptapTableRowMenuElement extends UmbLitElement implements UmbTiptapBubbleMenuElement { + @property({ attribute: false }) + editor?: Editor; + + #onAddRowBefore = () => this.editor?.chain().focus().addRowBefore().run(); + #onAddRowAfter = () => this.editor?.chain().focus().addRowAfter().run(); + #onDeleteRow = () => this.editor?.chain().focus().deleteRow().run(); + + override render() { + return html` + + + + + + + + + + `; + } + + static override readonly styles = [ + css` + :host { + --uui-menu-item-flat-structure: 1; + + display: flex; + flex-direction: column; + + background-color: var(--uui-color-surface); + border-radius: var(--uui-border-radius); + box-shadow: var(--uui-shadow-depth-3); + } + `, + ]; +} + +export default UmbTiptapTableRowMenuElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-tiptap-table-row-menu': UmbTiptapTableRowMenuElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts index e6cf3ecb7955..652984408d4f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/table/table.tiptap-api.ts @@ -3,6 +3,7 @@ import { css } from '@umbraco-cms/backoffice/external/lit'; import { UmbTable, UmbTableHeader, UmbTableRow, UmbTableCell } from '@umbraco-cms/backoffice/external/tiptap'; import './components/table-column-menu.element.js'; +import './components/table-row-menu.element.js'; export default class UmbTiptapTableExtensionApi extends UmbTiptapExtensionApiBase { getTiptapExtensions = () => [UmbTable, UmbTableHeader, UmbTableRow, UmbTableCell];