8
0
Просмотр исходного кода

Internal: Ported force disabling logic from the command class.

Marek Lewandowski 6 лет назад
Родитель
Сommit
7598bbd917

+ 73 - 0
packages/ckeditor5-widget/src/widgettoolbarrepository.js

@@ -70,6 +70,55 @@ export default class WidgetToolbarRepository extends Plugin {
 			}, { priority: 'high' } );
 		}
 
+		/**
+		 * Flag indicating whether a command is enabled or disabled.
+		 * A disabled command will do nothing when executed.
+		 *
+		 * A concrete command class should control this value by overriding the {@link #refresh `refresh()`} method.
+		 *
+		 * It is possible to disable a command from "outside". For instance, in your integration you may want to disable
+		 * a certain set of commands for the time being. To do that, you can use the fact that `isEnabled` is observable
+		 * and it fires the `set:isEnabled` event every time anyone tries to modify its value:
+		 *
+		 *		function disableCommand( cmd ) {
+		 *			cmd.on( 'set:isEnabled', forceDisable, { priority: 'highest' } );
+		 *
+		 *			cmd.isEnabled = false;
+		 *
+		 *			// Make it possible to enable the command again.
+		 *			return () => {
+		 *				cmd.off( 'set:isEnabled', forceDisable );
+		 *				cmd.refresh();
+		 *			};
+		 *
+		 *			function forceDisable( evt ) {
+		 *				evt.return = false;
+		 *				evt.stop();
+		 *			}
+		 *		}
+		 *
+		 *		// Usage:
+		 *
+		 *		// Disabling the command.
+		 *		const enableBold = disableCommand( editor.commands.get( 'bold' ) );
+		 *
+		 *		// Enabling the command again.
+		 *		enableBold();
+		 *
+		 * @observable
+		 * @readonly
+		 * @member {Boolean} #isEnabled
+		 */
+		this.set( 'isEnabled', true );
+
+		/**
+		 * Holds identifiers for {@link #forceDisabled} mechanism.
+		 *
+		 * @type {Set.<String>}
+		 * @private
+		 */
+		this._disableStack = new Set();
+
 		/**
 		 * A map of toolbar definitions.
 		 *
@@ -142,6 +191,24 @@ export default class WidgetToolbarRepository extends Plugin {
 		} );
 	}
 
+	forceDisabled( id ) {
+		this._disableStack.add( id );
+
+		if ( this._disableStack.size == 1 ) {
+			this.on( 'set:isEnabled', forceDisable, { priority: 'highest' } );
+			this.isEnabled = false;
+		}
+	}
+
+	clearForceDisabled( id ) {
+		this._disableStack.delete( id );
+
+		if ( this._disableStack.size == 0 ) {
+			this.off( 'set:isEnabled', forceDisable );
+			this.isEnabled = true;
+		}
+	}
+
 	/**
 	 * Iterates over stored toolbars and makes them visible or hidden.
 	 *
@@ -293,3 +360,9 @@ function isWidgetSelected( selection ) {
  * there is no such element). The function accepts an instance of {@link module:engine/view/selection~Selection}.
  * @property {String} balloonClassName CSS class for the widget balloon when a toolbar is displayed.
  */
+
+// Helper function that forces command to be disabled.
+function forceDisable( evt ) {
+	evt.return = false;
+	evt.stop();
+}

+ 76 - 0
packages/ckeditor5-widget/tests/widgettoolbarrepository.js

@@ -510,6 +510,82 @@ describe( 'WidgetToolbarRepository - integration with the BalloonToolbar', () =>
 
 		expect( balloon.visibleView ).to.equal( balloonToolbar.toolbarView );
 	} );
+
+	describe.only( 'disableable', () => {
+		describe( 'isEnabled', () => {
+			it( 'is enabled by default', () => {
+				expect( widgetToolbarRepository.isEnabled ).to.be.true;
+			} );
+
+			it( 'fires change event', () => {
+				const spy = sinon.spy();
+
+				widgetToolbarRepository.on( 'change:isEnabled', spy );
+
+				widgetToolbarRepository.isEnabled = false;
+
+				expect( spy.calledOnce ).to.be.true;
+			} );
+		} );
+
+		describe( 'forceDisabled() / clearForceDisabled()', () => {
+			it( 'forceDisabled() should disable the plugin', () => {
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.isEnabled = true;
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.false;
+			} );
+
+			it( 'clearForceDisabled() should enable the plugin', () => {
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.clearForceDisabled( 'foo' );
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.true;
+			} );
+
+			it( 'clearForceDisabled() used with wrong identifier should not enable the plugin', () => {
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.clearForceDisabled( 'bar' );
+				widgetToolbarRepository.isEnabled = true;
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.false;
+			} );
+
+			it( 'using forceDisabled() twice with the same identifier should not have any effect', () => {
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.clearForceDisabled( 'foo' );
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.true;
+			} );
+
+			it( 'plugin is enabled only after all disables were cleared', () => {
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.forceDisabled( 'bar' );
+				widgetToolbarRepository.clearForceDisabled( 'foo' );
+				widgetToolbarRepository.isEnabled = true;
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.false;
+
+				widgetToolbarRepository.clearForceDisabled( 'bar' );
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.true;
+			} );
+
+			it( 'plugin should remain disabled if isEnabled has a callback disabling it', () => {
+				widgetToolbarRepository.on( 'set:isEnabled', evt => {
+					evt.return = false;
+					evt.stop();
+				} );
+
+				widgetToolbarRepository.forceDisabled( 'foo' );
+				widgetToolbarRepository.clearForceDisabled( 'foo' );
+				widgetToolbarRepository.isEnabled = true;
+
+				expect( widgetToolbarRepository.isEnabled ).to.be.false;
+			} );
+		} );
+	} );
 } );
 
 function getSelectedFakeWidget( selection ) {