Report an issue

guideComments API

# Comments

The Comments class API was deprecated and will be removed in the next release. Please use the CommentsRepository API.

# CommentsRepository

It stores and manages all the comment thread data as well as publishes the most of the comments feature API. It is also responsible for using the comments adapter to communicate with the data source.

CommentsRepository is a context plugin. It can be added to a context or to an editor. Add it to the context configuration if you use context in your integration.

Below is the documentation of the public API that can be used to integrate the comments feature with your application.

/**
 * The currently active comment thread.
 * An annotation with this thread will be marked as active.
 *
 * @readonly
 * @observable
 */
activeCommentThread

/**
 * An adapter object that should communicate with the data source
 * to fetch or save the comments data.
 */
adapter

/**
 * Adds a new comment thread.
 *
 * When a target is provided, the comment balloon will be attached to this target.
 *
 * Use this method to load the comments data during the editor initialization
 * if you do not use the adapter integration.
 *
 * **Note:** This method fires the `addCommentThread` event and the default behavior
 * is added as a normal priority listener. It makes it possible to cancel the method
 * or call some custom code before or after the default behavior is executed.
 *
 * **Note:** The comments adapter will send the data only if `commentThreadData.comments`
 * is not empty and `commentThreadData.isFromAdapter` is set to `false`.
 *
 * See also `CommentsRepository#openNewCommentThread()`.
 *
 * An example of loading a comment thread on editor initialization:
 *
 *      commentsRepository.addCommentThread( {
 *          threadId: 'thread-id',
 *          channelId: 'channel-id',
 *          comments: [
 *              {
 *                  commentId: 'comment-1',      // String
 *                  authorId: 'author-id',       // String
 *                  content: 'First comment',    // String
 *                  createdAt: new Date( ... )   // Date instance
 *              },
 *              // ...
 *          ],
 *          target: () => ...,
 *          // Added through a custom UI, do not call the adapter:
 *          isFromAdapter: true
 *      } );
 *
 * @fires addCommentThread
 * @param {Object} commentThreadData The data of the comment thread to add.
 * @param {String} [data.channelId] The ID of a document or context to which the comment thread is added.
 * @param {String} data.threadId The ID of the added comment thread.
 * @param {Array.<Object>} [data.comments=[]] Comments in the comment thread. See the example above.
 * @param {HTMLElement|utils/rect~Rect|Function} [data.target] The target that the comment
 * balloon should be attached to. If a function is passed, it should return a DOM element or `Rect`.
 * @param {Boolean} [data.isFromAdapter=false] A flag describing whether the added data
 * comes from an adapter (`true`) or is new data (`false`). If set to `true`, the
 * comment data will be added only in the editor and will not be sent to the adapter.
 * @returns {CommentThread} An added comment thread.
 */
addCommentThread( commentThreadData ) {}

/**
 * Gets the comment thread data using the adapter and adds the thread to the editor.
 *
 * @param {Object} data
 * @param {String} data.channelId Channel ID.
 * @param {String} data.threadId Comment thread ID.
 * @returns {Promise.<CommentThread>}
 */
fetchCommentThread( data ) {}

/**
 * Returns a comment thread with the given ID.
 *
 * @param {String} threadId
 * @returns {CommentThread|undefined}
 */
getCommentThread( threadId ) {}

/**
 * Returns a list of comment threads added to the repository.
 *
 * You can provide a set of filtering options to narrow down the results set.
 *
 * @param {Object} [options]
 * @param {String} [options.channelId] Return only comment threads added to a given channel.
 * @param {Boolean} [options.skipNotAttached=false] Skip removed comment threads.
 * @param {Boolean} [options.skipEmpty=false] Skip empty comment threads.
 * @param {Boolean} [options.toJSON=false] Return the data in JSON format.
 * @returns {Array.<CommentThread|Object>}
 */
getCommentThreads( options ) {}

/**
 * Checks if a comment thread with a given ID is added to the repository.
 *
 * @param {String} threadId
 * @returns {Boolean}
 */
hasCommentThread( threadId ) {}

