1 | /**
|
2 | * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
3 | * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4 | */
|
5 |
|
6 | /**
|
7 | * @module core/pendingactions
|
8 | */
|
9 |
|
10 | import ContextPlugin from './contextplugin';
|
11 | import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
12 | import Collection from '@ckeditor/ckeditor5-utils/src/collection';
|
13 | import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
14 |
|
15 | /**
|
16 | * The list of pending editor actions.
|
17 | *
|
18 | * This plugin should be used to synchronise plugins that execute long-lasting actions
|
19 | * (e.g. file upload) with the editor integration. It gives the developer who integrates the editor
|
20 | * an easy way to check if there are any actions pending whenever such information is needed.
|
21 | * All plugins that register a pending action also provide a message about the action that is ongoing
|
22 | * which can be displayed to the user. This lets them decide if they want to interrupt the action or wait.
|
23 | *
|
24 | * Adding and updating a pending action:
|
25 | *
|
26 | * const pendingActions = editor.plugins.get( 'PendingActions' );
|
27 | * const action = pendingActions.add( 'Upload in progress: 0%.' );
|
28 | *
|
29 | * // You can update the message:
|
30 | * action.message = 'Upload in progress: 10%.';
|
31 | *
|
32 | * Removing a pending action:
|
33 | *
|
34 | * const pendingActions = editor.plugins.get( 'PendingActions' );
|
35 | * const action = pendingActions.add( 'Unsaved changes.' );
|
36 | *
|
37 | * pendingActions.remove( action );
|
38 | *
|
39 | * Getting pending actions:
|
40 | *
|
41 | * const pendingActions = editor.plugins.get( 'PendingActions' );
|
42 | *
|
43 | * const action1 = pendingActions.add( 'Action 1' );
|
44 | * const action2 = pendingActions.add( 'Action 2' );
|
45 | *
|
46 | * pendingActions.first; // Returns action1
|
47 | * Array.from( pendingActions ); // Returns [ action1, action2 ]
|
48 | *
|
49 | * This plugin is used by features like {@link module:upload/filerepository~FileRepository} to register their ongoing actions
|
50 | * and by features like {@link module:autosave/autosave~Autosave} to detect whether there are any ongoing actions.
|
51 | * Read more about saving the data in the {@glink installation/advanced/saving-data Saving and getting data} guide.
|
52 | *
|
53 | * @extends module:core/contextplugin~ContextPlugin
|
54 | */
|
55 | export default class PendingActions extends ContextPlugin {
|
56 | /**
|
57 | * @inheritDoc
|
58 | */
|
59 | static get pluginName() {
|
60 | return 'PendingActions';
|
61 | }
|
62 |
|
63 | /**
|
64 | * @inheritDoc
|
65 | */
|
66 | init() {
|
67 | /**
|
68 | * Defines whether there is any registered pending action.
|
69 | *
|
70 | * @readonly
|
71 | * @observable
|
72 | * @member {Boolean} #hasAny
|
73 | */
|
74 | this.set( 'hasAny', false );
|
75 |
|
76 | /**
|
77 | * A list of pending actions.
|
78 | *
|
79 | * @private
|
80 | * @type {module:utils/collection~Collection}
|
81 | */
|
82 | this._actions = new Collection( { idProperty: '_id' } );
|
83 | this._actions.delegate( 'add', 'remove' ).to( this );
|
84 | }
|
85 |
|
86 | /**
|
87 | * Adds an action to the list of pending actions.
|
88 | *
|
89 | * This method returns an action object with an observable message property.
|
90 | * The action object can be later used in the {@link #remove} method. It also allows you to change the message.
|
91 | *
|
92 | * @param {String} message The action message.
|
93 | * @returns {Object} An observable object that represents a pending action.
|
94 | */
|
95 | add( message ) {
|
96 | if ( typeof message !== 'string' ) {
|
97 | /**
|
98 | * The message must be a string.
|
99 | *
|
100 | * @error pendingactions-add-invalid-message
|
101 | */
|
102 | throw new CKEditorError( 'pendingactions-add-invalid-message', this );
|
103 | }
|
104 |
|
105 | const action = Object.create( ObservableMixin );
|
106 |
|
107 | action.set( 'message', message );
|
108 | this._actions.add( action );
|
109 | this.hasAny = true;
|
110 |
|
111 | return action;
|
112 | }
|
113 |
|
114 | /**
|
115 | * Removes an action from the list of pending actions.
|
116 | *
|
117 | * @param {Object} action An action object.
|
118 | */
|
119 | remove( action ) {
|
120 | this._actions.remove( action );
|
121 | this.hasAny = !!this._actions.length;
|
122 | }
|
123 |
|
124 | /**
|
125 | * Returns the first action from the list or null when list is empty
|
126 | *
|
127 | * returns {Object|null} The pending action object.
|
128 | */
|
129 | get first() {
|
130 | return this._actions.get( 0 );
|
131 | }
|
132 |
|
133 | /**
|
134 | * Iterable interface.
|
135 | *
|
136 | * @returns {Iterable.<*>}
|
137 | */
|
138 | [ Symbol.iterator ]() {
|
139 | return this._actions[ Symbol.iterator ]();
|
140 | }
|
141 |
|
142 | /**
|
143 | * Fired when an action is added to the list.
|
144 | *
|
145 | * @event add
|
146 | * @param {Object} action The added action.
|
147 | */
|
148 |
|
149 | /**
|
150 | * Fired when an action is removed from the list.
|
151 | *
|
152 | * @event remove
|
153 | * @param {Object} action The removed action.
|
154 | */
|
155 | }
|