diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts new file mode 100644 index 0000000000..996fc4482b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icon.registry.ts @@ -0,0 +1,81 @@ +import { UmbIconRegistry } from '@umbraco-cms/backoffice/icon'; + +export class UmbTiptapIconRegistry extends UmbIconRegistry { + constructor() { + super(); + + this.defineIcon( + 'bold', + ``, + ); + + this.defineIcon( + 'italic', + ``, + ); + this.defineIcon( + 'underline', + ``, + ); + this.defineIcon( + 'strike', + ``, + ); + this.defineIcon( + 'heading1', + ``, + ); + this.defineIcon( + 'heading2', + ``, + ); + this.defineIcon( + 'heading3', + ``, + ); + this.defineIcon( + 'blockquote', + ``, + ); + this.defineIcon( + 'code-block', + ``, + ); + this.defineIcon( + 'bullet-list', + ``, + ); + this.defineIcon( + 'ordered-list', + ``, + ); + this.defineIcon( + 'horizontal-rule', + ``, + ); + this.defineIcon( + 'text-align-left', + ``, + ); + this.defineIcon( + 'text-align-center', + ``, + ); + this.defineIcon( + 'text-align-right', + ``, + ); + this.defineIcon( + 'text-align-justify', + ``, + ); + this.defineIcon( + 'link', + ``, + ); + this.defineIcon( + 'umbraco', + ``, + ); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts deleted file mode 100644 index 2676404411..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/icons.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { html } from '@umbraco-cms/backoffice/external/lit'; - -const iconSize = '16px'; -export const bold = html` - -`; - -export const italic = html` - - - -`; - -export const underline = html` - - -`; - -export const strikethrough = html` - - - -`; -export const heading1 = html` - - - - -`; -export const heading2 = html` - - - - -`; -export const heading3 = html` - - - - - -`; -export const blockquote = html` - - - - -`; -export const code = html` - - -`; -export const bulletList = html` - - - - - - -`; -export const orderedList = html` - - - - - - -`; -export const horizontalRule = html` - - - -`; -export const alignLeft = html` - - - -`; -export const alignCenter = html` - - - -`; -export const alignRight = html` - - - -`; -export const alignJustify = html` - - - -`; - -export const link = html` - - -`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts index 4bb0a53188..095567fd41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/input-tiptap.element.ts @@ -3,28 +3,14 @@ import { css, customElement, html, property, state, when } from '@umbraco-cms/ba import { loadManifestApi } from '@umbraco-cms/backoffice/extension-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { - Blockquote, - Bold, - BulletList, - Code, - CodeBlock, Document, Dropcursor, Editor, Gapcursor, HardBreak, - Heading, History, - HorizontalRule, - Italic, - Link, - ListItem, - OrderedList, Paragraph, - Strike, Text, - TextAlign, - Underline, } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -83,26 +69,7 @@ export class UmbInputTiptapElement extends UmbFormControlMixin { this.value = editor.getHTML(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts index 1567febabb..39e75dc1b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-fixed-menu.element.ts @@ -1,7 +1,6 @@ -import type { UmbTiptapToolbarButton } from '../../extensions/types.js'; import type { ManifestTiptapExtension } from '../../extensions/tiptap-extension.js'; -import * as icons from './icons.js'; -import { css, customElement, html, property, state, when } from '@umbraco-cms/backoffice/external/lit'; +import { UmbTiptapIconRegistry } from './icon.registry.js'; +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'; @@ -10,135 +9,6 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { @property({ type: Boolean, reflect: true }) readonly = false; - @state() - actions: Array = [ - // TODO: I don't think we need a paragraph button. It's the default state. - // { - // name: 'paragraph', - // icon: html` - // - // - // `, - // command: (editor) => editor?.chain().focus().setParagraph().run(), - // }, - { - name: 'bold', - icon: icons.bold, - isActive: (editor) => editor?.isActive('bold'), - command: (editor) => editor?.chain().focus().toggleBold().run(), - }, - { - name: 'italic', - icon: icons.italic, - isActive: (editor) => editor?.isActive('italic'), - command: (editor) => editor?.chain().focus().toggleItalic().run(), - }, - { - name: 'underline', - icon: icons.underline, - isActive: (editor) => editor?.isActive('underline'), - command: (editor) => editor?.chain().focus().toggleUnderline().run(), - }, - { - name: 'strikethrough', - icon: icons.strikethrough, - isActive: (editor) => editor?.isActive('strike'), - command: (editor) => editor?.chain().focus().toggleStrike().run(), - }, - { - name: 'h1', - icon: icons.heading1, - isActive: (editor) => editor?.isActive('heading', { level: 1 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 1 }).run(), - }, - { - name: 'h2', - icon: icons.heading2, - isActive: (editor) => editor?.isActive('heading', { level: 2 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 2 }).run(), - }, - { - name: 'h3', - icon: icons.heading3, - isActive: (editor) => editor?.isActive('heading', { level: 3 }), - command: (editor) => editor?.chain().focus().toggleHeading({ level: 3 }).run(), - }, - { - name: 'blockquote', - icon: icons.blockquote, - isActive: (editor) => editor?.isActive('blockquote'), - command: (editor) => editor?.chain().focus().toggleBlockquote().run(), - }, - { - name: 'code', - icon: icons.code, - isActive: (editor) => editor?.isActive('codeBlock'), - command: (editor) => editor?.chain().focus().toggleCodeBlock().run(), - }, - { - name: 'bullet-list', - icon: icons.bulletList, - isActive: (editor) => editor?.isActive('bulletList'), - command: (editor) => editor?.chain().focus().toggleBulletList().run(), - }, - { - name: 'ordered-list', - icon: icons.orderedList, - isActive: (editor) => editor?.isActive('orderedList'), - command: (editor) => editor?.chain().focus().toggleOrderedList().run(), - }, - { - name: 'horizontal-rule', - icon: icons.horizontalRule, - isActive: (editor) => editor?.isActive('horizontalRule'), - command: (editor) => editor?.chain().focus().setHorizontalRule().run(), - }, - { - name: 'align-left', - icon: icons.alignLeft, - isActive: (editor) => editor?.isActive({ textAlign: 'left' }), - command: (editor) => editor?.chain().focus().setTextAlign('left').run(), - }, - { - name: 'align-center', - icon: icons.alignCenter, - isActive: (editor) => editor?.isActive({ textAlign: 'center' }), - command: (editor) => editor?.chain().focus().setTextAlign('center').run(), - }, - { - name: 'align-right', - icon: icons.alignRight, - isActive: (editor) => editor?.isActive({ textAlign: 'right' }), - command: (editor) => editor?.chain().focus().setTextAlign('right').run(), - }, - { - name: 'align-justify', - icon: icons.alignJustify, - isActive: (editor) => editor?.isActive({ textAlign: 'justify' }), - command: (editor) => editor?.chain().focus().setTextAlign('justify').run(), - }, - { - name: 'link', - icon: icons.link, - isActive: (editor) => editor?.isActive('link'), - command: () => { - const text = prompt('Enter the text'); - const url = prompt('Enter the URL'); - - if (url && text && this.editor) { - const { from } = this.editor.state.selection; - this.editor - .chain() - .focus() - .insertContent(text) - .setTextSelection({ from: from, to: from + text.length }) - .setLink({ href: url, target: '_blank' }) - .run(); - } - }, - }, - ]; - @property({ attribute: false }) set editor(value) { const oldValue = this.#editor; @@ -158,22 +28,15 @@ export class UmbTiptapFixedMenuElement extends UmbLitElement { this.requestUpdate(); }; + #registry = new UmbTiptapIconRegistry(); + + constructor() { + super(); + this.#registry.attach(this); + } + override render() { return html` - ${this.actions.map( - (action) => html` - - `, - )} !!ext.kind || !!ext.element} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts index 28c9feb62e..30f1458cb7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/components/input-tiptap/tiptap-hover-menu.element.ts @@ -24,7 +24,6 @@ export class UmbTiptapHoverMenuElement extends LitElement { } #onUpdate = () => { - console.log('LINK ACTIVE'); if (this.editor?.isActive('link')) { // show the popover this.showPopover(); @@ -34,7 +33,6 @@ export class UmbTiptapHoverMenuElement extends LitElement { }; override render() { - console.log('RENDER HOVER MENU'); return html``; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts new file mode 100644 index 0000000000..101299b5fb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/blockquote.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Blockquote } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Blockquote', + name: 'Blockquote Tiptap Extension', + api: () => import('./blockquote.extension.js'), + weight: 995, + meta: { + alias: 'blockquote', + icon: 'blockquote', + label: 'Blockquote', + }, +}; + +export default class UmbTiptapBlockquotePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Blockquote]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBlockquote().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts new file mode 100644 index 0000000000..6524e15b04 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bold.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Bold } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Bold', + name: 'Bold Tiptap Extension', + api: () => import('./bold.extension.js'), + weight: 999, + meta: { + alias: 'bold', + icon: 'bold', + label: 'Bold', + }, +}; + +export default class UmbTiptapBoldPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Bold]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBold().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts new file mode 100644 index 0000000000..853cbb202f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/bullet-list.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { BulletList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.BulletList', + name: 'Bullet List Tiptap Extension', + api: () => import('./bullet-list.extension.js'), + weight: 993, + meta: { + alias: 'bullet-list', + icon: 'bullet-list', + label: 'Bullet List', + }, +}; + +export default class UmbTiptapBulletListPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [BulletList, ListItem]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleBulletList().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts new file mode 100644 index 0000000000..42dea9c02f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/code-block.extension.ts @@ -0,0 +1,27 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Code, CodeBlock } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.CodeBlock', + name: 'Code Block Tiptap Extension', + api: () => import('./code-block.extension.js'), + weight: 994, + meta: { + alias: 'code-block', + icon: 'code-block', + label: 'Code Block', + }, +}; + +export default class UmbTiptapCodePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Code, CodeBlock]; + + override execute(editor?: Editor) { + // editor.chain().focus().toggleCode().run(); + editor?.chain().focus().toggleCodeBlock().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts new file mode 100644 index 0000000000..6f1ac09400 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading1.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading1', + name: 'Heading 1 Tiptap Extension', + api: () => import('./heading1.extension.js'), + weight: 949, + meta: { + alias: 'heading1', + icon: 'heading1', + label: 'Heading 1', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 1 }).run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts new file mode 100644 index 0000000000..8f7ad839b2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading2.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading2', + name: 'Heading 2 Tiptap Extension', + api: () => import('./heading2.extension.js'), + weight: 948, + meta: { + alias: 'heading2', + icon: 'heading2', + label: 'Heading 2', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 2 }).run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts new file mode 100644 index 0000000000..f1d11a912e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/heading3.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Heading } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Heading3', + name: 'Heading 3 Tiptap Extension', + api: () => import('./heading3.extension.js'), + weight: 947, + meta: { + alias: 'heading3', + icon: 'heading3', + label: 'Heading 3', + }, +}; + +export default class UmbTiptapHeading1Plugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Heading]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleHeading({ level: 3 }).run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts new file mode 100644 index 0000000000..4e29557f2a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/horizontal-rule.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { HorizontalRule } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.HorizontalRule', + name: 'Horizontal Rule Tiptap Extension', + api: () => import('./horizontal-rule.extension.js'), + weight: 991, + meta: { + alias: 'horizontal-rule', + icon: 'horizontal-rule', + label: 'Horizontal Rule', + }, +}; + +export default class UmbTiptapHorizontalRulePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [HorizontalRule]; + + override execute(editor?: Editor) { + editor?.chain().focus().setHorizontalRule().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts new file mode 100644 index 0000000000..1bd069617f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/image.extension.ts @@ -0,0 +1,17 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + alias: 'Umb.Tiptap.Image', + name: 'Image Tiptap Extension', + api: () => import('./image.extension.js'), + meta: {}, +}; + +export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { + getTiptapExtensions() { + return [UmbImage.configure({ inline: true })]; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts new file mode 100644 index 0000000000..2343355c08 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/italic.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Italic } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Italic', + name: 'Italic Tiptap Extension', + api: () => import('./italic.extension.js'), + weight: 998, + meta: { + alias: 'italic', + icon: 'italic', + label: 'Italic', + }, +}; + +export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Italic]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleItalic().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts new file mode 100644 index 0000000000..f45ce80e1b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/manifests.ts @@ -0,0 +1,38 @@ +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { manifest as blockquote } from './blockquote.extension.js'; +import { manifest as bold } from './bold.extension.js'; +import { manifest as bulletList } from './bullet-list.extension.js'; +import { manifest as codeBlock } from './code-block.extension.js'; +import { manifest as image } from './image.extension.js'; +import { manifest as italic } from './italic.extension.js'; +import { manifest as heading1 } from './heading1.extension.js'; +import { manifest as heading2 } from './heading2.extension.js'; +import { manifest as heading3 } from './heading3.extension.js'; +import { manifest as horizontalRule } from './horizontal-rule.extension.js'; +import { manifest as orderedList } from './ordered-list.extension.js'; +import { manifest as strike } from './strike.extension.js'; +import { manifest as textAlignLeft } from './text-align-left.extension.js'; +import { manifest as textAlignCenter } from './text-align-center.extension.js'; +import { manifest as textAlignRight } from './text-align-right.extension.js'; +import { manifest as textAlignJustify } from './text-align-justify.extension.js'; +import { manifest as underline } from './underline.extension.js'; + +export const manifests: Array = [ + blockquote, + bold, + bulletList, + codeBlock, + image, + italic, + heading1, + heading2, + heading3, + horizontalRule, + orderedList, + strike, + textAlignLeft, + textAlignCenter, + textAlignRight, + textAlignJustify, + underline, +]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts new file mode 100644 index 0000000000..85e650e273 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/ordered-list.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { OrderedList, ListItem } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.OrderedList', + name: 'Ordered List Tiptap Extension', + api: () => import('./ordered-list.extension.js'), + weight: 992, + meta: { + alias: 'ordered-list', + icon: 'ordered-list', + label: 'Ordered List', + }, +}; + +export default class UmbTiptapOrderedListPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [OrderedList, ListItem]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleOrderedList().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts new file mode 100644 index 0000000000..a3e5feb0a9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/strike.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Strike } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Strike', + name: 'Strike Tiptap Extension', + api: () => import('./strike.extension.js'), + weight: 996, + meta: { + alias: 'strike', + icon: 'strike', + label: 'Strike', + }, +}; + +export default class UmbTiptapStrikePlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Strike]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleStrike().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts new file mode 100644 index 0000000000..5e6caad8cd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-center.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignCenter', + name: 'Text Align Center Tiptap Extension', + api: () => import('./text-align-center.extension.js'), + weight: 918, + meta: { + alias: 'text-align-center', + icon: 'text-align-center', + label: 'Text Align Center', + }, +}; + +export default class UmbTiptapTextAlignCenterPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('center').run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts new file mode 100644 index 0000000000..0c021383b5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-justify.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignJustify', + name: 'Text Align Justify Tiptap Extension', + api: () => import('./text-align-justify.extension.js'), + weight: 916, + meta: { + alias: 'text-align-justify', + icon: 'text-align-justify', + label: 'Text Align Justify', + }, +}; + +export default class UmbTiptapTextAlignJustifyPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('justify').run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts new file mode 100644 index 0000000000..aedfbc2d85 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-left.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignLeft', + name: 'Text Align Left Tiptap Extension', + api: () => import('./text-align-left.extension.js'), + weight: 919, + meta: { + alias: 'text-align-left', + icon: 'text-align-left', + label: 'Text Align Left', + }, +}; + +export default class UmbTiptapTextAlignLeftPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('left').run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts new file mode 100644 index 0000000000..049781dd1e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/text-align-right.extension.ts @@ -0,0 +1,30 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { TextAlign } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.TextAlignRight', + name: 'Text Align Right Tiptap Extension', + api: () => import('./text-align-right.extension.js'), + weight: 917, + meta: { + alias: 'text-align-right', + icon: 'text-align-right', + label: 'Text Align Right', + }, +}; + +export default class UmbTiptapTextAlignRightPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [ + TextAlign.configure({ + types: ['heading', 'paragraph', 'blockquote', 'orderedList', 'bulletList', 'codeBlock'], + }), + ]; + + override execute(editor?: Editor) { + editor?.chain().focus().setTextAlign('right').run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts new file mode 100644 index 0000000000..52387b9b62 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/core/underline.extension.ts @@ -0,0 +1,26 @@ +import { UmbTiptapExtensionApi } from '../types.js'; +import type { ManifestTiptapExtension } from '../tiptap-extension.js'; +import { Underline } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Underline', + name: 'Underline Tiptap Extension', + api: () => import('./underline.extension.js'), + weight: 997, + meta: { + alias: 'underline', + icon: 'underline', + label: 'Underline', + }, +}; + +export default class UmbTiptapItalicPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => [Underline]; + + override execute(editor?: Editor) { + editor?.chain().focus().toggleUnderline().run(); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts new file mode 100644 index 0000000000..4967051267 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/embed.extension.ts @@ -0,0 +1,25 @@ +import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.Embed', + name: 'Embed Tiptap Extension', + api: () => import('./embed.extension.js'), + meta: { + alias: 'umb-embed', + icon: 'umbraco', + label: 'Embed', + }, +}; + +export default class UmbTiptapEmbedPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions = () => []; + + override async execute(editor?: Editor) { + console.log('umb-embed.execute', editor); + // Research: https://github.com/ueberdosis/tiptap/tree/main/packages/extension-youtube + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts index 8afb03e629..754fb3dcf1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/manifests.ts @@ -1,4 +1,8 @@ import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { manifests as core } from './core/manifests.js'; +import { manifest as embed } from './embed.extension.js'; +import { manifest as mediaPicker } from './mediapicker.extension.js'; +import { manifest as urlPicker } from './urlpicker.extension.js'; import type { ManifestTypes, UmbExtensionManifestKind } from '@umbraco-cms/backoffice/extension-registry'; const kinds: Array = [ @@ -13,28 +17,6 @@ const kinds: Array = [ }, ]; -const extensions: Array = [ - { - type: 'tiptapExtension', - alias: 'Umb.Tiptap.Image', - name: 'Image Tiptap Extension', - weight: 1000, - api: () => import('./tiptap-image.extension.js'), - meta: {}, - }, - { - type: 'tiptapExtension', - kind: 'button', - alias: 'Umb.Tiptap.MediaPicker', - name: 'Media Picker Tiptap Extension', - weight: 900, - api: () => import('./tiptap-mediapicker.extension.js'), - meta: { - alias: 'umb-media', - icon: 'icon-picture', - label: 'Media picker', - }, - }, -]; +const extensions: Array = [...core, embed, mediaPicker, urlPicker]; export const manifests: Array = [...kinds, ...extensions]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts similarity index 83% rename from src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts rename to src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts index 7d96091761..ee3923cfa9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-mediapicker.extension.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/mediapicker.extension.ts @@ -1,9 +1,23 @@ import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; import { mergeAttributes, Node } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_MEDIA_PICKER_MODAL } from '@umbraco-cms/backoffice/media'; import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.MediaPicker', + name: 'Media Picker Tiptap Extension', + api: () => import('./mediapicker.extension.js'), + meta: { + alias: 'umb-media', + icon: 'umbraco', + label: 'Media picker', + }, +}; + export default class UmbTiptapMediaPickerPlugin extends UmbTiptapExtensionApi { getTiptapExtensions() { return [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts deleted file mode 100644 index f6d64e11a2..0000000000 --- a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/tiptap-image.extension.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { UmbTiptapExtensionApi } from './types.js'; -import { UmbImage } from '@umbraco-cms/backoffice/external/tiptap'; - -export default class UmbTiptapImageExtension extends UmbTiptapExtensionApi { - getTiptapExtensions() { - return [UmbImage.configure({ inline: true })]; - } -} diff --git a/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts new file mode 100644 index 0000000000..6f01aa8bcd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/rte/tiptap/extensions/urlpicker.extension.ts @@ -0,0 +1,41 @@ +import { UmbTiptapExtensionApi } from './types.js'; +import type { ManifestTiptapExtension } from './tiptap-extension.js'; +import { Link } from '@umbraco-cms/backoffice/external/tiptap'; +import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; + +export const manifest: ManifestTiptapExtension = { + type: 'tiptapExtension', + kind: 'button', + alias: 'Umb.Tiptap.UrlPicker', + name: 'URL Picker Tiptap Extension', + api: () => import('./urlpicker.extension.js'), + meta: { + alias: 'umb-link', + icon: 'umbraco', + label: 'URL picker', + }, +}; + +export default class UmbTiptapUrlPickerPlugin extends UmbTiptapExtensionApi { + getTiptapExtensions() { + return [Link.extend({ openOnClick: false })]; + } + + override async execute(editor?: Editor) { + console.log('umb-link.execute', editor); + + const text = prompt('Enter the text'); + const url = prompt('Enter the URL'); + + if (url && text && editor) { + const { from } = editor.state.selection; + editor + .chain() + .focus() + .insertContent(text) + .setTextSelection({ from: from, to: from + text.length }) + .setLink({ href: url, target: '_blank' }) + .run(); + } + } +}