/**
 * Creates a new, empty comment thread.
 *
 * Displays a new comment balloon attached to the target and focuses the comment editor.
 * When the comment data is submitted, the comment thread is added to the editor
 * and sent to the adapter.
 *
 * Use this method to start a new comment thread after a user made an action
 * (clicked a button, etc.).
 *
 * @fires addCommentThread
 * @param {Object} commentThreadData
 * @param {String} [commentThreadData.channelId] The ID of a document or context to
 * which the comment is added.
 * @param {String} [commentThreadData.threadId] The ID of the comment thread.
 * Random id will be generated if it is not set. All thread IDs should be unique.
 * @param {HTMLElement|utils/rect~Rect|Function} commentThreadData.target The target that the comment
 * balloon should be attached to. If a function is passed, it should return a DOM element or `Rect`.
 * @returns {CommentThread|null} The created comment thread or `null` if there was a problem
 * creating the thread (for example, if the comments repository was in read-only mode).
 */
openNewCommentThread( commentThreadData ) {}

/**
 * Marks a comment thread with the given ID as active.
 * When `threadId` is `null`, the currently active comment thread will be deactivated.
 *
 * @param threadId {String|null}
 */
setActiveCommentThread( threadId ) {}

/**
 * Changes the read-only state for comment threads.
 *
 * If `channelId` is set, the new state will be applied only to comment threads created in this channel.
 *
 * @param {Boolean} value The new read-only state.
 * @param {String} [channelId] Channel ID.
 */
switchReadOnly( value, channelId ) {}

To use this API, you need to get the comments repository plugin:

// Get the comments repository:
const commentsRepository = editor.plugins.get( 'CommentsRepository' );

// Create a new, empty comment thread on a DOM form field element:
commentsRepository.openNewCommentThread( { channelId, target: formFieldElement } );

// Get all comment threads:
commentsRepository.getCommentThreads();

// Set the adapter:
commentsRepository.adapter = {
    ...
};

# CommentsRepository events

CommentsRepository fires multiple events. It is possible to extend the comments repository behavior using these events or even cancel them. This allows for a further customization of comments feature.

/**
 * Fired whenever a comment thread is added to the comments repository.
 *
 * The event name includes `channelId` so it is possible to listen only
 * on changes happening in the specified channel.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of a document or context that the comment thread is added to.
 * @param {String} data.threadId The ID of the added comment thread.
 * @param {Array.<Object>} data.comments Comments in the comment thread.
 * @param {HTMLElement|utils/rect~Rect|Function} [data.target] The target that the comment
 * balloon should be attached to.
 * @param {Boolean} data.isFromAdapter A flag describing whether the added comment thread
 * is remote (`true`) or local (`false`).
 */
addCommentThread:channelId

/**
 * Fired whenever a comment thread is removed from the comments repository.
 *
 * The event name includes `channelId` so it is possible to listen only
 * on changes happening in the specified channel.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of a document or context that the comment thread is added to.
 * @param {String} data.threadId The ID of the comment thread to remove.
 * @param {Boolean} data.isFromAdapter A flag describing whether the remove action
 * is remote (`true`) or local (`false`).
 */
removeCommentThread:channelId

/**
 * Fired whenever a comment is added.
 *
 * The event name includes `channelId` so it is possible to listen only
 * on changes happening in the specified channel.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of a document or context that the comment thread is added to.
 * @param {String} data.threadId The ID of the comment thread that the comment is added to.
 * @param {String} data.commentId The comment ID.
 * @param {String} data.authorId The comment author ID.
 * @param {String} data.content The comment content.
 * @param {Date} data.createdAt The comment creation date.
 * @param {Boolean} data.isFromAdapter A flag describing whether the added comment
 * is remote (`true`) or local (`false`).
 */
addComment:channelId:threadId

/**
 * Fired whenever a comment is added.
 *
 * The event name includes `channelId` so it is possible to listen only
 * on changes happening in the specified channel.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of a document or context that the comment thread is added to.
 * @param {String} data.threadId The ID of the comment thread that the comment is added to.
 * @param {String} data.commentId The comment ID.
 * @param {String} data.content The comment content.
 * @param {Date} [data.createdAt] The comment creation date.
 * @param {Boolean} data.isFromAdapter A flag describing whether the update action
 * is remote (`true`) or local (`false`).
 */
updateComment:channelId:threadId

/**
 * Fired whenever a comment is removed.
 *
 * The event name includes `channelId` so it is possible to listen only
 * on changes happening in the specified channel.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of a document or context that the comment thread is added to.
 * @param {String} data.threadId The ID of the comment thread that the comment is added to.
 * @param {String} data.commentId The comment ID.
 * @param {Boolean} data.isFromAdapter A flag describing whether the remove action
 * is remote (`true`) or local (`false`).
 */
