@ckeditor/ckeditor5-core
Version:
The core architecture of CKEditor 5 – the best browser-based rich text editor.
128 lines (127 loc) • 4.08 kB
JavaScript
/**
* @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 core/pendingactions
*/
import ContextPlugin from './contextplugin.js';
import { CKEditorError, Collection, ObservableMixin } from '@ckeditor/ckeditor5-utils';
/**
* The list of pending editor actions.
*
* This plugin should be used to synchronise plugins that execute long-lasting actions
* (e.g. file upload) with the editor integration. It gives the developer who integrates the editor
* an easy way to check if there are any actions pending whenever such information is needed.
* All plugins that register a pending action also provide a message about the action that is ongoing
* which can be displayed to the user. This lets them decide if they want to interrupt the action or wait.
*
* Adding and updating a pending action:
*
* ```ts
* const pendingActions = editor.plugins.get( 'PendingActions' );
* const action = pendingActions.add( 'Upload in progress: 0%.' );
*
* // You can update the message:
* action.message = 'Upload in progress: 10%.';
* ```
*
* Removing a pending action:
*
* ```ts
* const pendingActions = editor.plugins.get( 'PendingActions' );
* const action = pendingActions.add( 'Unsaved changes.' );
*
* pendingActions.remove( action );
* ```
*
* Getting pending actions:
*
* ```ts
* const pendingActions = editor.plugins.get( 'PendingActions' );
*
* const action1 = pendingActions.add( 'Action 1' );
* const action2 = pendingActions.add( 'Action 2' );
*
* pendingActions.first; // Returns action1
* Array.from( pendingActions ); // Returns [ action1, action2 ]
* ```
*
* This plugin is used by features like {@link module:upload/filerepository~FileRepository} to register their ongoing actions
* and by features like {@link module:autosave/autosave~Autosave} to detect whether there are any ongoing actions.
* Read more about saving the data in the
* {@glink getting-started/setup/getting-and-setting-data Saving and getting data} guide.
*/
export default class PendingActions extends ContextPlugin {
/**
* A list of pending actions.
*/
_actions;
/**
* @inheritDoc
*/
static get pluginName() {
return 'PendingActions';
}
/**
* @inheritDoc
*/
static get isOfficialPlugin() {
return true;
}
/**
* @inheritDoc
*/
init() {
this.set('hasAny', false);
this._actions = new Collection({ idProperty: '_id' });
this._actions.delegate('add', 'remove').to(this);
}
/**
* Adds an action to the list of pending actions.
*
* This method returns an action object with an observable message property.
* The action object can be later used in the {@link #remove} method. It also allows you to change the message.
*
* @param message The action message.
* @returns An observable object that represents a pending action.
*/
add(message) {
if (typeof message !== 'string') {
/**
* The message must be a string.
*
* @error pendingactions-add-invalid-message
*/
throw new CKEditorError('pendingactions-add-invalid-message', this);
}
const action = new (ObservableMixin())();
action.set('message', message);
this._actions.add(action);
this.hasAny = true;
return action;
}
/**
* Removes an action from the list of pending actions.
*
* @param action An action object.
*/
remove(action) {
this._actions.remove(action);
this.hasAny = !!this._actions.length;
}
/**
* Returns the first action from the list or null if the list is empty
*
* @returns The pending action object.
*/
get first() {
return this._actions.get(0);
}
/**
* Iterable interface.
*/
[Symbol.iterator]() {
return this._actions[Symbol.iterator]();
}
}