From ecda4e1d6f42108fef9c62ff4a9a73a056caa089 Mon Sep 17 00:00:00 2001
From: Dan Brown
Date: Sat, 26 Apr 2025 21:05:54 +0100
Subject: [PATCH] Comments: Added reference marker to comments
---
resources/icons/bookmark.svg | 1 +
resources/js/components/page-comment.ts | 17 +++++++---
resources/js/components/page-display.js | 3 ++
resources/sass/_components.scss | 38 ++++++++++++++++++++++
resources/sass/_pages.scss | 3 ++
resources/views/comments/comment.blade.php | 5 +++
6 files changed, 63 insertions(+), 4 deletions(-)
create mode 100644 resources/icons/bookmark.svg
diff --git a/resources/icons/bookmark.svg b/resources/icons/bookmark.svg
new file mode 100644
index 000000000..30e487c52
--- /dev/null
+++ b/resources/icons/bookmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/js/components/page-comment.ts b/resources/js/components/page-comment.ts
index 9192c7c56..24964bf5c 100644
--- a/resources/js/components/page-comment.ts
+++ b/resources/js/components/page-comment.ts
@@ -5,6 +5,7 @@ import {el} from "../wysiwyg/utils/dom";
import commentIcon from "@icons/comment.svg"
import closeIcon from "@icons/close.svg"
+import {PageDisplay} from "./page-display";
/**
* Track the close function for the current open marker so it can be closed
@@ -35,6 +36,7 @@ export class PageComment extends Component {
protected deleteButton: HTMLElement;
protected replyButton: HTMLElement;
protected input: HTMLInputElement;
+ protected contentRefLink: HTMLLinkElement|null;
setup() {
// Options
@@ -60,6 +62,7 @@ export class PageComment extends Component {
this.deleteButton = this.$refs.deleteButton;
this.replyButton = this.$refs.replyButton;
this.input = this.$refs.input as HTMLInputElement;
+ this.contentRefLink = (this.$refs.contentRef || null) as HTMLLinkElement|null;
this.setupListeners();
this.positionForReference();
@@ -153,21 +156,20 @@ export class PageComment extends Component {
}
protected positionForReference() {
- if (!this.commentContentRef) {
+ if (!this.commentContentRef || !this.contentRefLink) {
return;
}
const [refId, refHash, refRange] = this.commentContentRef.split(':');
const refEl = document.getElementById(refId);
if (!refEl) {
- // TODO - Show outdated marker for comment
+ this.contentRefLink.classList.add('outdated', 'missing');
return;
}
const actualHash = hashElement(refEl);
if (actualHash !== refHash) {
- // TODO - Show outdated marker for comment
- return;
+ this.contentRefLink.classList.add('outdated');
}
const refElBounds = refEl.getBoundingClientRect();
@@ -204,6 +206,13 @@ export class PageComment extends Component {
refEl.style.position = 'relative';
refEl.append(markerWrap);
+
+ this.contentRefLink.href = `#${refEl.id}`;
+ this.contentRefLink.addEventListener('click', (event: MouseEvent) => {
+ const pageDisplayComponent = window.$components.get('page-display')[0] as PageDisplay;
+ event.preventDefault();
+ pageDisplayComponent.goToText(refId);
+ });
}
protected showCommentAtMarker(marker: HTMLElement): void {
diff --git a/resources/js/components/page-display.js b/resources/js/components/page-display.js
index d3ac78a4a..13670c4bf 100644
--- a/resources/js/components/page-display.js
+++ b/resources/js/components/page-display.js
@@ -57,6 +57,9 @@ export class PageDisplay extends Component {
}
}
+ /**
+ * @public
+ */
goToText(text) {
const idElem = document.getElementById(text);
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index 26b051827..5486d6112 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -746,6 +746,44 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
height: calc(100% - vars.$m);
}
+.comment-reference-indicator-wrap a {
+ float: left;
+ margin-top: vars.$xs;
+ font-size: 12px;
+ display: inline-block;
+ font-weight: bold;
+ position: relative;
+ border-radius: 4px;
+ overflow: hidden;
+ padding: 2px 6px 2px 0;
+ margin-inline-end: vars.$xs;
+ color: var(--color-link);
+ span {
+ display: none;
+ }
+ &.outdated span {
+ display: inline;
+ }
+ &.outdated.missing {
+ color: var(--color-warning);
+ pointer-events: none;
+ }
+ svg {
+ width: 24px;
+ margin-inline-end: 0;
+ }
+ &:after {
+ background-color: currentColor;
+ content: '';
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: 0;
+ opacity: 0.15;
+ }
+}
+
.comment-branch .comment-box {
margin-bottom: vars.$m;
}
diff --git a/resources/sass/_pages.scss b/resources/sass/_pages.scss
index be5a0f7c3..dbdcc0665 100755
--- a/resources/sass/_pages.scss
+++ b/resources/sass/_pages.scss
@@ -280,6 +280,9 @@ body.tox-fullscreen, body.markdown-fullscreen {
max-height: 200px;
overflow-y: scroll;
}
+.content-comment-window-content .comment-reference-indicator-wrap {
+ display: none;
+}
.content-comment-marker {
position: absolute;
right: -16px;
diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php
index 5b79da4ac..7cc84a54c 100644
--- a/resources/views/comments/comment.blade.php
+++ b/resources/views/comments/comment.blade.php
@@ -77,6 +77,11 @@
@icon('reply'){{ trans('entities.comment_in_reply_to', ['commentId' => '#' . $comment->parent_id]) }}
@endif
+ @if($comment->content_ref)
+
+ @endif
{!! $commentHtml !!}