removeComment:channelId:threadId

# CommentThread

Represents a comment thread. It stores the comment thread data and the data of all comments in that thread.

/**
 * The channel in which the comment thread was created.
 *
 * @readonly
 * @type {String|undefined}
 */
channelId

/**
 * Comments added to the comment thread. A collection of `Comment` instances.
 *
 * @readonly
 * @type {module:utils/collection~Collection}
 */
comments

/**
 * The comment thread ID.
 *
 * @readonly
 * @type {String}
 */
id

/**
 * Whether the comment thread is in read-only state (`true`) or not (`false`).
 *
 * @readonly
 * @observable
 * @member {Boolean} #isReadOnly
 */
isReadOnly

/**
 * Whether the comment thread is attached to any target at the moment.
 *
 * @readonly
 * @type {Boolean}
 */
isAttached

/**
 * The number of comments in a comment thread.
 *
 * @readonly
 * @type {Number}
 */
length

/**
 * Adds a new comment to a comment thread.
 *
 * **Note:** This method fires the `CommentsRepository#event:addComment` event
 * and the default behavior is added as a normal priority listener. It makes it
 * possible to cancel the method or call some custom code before or after the default
 * behavior is executed.
 *
 * @fires CommentsRepository#event:addComment
 * @param {Object} commentData The comment data.
 * @param {String} commentData.commentId The comment ID.
 * @param {String} commentData.authorId The comment author ID.
 * @param {String} commentData.content The comment content.
 * @param {Date} [commentData.createdAt] Creation date. If not set, current
 * date (`new Date()`) will be used.
 * @param {Boolean} [commentData.isFromAdapter=false] A flag describing whether
 * the added comment comes from an adapter (`true`) or from the UI (`false`).
 * If set to `true`, the adapter will not be called.
 */
addComment( commentData ) {}

/**
 * Removes a comment thread.
 *
 * **Note:** This method fires the `CommentsRepository#event:removeCommentThread` event
 * and the default behavior is added as a normal priority listener. It makes it
 * possible to cancel the method or call some custom code before or after the default
 * behavior is executed.
 *
 * @fires CommentsRepository#event:removeCommentThread
 * @param {Object} data
 * @param {Boolean} [data.isFromAdapter=false] A flag describing whether the remove action
 * comes from an adapter (`true`) or from the UI (`false`). If set to `true`, the adapter
 * will not be called.
 */
remove( data ) {}

/**
 * Creates comment annotations and displays the comment thread attached to the given target.
 *
 * Use only if a comment thread has not been attached yet.
 *
 * @param {HTMLElement|utils/rect~Rect|Function} target A DOM element, `Rect` or
 * a function that returns either of them.
 */
attachTo( target ) {}

# Comment

Represents a single comment.

/**
 * Comment author.
 *
 * @readonly
 * @type {User}
 */
author

/**
 * Comment creation date.
 *
 * @readonly
 * @type {Date}
 */
createdAt

/**
 * Comment content.
 *
 * @readonly
 * @type {String}
 */
content

/**
 * Comment ID.
 *
 * @readonly
 * @type {String}
 */
id

/**
 * Read-only state inherited from the parent comment thread.
 *
 * @readonly
 * @observable
 * @type {Boolean}
 */
readOnly

/**
 * The ID of the comment thread that contains this comment.
 *
 * @readonly
 * @type {String}
 */
threadId

/**
 * Removes a comment.
 *
 * **Note:** This method fires the `CommentsRepository#event:removeComment` event
 * and the default behavior is added as a normal priority listener. It makes it
 * possible to cancel the method or call some custom code before or after the default
 * behavior is executed.
 *
 * @fires CommentsRepository#event:removeComment
 * @param {Object} data
 * @param {Boolean} [data.isFromAdapter=false] A flag describing whether the remove action
 * comes from an adapter (`true`) or from the UI (`false`). If set to `true`, the adapter
 * will not be called.
 */
remove( data ) {}

/**
 * Updates a comment.
 *
 * **Note:** This method fires the `CommentsRepository#event:updateComment` event
 * and the default behavior is added as a normal priority listener. It makes it
 * possible to cancel the method or call some custom code before or after the default
 * behavior is executed.
 *
 * @fires CommentsRepository#event:updateComment
 * @param {Object} data The data object.
 * @param {String} [data.content] The comment content.
 * @param {Date} [data.createdAt] The comment creation date.
 * @param {Boolean} [data.isFromAdapter=false] A flag describing whether the update action
 * comes from an adapter (`true`) or from the UI (`false`). If set to `true`, the adapter
 * will not be called.
 */
