import {Component} from './component';
-import {getLoading, htmlToDom} from '../services/dom.ts';
+import {findTargetNodeAndOffset, getLoading, hashElement, htmlToDom} from '../services/dom.ts';
import {buildForInput} from '../wysiwyg-tinymce/config';
+import {el} from "../wysiwyg/utils/dom";
+
+import commentIcon from "@icons/comment.svg"
export class PageComment extends Component {
protected commentContentRef: string;
protected deletedText: string;
protected updatedText: string;
+ protected viewCommentText: string;
protected wysiwygEditor: any = null;
protected wysiwygLanguage: string;
this.commentContentRef = this.$opts.commentContentRef;
this.deletedText = this.$opts.deletedText;
this.updatedText = this.$opts.updatedText;
+ this.viewCommentText = this.$opts.viewCommentText;
// Editor reference and text options
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
this.input = this.$refs.input as HTMLInputElement;
this.setupListeners();
+ this.positionForReference();
}
protected setupListeners(): void {
return loading;
}
+ protected positionForReference() {
+ if (!this.commentContentRef) {
+ return;
+ }
+
+ const [refId, refHash, refRange] = this.commentContentRef.split(':');
+ const refEl = document.getElementById(refId);
+ if (!refEl) {
+ // TODO - Show outdated marker for comment
+ return;
+ }
+
+ const actualHash = hashElement(refEl);
+ if (actualHash !== refHash) {
+ // TODO - Show outdated marker for comment
+ return;
+ }
+
+ const refElBounds = refEl.getBoundingClientRect();
+ let bounds = refElBounds;
+ const [rangeStart, rangeEnd] = refRange.split('-');
+ if (rangeStart && rangeEnd) {
+ const range = new Range();
+ const relStart = findTargetNodeAndOffset(refEl, Number(rangeStart));
+ const relEnd = findTargetNodeAndOffset(refEl, Number(rangeEnd));
+ if (relStart && relEnd) {
+ range.setStart(relStart.node, relStart.offset);
+ range.setEnd(relEnd.node, relEnd.offset);
+ bounds = range.getBoundingClientRect();
+ }
+ }
+
+ const relLeft = bounds.left - refElBounds.left;
+ const relTop = bounds.top - refElBounds.top;
+
+ const marker = el('button', {
+ type: 'button',
+ class: 'content-comment-marker',
+ title: this.viewCommentText,
+ });
+ marker.innerHTML = <string>commentIcon;
+ marker.addEventListener('click', event => {
+ this.showCommentAtMarker(marker);
+ });
+
+ const markerWrap = el('div', {
+ class: 'content-comment-highlight',
+ style: `left: ${relLeft}px; top: ${relTop}px; width: ${bounds.width}px; height: ${bounds.height}px;`
+ }, [marker]);
+
+ refEl.style.position = 'relative';
+ refEl.append(markerWrap);
+ }
+
+ protected showCommentAtMarker(marker: HTMLElement): void {
+
+ marker.hidden = true;
+ const readClone = this.container.closest('.comment-branch').cloneNode(true) as HTMLElement;
+ const toRemove = readClone.querySelectorAll('.actions, form');
+ for (const el of toRemove) {
+ el.remove();
+ }
+
+ const close = el('button', {type: 'button'}, ['x']);
+ const jump = el('button', {type: 'button'}, ['Jump to thread']);
+
+ const commentWindow = el('div', {
+ class: 'content-comment-window'
+ }, [
+ el('div', {
+ class: 'content-comment-window-actions',
+ }, [jump, close]),
+ el('div', {
+ class: 'content-comment-window-content',
+ }, [readClone]),
+ ]);
+
+ marker.parentElement.append(commentWindow);
+
+ const closeAction = () => {
+ commentWindow.remove();
+ marker.hidden = false;
+ };
+
+ close.addEventListener('click', closeAction.bind(this));
+
+ jump.addEventListener('click', () => {
+ closeAction();
+ this.container.scrollIntoView({behavior: 'smooth'});
+ const highlightTarget = this.container.querySelector('.header') as HTMLElement;
+ highlightTarget.classList.add('anim-highlight');
+ highlightTarget.addEventListener('animationend', () => highlightTarget.classList.remove('anim-highlight'))
+ });
+
+ // TODO - Position wrapper sensibly
+ // TODO - Movement control?
+ }
}