From c82fa332100fb9f423f5db6ae3de5423de0a2941 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Thu, 1 May 2025 17:22:12 +0100 Subject: [PATCH] Comments: Further range of content reference ux improvements - Added reference indicator to comment create form. - Added remove action. - Extracted reference text to translations. - Changed reference hash to be text-based instead of HTML based. - Added reference display for newly added comments. - Handled reference marker delete on comment delete. --- lang/en/entities.php | 2 + .../js/components/page-comment-reference.ts | 26 ++++++------- resources/js/components/page-comment.ts | 11 +++++- resources/js/components/page-comments.ts | 38 ++++++++++++++++--- resources/js/services/dom.ts | 6 +-- resources/sass/_components.scss | 3 ++ resources/views/comments/comment.blade.php | 2 +- resources/views/comments/comments.blade.php | 4 +- resources/views/comments/create.blade.php | 10 +++++ 9 files changed, 74 insertions(+), 28 deletions(-) diff --git a/lang/en/entities.php b/lang/en/entities.php index c70658c01..e25a83299 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -410,6 +410,8 @@ return [ 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/resources/js/components/page-comment-reference.ts b/resources/js/components/page-comment-reference.ts index edf24354b..5a71f0c51 100644 --- a/resources/js/components/page-comment-reference.ts +++ b/resources/js/components/page-comment-reference.ts @@ -28,10 +28,7 @@ export class PageCommentReference extends Component { this.closeText = this.$opts.closeText; // Show within page display area if seen - const pageContentArea = document.querySelector('.page-content'); - if (pageContentArea instanceof HTMLElement && this.link.checkVisibility()) { - this.updateMarker(pageContentArea); - } + this.showForDisplay(); // Handle editor view to show on comments toolbox view window.addEventListener('editor-toolbox-change', (event) => { @@ -47,19 +44,26 @@ export class PageCommentReference extends Component { // Handle comments tab changes to hide/show markers & indicators window.addEventListener('tabs-change', event => { const sectionId = (event as {detail: {showing: string}}).detail.showing; - if (!sectionId.startsWith('comment-tab-panel') || !(pageContentArea instanceof HTMLElement)) { + if (!sectionId.startsWith('comment-tab-panel')) { return; } const panel = document.getElementById(sectionId); if (panel?.contains(this.link)) { - this.updateMarker(pageContentArea); + this.showForDisplay(); } else { this.hideMarker(); } }); } + public showForDisplay() { + const pageContentArea = document.querySelector('.page-content'); + if (pageContentArea instanceof HTMLElement && this.link.checkVisibility()) { + this.updateMarker(pageContentArea); + } + } + protected showForEditor() { const contentWrap = document.querySelector('.editor-content-wrap'); if (contentWrap instanceof HTMLElement) { @@ -90,15 +94,7 @@ export class PageCommentReference extends Component { return; } - const refCloneToAssess = refEl.cloneNode(true) as HTMLElement; - const toRemove = refCloneToAssess.querySelectorAll('[data-lexical-text]'); - refCloneToAssess.removeAttribute('style'); - for (const el of toRemove) { - el.after(...el.childNodes); - el.remove(); - } - - const actualHash = hashElement(refCloneToAssess); + const actualHash = hashElement(refEl); if (actualHash !== refHash) { this.link.classList.add('outdated'); } diff --git a/resources/js/components/page-comment.ts b/resources/js/components/page-comment.ts index 18b9ab73b..ce35cdc4a 100644 --- a/resources/js/components/page-comment.ts +++ b/resources/js/components/page-comment.ts @@ -131,7 +131,16 @@ export class PageComment extends Component { await window.$http.delete(`/comment/${this.commentId}`); this.$emit('delete'); - this.container.closest('.comment-branch')?.remove(); + + const branch = this.container.closest('.comment-branch'); + if (branch instanceof HTMLElement) { + const refs = window.$components.allWithinElement(branch, 'page-comment-reference'); + for (const ref of refs) { + ref.hideMarker(); + } + branch.remove(); + } + window.$events.success(this.deletedText); } diff --git a/resources/js/components/page-comments.ts b/resources/js/components/page-comments.ts index 2482c9dcb..04c812580 100644 --- a/resources/js/components/page-comments.ts +++ b/resources/js/components/page-comments.ts @@ -2,6 +2,8 @@ import {Component} from './component'; import {getLoading, htmlToDom} from '../services/dom.ts'; import {buildForInput} from '../wysiwyg-tinymce/config'; import {Tabs} from "./tabs"; +import {PageCommentReference} from "./page-comment-reference"; +import {scrollAndHighlightElement} from "../services/util"; export interface CommentReplyEvent extends Event { detail: { @@ -27,13 +29,16 @@ export class PageComments extends Component { private addButtonContainer: HTMLElement; private archiveContainer: HTMLElement; private replyToRow: HTMLElement; + private referenceRow: HTMLElement; private formContainer: HTMLElement; private form: HTMLFormElement; private formInput: HTMLInputElement; private formReplyLink: HTMLAnchorElement; + private formReferenceLink: HTMLAnchorElement; private addCommentButton: HTMLElement; private hideFormButton: HTMLElement; private removeReplyToButton: HTMLElement; + private removeReferenceButton: HTMLElement; private wysiwygLanguage: string; private wysiwygTextDirection: string; private wysiwygEditor: any = null; @@ -56,13 +61,16 @@ export class PageComments extends Component { this.addButtonContainer = this.$refs.addButtonContainer; this.archiveContainer = this.$refs.archiveContainer; this.replyToRow = this.$refs.replyToRow; + this.referenceRow = this.$refs.referenceRow; this.formContainer = this.$refs.formContainer; this.form = this.$refs.form as HTMLFormElement; this.formInput = this.$refs.formInput as HTMLInputElement; this.formReplyLink = this.$refs.formReplyLink as HTMLAnchorElement; + this.formReferenceLink = this.$refs.formReferenceLink as HTMLAnchorElement; this.addCommentButton = this.$refs.addCommentButton; this.hideFormButton = this.$refs.hideFormButton; this.removeReplyToButton = this.$refs.removeReplyToButton; + this.removeReferenceButton = this.$refs.removeReferenceButton; // WYSIWYG options this.wysiwygLanguage = this.$opts.wysiwygLanguage; @@ -100,6 +108,7 @@ export class PageComments extends Component { if (this.form) { this.removeReplyToButton.addEventListener('click', this.removeReplyTo.bind(this)); + this.removeReferenceButton.addEventListener('click', () => this.setContentReference('')); this.hideFormButton.addEventListener('click', this.hideForm.bind(this)); this.addCommentButton.addEventListener('click', this.showForm.bind(this)); this.form.addEventListener('submit', this.saveComment.bind(this)); @@ -118,7 +127,7 @@ export class PageComments extends Component { const reqData = { html: this.wysiwygEditor.getContent(), parent_id: this.parentId || null, - content_ref: this.contentReference || '', + content_ref: this.contentReference, }; window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => { @@ -130,6 +139,11 @@ export class PageComments extends Component { this.container.append(newElem); } + const refs = window.$components.allWithinElement(newElem, 'page-comment-reference'); + for (const ref of refs) { + ref.showForDisplay(); + } + window.$events.success(this.createdText); this.hideForm(); this.updateCount(); @@ -152,10 +166,8 @@ export class PageComments extends Component { protected resetForm(): void { this.removeEditor(); this.formInput.value = ''; - this.parentId = null; - this.contentReference = ''; - this.replyToRow.toggleAttribute('hidden', true); - this.container.append(this.formContainer); + this.setContentReference(''); + this.removeReplyTo(); } protected showForm(): void { @@ -240,7 +252,21 @@ export class PageComments extends Component { public startNewComment(contentReference: string): void { this.removeReplyTo(); - this.contentReference = contentReference; + this.setContentReference(contentReference); + } + + protected setContentReference(reference: string): void { + this.contentReference = reference; + this.referenceRow.toggleAttribute('hidden', !Boolean(reference)); + const [id] = reference.split(':'); + this.formReferenceLink.href = `#${id}`; + this.formReferenceLink.onclick = function(event) { + event.preventDefault(); + const el = document.getElementById(id); + if (el) { + scrollAndHighlightElement(el); + } + }; } } diff --git a/resources/js/services/dom.ts b/resources/js/services/dom.ts index 661ed7ca3..77c19a761 100644 --- a/resources/js/services/dom.ts +++ b/resources/js/services/dom.ts @@ -251,9 +251,9 @@ export function findTargetNodeAndOffset(parentNode: HTMLElement, offset: number) } /** - * Create a hash for the given HTML element. + * Create a hash for the given HTML element content. */ export function hashElement(element: HTMLElement): string { - const normalisedElemHtml = element.outerHTML.replace(/\s{2,}/g, ''); - return cyrb53(normalisedElemHtml); + const normalisedElemText = (element.textContent || '').replace(/\s{2,}/g, ''); + return cyrb53(normalisedElemText); } \ No newline at end of file diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss index d25fab299..a86d31ce3 100644 --- a/resources/sass/_components.scss +++ b/resources/sass/_components.scss @@ -569,6 +569,9 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group { border-bottom: 0; padding: 0 vars.$xs; } +.tab-container [role="tabpanel"].no-outline:focus { + outline: none; +} .image-picker .none { display: none; diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php index fe61bf1a4..eadf35187 100644 --- a/resources/views/comments/comment.blade.php +++ b/resources/views/comments/comment.blade.php @@ -87,7 +87,7 @@ option:page-comment-reference:view-comment-text="{{ trans('entities.comment_view') }}" option:page-comment-reference:jump-to-thread-text="{{ trans('entities.comment_jump_to_thread') }}" option:page-comment-reference:close-text="{{ trans('common.close') }}" - href="#">@icon('bookmark')Reference - Outdated + href="#">@icon('bookmark'){{ trans('entities.comment_reference') }} {{ trans('entities.comment_reference_outdated') }} @endif {!! $commentHtml !!} diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index 882cfdf45..51c08d69a 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -36,7 +36,7 @@ tabindex="0" role="tabpanel" aria-labelledby="comment-tab-active" - class="comment-container"> + class="comment-container no-outline">
@foreach($commentTree->getActive() as $branch) @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false]) @@ -63,7 +63,7 @@ role="tabpanel" aria-labelledby="comment-tab-archived" hidden="hidden" - class="comment-container"> + class="comment-container no-outline"> @foreach($commentTree->getArchived() as $branch) @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false]) @endforeach diff --git a/resources/views/comments/create.blade.php b/resources/views/comments/create.blade.php index 417f0c606..134ed5164 100644 --- a/resources/views/comments/create.blade.php +++ b/resources/views/comments/create.blade.php @@ -12,6 +12,16 @@
+