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_updated_success' => 'Comment updated',
|
||||
'comment_view' => 'View comment',
|
||||
'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_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 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 {
|
||||
|
||||
|
@ -13,6 +20,8 @@ export class PageComment extends Component {
|
|||
protected deletedText: string;
|
||||
protected updatedText: string;
|
||||
protected viewCommentText: string;
|
||||
protected jumpToThreadText: string;
|
||||
protected closeText: string;
|
||||
|
||||
protected wysiwygEditor: any = null;
|
||||
protected wysiwygLanguage: string;
|
||||
|
@ -35,6 +44,8 @@ export class PageComment extends Component {
|
|||
this.deletedText = this.$opts.deletedText;
|
||||
this.updatedText = this.$opts.updatedText;
|
||||
this.viewCommentText = this.$opts.viewCommentText;
|
||||
this.jumpToThreadText = this.$opts.jumpToThreadText;
|
||||
this.closeText = this.$opts.closeText;
|
||||
|
||||
// Editor reference and text options
|
||||
this.wysiwygLanguage = this.$opts.wysiwygLanguage;
|
||||
|
@ -130,7 +141,7 @@ export class PageComment extends Component {
|
|||
|
||||
await window.$http.delete(`/comment/${this.commentId}`);
|
||||
this.$emit('delete');
|
||||
this.container.closest('.comment-branch').remove();
|
||||
this.container.closest('.comment-branch')?.remove();
|
||||
window.$events.success(this.deletedText);
|
||||
}
|
||||
|
||||
|
@ -196,16 +207,22 @@ export class PageComment extends Component {
|
|||
}
|
||||
|
||||
protected showCommentAtMarker(marker: HTMLElement): void {
|
||||
|
||||
// Hide marker and close existing marker windows
|
||||
if (openMarkerClose) {
|
||||
openMarkerClose();
|
||||
}
|
||||
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');
|
||||
for (const el of toRemove) {
|
||||
el.remove();
|
||||
}
|
||||
|
||||
const close = el('button', {type: 'button'}, ['x']);
|
||||
const jump = el('button', {type: 'button'}, ['Jump to thread']);
|
||||
const close = el('button', {type: 'button', title: this.closeText});
|
||||
close.innerHTML = (closeIcon as string);
|
||||
const jump = el('button', {type: 'button', 'data-action': 'jump'}, [this.jumpToThreadText]);
|
||||
|
||||
const commentWindow = el('div', {
|
||||
class: 'content-comment-window'
|
||||
|
@ -214,19 +231,29 @@ export class PageComment extends Component {
|
|||
class: 'content-comment-window-actions',
|
||||
}, [jump, close]),
|
||||
el('div', {
|
||||
class: 'content-comment-window-content',
|
||||
class: 'content-comment-window-content comment-container-compact comment-container-super-compact',
|
||||
}, [readClone]),
|
||||
]);
|
||||
|
||||
marker.parentElement.append(commentWindow);
|
||||
marker.parentElement?.append(commentWindow);
|
||||
|
||||
// Handle interaction within window
|
||||
const closeAction = () => {
|
||||
commentWindow.remove();
|
||||
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', () => {
|
||||
closeAction();
|
||||
this.container.scrollIntoView({behavior: 'smooth'});
|
||||
|
@ -235,7 +262,12 @@ export class PageComment extends Component {
|
|||
highlightTarget.addEventListener('animationend', () => highlightTarget.classList.remove('anim-highlight'))
|
||||
});
|
||||
|
||||
// TODO - Position wrapper sensibly
|
||||
// TODO - Movement control?
|
||||
// Position window within bounds
|
||||
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);
|
||||
}
|
||||
|
||||
.comment-branch .comment-box {
|
||||
margin-bottom: vars.$m;
|
||||
}
|
||||
|
||||
.comment-branch .comment-branch .comment-branch .comment-branch .comment-thread-indicator {
|
||||
display: none;
|
||||
}
|
||||
|
@ -761,6 +765,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
}
|
||||
|
||||
.comment-container-compact .comment-box {
|
||||
margin-bottom: vars.$xs;
|
||||
.meta {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
@ -778,6 +783,28 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
|
|||
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 {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
|
|
@ -242,12 +242,13 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
|||
.content-comment-window {
|
||||
font-size: vars.$fs-m;
|
||||
line-height: 1.4;
|
||||
position: relative;
|
||||
z-index: 90;
|
||||
position: absolute;
|
||||
top: calc(100% + 3px);
|
||||
left: 0;
|
||||
z-index: 92;
|
||||
pointer-events: all;
|
||||
min-width: min(340px, 80vw);
|
||||
background-color: #FFF;
|
||||
//border: 1px solid var(--color-primary);
|
||||
box-shadow: vars.$bs-hover;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
@ -258,9 +259,24 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
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 {
|
||||
padding: vars.$xs;
|
||||
padding: vars.$xs vars.$s vars.$xs vars.$xs;
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
@ -280,11 +296,16 @@ body.tox-fullscreen, body.markdown-fullscreen {
|
|||
color: #FFF;
|
||||
cursor: pointer;
|
||||
z-index: 90;
|
||||
transform: scale(1);
|
||||
transition: transform ease-in-out 120ms;
|
||||
svg {
|
||||
fill: #FFF;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
.page-content [id^="bkmrk-"]:hover .content-comment-marker {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
// Page editor sidebar toolbox
|
||||
.floating-toolbox {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="comment-branch">
|
||||
<div class="mb-m">
|
||||
<div>
|
||||
@include('comments.comment', ['comment' => $branch['comment']])
|
||||
</div>
|
||||
<div class="flex-container-row">
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
option:page-comment:updated-text="{{ trans('entities.comment_updated_success') }}"
|
||||
option:page-comment:deleted-text="{{ trans('entities.comment_deleted_success') }}"
|
||||
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-text-direction="{{ $locale->htmlDirection() }}"
|
||||
id="comment{{$comment->local_id}}"
|
||||
|
|
Loading…
Reference in New Issue