Newer
Older
Component, EventEmitter, Input, Output,
AfterViewInit, Injector, OnInit
import { Editor } from '@tiptap/core';
import { Underline } from '@tiptap/extension-underline';
import { Superscript } from '@tiptap/extension-superscript';
import { Subscript } from '@tiptap/extension-subscript';
import { TextStyle } from '@tiptap/extension-text-style';
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 { Blockquote } from '@tiptap/extension-blockquote';
import { Document } from '@tiptap/extension-document';
import { History } from '@tiptap/extension-history';
import { Text } from '@tiptap/extension-text';
import { ListItem } from '@tiptap/extension-list-item';
import { Bold } from '@tiptap/extension-bold';
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 { Tooltip } from 'editor/src/app/text-editor/extensions/tooltip';
import { DialogService } from 'editor/src/app/services/dialog.service';
import { AnchorId } from './extensions/anchorId';
import { Indent } from './extensions/indent';
import { HangingIndent } from './extensions/hanging-indent';
import { ParagraphExtension } from './extensions/paragraph-extension';
import { FontSize } from './extensions/font-size';
import { BulletListExtension } from './extensions/bullet-list';
import { OrderedListExtension } from './extensions/ordered-list';
import ToggleButtonComponentExtension from './angular-node-views/toggle-button-component-extension';
import DropListComponentExtension from './angular-node-views/drop-list-component-extension';
import TextFieldComponentExtension from './angular-node-views/text-field-component-extension';
import CheckboxComponentExtension from './angular-node-views/checkbox-component-extension';
selector: 'aspect-rich-text-editor',
templateUrl: './rich-text-editor.component.html',
styleUrls: ['./rich-text-editor.component.scss']
export class RichTextEditorComponent implements OnInit, AfterViewInit {
@Input() content!: string | Record<string, any>;
@Output() contentChange = new EventEmitter<string | Record<string, any>>();
selectedFontColor = 'black';
selectedHighlightColor = 'lightgrey';
selectedAnchorColor = '#adff2f';
selectedAnchorIdText = '';
bulletListStyle: string = 'disc';
orderedListStyle: string = 'decimal';
defaultExtensions = [
Document, Text, ListItem,
Underline, Superscript, Subscript,
Bold, Italic, Strike, History,
Highlight.configure({
multicolor: true
}),
TextAlign.configure({
types: ['paragraph', 'heading']
}),
Indent.configure({
types: ['listItem', 'paragraph'],
minLevel: 0,
maxLevel: 4
}),
Heading.configure({
levels: [1, 2, 3, 4]
}),
ParagraphExtension,
FontSize,
BulletListExtension,
OrderedListExtension,
HangingIndent,
InlineImage,
BlockImage,
extensions: this.defaultExtensions
constructor(private injector: Injector, private dialogService: DialogService) { }
ngOnInit(): void {
const activeExtensions = this.defaultExtensions;
if (this.clozeMode) {
activeExtensions.push(ToggleButtonComponentExtension(this.injector));
activeExtensions.push(DropListComponentExtension(this.injector));
activeExtensions.push(TextFieldComponentExtension(this.injector));
activeExtensions.push(ButtonComponentExtension(this.injector));
activeExtensions.push(CheckboxComponentExtension(this.injector));
extensions: activeExtensions
ngAfterViewInit(): void {
this.editor.commands.focus();
}
toggleBold(): void {
this.editor.chain().toggleBold().focus().run();
}
toggleItalic(): void {
this.editor.chain().toggleItalic().focus().run();
}
toggleUnderline(): void {
this.editor.chain().toggleUnderline().focus().run();
}
toggleStrike(): void {
this.editor.commands.toggleStrike();
}
toggleSuperscript(): void {
this.editor.chain().toggleSuperscript().focus().run();
}
showTooltipPropertiesDialog(): void {
this.dialogService.showTooltipDialog(
this.editor.getAttributes('tooltip').tooltipText,
this.editor.getAttributes('tooltip').tooltipPosition
).subscribe(result => {
if (result) {
if (result.action === 'delete') {
this.editor.chain().focus().unsetTooltip().run();
} else {
this.editor.chain().focus().setTooltip({
tooltipText: result.tooltipText,
tooltipPosition: result.tooltipPosition
}).run();
}
}
});
}
toggleSubscript(): void {
this.editor.chain().toggleSubscript().focus().run();
}
applyFontSize(size: string): void {
this.selectedFontSize = size;
this.editor.commands.setFontSize(size);
applyFontColor(): void {
this.editor.chain().focus().setColor(this.selectedFontColor).run();
applyHighlightColor(): void {
this.editor.chain().focus().toggleHighlight({ color: this.selectedHighlightColor }).run();
applyAnchorId(): void {
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
const id = this.getAnchorIdFromSelection();
if (id) {
const activeAnchorId = this.editor.getAttributes('anchorId').anchorId;
const activeAnchorColor = this.editor.getAttributes('anchorId').anchorColor;
const activeParentAnchorId = this.editor.getAttributes('anchorId').parentAnchorId;
const activeParentAnchorColor = this.editor.getAttributes('anchorId').parentAnchorColor;
if (activeParentAnchorId) { // reset nested child
if (this.selectedAnchorColor === activeParentAnchorColor || this.selectedAnchorColor === activeAnchorColor) {
this.editor.chain().focus().setAnchorId({
anchorId: activeParentAnchorId,
parentAnchorId: '',
anchorColor: activeParentAnchorColor,
parentAnchorColor: ''
}).run();
} else { // set new color for nested Child
this.editor.chain().focus().setAnchorId({
anchorId: activeAnchorId,
parentAnchorId: activeParentAnchorId,
anchorColor: this.selectedAnchorColor,
parentAnchorColor: activeParentAnchorColor
}).run();
}
} else { // standard toggle
this.editor.chain().focus().toggleAnchorId({
anchorId: id,
parentAnchorId: (activeAnchorId !== id) ? activeAnchorId : '',
anchorColor: this.selectedAnchorColor,
parentAnchorColor: (activeAnchorId !== id) ? activeAnchorColor : ''
}).run();
}
this.resetSelectedAnchorIdText();
} else {
console.warn('No text selected for anchor!');
}
}
private getAnchorIdFromSelection(): string {
const selection = window?.getSelection()?.toString() || this.selectedAnchorIdText;
return selection.replace(/[^0-9a-zA-Z]/g, '_').substring(0, 20);
}
private resetSelectedAnchorIdText(): void {
this.selectedAnchorIdText = '';
}
alignText(direction: string): void {
this.editor.chain().focus().setTextAlign(direction).run();
}
indent(): void {
this.editor.commands.indent(this.selectedIndentSize);
this.editor.commands.outdent(this.selectedIndentSize);
}
toggleBulletList(): void {
this.editor.chain().toggleBulletList().focus().run();
this.editor.commands.setBulletListStyle(this.bulletListStyle);
this.editor.chain().toggleOrderedList().focus().run();
this.editor.commands.setOrderedListStyle(this.orderedListStyle);
this.editor.commands.setOrderedListFontSize(this.selectedFontSize);
}
applyListStyle(listType: string, style: string): void {
if (listType === 'bulletList') {
this.bulletListStyle = style;
this.editor.commands.setBulletListStyle(style);
if (!this.editor.isActive('bulletList')) {
this.toggleBulletList();
}
this.orderedListStyle = style;
this.editor.commands.setOrderedListStyle(style);
this.editor.commands.setOrderedListFontSize(this.selectedFontSize);
if (!this.editor.isActive('orderedList')) {
}
toggleHeading(level?: string): void {
if (!level) {
this.editor.commands.toggleNode('heading', 'paragraph');
} else {
this.editor.commands.toggleHeading({ level: Number(level) as 1 | 2 | 3 | 4 });
}
}
applyParagraphStyle(margin: number): void {
this.editor.commands.setMargin(margin);
}
insertSpecialChar(char: string): void {
this.editor.chain().insertContent(char).focus().run();
}
this.editor.commands.indent(this.selectedIndentSize);
this.editor.commands.hangIndent(this.selectedIndentSize);
this.editor.commands.outdent(this.selectedIndentSize);
this.editor.commands.unhangIndent(this.selectedIndentSize);
async insertImage(): Promise<void> {
const mediaSrc = await FileService.loadImage();
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 {
this.editor.commands.toggleBlockquote();
}
insertToggleButton(): void {
this.editor.commands.insertContent('<aspect-nodeview-toggle-button></aspect-nodeview-toggle-button>');
}
insertDropList(): void {
this.editor.commands.insertContent('<aspect-nodeview-drop-list></aspect-nodeview-drop-list>');
}
insertTextField(): void {
this.editor.commands.insertContent('<aspect-nodeview-text-field></aspect-nodeview-text-field>');
insertButton() {
this.editor.commands.insertContent('<aspect-nodeview-button></aspect-nodeview-button>');
this.editor.commands.focus();
}
insertCheckbox() {
this.editor.commands.insertContent('<aspect-nodeview-checkbox></aspect-nodeview-checkbox>');
this.editor.commands.focus();
}