1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-07-24 07:42:07 +03:00

Comments: Switched to lexical editor

Required a lot of changes to provide at least a decent attempt at proper
editor teardown control.
Also updates HtmlDescriptionFilter and testing to address issue with bad
child iteration which could lead to missed items.
Renamed editor version from comments to basic as it'll also be used for
item descriptions.
This commit is contained in:
Dan Brown
2025-06-25 14:16:01 +01:00
parent c606970e38
commit b80992ca59
16 changed files with 176 additions and 92 deletions

View File

@ -1,8 +1,9 @@
import {Component} from './component';
import {getLoading, htmlToDom} from '../services/dom';
import {buildForInput} from '../wysiwyg-tinymce/config';
import {PageCommentReference} from "./page-comment-reference";
import {HttpError} from "../services/http";
import {SimpleWysiwygEditorInterface} from "../wysiwyg";
import {el} from "../wysiwyg/utils/dom";
export interface PageCommentReplyEventData {
id: string; // ID of comment being replied to
@ -21,8 +22,7 @@ export class PageComment extends Component {
protected updatedText!: string;
protected archiveText!: string;
protected wysiwygEditor: any = null;
protected wysiwygLanguage!: string;
protected wysiwygEditor: SimpleWysiwygEditorInterface|null = null;
protected wysiwygTextDirection!: string;
protected container!: HTMLElement;
@ -44,7 +44,6 @@ export class PageComment extends Component {
this.archiveText = this.$opts.archiveText;
// Editor reference and text options
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
this.wysiwygTextDirection = this.$opts.wysiwygTextDirection;
// Element references
@ -90,7 +89,7 @@ export class PageComment extends Component {
this.form.toggleAttribute('hidden', !show);
}
protected startEdit() : void {
protected async startEdit(): Promise<void> {
this.toggleEditMode(true);
if (this.wysiwygEditor) {
@ -98,21 +97,20 @@ export class PageComment extends Component {
return;
}
const config = buildForInput({
language: this.wysiwygLanguage,
containerElement: this.input,
type WysiwygModule = typeof import('../wysiwyg');
const wysiwygModule = (await window.importVersioned('wysiwyg')) as WysiwygModule;
const editorContent = this.input.value;
const container = el('div', {class: 'comment-editor-container'});
this.input.parentElement?.appendChild(container);
this.input.hidden = true;
this.wysiwygEditor = wysiwygModule.createBasicEditorInstance(container as HTMLElement, editorContent, {
darkMode: document.documentElement.classList.contains('dark-mode'),
textDirection: this.wysiwygTextDirection,
drawioUrl: '',
pageId: 0,
translations: {},
translationMap: (window as unknown as Record<string, Object>).editor_translations,
textDirection: this.$opts.textDirection,
translations: (window as unknown as Record<string, Object>).editor_translations,
});
(window as unknown as {tinymce: {init: (arg0: Object) => Promise<any>}}).tinymce.init(config).then(editors => {
this.wysiwygEditor = editors[0];
setTimeout(() => this.wysiwygEditor.focus(), 50);
});
this.wysiwygEditor.focus();
}
protected async update(event: Event): Promise<void> {
@ -121,7 +119,7 @@ export class PageComment extends Component {
this.form.toggleAttribute('hidden', true);
const reqData = {
html: this.wysiwygEditor.getContent(),
html: await this.wysiwygEditor?.getContentAsHtml() || '',
};
try {

View File

@ -1,10 +1,11 @@
import {Component} from './component';
import {getLoading, htmlToDom} from '../services/dom';
import {buildForInput} from '../wysiwyg-tinymce/config';
import {Tabs} from "./tabs";
import {PageCommentReference} from "./page-comment-reference";
import {scrollAndHighlightElement} from "../services/util";
import {PageCommentArchiveEventData, PageCommentReplyEventData} from "./page-comment";
import {el} from "../wysiwyg/utils/dom";
import {SimpleWysiwygEditorInterface} from "../wysiwyg";
export class PageComments extends Component {
@ -28,9 +29,8 @@ export class PageComments extends Component {
private hideFormButton!: HTMLElement;
private removeReplyToButton!: HTMLElement;
private removeReferenceButton!: HTMLElement;
private wysiwygLanguage!: string;
private wysiwygTextDirection!: string;
private wysiwygEditor: any = null;
private wysiwygEditor: SimpleWysiwygEditorInterface|null = null;
private createdText!: string;
private countText!: string;
private archivedCountText!: string;
@ -63,7 +63,6 @@ export class PageComments extends Component {
this.removeReferenceButton = this.$refs.removeReferenceButton;
// WYSIWYG options
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
this.wysiwygTextDirection = this.$opts.wysiwygTextDirection;
// Translations
@ -107,7 +106,7 @@ export class PageComments extends Component {
}
}
protected saveComment(event: SubmitEvent): void {
protected async saveComment(event: SubmitEvent): Promise<void> {
event.preventDefault();
event.stopPropagation();
@ -117,7 +116,7 @@ export class PageComments extends Component {
this.form.toggleAttribute('hidden', true);
const reqData = {
html: this.wysiwygEditor.getContent(),
html: (await this.wysiwygEditor?.getContentAsHtml()) || '',
parent_id: this.parentId || null,
content_ref: this.contentReference,
};
@ -189,27 +188,25 @@ export class PageComments extends Component {
this.addButtonContainer.toggleAttribute('hidden', false);
}
protected loadEditor(): void {
protected async loadEditor(): Promise<void> {
if (this.wysiwygEditor) {
this.wysiwygEditor.focus();
return;
}
const config = buildForInput({
language: this.wysiwygLanguage,
containerElement: this.formInput,
type WysiwygModule = typeof import('../wysiwyg');
const wysiwygModule = (await window.importVersioned('wysiwyg')) as WysiwygModule;
const container = el('div', {class: 'comment-editor-container'});
this.formInput.parentElement?.appendChild(container);
this.formInput.hidden = true;
this.wysiwygEditor = wysiwygModule.createBasicEditorInstance(container as HTMLElement, '', {
darkMode: document.documentElement.classList.contains('dark-mode'),
textDirection: this.wysiwygTextDirection,
drawioUrl: '',
pageId: 0,
translations: {},
translationMap: (window as unknown as Record<string, Object>).editor_translations,
translations: (window as unknown as Record<string, Object>).editor_translations,
});
(window as unknown as {tinymce: {init: (arg0: Object) => Promise<any>}}).tinymce.init(config).then(editors => {
this.wysiwygEditor = editors[0];
setTimeout(() => this.wysiwygEditor.focus(), 50);
});
this.wysiwygEditor.focus();
}
protected removeEditor(): void {