@ckeditor/ckeditor5-track-changes
Version:
CKEditor 5 track changes plugin.
189 lines (188 loc) • 7.77 kB
TypeScript
/**
* @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 track-changes/trackchanges
* @publicApi
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { TrackChangesUI } from './trackchangesui.js';
import { TrackChangesEditing } from './trackchangesediting.js';
import 'ckeditor5-collaboration/src/collaboration-core.js';
import type { Suggestion, SuggestionJSON } from './suggestion.js';
/**
* A plugin that provides track changes mode for the editor. In track changes mode, all insertions are visually marked and all deletions
* are not deleted but also visually marked. Unsupported commands are disabled when the editor is in the track changes mode.
*
* To learn how to integrate the track changes feature with your editor, refer to the
* {@glink features/collaboration/track-changes/track-changes-integration Track changes integration} guide.
*
* Basic API:
*
* ```ts
* // Get the track changes plugin:
* const trackChangesPlugin = editor.plugins.get( 'TrackChanges' );
*
* // Add a suggestion:
* trackChangesPlugin.addSuggestion( suggestionData );
*
* // Get all suggestions:
* trackChangesPlugin.getSuggestions();
*
* // Set the adapter:
* trackChangesPlugin.adapter = {
* // ...
* }
* ```
*
* The plugin registers several commands:
*
* * `trackChanges` – Toggles the track changes mode in the editor.
* * `acceptSuggestion` – Accepts a suggestion with the specified ID.
* * `discardSuggestion` – Discards a suggestion with the specified ID.
* * `acceptAllSuggestions` – Accepts all suggestions.
* * `discardAllSuggestions` – Discards all suggestions.
* * `acceptSelectedSuggestions` – Accepts all suggestions in the current selection.
* * `discardSelectedSuggestions` – Discards all suggestions in the current selection.
*
* Examples:
*
* ```ts
* editor.execute( 'trackChanges' );
* editor.execute( 'acceptSuggestion', 'suggestion-1' );
* editor.execute( 'discardSuggestion', 'suggestion-1' );
* editor.execute( 'acceptAllSuggestions' );
* editor.execute( 'discardAllSuggestions' );
* editor.execute( 'acceptSelectedSuggestions' );
* editor.execute( 'discardSelectedSuggestions' )
* ```
*
* Note that there is no command to add a suggestion. This is because suggestions
* are added automatically when editing commands are executed while the editor is in track
* changes mode. For instance:
*
* ```ts
* // Turn on the track changes mode:
* editor.execute( 'trackChanges' );
*
* // Insert some text. It will be automatically inserted as a suggestion:
* editor.execute( 'input', { text: 'foo' } );
* ```
*/
export declare class TrackChanges extends Plugin {
static get requires(): readonly [typeof TrackChangesEditing, typeof TrackChangesUI, "Comments"];
static get pluginName(): "TrackChanges";
/**
* @inheritDoc
*/
static get isOfficialPlugin(): true;
/**
* @inheritDoc
*/
static get isPremiumPlugin(): true;
/**
* An adapter object that should communicate with the data source to fetch or save the suggestion data.
*
* This is a shorthand to {@link module:track-changes/trackchangesediting~TrackChangesEditing#adapter `TrackChangesEditing#adapter`}.
*/
set adapter(adapter: TrackChangesAdapter | null);
get adapter(): TrackChangesAdapter | null;
/**
* Adds suggestion data.
*
* Use this method to load the suggestion data during the editor initialization if you do not use the adapter integration.
*/
addSuggestion(suggestionData: SuggestionData): Suggestion;
getSuggestions(options: {
skipNotAttached?: boolean;
toJSON: true;
}): Array<SuggestionJSON>;
getSuggestions(options?: {
skipNotAttached?: boolean;
toJSON?: false;
}): Array<Suggestion>;
getSuggestions(options: {
skipNotAttached?: boolean;
toJSON: boolean;
}): Array<Suggestion> | Array<SuggestionJSON>;
/**
* Returns the suggestion instance for a given ID.
*/
getSuggestion(id: string): Suggestion;
}
/**
* Track changes adapter.
*
* The track changes adapter is an object that communicates asynchronously with the data source to fetch or save the suggestion data.
* It is used internally by the track changes feature whenever a suggestion is loaded, created or deleted.
*
* The adapter is optional. You might need to provide it if you are {@glink features/collaboration/track-changes/track-changes-integration}
* using the track changes feature without real-time collaboration.
*
* To set the adapter, overwrite {@link module:track-changes/trackchanges~TrackChanges#adapter the `TrackChanges#adapter` property}.
*/
export interface TrackChangesAdapter {
/**
* Called each time the suggestion data is needed.
*
* The method should return a promise that resolves with the suggestion data object.
*
* @param id The ID of the suggestion to get.
*/
getSuggestion(id: string): Promise<SuggestionData>;
/**
* Called each time a new suggestion is created.
*
* The method should save the suggestion data in the database
* and return a promise that should be resolved when the save is
* completed.
*
* If the promise resolves with an object with the `createdAt` property,
* this suggestion property will be updated in the suggestion in the editor.
* This lets you update the suggestion data with server-side information.
*
* The `suggestionData` object does not expect the `authorId` property.
* For security reasons, the author of the suggestion should be set
* on the server side.
*
* If `suggestionData.originalSuggestionId` is set, the new suggestion should
* have the `authorId` property set to the same as the suggestion with
* `originalSuggestionId`. This happens when one user splits
* another user's suggestion, creating a new suggestion as a result. See
* {@glink features/collaboration/track-changes/track-changes-integration#implementation Track changes integration} guide.
*
* **Note:** Failure to properly handle this property will result in editor crash in some scenarios.
*
* In any other case, use the current (local) user to set `authorId`.
*
* The `suggestionData` 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.
*
* It is recommended to stringify `suggestionData.attributes` value to JSON and save it as a string in your database,
* and then to parse the strings when loading suggestions.
*/
addSuggestion(suggestionData: AddSuggestionInput): Promise<SuggestionData>;
/**
* Called each time the suggestion properties change.
*
* The method should update the suggestion properties in the database
* and return a promise that should be resolved when the save is
* completed.
*
* Keep in mind that the `data` parameter only contains those
* properties of a suggestion which changed.
*/
updateSuggestion(id: string, suggestionData: UpdateSuggestionInput): Promise<void>;
}
export interface SuggestionData extends SuggestionJSON {
/**
* Original suggestion ID from which the current one was split.
*/
originalSuggestionId?: string | null;
}
export type AddSuggestionInput = Omit<SuggestionData, 'authorId' | 'createdAt'>;
export type UpdateSuggestionInput = Partial<Pick<SuggestionJSON, 'hasComments' | 'attributes'>> & {
state?: 'open' | 'accepted' | 'rejected';
};