X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/ddb7f33868ea499ab8f48a7062f145e8c0fbe02f..refs/pull/5036/head:/resources/js/components/page-comments.js diff --git a/resources/js/components/page-comments.js b/resources/js/components/page-comments.js index 5d8d16958..cfb0634a9 100644 --- a/resources/js/components/page-comments.js +++ b/resources/js/components/page-comments.js @@ -1,191 +1,169 @@ -import MarkdownIt from "markdown-it"; -import {scrollAndHighlightElement} from "../services/util"; - -const md = new MarkdownIt({ html: false }); +import {Component} from './component'; +import {getLoading, htmlToDom} from '../services/dom'; +import {buildForInput} from '../wysiwyg/config'; + +export class PageComments extends Component { + + setup() { + this.elem = this.$el; + this.pageId = Number(this.$opts.pageId); + + // Element references + this.container = this.$refs.commentContainer; + this.commentCountBar = this.$refs.commentCountBar; + this.commentsTitle = this.$refs.commentsTitle; + this.addButtonContainer = this.$refs.addButtonContainer; + this.replyToRow = this.$refs.replyToRow; + this.formContainer = this.$refs.formContainer; + this.form = this.$refs.form; + this.formInput = this.$refs.formInput; + this.formReplyLink = this.$refs.formReplyLink; + this.addCommentButton = this.$refs.addCommentButton; + this.hideFormButton = this.$refs.hideFormButton; + this.removeReplyToButton = this.$refs.removeReplyToButton; + + // WYSIWYG options + this.wysiwygLanguage = this.$opts.wysiwygLanguage; + this.wysiwygTextDirection = this.$opts.wysiwygTextDirection; + this.wysiwygEditor = null; + + // Translations + this.createdText = this.$opts.createdText; + this.countText = this.$opts.countText; + + // Internal State + this.parentId = null; + this.formReplyText = this.formReplyLink?.textContent || ''; -class PageComments { + this.setupListeners(); + } - constructor(elem) { - this.elem = elem; - this.pageId = Number(elem.getAttribute('page-id')); - this.editingComment = null; - this.parentId = null; + setupListeners() { + this.elem.addEventListener('page-comment-delete', () => { + this.updateCount(); + this.hideForm(); + }); - this.container = elem.querySelector('[comment-container]'); - this.formContainer = elem.querySelector('[comment-form-container]'); + this.elem.addEventListener('page-comment-reply', event => { + this.setReply(event.detail.id, event.detail.element); + }); - if (this.formContainer) { - this.form = this.formContainer.querySelector('form'); - this.formInput = this.form.querySelector('textarea'); + if (this.form) { + this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this)); + this.hideFormButton.addEventListener('click', this.hideForm.bind(this)); + this.addCommentButton.addEventListener('click', this.showForm.bind(this)); this.form.addEventListener('submit', this.saveComment.bind(this)); } - - this.elem.addEventListener('click', this.handleAction.bind(this)); - this.elem.addEventListener('submit', this.updateComment.bind(this)); } - handleAction(event) { - let actionElem = event.target.closest('[action]'); - - if (event.target.matches('a[href^="#"]')) { - const id = event.target.href.split('#')[1]; - scrollAndHighlightElement(document.querySelector('#' + id)); - } - - if (actionElem === null) return; + saveComment(event) { event.preventDefault(); + event.stopPropagation(); - let action = actionElem.getAttribute('action'); - if (action === 'edit') this.editComment(actionElem.closest('[comment]')); - if (action === 'closeUpdateForm') this.closeUpdateForm(); - if (action === 'delete') this.deleteComment(actionElem.closest('[comment]')); - if (action === 'addComment') this.showForm(); - if (action === 'hideForm') this.hideForm(); - if (action === 'reply') this.setReply(actionElem.closest('[comment]')); - if (action === 'remove-reply-to') this.removeReplyTo(); - } - - closeUpdateForm() { - if (!this.editingComment) return; - this.editingComment.querySelector('[comment-content]').style.display = 'block'; - this.editingComment.querySelector('[comment-edit-container]').style.display = 'none'; - } - - editComment(commentElem) { - this.hideForm(); - if (this.editingComment) this.closeUpdateForm(); - commentElem.querySelector('[comment-content]').style.display = 'none'; - commentElem.querySelector('[comment-edit-container]').style.display = 'block'; - let textArea = commentElem.querySelector('[comment-edit-container] textarea'); - let lineCount = textArea.value.split('\n').length; - textArea.style.height = ((lineCount * 20) + 40) + 'px'; - this.editingComment = commentElem; - } + const loading = getLoading(); + loading.classList.add('px-l'); + this.form.after(loading); + this.form.toggleAttribute('hidden', true); - updateComment(event) { - let form = event.target; - event.preventDefault(); - let text = form.querySelector('textarea').value; - let reqData = { - text: text, - html: md.render(text), + const reqData = { + html: this.wysiwygEditor.getContent(), parent_id: this.parentId || null, }; - this.showLoading(form); - let commentId = this.editingComment.getAttribute('comment'); - window.$http.put(window.baseUrl(`/ajax/comment/${commentId}`), reqData).then(resp => { - let newComment = document.createElement('div'); - newComment.innerHTML = resp.data; - this.editingComment.innerHTML = newComment.children[0].innerHTML; - window.$events.emit('success', window.trans('entities.comment_updated_success')); - window.components.init(this.editingComment); - this.closeUpdateForm(); - this.editingComment = null; - this.hideLoading(form); - }); - } - deleteComment(commentElem) { - let id = commentElem.getAttribute('comment'); - this.showLoading(commentElem.querySelector('[comment-content]')); - window.$http.delete(window.baseUrl(`/ajax/comment/${id}`)).then(resp => { - commentElem.parentNode.removeChild(commentElem); - window.$events.emit('success', window.trans('entities.comment_deleted_success')); - this.updateCount(); + window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => { + const newElem = htmlToDom(resp.data); + this.formContainer.after(newElem); + window.$events.success(this.createdText); this.hideForm(); - }); - } - - saveComment(event) { - event.preventDefault(); - event.stopPropagation(); - let text = this.formInput.value; - let reqData = { - text: text, - html: md.render(text), - parent_id: this.parentId || null, - }; - this.showLoading(this.form); - window.$http.post(window.baseUrl(`/ajax/page/${this.pageId}/comment`), reqData).then(resp => { - let newComment = document.createElement('div'); - newComment.innerHTML = resp.data; - let newElem = newComment.children[0]; - this.container.appendChild(newElem); - window.components.init(newElem); - window.$events.emit('success', window.trans('entities.comment_created_success')); - this.resetForm(); this.updateCount(); + }).catch(err => { + this.form.toggleAttribute('hidden', false); + window.$events.showValidationErrors(err); }); + + this.form.toggleAttribute('hidden', false); + loading.remove(); } updateCount() { - let count = this.container.children.length; - this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count}); + const count = this.getCommentCount(); + this.commentsTitle.textContent = window.trans_plural(this.countText, count, {count}); } resetForm() { + this.removeEditor(); this.formInput.value = ''; - this.formContainer.appendChild(this.form); - this.hideForm(); - this.removeReplyTo(); - this.hideLoading(this.form); + this.parentId = null; + this.replyToRow.toggleAttribute('hidden', true); + this.container.append(this.formContainer); } showForm() { - this.formContainer.style.display = 'block'; - this.formContainer.parentNode.style.display = 'block'; - this.elem.querySelector('[comment-add-button-container]').style.display = 'none'; - this.formInput.focus(); - this.formInput.scrollIntoView({behavior: "smooth"}); + this.removeEditor(); + this.formContainer.toggleAttribute('hidden', false); + this.addButtonContainer.toggleAttribute('hidden', true); + this.formContainer.scrollIntoView({behavior: 'smooth', block: 'nearest'}); + this.loadEditor(); } hideForm() { - this.formContainer.style.display = 'none'; - this.formContainer.parentNode.style.display = 'none'; - const addButtonContainer = this.elem.querySelector('[comment-add-button-container]'); + this.resetForm(); + this.formContainer.toggleAttribute('hidden', true); if (this.getCommentCount() > 0) { - this.elem.appendChild(addButtonContainer) + this.elem.append(this.addButtonContainer); } else { - const countBar = this.elem.querySelector('[comment-count-bar]'); - countBar.appendChild(addButtonContainer); + this.commentCountBar.append(this.addButtonContainer); } - addButtonContainer.style.display = 'block'; + this.addButtonContainer.toggleAttribute('hidden', false); } - getCommentCount() { - return this.elem.querySelectorAll('.comment-box[comment]').length; + loadEditor() { + if (this.wysiwygEditor) { + this.wysiwygEditor.focus(); + return; + } + + const config = buildForInput({ + language: this.wysiwygLanguage, + containerElement: this.formInput, + darkMode: document.documentElement.classList.contains('dark-mode'), + textDirection: this.wysiwygTextDirection, + translations: {}, + translationMap: window.editor_translations, + }); + + window.tinymce.init(config).then(editors => { + this.wysiwygEditor = editors[0]; + setTimeout(() => this.wysiwygEditor.focus(), 50); + }); } - setReply(commentElem) { - this.showForm(); - this.parentId = Number(commentElem.getAttribute('local-id')); - this.elem.querySelector('[comment-form-reply-to]').style.display = 'block'; - let replyLink = this.elem.querySelector('[comment-form-reply-to] a'); - replyLink.textContent = `#${this.parentId}`; - replyLink.href = `#comment${this.parentId}`; + removeEditor() { + if (this.wysiwygEditor) { + this.wysiwygEditor.remove(); + this.wysiwygEditor = null; + } } - removeReplyTo() { - this.parentId = null; - this.elem.querySelector('[comment-form-reply-to]').style.display = 'none'; + getCommentCount() { + return this.container.querySelectorAll('[component="page-comment"]').length; } - showLoading(formElem) { - let groups = formElem.querySelectorAll('.form-group'); - for (let i = 0, len = groups.length; i < len; i++) { - groups[i].style.display = 'none'; - } - formElem.querySelector('.form-group.loading').style.display = 'block'; + setReply(commentLocalId, commentElement) { + const targetFormLocation = commentElement.closest('.comment-branch').querySelector('.comment-branch-children'); + targetFormLocation.append(this.formContainer); + this.showForm(); + this.parentId = commentLocalId; + this.replyToRow.toggleAttribute('hidden', false); + this.formReplyLink.textContent = this.formReplyText.replace('1234', this.parentId); + this.formReplyLink.href = `#comment${this.parentId}`; } - hideLoading(formElem) { - let groups = formElem.querySelectorAll('.form-group'); - for (let i = 0, len = groups.length; i < len; i++) { - groups[i].style.display = 'block'; - } - formElem.querySelector('.form-group.loading').style.display = 'none'; + removeReplyTo() { + this.parentId = null; + this.replyToRow.toggleAttribute('hidden', true); + this.container.append(this.formContainer); + this.showForm(); } } - -export default PageComments; \ No newline at end of file