]> BookStack Code Mirror - bookstack/blobdiff - resources/js/components/page-comment-reference.ts
Opensearch: Fixed XML declaration when php short tags enabled
[bookstack] / resources / js / components / page-comment-reference.ts
index 72e3dbe480d815ea801758b22edbfc33070afb1e..009e806c104d429cfa2451f4af68b20b26f803db 100644 (file)
@@ -3,7 +3,9 @@ import {findTargetNodeAndOffset, hashElement} from "../services/dom";
 import {el} from "../wysiwyg/utils/dom";
 import commentIcon from "@icons/comment.svg";
 import closeIcon from "@icons/close.svg";
-import {scrollAndHighlightElement} from "../services/util";
+import {debounce, scrollAndHighlightElement} from "../services/util";
+import {EditorToolboxChangeEventData} from "./editor-toolbox";
+import {TabsChangeEvent} from "./tabs";
 
 /**
  * Track the close function for the current open marker so it can be closed
@@ -12,13 +14,13 @@ import {scrollAndHighlightElement} from "../services/util";
 let openMarkerClose: Function|null = null;
 
 export class PageCommentReference extends Component {
-    protected link: HTMLLinkElement;
-    protected reference: string;
+    protected link!: HTMLLinkElement;
+    protected reference!: string;
     protected markerWrap: HTMLElement|null = null;
 
-    protected viewCommentText: string;
-    protected jumpToThreadText: string;
-    protected closeText: string;
+    protected viewCommentText!: string;
+    protected jumpToThreadText!: string;
+    protected closeText!: string;
 
     setup() {
         this.link = this.$el as HTMLLinkElement;
@@ -28,21 +30,53 @@ export class PageCommentReference extends Component {
         this.closeText = this.$opts.closeText;
 
         // Show within page display area if seen
+        this.showForDisplay();
+
+        // Handle editor view to show on comments toolbox view
+        window.addEventListener('editor-toolbox-change', ((event: CustomEvent<EditorToolboxChangeEventData>) => {
+            const tabName: string = event.detail.tab;
+            const isOpen = event.detail.open;
+            if (tabName === 'comments' && isOpen && this.link.checkVisibility()) {
+                this.showForEditor();
+            } else {
+                this.hideMarker();
+            }
+        }) as EventListener);
+
+        // Handle visibility changes within editor toolbox archived details dropdown
+        window.addEventListener('toggle', event => {
+            if (event.target instanceof HTMLElement && event.target.contains(this.link)) {
+                window.requestAnimationFrame(() => {
+                    if (this.link.checkVisibility()) {
+                        this.showForEditor();
+                    } else {
+                        this.hideMarker();
+                    }
+                });
+            }
+        }, {capture: true});
+
+        // Handle comments tab changes to hide/show markers & indicators
+        window.addEventListener('tabs-change', ((event: CustomEvent<TabsChangeEvent>) => {
+            const sectionId = event.detail.showing;
+            if (!sectionId.startsWith('comment-tab-panel')) {
+                return;
+            }
+
+            const panel = document.getElementById(sectionId);
+            if (panel?.contains(this.link)) {
+                this.showForDisplay();
+            } else {
+                this.hideMarker();
+            }
+        }) as EventListener);
+    }
+
+    public showForDisplay() {
         const pageContentArea = document.querySelector('.page-content');
-        if (pageContentArea instanceof HTMLElement) {
+        if (pageContentArea instanceof HTMLElement && this.link.checkVisibility()) {
             this.updateMarker(pageContentArea);
         }
-
-        // Handle editor view to show on comments toolbox view
-        window.addEventListener('editor-toolbox-change', (event) => {
-             const tabName: string = (event as {detail: {tab: string, open: boolean}}).detail.tab;
-             const isOpen = (event as {detail: {tab: string, open: boolean}}).detail.open;
-             if (tabName === 'comments' && isOpen) {
-                 this.showForEditor();
-             } else {
-                 this.hideMarker();
-             }
-        });
     }
 
     protected showForEditor() {
@@ -75,15 +109,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');
         }
@@ -111,9 +137,10 @@ export class PageCommentReference extends Component {
             scrollAndHighlightElement(refEl);
         });
 
-        window.addEventListener('resize', () => {
+        const debouncedReposition = debounce(() => {
             this.positionMarker(refEl, refRange);
-        });
+        }, 50, false).bind(this);
+        window.addEventListener('resize', debouncedReposition);
     }
 
     protected positionMarker(targetEl: HTMLElement, range: string) {
@@ -145,12 +172,13 @@ export class PageCommentReference extends Component {
         this.markerWrap.style.height = `${targetBounds.height}px`;
     }
 
-    protected hideMarker() {
+    public hideMarker() {
         // Hide marker and close existing marker windows
         if (openMarkerClose) {
             openMarkerClose();
         }
         this.markerWrap?.remove();
+        this.markerWrap = null;
     }
 
     protected showCommentAtMarker(marker: HTMLElement): void {