From ac4590378f5dd2b702ac31cad52b25bf5ae60aa3 Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Thu, 2 Dec 2021 17:13:55 +0100 Subject: [PATCH] [editor] Add hanging indent functionality to TextEditor --- .../src/app/text-editor/hanging-indent.ts | 82 +++++++++++++++++++ .../rich-text-editor.component.html | 8 ++ .../text-editor/rich-text-editor.component.ts | 12 ++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 projects/editor/src/app/text-editor/hanging-indent.ts diff --git a/projects/editor/src/app/text-editor/hanging-indent.ts b/projects/editor/src/app/text-editor/hanging-indent.ts new file mode 100644 index 000000000..5b7af447d --- /dev/null +++ b/projects/editor/src/app/text-editor/hanging-indent.ts @@ -0,0 +1,82 @@ +import { Command, Extension } from '@tiptap/core'; +import { TextSelection, AllSelection, Transaction } from 'prosemirror-state'; + +declare module '@tiptap/core' { + interface Commands<ReturnType> { + hangingIndent: { + hangIndent: () => ReturnType; + unhangIndent: () => ReturnType; + }; + } +} + +export const HangingIndent = Extension.create({ + name: 'hangingIndent', + + defaultOptions: { + types: ['paragraph'] + }, + + addGlobalAttributes() { + return [ + { + types: this.options.types, + attributes: { + hangingIndent: { + default: false, + renderHTML: attributes => (attributes.hangingIndent ? + { style: 'text-indent: -20px' } : { style: 'text-indent: 0px' } + ), + parseHTML: element => element.style.textIndent === '-20px' + } + } + } + ]; + }, + + addCommands() { + const setNodeIndentMarkup = (tr: Transaction, pos: number, indent: boolean): Transaction => { + const node = tr?.doc?.nodeAt(pos); + + if (node) { + if (indent !== node.attrs.indent) { + const nodeAttrs = { ...node.attrs, hangingIndent: indent }; + return tr.setNodeMarkup(pos, node.type, nodeAttrs, node.marks); + } + } + return tr; + }; + + const updateIndentLevel = (tr: Transaction, indent: boolean): Transaction => { + const { doc, selection } = tr; + + if (doc && selection && (selection instanceof TextSelection || selection instanceof AllSelection)) { + const { from, to } = selection; + doc.nodesBetween(from, to, (node, pos) => { + setNodeIndentMarkup(tr, pos, indent); + return false; + }); + } + + return tr; + }; + const applyIndent: (indent: boolean) => () => Command = + indent => () => ({ tr, state, dispatch }) => { + const { selection } = state; + tr = tr.setSelection(selection); + tr = updateIndentLevel(tr, indent); + + if (tr.docChanged) { + dispatch?.(tr); + return true; + } + + return false; + }; + + return { + hangIndent: applyIndent(true), + unhangIndent: applyIndent(false) + }; + } +}); 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 a0ac6502a..b70bd8eca 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 @@ -125,6 +125,14 @@ (click)="outdent()"> <mat-icon>format_indent_decrease</mat-icon> </button> + <button mat-icon-button matTooltip="Hängender Einzug" [matTooltipShowDelay]="300" + (click)="hangIndent()"> + <mat-icon>segment</mat-icon> + </button> + <button mat-icon-button matTooltip="Hängenden Einzug entfernen" [matTooltipShowDelay]="300" + (click)="unhangIndent()"> + <mat-icon>view_headline</mat-icon> + </button> </div> <div fxLayout="row"> <div class="combo-button" fxLayout="row" [style.background-color]="editor.isActive('bulletList') ? 'lightgrey' : ''"> 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 a3e8f26a9..a94b144af 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 @@ -13,6 +13,7 @@ import { Highlight } from '@tiptap/extension-highlight'; import { TextAlign } from '@tiptap/extension-text-align'; import { Heading } from '@tiptap/extension-heading'; import { Indent } from './indent'; +import { HangingIndent } from './hanging-indent'; import { customParagraph } from './paragraph-extension'; import { fontSizeExtension } from './font-size-extension'; import { bulletListExtension } from './bulletList-extension'; @@ -57,7 +58,8 @@ export class RichTextEditorComponent implements AfterViewInit { customParagraph, fontSizeExtension, bulletListExtension, - orderedListExtension + orderedListExtension, + HangingIndent ] }); @@ -155,4 +157,12 @@ export class RichTextEditorComponent implements AfterViewInit { insertSpecialChar(char: string): void { this.editor.chain().insertContent(char).focus().run(); } + + hangIndent() { + this.editor.commands.hangIndent(); + } + + unhangIndent() { + this.editor.commands.unhangIndent(); + } } -- GitLab