UNPKG

@ckeditor/ckeditor5-comments

Version:

Collaborative comments feature for CKEditor 5.

245 lines (244 loc) • 9.67 kB
/** * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ /** * @module comments/annotations/annotationsuis * @publicApi */ import { ContextPlugin, type Plugin, type Editor, type Context } from 'ckeditor5/src/core.js'; import { type Emitter } from 'ckeditor5/src/utils.js'; import { AnnotationCollection } from './annotationcollection.js'; import { Annotations } from './annotations.js'; import { type Annotation } from './annotation.js'; /** * A repository of annotations UIs. * * The main entry point for {@link module:comments/annotations/annotationsuis~AnnotationsUIs#register registering} UIs * and activating the annotations UI(s), which display annotation views. * * To register a custom annotations UI use following code in the annotations UI plugin `init()` function: * * ```ts * const annotationsUIs = editor.plugins.get( 'annotationsUIs' ); * * annotationsUIs.register( customAnnotationsUIPlugin ); * ``` * * Note that the custom annotations UI must implement {@link module:comments/annotations/annotationsuis~AnnotationsUI * the `AnnotationsUI` interface}. * * To activate an annotations UI, use the {@link module:comments/annotations/annotationsuis~AnnotationsUIs#switchTo * `switchTo( uiName )`} method. This method activates the given UI and deactivates all the other UIs. * All annotations will be handled by the activated UI. * * It is also possible to activate multiple annotations UIs at the same time and make the UIs handle different sets of annotations. * To do that, use the {@link module:comments/annotations/annotationsuis~AnnotationsUIs#activate `activate( uiName, filter )`} method. * * ```ts * // Suggestions annotations are shown inline in a balloon: * annotationsUIs.activate( 'inline', annotation => annotation.type.startsWith( 'suggestion' ) ); * * // At the same time, comments annotations are shown in a sidebar: * annotationsUIs.activate( 'wideSidebar', annotation => annotation.type === 'comment' ); * ``` * * Limitations: * * * Some annotations UI plugins might collide with each other (like {@link module:comments/annotations/narrowsidebar~NarrowSidebar} and * {@link module:comments/annotations/widesidebar~WideSidebar} that operates on the same sidebar). They cannot be activated at the same * time. * * It is not possible to display the same annotation in two different annotations UIs. In this scenario an error will be thrown. */ export declare class AnnotationsUIs extends ContextPlugin { /** * A set of names of the active annotations UIs. * * To activate the annotations UI, use {@link module:comments/annotations/annotationsuis~AnnotationsUIs#activate} * or {@link #switchTo} methods. */ activeUIs: Set<string>; /** * @inheritDoc */ static get requires(): readonly [typeof Annotations]; /** * @inheritDoc */ static get pluginName(): "AnnotationsUIs"; /** * @inheritDoc */ static get isOfficialPlugin(): true; /** * @inheritDoc */ static get isPremiumPlugin(): true; /** * @inheritDoc */ constructor(context: Context | Editor); /** * @inheritDoc */ init(): void; /** * Returns `true` if at least one registered UI is active. */ hasActive(): boolean; /** * Returns `true` if the given UI is active. */ isActive(uiName: string): boolean; /** * Activates an annotations UI. * * Note that the custom annotations UI should be {@link #register registered} before the activation. * * The `filter` parameter can be used to display on some of the annotations in the given annotations UI. Thanks to that, * all annotations can be split into various annotations UIs. * * The `filter` function takes an {@link module:comments/annotations/annotation~Annotation} instance as the first and only parameter, * and should return `true` if that annotation should be placed in the given annotations UI. * * @param uiName The name of the annotations UI to activate. * @param filter The annotation filter function. If not specified, the UI will use all annotations. */ activate(uiName: string, filter?: (annotation: Annotation) => boolean): void; /** * Deactivates annotations UI with given name. * * @param uiName The name of the annotations UI to deactivate. */ deactivate(uiName: string): void; /** * Switches the annotations UI to the one with given name. * * It preserves the currently active annotation. * * @param uiName The name of the annotations UI to switch to. */ switchTo(uiName: string): void; /** * Deactivates all annotations UIs. */ deactivateAll(): void; /** * Registers an annotations UI. It might be one of: * * * {@link module:comments/annotations/widesidebar~WideSidebar}, * * {@link module:comments/annotations/narrowsidebar~NarrowSidebar}, * * {@link module:comments/annotations/inlineannotations~InlineAnnotations}. * * It is possible to provide your own, custom annotations UI plugin. It has to implement * {@link module:comments/annotations/annotationsuis~AnnotationsUI the `AnnotationsUI` interface}. * * @param uiName Annotations UI name. * @param annotationsUI Annotations UI plugin instance. */ register(uiName: string, annotationsUI: AnnotationsUI<ContextPlugin | Plugin>): void; /** * Refilters annotations to proper UIs based on filters provided earlier during the * {@link module:comments/annotations/annotationsuis~AnnotationsUIs#activate annotations UIs activation}. * * This method should be used if the annotations UIs filtering functions return different results than before * for some annotations. It only reattaches these annotations, which should change their UIs. */ refilterAnnotations(): void; /** * @inheritDoc */ destroy(): void; /** * A default pass-by filter function that returns `true` for all annotations to be used when no filter for UI is provided. */ defaultFilter(): boolean; } /** * An interface for the annotations UI plugin class. * * The annotations UI class handles displaying, focusing, activating and hiding annotations views. * * The annotations UI class must be a plugin, so it has to extend the {@link module:core/plugin~Plugin} or * {@link module:core/contextplugin~ContextPlugin} class. * * Examples of `AnnotationsUI` are: * * * {@link module:comments/annotations/widesidebar~WideSidebar}, * * {@link module:comments/annotations/narrowsidebar~NarrowSidebar}, * * {@link module:comments/annotations/inlineannotations~InlineAnnotations}. * * You can use the following snippet as a base for your own annotations UI: * * ```ts * class MyAnnotationsUI extends ContextPlugin { * constructor( ...args ) { * super( ...args ); * * this.set( 'activeAnnotation', null ); * } * * attach( annotations ) { * // Do something when an annotation is added. * this.listenTo( annotations, 'add', ( evt, annotation ) => { ... } ); * * // Do something when an annotation is removed. * this.listenTo( annotations, 'remove', ( evt, annotation ) => { ... } ); * } * * detach() { * this.stopListening(); * } * * setActiveAnnotation( annotation ) { * if ( this.activeAnnotation ) { * this.activeAnnotation.isActive = false; * * // You can do something in your UI with the annotation that is no longer active. * } * * this.activeAnnotation = annotation; * this.activeAnnotation.isActive = true; * * // You can do something in your UI to highlight the active annotation. * } * } * ``` */ export interface AnnotationUI { /** * Observable `activeAnnotation` property. {@link module:comments/annotations/annotationsuis~AnnotationsUIs} listens to changes on that * property. * * To make this property observable use `this.set( 'activeAnnotation', null )` in the constructor. */ activeAnnotation: Annotation | null; /** * Creates everything needed for the UI and attaches all listeners. This method is called when the UI is activated. * * The observable collection of annotations is passed as the first argument, * and the annotations UI is responsible for reacting to its changes. */ attach: (annotationCollection: AnnotationCollection) => void; /** * Destroys the UI and removes all listeners. This method is called when the UI is deactivated. */ detach: () => void; /** * Sets or unsets the active annotation. This method is called when an annotation is activated, for example, user puts their * selection into a marker connected with given annotation. * * This method should change the UI so the new active annotation is differentiated from other annotations. * * This method should set the * {@link #activeAnnotation `AnnotationUI#activeAnnotation`} property. * * It also should set {@link module:comments/annotations/annotation~Annotation#isActive `Annotation#isActive`} of the deactivated * and the activated annotation. * * @param annotation The new active annotation or null when no annotation is active. */ setActiveAnnotation: (annotation: Annotation | null) => void; _setSelectedAnnotations?: (annotations: Array<Annotation>) => void; } export type AnnotationsUI<T extends Emitter> = T & AnnotationUI;