update( data ) {}

# Comments adapter

Comments adapter is an object that communicates asynchronously with the data source to fetch or save the comments data. It is used internally by the comments feature whenever a comment is loaded, created or deleted.

The adapter is optional. You might need to provide it if you are using the comments feature without real-time collaboration.

To set the adapter, overwrite the CommentsRepository#adapter property.

/**
 * Called when the editor needs the data for a comment thread.
 *
 * It should return a promise that resolves with the comment thread data.
 * The resolved data object should also have the `isFromAdapter` property set to `true`.
 *
 * @param {String} data.channelId The ID of the document or context to which
 * the comment is added.
 * @param {String} data.threadId The ID of the comment thread that
 * the comment is added to.
 * @returns {Promise}
 */
adapter.getCommentThread( data ) {}

/**
 * Called each time the user removes a comment thread.
 *
 * Keep in mind that comment thread removal happens only
 * for comment threads created outside of the editor.
 * You do not need to implement this method if you use
 * the comments feature inside the editor only.
 *
 * It should return a promise that resolves when the thread is removed.
 *
 * @param {Object} data
 * @param {String} channelId The ID of the document or context to which
 * the comment is added.
 * @param {String} threadId The ID of the thread to remove.
 * @returns {Promise}
 */
adapter.removeCommentThread( data ) {}

/**
 * Called each time the user adds a new comment to a thread.
 *
 * It saves the comment data in the database and returns a promise
 * that should get resolved when the save is completed.
 *
 * If the promise resolves with an object with the `createdAt` property, the
 * comment property will be updated in the comment in the editor.
 * This is to update the comment data with the server-side information.
 *
 * The `data` object does not expect the `authorId` property.
 * For security reasons, the author of the comment should be set
 * on the server side.
 *
 * The `data` object does not expect the `createdAt` property either.
 * You should use the server-side time generator to ensure that all users
 * see the same date.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of the document or context to which
 * the comment is added.
 * @param {String} data.threadId The ID of the comment thread that
 * the comment is added to.
 * @param {String} data.commentId The comment ID.
 * @param {String} data.content The comment content.
 * @returns {Promise}
 */
adapter.addComment( data ) {}

/**
 * Called each time the user changes the existing comment.
 *
 * It updates the comment data in the database and returns a promise
 * that will be resolved when the update is completed.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of the document or context to which
 * the comment is added.
 * @param {String} data.threadId The ID of the comment thread to which
 * the comment is added.
 * @param {String} data.commentId The ID of the comment to update.
 * @param {String} data.content The new content of the comment.
 * @returns {Promise}
 */
adapter.updateComment( data ) {}

/**
 * Called each time the user removes a comment from the thread.
 *
 * It removes the comment from the database and returns a promise
 * that will be resolved when the removal is completed.
 *
 * @param {Object} data
 * @param {String} data.channelId The ID of the document or context to which
 * the comment is added.
 * @param {String} data.threadId The ID of the comment thread that
 * the comment is added to.
 * @param {String} data.commentId The ID of the comment to remove.
 * @returns {Promise}
 */
adapter.removeComment( data ) {}

# Comments commands

The comments feature adds the addCommentThread command to the editor. It is executed when you click the toolbar button to start a new comment thread. The command is disabled if the selection is collapsed.

editor.execute( 'addCommentThread', { threadId: 'thread-1' } );

// If `threadId` is not specified, `addCommentThread()` will generate a unique ID and use it:
editor.execute( 'addCommentThread' );

You might want to use the addCommentThread command if you provide your own custom UI.

# Annotations

Stores and manages annotation views, i.e. balloons which are shown in the sidebar or inline (or a custom display mode). Comments and track changes features add annotation views to the annotation plugin, while UI plugins observe the annotations plugin and create the UI basing on the added views.

Using the annotations plugin you can add your own views connected with your custom features or create your own sidebar or another display mode.

Annotations is a context plugin. However, it is required by CommentsRepository, so you do not need to add it to the configuration on your own.

/**
 * Stores all created annotation views. A collection of `AnnotationView`.
 *
 * You can listen to the events fired by this collection to refresh
 * your custom UI (custom sidebar).
 *
 * @readonly
 * @type {module:utils/collection~Collection}
 */
