Report an issue

guideComments archive custom UI

By default, the comments archive dropdown panel is displayed in the toolbar by adding the 'commentsArchive' button in the toolbar configuration. Refer to the comments guide for more information.

In this guide, you will learn how to prepare a custom comments archive UI in your application and display it in the container of your choice.

You should display only one instance of the comments archive UI to avoid using the same DOM elements in multiple places. This means, among other things, that you should not add the 'commentsArchive' button to the toolbar configuration if you provide your own custom UI for the comments archive.

# Before you start

For the purpose of this guide, the CKEditor Cloud Services and the real-time collaborative comments feature will be used. However, the comments feature API can also be used in a similar way together with the standalone comments feature.

Make sure that your editor is properly integrated with the comments feature before moving on.

# Preparing the HTML structure

In this guide, we will prepare an editor integration with a custom side panel.

There will be two tabs that will let the user switch between what is displayed in the side panel. By default, the side panel will display the editor’s regular wide sidebar. The other tab will switch the side panel content to display the resolved comments.

<div class="container">
    <div class="editor"></div>

    <div class="tabs-wrapper">
        <div class="tabs">
            <button class="tabs__item active" data-target="sidebar">Sidebar</button>
            <button class="tabs__item" data-target="archive">Comments archive</button>
        </div>

        <div class="sidebar active" id="sidebar"></div>

        <div class="sidebar sidebar--archive" id="archive">
            <div class="comments-archive">
                <div class="comments-archive__list"></div>
            </div>
        </div>
    </div>
</div>

Add some styles for the side panel:

<style>
    .container {
        display: flex;
    }

    .ck.ck-toolbar_grouping {
        height: 40px;
    }

    .sidebar {
        display: none;
        font-size: 20px;
        padding: 0 10px 10px;
        border: 1px solid #ccced1;
        border-left: none;
        overflow: hidden;
        box-sizing: border-box;
        height: 0;
    }

    .sidebar.active {
        display: block;
        min-height: calc(100% - 40px);
    }

    .sidebar--archive.active {
        overflow: auto;
    }

    .comments-archive__list {
        padding: 0;
        margin: 0;
    }

    .comments-archive__list:empty:before {
        display: block;
        content: "There are no archived comment threads.";
        margin-top: 20px;
        font-size: 13px;
        text-align: center;
        font-style: italic;
        color: #555;
    }

    .comments-archive__list  > .ck-annotation-wrapper {
        margin-top: 10px;
    }

    .tabs-wrapper {
        display: flex;
        flex-direction: column;
        flex: 1;
        min-width: 300px;
    }

    .tabs {
        width: 100%;
        min-height: 40px;
        display: flex;
        flex-direction: row;
        align-items: center;
    }

    .tabs__item {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
        width: 100%;
        height: 100%;
        border: 1px solid #ccced1;
        border-bottom: none;
        opacity: 0.5;
        box-sizing: border-box;
    }

    .tabs__item:first-child {
        border-left: none;
    }

    .tabs__item.active {
        background: #fff;
        opacity: 1.0;
    }
</style>

# Implementing the custom comments archive UI plugin

Now, create a plugin that will use the provided HTML structure and fill the comments archive container with resolved comment threads.

The behavior for the tabs will be implemented as simple DOM event listeners.

You can observe changes on the CommentsArchiveUI#annotationViews collection to fill the comments archive tab content.

Additionally, for a better user experience, as long as the comments archive is shown in the side panel, the annotations for regular comment threads will be displayed in the inline display mode. This will give the users access to the regular comment threads data also when the archive is open.

class CustomCommentsArchiveUI extends Plugin {
    static get requires() {
        // We will use a property from the `CommentsArchiveUI` plugin, so add it to requires.
        return [ 'CommentsArchiveUI' ];
    }

    init() {
        this.tabs = document.querySelectorAll( '.tabs__item' );
        this.sidebars = document.querySelectorAll( '.sidebar' );

        // Switch the side panel to the appropriate tab after clicking it.
        this.tabs.forEach( item => {
            item.addEventListener( 'click', () => this.handleTabClick( item ) );
        } );

        this.initCommentsArchive();
    }

    // Switches between the active tabs.
    // Shows appropriate tab container and set the CSS classes to reflect the changes.
    handleTabClick( tabElement ) {
        if ( tabElement.classList.contains( 'active' ) ) {
            return;
        }

        const annotationsUIs = this.editor.plugins.get( 'AnnotationsUIs' );
        const targetId = tabElement.dataset.target;
        const sidebarContainer = document.getElementById( targetId );

        this.tabs.forEach( item => {
            item.classList.remove( 'active' );
        } );

        this.sidebars.forEach( item => {
            item.classList.remove( 'active' );
        } );

        tabElement.classList.add( 'active' );
        sidebarContainer.classList.add( 'active' );

        const isCommentsArchiveOpen = targetId === 'archive';

        // If the comments archive is open, switch the display mode for comments to "inline".
        //
        // This way the annotations for regular comments threads will be displayed next to them
        // when a user clicks on the comment thread marker.
        //
        // When the comments archive is closed, switch back to displaying comments annotations in the wide sidebar.
        annotationsUIs.switchTo( isCommentsArchiveOpen ? 'inline' : 'wideSidebar' );
    }

    initCommentsArchive() {
        // Container for the resolved comment threads annotations.
        const commentsArchiveList = document.querySelector( '.comments-archive__list' );

        // The `CommentsArchiveUI` plugin handles all annotation views that can be used
        // to render resolved comment threads inside the comments archive container.
        const commentsArchiveUI = this.editor.plugins.get( 'CommentsArchiveUI' );

        // First, handle the initial resolved comment threads.
        for ( const annotationView of commentsArchiveUI.annotationViews ) {
            commentsArchiveList.appendChild( annotationView.element );
        }

        // Handler to append new resolved thread inside the comments archive custom view.
        commentsArchiveUI.annotationViews.on( 'add', ( _, annotationView ) => {
            if ( !commentsArchiveList.contains( annotationView.element ) ) {
                commentsArchiveList.appendChild( annotationView.element );
            }
        } );

        // Handler to remove the element when thread has been removed or reopened.
        commentsArchiveUI.annotationViews.on( 'remove', ( _, annotationView ) => {
            if ( commentsArchiveList.contains( annotationView.element ) ) {
                commentsArchiveList.removeChild( annotationView.element );
            }
        } );
    }
}

Finally, add the new plugin to the editor.

ClassicEditor.create( document.querySelector( '.editor' ), {
    // ...
    plugins: [
        // ...
        CustomCommentsArchiveUI
    ]
} );

# Demo

Share the complete URL of this page with your colleagues to collaborate in real-time!

Click the “add comment” button in the toolbar to add a comment thread, then use the “tick” icon to resolve a comment thread. Finally, you can see the resolved comment threads in the “Comments archive” tab.