1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-07-28 17:02:04 +03:00

Merge pull request #5676 from BookStackApp/lexical_comments

New WYSIWYG editor for comments & descriptions
This commit is contained in:
Dan Brown
2025-07-09 18:01:25 +01:00
committed by GitHub
26 changed files with 388 additions and 313 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, '<p></p>', {
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 {

View File

@ -1,23 +0,0 @@
import {Component} from './component';
import {buildForInput} from '../wysiwyg-tinymce/config';
export class WysiwygInput extends Component {
setup() {
this.elem = this.$el;
const config = buildForInput({
language: this.$opts.language,
containerElement: this.elem,
darkMode: document.documentElement.classList.contains('dark-mode'),
textDirection: this.$opts.textDirection,
translations: {},
translationMap: window.editor_translations,
});
window.tinymce.init(config).then(editors => {
this.editor = editors[0];
});
}
}

View File

@ -0,0 +1,32 @@
import {Component} from './component';
import {el} from "../wysiwyg/utils/dom";
import {SimpleWysiwygEditorInterface} from "../wysiwyg";
export class WysiwygInput extends Component {
private elem!: HTMLTextAreaElement;
private wysiwygEditor!: SimpleWysiwygEditorInterface;
private textDirection!: string;
async setup() {
this.elem = this.$el as HTMLTextAreaElement;
this.textDirection = this.$opts.textDirection;
type WysiwygModule = typeof import('../wysiwyg');
const wysiwygModule = (await window.importVersioned('wysiwyg')) as WysiwygModule;
const container = el('div', {class: 'basic-editor-container'});
this.elem.parentElement?.appendChild(container);
this.elem.hidden = true;
this.wysiwygEditor = wysiwygModule.createBasicEditorInstance(container as HTMLElement, this.elem.value, {
darkMode: document.documentElement.classList.contains('dark-mode'),
textDirection: this.textDirection,
translations: (window as unknown as Record<string, Object>).editor_translations,
});
this.wysiwygEditor.onChange(() => {
this.wysiwygEditor.getContentAsHtml().then(html => {
this.elem.value = html;
});
});
}
}