items

/**
 * The currently selected views.
 *
 * Multiple annotation views can be selected at once.
 * For example, the selection can be inside multiple comment markers:
 *
 * <p>
 * 		F<comment-1>oo <comment-2>b{}ar</comment-2> bi</comment-1>z
 * </p>
 *
 * In this situation both `comment-1` and `comment-2` are selected. Different UIs may use this
 * information to show more accurate information. For example, in this case, annotations for
 * both comments should be added to the inline balloon.
 *
 * @observable
 * @member {Array.<module:ui/view~View>} #selectedViews
 */
selectedViews

/**
 * The currently active view.
 * Only one view can be active at a given moment.
 *
 * @observable
 * @member {module:ui/view~View} #activeView
 */
activeView

/**
 * Tracks focus for the annotations plugin.
 *
 * Add a DOM element to this focus tracker to prevent blurring annotations
 * when that DOM element is focused.
 *
 * @type {module:utils/focustracker~FocusTracker}
 */
focusTracker

/**
 * The type of the UI used to display annotations content. Possible values are
 * 'inline' (by default), 'wideSidebar', 'narrowSidebar' or a custom name
 * if a custom UI is used.
 *
 * @readonly
 * @type {String}
 */
uiType

/**
 * Adds a given view to annotations repository.
 * Creates a new annotation view that will wrap the given view.
 *
 * @param {module:ui/view~View} customView The view to add.
 * @param {HTMLElement|module:utils/dom/rect~Rect|Function} target The target that
 * the annotation should be attached to. If a function is passed, it should return
 * a DOM element or `Rect`.
 * @returns {AnnotationView} Annotation view wrapping the given view.
 */
add( customView, target ) {}

/**
 * Removes a given view from annotations together with its wrapping annotation view.
 *
 * @param {module:ui/view~View} customView The view of the item to remove.
 */
remove( customView ) {}

/**
 * Returns an annotation view that wraps the custom view.
 *
 * @param {module:ui/view~View} customView
 * @returns {AnnotationView}
 */
getAnnotationView( customView ) {}

/**
 * Refreshes the order of annotations and fires the `refresh` event.
 *
 * Annotations order is calculated according to the top and left offset
 * of the annotation target.
 */
refresh() {}

/**
 * Registers a custom annotations UI.
 *
 * The UI should be an object that implements the `attach()` and `detach()` methods.
 * These methods are called when the UI is switched to (initialization) and
 * switched from (destruction).
 *
 * @param {String} name The UI name. Used when switching UIs.
 * @param {module:core/plugin~Plugin} plugin The UI instance.
 */
register( name, plugin ) {}

/**
 * Switches the currently used UI.
 *
 * @param {String} name The name of the UI to switch to.
 */
switchTo( name ) {}

# AnnotationView

AnnotationView is a wrapping view for any view added to the Annotations plugin.

/**
 * Informs if the annotation is active.
 *
 * @observable
 * @type {Boolean}
 */
active

/**
 * The view that is wrapped with the annotation view.
 *
 * @readonly
 * @type {module:ui/view~View}
 */
customView

/**
 * Tracks focus on the annotation view.
 *
 * Add a DOM element to this focus tracker to prevent blurring annotation view
 * when the DOM element is focused.
 *
 * @type {module:utils/focustracker~FocusTracker}
 */
focusTracker

/**
 * Informs if there are unsaved changes in the annotation.
 *
 * @observable
 * @type {Boolean}
 */
hasChanges

/**
 * Annotation view height.
 *
 * @observable
 * @type {Number}
 */
height

/**
 * The number of items in the annotation.
 *
 * @observable
 * @member {Number} #length
 */
length

/**
 * The target rectangle that the annotation should be attached to.
 *
 * This is an object with the `top`, `bottom`, `left` and `right` properties.
 * It is used by UIs to properly position the annotation balloon.
 *
 * @observable
 * @type {Object}
 */
targetRect

/**
 * Focuses the annotation view.
 */
focus() {}

To use this API you need to get the annotations plugin:

// Get the annotations repository:
const annotations = editor.plugins.get( 'Annotations' );

// Add a callback fired whenever an active view changes:
annotations.on( 'change:activeView', ( evt, newView, oldView ) => {
    // ...
} );

// Add a view to the annotations:
const annotationView = annotations.add( myView );