Comments: Styled content comments & improved interaction
This commit is contained in:
parent
5bfba281fc
commit
f656a82fe7
|
@ -403,6 +403,7 @@ return [
|
||||||
'comment_created_success' => 'Comment added',
|
'comment_created_success' => 'Comment added',
|
||||||
'comment_updated_success' => 'Comment updated',
|
'comment_updated_success' => 'Comment updated',
|
||||||
'comment_view' => 'View comment',
|
'comment_view' => 'View comment',
|
||||||
|
'comment_jump_to_thread' => 'Jump to thread',
|
||||||
'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
|
'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
|
||||||
'comment_in_reply_to' => 'In reply to :commentId',
|
'comment_in_reply_to' => 'In reply to :commentId',
|
||||||
'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
|
'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
|
||||||
|
|
|
@ -4,6 +4,13 @@ import {buildForInput} from '../wysiwyg-tinymce/config';
|
||||||
import {el} from "../wysiwyg/utils/dom";
|
import {el} from "../wysiwyg/utils/dom";
|
||||||
|
|
||||||
import commentIcon from "@icons/comment.svg"
|
import commentIcon from "@icons/comment.svg"
|
||||||
|
import closeIcon from "@icons/close.svg"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track the close function for the current open marker so it can be closed
|
||||||
|
* when another is opened so we only show one marker comment thread at one time.
|
||||||
|
*/
|
||||||
|
let openMarkerClose: Function|null = null;
|
||||||
|
|
||||||
export class PageComment extends Component {
|
export class PageComment extends Component {
|
||||||
|
|
||||||
|
@ -13,6 +20,8 @@ export class PageComment extends Component {
|
||||||
protected deletedText: string;
|
protected deletedText: string;
|
||||||
protected updatedText: string;
|
protected updatedText: string;
|
||||||
protected viewCommentText: string;
|
protected viewCommentText: string;
|
||||||
|
protected jumpToThreadText: string;
|
||||||
|
protected closeText: string;
|
||||||
|
|
||||||
protected wysiwygEditor: any = null;
|
protected wysiwygEditor: any = null;
|
||||||
protected wysiwygLanguage: string;
|
protected wysiwygLanguage: string;
|
||||||
|
@ -35,6 +44,8 @@ export class PageComment extends Component {
|
||||||
this.deletedText = this.$opts.deletedText;
|
this.deletedText = this.$opts.deletedText;
|
||||||
this.updatedText = this.$opts.updatedText;
|
this.updatedText = this.$opts.updatedText;
|
||||||
this.viewCommentText = this.$opts.viewCommentText;
|
this.viewCommentText = this.$opts.viewCommentText;
|
||||||
|
this.jumpToThreadText = this.$opts.jumpToThreadText;
|
||||||
|
this.closeText = this.$opts.closeText;
|
||||||
|
|
||||||
// Editor reference and text options
|
// Editor reference and text options
|
||||||
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
|
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
|
||||||
|
@ -130,7 +141,7 @@ export class PageComment extends Component {
|
||||||
|
|
||||||
await window.$http.delete(`/comment/${this.commentId}`);
|
await window.$http.delete(`/comment/${this.commentId}`);
|
||||||
this.$emit('delete');
|
this.$emit('delete');
|
||||||
this.container.closest('.comment-branch').remove();
|
this.container.closest('.comment-branch')?.remove();
|
||||||
window.$events.success(this.deletedText);
|
window.$events.success(this.deletedText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,16 +207,22 @@ export class PageComment extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected showCommentAtMarker(marker: HTMLElement): void {
|
protected showCommentAtMarker(marker: HTMLElement): void {
|
||||||
|
// Hide marker and close existing marker windows
|
||||||
|
if (openMarkerClose) {
|
||||||
|
openMarkerClose();
|
||||||
|
}
|
||||||
marker.hidden = true;
|
marker.hidden = true;
|
||||||
const readClone = this.container.closest('.comment-branch').cloneNode(true) as HTMLElement;
|
|
||||||
|
// Build comment window
|
||||||
|
const readClone = (this.container.closest('.comment-branch') as HTMLElement).cloneNode(true) as HTMLElement;
|
||||||
const toRemove = readClone.querySelectorAll('.actions, form');
|
const toRemove = readClone.querySelectorAll('.actions, form');
|
||||||
for (const el of toRemove) {
|
for (const el of toRemove) {
|
||||||
el.remove();
|
el.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const close = el('button', {type: 'button'}, ['x']);
|
const close = el('button', {type: 'button', title: this.closeText});
|
||||||
const jump = el('button', {type: 'button'}, ['Jump to thread']);
|
close.innerHTML = (closeIcon as string);
|
||||||
|
const jump = el('button', {type: 'button', 'data-action': 'jump'}, [this.jumpToThreadText]);
|
||||||
|
|
||||||
const commentWindow = el('div', {
|
const commentWindow = el('div', {
|
||||||
class: 'content-comment-window'
|
class: 'content-comment-window'
|
||||||
|
@ -214,19 +231,29 @@ export class PageComment extends Component {
|
||||||
class: 'content-comment-window-actions',
|
class: 'content-comment-window-actions',
|
||||||
}, [jump, close]),
|
}, [jump, close]),
|
||||||
el('div', {
|
el('div', {
|
||||||
class: 'content-comment-window-content',
|
class: 'content-comment-window-content comment-container-compact comment-container-super-compact',
|
||||||
}, [readClone]),
|
}, [readClone]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
marker.parentElement.append(commentWindow);
|
marker.parentElement?.append(commentWindow);
|
||||||
|
|
||||||
|
// Handle interaction within window
|
||||||
const closeAction = () => {
|
const closeAction = () => {
|
||||||
commentWindow.remove();
|
commentWindow.remove();
|
||||||
marker.hidden = false;
|
marker.hidden = false;
|
||||||
|
window.removeEventListener('click', windowCloseAction);
|
||||||
|
openMarkerClose = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
close.addEventListener('click', closeAction.bind(this));
|
const windowCloseAction = (event: MouseEvent) => {
|
||||||
|
if (!(marker.parentElement as HTMLElement).contains(event.target as HTMLElement)) {
|
||||||
|
closeAction();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('click', windowCloseAction);
|
||||||
|
|
||||||
|
openMarkerClose = closeAction;
|
||||||
|
close.addEventListener('click', closeAction.bind(this));
|
||||||
jump.addEventListener('click', () => {
|
jump.addEventListener('click', () => {
|
||||||
closeAction();
|
closeAction();
|
||||||
this.container.scrollIntoView({behavior: 'smooth'});
|
this.container.scrollIntoView({behavior: 'smooth'});
|
||||||
|
@ -235,7 +262,12 @@ export class PageComment extends Component {
|
||||||
highlightTarget.addEventListener('animationend', () => highlightTarget.classList.remove('anim-highlight'))
|
highlightTarget.addEventListener('animationend', () => highlightTarget.classList.remove('anim-highlight'))
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - Position wrapper sensibly
|
// Position window within bounds
|
||||||
// TODO - Movement control?
|
const commentWindowBounds = commentWindow.getBoundingClientRect();
|
||||||
|
const contentBounds = document.querySelector('.page-content')?.getBoundingClientRect();
|
||||||
|
if (contentBounds && commentWindowBounds.right > contentBounds.right) {
|
||||||
|
const diff = commentWindowBounds.right - contentBounds.right;
|
||||||
|
commentWindow.style.left = `-${diff}px`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -746,6 +746,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
||||||
height: calc(100% - vars.$m);
|
height: calc(100% - vars.$m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-branch .comment-box {
|
||||||
|
margin-bottom: vars.$m;
|
||||||
|
}
|
||||||
|
|
||||||
.comment-branch .comment-branch .comment-branch .comment-branch .comment-thread-indicator {
|
.comment-branch .comment-branch .comment-branch .comment-branch .comment-thread-indicator {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -761,6 +765,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-container-compact .comment-box {
|
.comment-container-compact .comment-box {
|
||||||
|
margin-bottom: vars.$xs;
|
||||||
.meta {
|
.meta {
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
@ -778,6 +783,28 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
||||||
width: vars.$m;
|
width: vars.$m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-container-super-compact .comment-box {
|
||||||
|
.meta {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 18px;
|
||||||
|
margin-inline-end: 2px !important;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: vars.$xxs vars.$s;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-container-super-compact .comment-thread-indicator {
|
||||||
|
width: (vars.$xs + 3px);
|
||||||
|
margin-inline-start: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
#tag-manager .drag-card {
|
#tag-manager .drag-card {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,12 +242,13 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
||||||
.content-comment-window {
|
.content-comment-window {
|
||||||
font-size: vars.$fs-m;
|
font-size: vars.$fs-m;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
position: relative;
|
position: absolute;
|
||||||
z-index: 90;
|
top: calc(100% + 3px);
|
||||||
|
left: 0;
|
||||||
|
z-index: 92;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
min-width: min(340px, 80vw);
|
min-width: min(340px, 80vw);
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
//border: 1px solid var(--color-primary);
|
|
||||||
box-shadow: vars.$bs-hover;
|
box-shadow: vars.$bs-hover;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -258,9 +259,24 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
gap: vars.$xs;
|
||||||
|
button {
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: vars.$xs;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button[data-action="jump"] {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
fill: currentColor;
|
||||||
|
width: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.content-comment-window-content {
|
.content-comment-window-content {
|
||||||
padding: vars.$xs;
|
padding: vars.$xs vars.$s vars.$xs vars.$xs;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
@ -280,11 +296,16 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 90;
|
z-index: 90;
|
||||||
|
transform: scale(1);
|
||||||
|
transition: transform ease-in-out 120ms;
|
||||||
svg {
|
svg {
|
||||||
fill: #FFF;
|
fill: #FFF;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.page-content [id^="bkmrk-"]:hover .content-comment-marker {
|
||||||
|
transform: scale(1.15);
|
||||||
|
}
|
||||||
|
|
||||||
// Page editor sidebar toolbox
|
// Page editor sidebar toolbox
|
||||||
.floating-toolbox {
|
.floating-toolbox {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="comment-branch">
|
<div class="comment-branch">
|
||||||
<div class="mb-m">
|
<div>
|
||||||
@include('comments.comment', ['comment' => $branch['comment']])
|
@include('comments.comment', ['comment' => $branch['comment']])
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-container-row">
|
<div class="flex-container-row">
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
option:page-comment:updated-text="{{ trans('entities.comment_updated_success') }}"
|
option:page-comment:updated-text="{{ trans('entities.comment_updated_success') }}"
|
||||||
option:page-comment:deleted-text="{{ trans('entities.comment_deleted_success') }}"
|
option:page-comment:deleted-text="{{ trans('entities.comment_deleted_success') }}"
|
||||||
option:page-comment:view-comment-text="{{ trans('entities.comment_view') }}"
|
option:page-comment:view-comment-text="{{ trans('entities.comment_view') }}"
|
||||||
|
option:page-comment:jump-to-thread-text="{{ trans('entities.comment_jump_to_thread') }}"
|
||||||
|
option:page-comment:close-text="{{ trans('common.close') }}"
|
||||||
option:page-comment:wysiwyg-language="{{ $locale->htmlLang() }}"
|
option:page-comment:wysiwyg-language="{{ $locale->htmlLang() }}"
|
||||||
option:page-comment:wysiwyg-text-direction="{{ $locale->htmlDirection() }}"
|
option:page-comment:wysiwyg-text-direction="{{ $locale->htmlDirection() }}"
|
||||||
id="comment{{$comment->local_id}}"
|
id="comment{{$comment->local_id}}"
|
||||||
|
|
Loading…
Reference in New Issue