Comments: Moved to tab UI, Converted tabs component to ts
This commit is contained in:
		
							parent
							
								
									099f6104d0
								
							
						
					
					
						commit
						e7dcc2dcdf
					
				| 
						 | 
				
			
			@ -28,7 +28,7 @@ class CommentTree
 | 
			
		|||
 | 
			
		||||
    public function empty(): bool
 | 
			
		||||
    {
 | 
			
		||||
        return count($this->tree) === 0;
 | 
			
		||||
        return count($this->getActive()) === 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function count(): int
 | 
			
		||||
| 
						 | 
				
			
			@ -41,11 +41,21 @@ class CommentTree
 | 
			
		|||
        return array_filter($this->tree, fn (CommentTreeNode $node) => !$node->comment->archived);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function activeThreadCount(): int
 | 
			
		||||
    {
 | 
			
		||||
        return count($this->getActive());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getArchived(): array
 | 
			
		||||
    {
 | 
			
		||||
        return array_filter($this->tree, fn (CommentTreeNode $node) => $node->comment->archived);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function archivedThreadCount(): int
 | 
			
		||||
    {
 | 
			
		||||
        return count($this->getArchived());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getCommentNodeForId(int $commentId): ?CommentTreeNode
 | 
			
		||||
    {
 | 
			
		||||
        foreach ($this->tree as $node) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -392,9 +392,10 @@ return [
 | 
			
		|||
    'comment' => 'Comment',
 | 
			
		||||
    'comments' => 'Comments',
 | 
			
		||||
    'comment_add' => 'Add Comment',
 | 
			
		||||
    'comment_archived' => ':count Archived Comment|:count Archived Comments',
 | 
			
		||||
    'comment_none' => 'No comments to display',
 | 
			
		||||
    'comment_placeholder' => 'Leave a comment here',
 | 
			
		||||
    'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments',
 | 
			
		||||
    'comment_thread_count' => ':count Comment Thread|:count Comment Threads',
 | 
			
		||||
    'comment_archived_count' => ':count Archived',
 | 
			
		||||
    'comment_save' => 'Save Comment',
 | 
			
		||||
    'comment_new' => 'New Comment',
 | 
			
		||||
    'comment_created' => 'commented :createDiff',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -140,8 +140,8 @@ export class PageComment extends Component {
 | 
			
		|||
        const action = isArchived ? 'unarchive' : 'archive';
 | 
			
		||||
 | 
			
		||||
        const response = await window.$http.put(`/comment/${this.commentId}/${action}`);
 | 
			
		||||
        this.$emit(action, {new_thread_dom: htmlToDom(response.data as string)});
 | 
			
		||||
        window.$events.success(this.archiveText);
 | 
			
		||||
        this.$emit(action, {new_thread_dom: htmlToDom(response.data as string)});
 | 
			
		||||
        this.container.closest('.comment-branch')?.remove();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
import {Component} from './component';
 | 
			
		||||
import {getLoading, htmlToDom} from '../services/dom.ts';
 | 
			
		||||
import {buildForInput} from '../wysiwyg-tinymce/config';
 | 
			
		||||
import {Tabs} from "./tabs";
 | 
			
		||||
 | 
			
		||||
export interface CommentReplyEvent extends Event {
 | 
			
		||||
    detail: {
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +22,8 @@ export class PageComments extends Component {
 | 
			
		|||
    private pageId: number;
 | 
			
		||||
    private container: HTMLElement;
 | 
			
		||||
    private commentCountBar: HTMLElement;
 | 
			
		||||
    private commentsTitle: HTMLElement;
 | 
			
		||||
    private activeTab: HTMLElement;
 | 
			
		||||
    private archivedTab: HTMLElement;
 | 
			
		||||
    private addButtonContainer: HTMLElement;
 | 
			
		||||
    private archiveContainer: HTMLElement;
 | 
			
		||||
    private replyToRow: HTMLElement;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +39,7 @@ export class PageComments extends Component {
 | 
			
		|||
    private wysiwygEditor: any = null;
 | 
			
		||||
    private createdText: string;
 | 
			
		||||
    private countText: string;
 | 
			
		||||
    private archivedCountText: string;
 | 
			
		||||
    private parentId: number | null = null;
 | 
			
		||||
    private contentReference: string = '';
 | 
			
		||||
    private formReplyText: string = '';
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +51,8 @@ export class PageComments extends Component {
 | 
			
		|||
        // Element references
 | 
			
		||||
        this.container = this.$refs.commentContainer;
 | 
			
		||||
        this.commentCountBar = this.$refs.commentCountBar;
 | 
			
		||||
        this.commentsTitle = this.$refs.commentsTitle;
 | 
			
		||||
        this.activeTab = this.$refs.activeTab;
 | 
			
		||||
        this.archivedTab = this.$refs.archivedTab;
 | 
			
		||||
        this.addButtonContainer = this.$refs.addButtonContainer;
 | 
			
		||||
        this.archiveContainer = this.$refs.archiveContainer;
 | 
			
		||||
        this.replyToRow = this.$refs.replyToRow;
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +71,7 @@ export class PageComments extends Component {
 | 
			
		|||
        // Translations
 | 
			
		||||
        this.createdText = this.$opts.createdText;
 | 
			
		||||
        this.countText = this.$opts.countText;
 | 
			
		||||
        this.archivedCountText = this.$opts.archivedCountText;
 | 
			
		||||
 | 
			
		||||
        this.formReplyText = this.formReplyLink?.textContent || '';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -85,10 +90,12 @@ export class PageComments extends Component {
 | 
			
		|||
 | 
			
		||||
        this.elem.addEventListener('page-comment-archive', (event: ArchiveEvent) => {
 | 
			
		||||
            this.archiveContainer.append(event.detail.new_thread_dom);
 | 
			
		||||
            setTimeout(() => this.updateCount(), 1);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.elem.addEventListener('page-comment-unarchive', (event: ArchiveEvent) => {
 | 
			
		||||
            this.container.append(event.detail.new_thread_dom)
 | 
			
		||||
            this.container.append(event.detail.new_thread_dom);
 | 
			
		||||
            setTimeout(() => this.updateCount(), 1);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (this.form) {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,8 +143,10 @@ export class PageComments extends Component {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    protected updateCount(): void {
 | 
			
		||||
        const count = this.getCommentCount();
 | 
			
		||||
        this.commentsTitle.textContent = window.$trans.choice(this.countText, count);
 | 
			
		||||
        const activeCount = this.getActiveThreadCount();
 | 
			
		||||
        this.activeTab.textContent = window.$trans.choice(this.countText, activeCount);
 | 
			
		||||
        const archivedCount = this.getArchivedThreadCount();
 | 
			
		||||
        this.archivedTab.textContent = window.$trans.choice(this.archivedCountText, archivedCount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected resetForm(): void {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,12 +164,18 @@ export class PageComments extends Component {
 | 
			
		|||
        this.addButtonContainer.toggleAttribute('hidden', true);
 | 
			
		||||
        this.formContainer.scrollIntoView({behavior: 'smooth', block: 'nearest'});
 | 
			
		||||
        this.loadEditor();
 | 
			
		||||
 | 
			
		||||
        // Ensure the active comments tab is displaying
 | 
			
		||||
        const tabs = window.$components.firstOnElement(this.elem, 'tabs');
 | 
			
		||||
        if (tabs instanceof Tabs) {
 | 
			
		||||
            tabs.show('comment-tab-panel-active');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected hideForm(): void {
 | 
			
		||||
        this.resetForm();
 | 
			
		||||
        this.formContainer.toggleAttribute('hidden', true);
 | 
			
		||||
        if (this.getCommentCount() > 0) {
 | 
			
		||||
        if (this.getActiveThreadCount() > 0) {
 | 
			
		||||
            this.elem.append(this.addButtonContainer);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.commentCountBar.append(this.addButtonContainer);
 | 
			
		||||
| 
						 | 
				
			
			@ -198,8 +213,12 @@ export class PageComments extends Component {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected getCommentCount(): number {
 | 
			
		||||
        return this.container.querySelectorAll('[component="page-comment"]').length;
 | 
			
		||||
    protected getActiveThreadCount(): number {
 | 
			
		||||
        return this.container.querySelectorAll(':scope > .comment-branch:not([hidden])').length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected getArchivedThreadCount(): number {
 | 
			
		||||
        return this.archiveContainer.querySelectorAll(':scope > .comment-branch').length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected setReply(commentLocalId, commentElement): void {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,18 +19,25 @@ import {Component} from './component';
 | 
			
		|||
 */
 | 
			
		||||
export class Tabs extends Component {
 | 
			
		||||
 | 
			
		||||
    protected container: HTMLElement;
 | 
			
		||||
    protected tabList: HTMLElement;
 | 
			
		||||
    protected tabs: HTMLElement[];
 | 
			
		||||
    protected panels: HTMLElement[];
 | 
			
		||||
 | 
			
		||||
    protected activeUnder: number;
 | 
			
		||||
    protected active: null|boolean = null;
 | 
			
		||||
 | 
			
		||||
    setup() {
 | 
			
		||||
        this.container = this.$el;
 | 
			
		||||
        this.tabList = this.container.querySelector('[role="tablist"]');
 | 
			
		||||
        this.tabList = this.container.querySelector('[role="tablist"]') as HTMLElement;
 | 
			
		||||
        this.tabs = Array.from(this.tabList.querySelectorAll('[role="tab"]'));
 | 
			
		||||
        this.panels = Array.from(this.container.querySelectorAll(':scope > [role="tabpanel"], :scope > * > [role="tabpanel"]'));
 | 
			
		||||
        this.activeUnder = this.$opts.activeUnder ? Number(this.$opts.activeUnder) : 10000;
 | 
			
		||||
        this.active = null;
 | 
			
		||||
 | 
			
		||||
        this.container.addEventListener('click', event => {
 | 
			
		||||
            const tab = event.target.closest('[role="tab"]');
 | 
			
		||||
            if (tab && this.tabs.includes(tab)) {
 | 
			
		||||
                this.show(tab.getAttribute('aria-controls'));
 | 
			
		||||
            const tab = (event.target as HTMLElement).closest('[role="tab"]');
 | 
			
		||||
            if (tab instanceof HTMLElement && this.tabs.includes(tab)) {
 | 
			
		||||
                this.show(tab.getAttribute('aria-controls') || '');
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +47,7 @@ export class Tabs extends Component {
 | 
			
		|||
        this.updateActiveState();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    show(sectionId) {
 | 
			
		||||
    public show(sectionId: string): void {
 | 
			
		||||
        for (const panel of this.panels) {
 | 
			
		||||
            panel.toggleAttribute('hidden', panel.id !== sectionId);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +61,7 @@ export class Tabs extends Component {
 | 
			
		|||
        this.$emit('change', {showing: sectionId});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    updateActiveState() {
 | 
			
		||||
    protected updateActiveState(): void {
 | 
			
		||||
        const active = window.innerWidth < this.activeUnder;
 | 
			
		||||
        if (active === this.active) {
 | 
			
		||||
            return;
 | 
			
		||||
| 
						 | 
				
			
			@ -69,13 +76,13 @@ export class Tabs extends Component {
 | 
			
		|||
        this.active = active;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    activate() {
 | 
			
		||||
    protected activate(): void {
 | 
			
		||||
        const panelToShow = this.panels.find(p => !p.hasAttribute('hidden')) || this.panels[0];
 | 
			
		||||
        this.show(panelToShow.id);
 | 
			
		||||
        this.tabList.toggleAttribute('hidden', false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    deactivate() {
 | 
			
		||||
    protected deactivate(): void {
 | 
			
		||||
        for (const panel of this.panels) {
 | 
			
		||||
            panel.removeAttribute('hidden');
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -802,6 +802,13 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 | 
			
		|||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.comment-container .empty-state {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
.comment-container:not(:has([component="page-comment"])) .empty-state {
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.comment-container-compact .comment-box {
 | 
			
		||||
  margin-bottom: vars.$xs;
 | 
			
		||||
  .meta {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,49 +1,73 @@
 | 
			
		|||
<section component="page-comments"
 | 
			
		||||
<section components="page-comments tabs"
 | 
			
		||||
         option:page-comments:page-id="{{ $page->id }}"
 | 
			
		||||
         option:page-comments:created-text="{{ trans('entities.comment_created_success') }}"
 | 
			
		||||
         option:page-comments:count-text="{{ trans('entities.comment_count') }}"
 | 
			
		||||
         option:page-comments:count-text="{{ trans('entities.comment_thread_count') }}"
 | 
			
		||||
         option:page-comments:archived-count-text="{{ trans('entities.comment_archived_count') }}"
 | 
			
		||||
         option:page-comments:wysiwyg-language="{{ $locale->htmlLang() }}"
 | 
			
		||||
         option:page-comments:wysiwyg-text-direction="{{ $locale->htmlDirection() }}"
 | 
			
		||||
         class="comments-list"
 | 
			
		||||
         class="comments-list tab-container"
 | 
			
		||||
         aria-label="{{ trans('entities.comments') }}">
 | 
			
		||||
 | 
			
		||||
    <div refs="page-comments@comment-count-bar" class="grid half left-focus v-center no-row-gap">
 | 
			
		||||
        <h5 refs="page-comments@comments-title">{{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}</h5>
 | 
			
		||||
    <div refs="page-comments@comment-count-bar" class="flex-container-row items-center">
 | 
			
		||||
        <div role="tablist" class="flex">
 | 
			
		||||
            <button type="button"
 | 
			
		||||
                    role="tab"
 | 
			
		||||
                    id="comment-tab-active"
 | 
			
		||||
                    aria-controls="comment-tab-panel-active"
 | 
			
		||||
                    refs="page-comments@active-tab"
 | 
			
		||||
                    aria-selected="true">{{ trans_choice('entities.comment_thread_count', $commentTree->activeThreadCount()) }}</button>
 | 
			
		||||
            <button type="button"
 | 
			
		||||
                    role="tab"
 | 
			
		||||
                    id="comment-tab-archived"
 | 
			
		||||
                    aria-controls="comment-tab-panel-archived"
 | 
			
		||||
                    refs="page-comments@archived-tab"
 | 
			
		||||
                    aria-selected="false">{{ trans_choice('entities.comment_archived_count', count($commentTree->getArchived())) }}</button>
 | 
			
		||||
        </div>
 | 
			
		||||
        @if ($commentTree->empty() && userCan('comment-create-all'))
 | 
			
		||||
            <div class="text-m-right" refs="page-comments@add-button-container">
 | 
			
		||||
            <div class="ml-m" refs="page-comments@add-button-container">
 | 
			
		||||
                <button type="button"
 | 
			
		||||
                        refs="page-comments@add-comment-button"
 | 
			
		||||
                        class="button outline">{{ trans('entities.comment_add') }}</button>
 | 
			
		||||
                        class="button outline mb-m">{{ trans('entities.comment_add') }}</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div refs="page-comments@comment-container" class="comment-container">
 | 
			
		||||
        @foreach($commentTree->getActive() as $branch)
 | 
			
		||||
            @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false])
 | 
			
		||||
        @endforeach
 | 
			
		||||
    <div id="comment-tab-panel-active"
 | 
			
		||||
         tabindex="0"
 | 
			
		||||
         role="tabpanel"
 | 
			
		||||
         aria-labelledby="comment-tab-active"
 | 
			
		||||
         class="comment-container">
 | 
			
		||||
        <div refs="page-comments@comment-container">
 | 
			
		||||
            @foreach($commentTree->getActive() as $branch)
 | 
			
		||||
                @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false])
 | 
			
		||||
            @endforeach
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <p class="text-center text-muted italic empty-state">{{ trans('entities.comment_none') }}</p>
 | 
			
		||||
 | 
			
		||||
        @if(userCan('comment-create-all'))
 | 
			
		||||
            @include('comments.create')
 | 
			
		||||
            @if (!$commentTree->empty())
 | 
			
		||||
                <div refs="page-comments@addButtonContainer" class="flex-container-row">
 | 
			
		||||
                    <button type="button"
 | 
			
		||||
                            refs="page-comments@add-comment-button"
 | 
			
		||||
                            class="button outline ml-auto">{{ trans('entities.comment_add') }}</button>
 | 
			
		||||
                </div>
 | 
			
		||||
            @endif
 | 
			
		||||
        @endif
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    @if(userCan('comment-create-all'))
 | 
			
		||||
        @include('comments.create')
 | 
			
		||||
        @if (!$commentTree->empty())
 | 
			
		||||
            <div refs="page-comments@addButtonContainer" class="flex-container-row">
 | 
			
		||||
 | 
			
		||||
                <button type="button"
 | 
			
		||||
                        refs="page-comments@show-archived-button"
 | 
			
		||||
                        class="text-button hover-underline">{{ trans_choice('entities.comment_archived', count($commentTree->getArchived())) }}</button>
 | 
			
		||||
 | 
			
		||||
                <button type="button"
 | 
			
		||||
                        refs="page-comments@add-comment-button"
 | 
			
		||||
                        class="button outline ml-auto">{{ trans('entities.comment_add') }}</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
    @endif
 | 
			
		||||
 | 
			
		||||
    <div refs="page-comments@archive-container" class="comment-container">
 | 
			
		||||
    <div refs="page-comments@archive-container"
 | 
			
		||||
         id="comment-tab-panel-archived"
 | 
			
		||||
         tabindex="0"
 | 
			
		||||
         role="tabpanel"
 | 
			
		||||
         aria-labelledby="comment-tab-archived"
 | 
			
		||||
         hidden="hidden"
 | 
			
		||||
         class="comment-container">
 | 
			
		||||
        @foreach($commentTree->getArchived() as $branch)
 | 
			
		||||
            @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false])
 | 
			
		||||
        @endforeach
 | 
			
		||||
            <p class="text-center text-muted italic empty-state">{{ trans('entities.comment_none') }}</p>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    @if(userCan('comment-create-all') || $commentTree->canUpdateAny())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,12 +28,6 @@
 | 
			
		|||
    @include('entities.sibling-navigation', ['next' => $next, 'previous' => $previous])
 | 
			
		||||
 | 
			
		||||
    @if ($commentTree->enabled())
 | 
			
		||||
        @if(($previous || $next))
 | 
			
		||||
            <div class="px-xl print-hidden">
 | 
			
		||||
                <hr class="darker">
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
 | 
			
		||||
        <div class="comments-container mb-l print-hidden">
 | 
			
		||||
            @include('comments.comments', ['commentTree' => $commentTree, 'page' => $page])
 | 
			
		||||
            <div class="clearfix"></div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue