From 60918a3339a0320737eddbc99d43ae6ebef4ac12 Mon Sep 17 00:00:00 2001 From: jojohoch <joachim.hoch@iqb.hu-berlin.de> Date: Wed, 30 Nov 2022 15:53:42 +0100 Subject: [PATCH] Enable adding images with surrounding text in the TipTap editor --- .../app/text-editor/extensions/block-image.ts | 51 +++++++++++++++++++ .../text-editor/extensions/inline-image.ts | 37 ++++++++++++++ .../rich-text-editor.component.html | 14 ++++- .../text-editor/rich-text-editor.component.ts | 32 ++++++++---- 4 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 projects/editor/src/app/text-editor/extensions/block-image.ts create mode 100644 projects/editor/src/app/text-editor/extensions/inline-image.ts diff --git a/projects/editor/src/app/text-editor/extensions/block-image.ts b/projects/editor/src/app/text-editor/extensions/block-image.ts new file mode 100644 index 000000000..7397997cc --- /dev/null +++ b/projects/editor/src/app/text-editor/extensions/block-image.ts @@ -0,0 +1,51 @@ +import { Image } from '@tiptap/extension-image'; + +declare module '@tiptap/core' { + interface Commands<ReturnType> { + blockImage: { + insertBlockImage: (options: { src: string, alt?: string, title?: string, style?: string }) => ReturnType, + } + } +} + +export const BlockImage = Image.extend({ + name: 'blockImage', + addOptions() { + return { + ...this.parent?.(), + allowBase64: true + }; + }, + addAttributes() { + return { + ...this.parent?.(), + style: { + default: null, + renderHTML: attributes => { + if (attributes.style && !this.options.inline) { + return { + 'data-style': attributes.style, + style: attributes.style + }; + } + return {}; + } + } + }; + }, + parseHTML() { + return [ + { + tag: 'img', + getAttrs: node => (node as HTMLElement).style.verticalAlign !== 'middle' && !this.options.inline && null + } + ]; + }, + addCommands() { + return { + insertBlockImage: ( + options: { src: string, alt?: string, title?: string, style?: string } + ) => ({ commands }) => commands.insertContent({ type: this.name, attrs: options }) + }; + } +}); diff --git a/projects/editor/src/app/text-editor/extensions/inline-image.ts b/projects/editor/src/app/text-editor/extensions/inline-image.ts new file mode 100644 index 000000000..69032bfda --- /dev/null +++ b/projects/editor/src/app/text-editor/extensions/inline-image.ts @@ -0,0 +1,37 @@ +import { Image } from '@tiptap/extension-image'; + +declare module '@tiptap/core' { + interface Commands<ReturnType> { + inlineImage: { + insertInlineImage: (options: { src: string, alt?: string, title?: string }) => ReturnType, + } + } +} + +export const InlineImage = Image.extend({ + name: 'inlineImage', + addOptions() { + return { + inline: true, + allowBase64: true, + HTMLAttributes: { + style: 'display: inline-block; height: 1em; vertical-align: middle' + } + }; + }, + parseHTML() { + return [ + { + tag: 'img', + getAttrs: node => (node as HTMLElement).style.verticalAlign === 'middle' && this.options.inline && null + } + ]; + }, + addCommands() { + return { + insertInlineImage: ( + options: { src: string, alt?: string, title?: string } + ) => ({ commands }) => commands.insertContent({ type: this.name, attrs: options }) + }; + } +}); diff --git a/projects/editor/src/app/text-editor/rich-text-editor.component.html b/projects/editor/src/app/text-editor/rich-text-editor.component.html index 0ebce8d6f..c0a704e72 100644 --- a/projects/editor/src/app/text-editor/rich-text-editor.component.html +++ b/projects/editor/src/app/text-editor/rich-text-editor.component.html @@ -253,10 +253,22 @@ <button mat-button (click)="insertSpecialChar('♥')">♥</button> <button mat-button (click)="insertSpecialChar('♦')">♦</button> </mat-menu> - <button mat-icon-button matTooltip="Bild" [matTooltipShowDelay]="300" + <button mat-icon-button matTooltip="Bild in Zeile einfügen" [matTooltipShowDelay]="300" (click)="insertImage()"> + <mat-icon>burst_mode</mat-icon> + </button> + <button mat-icon-button matTooltip="Bild in eigenem Absatz einfügen" [matTooltipShowDelay]="300" + (click)="insertBlockImage('none')"> <mat-icon>image</mat-icon> </button> + <button mat-icon-button matTooltip="Bild rechts einfügen" [matTooltipShowDelay]="300" + (click)="insertBlockImage('right')"> + <mat-icon style="transform: scaleX(-1);">art_track</mat-icon> + </button> + <button mat-icon-button matTooltip="Bild links einfügen" [matTooltipShowDelay]="300" + (click)="insertBlockImage('left')"> + <mat-icon>art_track</mat-icon> + </button> <button mat-icon-button matTooltip="Zitat" [matTooltipShowDelay]="300" [class.active]="editor.isActive('blockquote')" (click)="toggleBlockquote()"> diff --git a/projects/editor/src/app/text-editor/rich-text-editor.component.ts b/projects/editor/src/app/text-editor/rich-text-editor.component.ts index 84fc5d46e..384b9d652 100644 --- a/projects/editor/src/app/text-editor/rich-text-editor.component.ts +++ b/projects/editor/src/app/text-editor/rich-text-editor.component.ts @@ -11,7 +11,6 @@ import { Color } from '@tiptap/extension-color'; import { Highlight } from '@tiptap/extension-highlight'; import { TextAlign } from '@tiptap/extension-text-align'; import { Heading } from '@tiptap/extension-heading'; -import { Image } from '@tiptap/extension-image'; import { Blockquote } from '@tiptap/extension-blockquote'; import { Document } from '@tiptap/extension-document'; import { Text } from '@tiptap/extension-text'; @@ -21,6 +20,8 @@ import { Italic } from '@tiptap/extension-italic'; import { Strike } from '@tiptap/extension-strike'; import { FileService } from 'common/services/file.service'; import ButtonComponentExtension from 'editor/src/app/text-editor/angular-node-views/button-component-extension'; +import { BlockImage } from 'editor/src/app/text-editor/extensions/block-image'; +import { InlineImage } from 'editor/src/app/text-editor/extensions/inline-image'; import { AnchorId } from './extensions/anchorId'; import { Indent } from './extensions/indent'; import { HangingIndent } from './extensions/hanging-indent'; @@ -75,13 +76,8 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { BulletListExtension, OrderedListExtension, HangingIndent, - Image.configure({ - inline: true, - allowBase64: true, - HTMLAttributes: { - style: 'display: inline-block; height: 1em; vertical-align: middle' - } - }), + InlineImage, + BlockImage, Blockquote ]; @@ -223,7 +219,24 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { async insertImage(): Promise<void> { const mediaSrc = await FileService.loadImage(); - this.editor.commands.setImage({ src: mediaSrc }); + this.editor.commands.insertInlineImage({ src: mediaSrc }); + } + + async insertBlockImage(alignment: 'none' | 'right' | 'left'): Promise<void> { + const mediaSrc = await FileService.loadImage(); + switch (alignment) { + case 'left': { + this.editor.commands.insertBlockImage({ src: mediaSrc, style: 'float: left; margin-right: 10px;' }); + break; + } + case 'right': { + this.editor.commands.insertBlockImage({ src: mediaSrc, style: 'float: right; margin-left: 10px' }); + break; + } + default: { + this.editor.commands.insertBlockImage({ src: mediaSrc }); + } + } } toggleBlockquote(): void { @@ -246,7 +259,6 @@ export class RichTextEditorComponent implements OnInit, AfterViewInit { } insertButton() { - console.log('inserting button'); this.editor.commands.insertContent('<aspect-nodeview-button></aspect-nodeview-button>'); this.editor.commands.focus(); } -- GitLab