Browse Source

Created the injectCSSTransitionDisabling() View decorator. Disabled CSS transitions on show in MediaEmbedUI, TextAlternativeUI and LinkUI to prevent annoying animations.

Aleksander Nowodzinski 5 years ago
parent
commit
5c95ff408b

+ 4 - 0
packages/ckeditor5-image/src/imagetextalternative/imagetextalternativeui.js

@@ -165,6 +165,8 @@ export default class ImageTextAlternativeUI extends Plugin {
 		const command = editor.commands.get( 'imageTextAlternative' );
 		const labeledInput = this._form.labeledInput;
 
+		this._form.disableCSSTransitions();
+
 		if ( !this._isInBalloon ) {
 			this._balloon.add( {
 				view: this._form,
@@ -180,6 +182,8 @@ export default class ImageTextAlternativeUI extends Plugin {
 		labeledInput.fieldView.value = labeledInput.fieldView.element.value = command.value || '';
 
 		this._form.labeledInput.fieldView.select();
+
+		this._form.enableCSSTransitions();
 	}
 
 	/**

+ 3 - 0
packages/ckeditor5-image/src/imagetextalternative/ui/textalternativeformview.js

@@ -14,6 +14,7 @@ import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 
 import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
 import { createLabeledInputText } from '@ckeditor/ckeditor5-ui/src/labeledfield/utils';
+import injectCSSTransitionDisabling from '@ckeditor/ckeditor5-ui/src/bindings/injecttransitiondisabling';
 
 import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
@@ -124,6 +125,8 @@ export default class TextAlternativeFormView extends View {
 				this.cancelButtonView
 			]
 		} );
+
+		injectCSSTransitionDisabling( this );
 	}
 
 	/**

+ 13 - 0
packages/ckeditor5-image/tests/imagetextalternative/imagetextalternativeui.js

@@ -96,6 +96,19 @@ describe( 'ImageTextAlternativeUI', () => {
 			sinon.assert.calledOnce( spy );
 			expect( form.labeledInput.fieldView.value ).equals( '' );
 		} );
+
+		it( 'should disable CSS transitions before showing the form to avoid unnecessary animations (and then enable them again)', () => {
+			const addSpy = sinon.spy( balloon, 'add' );
+			const disableCSSTransitionsSpy = sinon.spy( form, 'disableCSSTransitions' );
+			const enableCSSTransitionsSpy = sinon.spy( form, 'enableCSSTransitions' );
+			const selectSpy = sinon.spy( form.labeledInput.fieldView, 'select' );
+
+			setData( model, '[<image src="" alt="foo bar"></image>]' );
+
+			button.fire( 'execute' );
+
+			sinon.assert.callOrder( disableCSSTransitionsSpy, addSpy, selectSpy, enableCSSTransitionsSpy );
+		} );
 	} );
 
 	describe( 'balloon panel form', () => {

+ 4 - 0
packages/ckeditor5-image/tests/imagetextalternative/ui/textalternativeformview.js

@@ -66,6 +66,10 @@ describe( 'TextAlternativeFormView', () => {
 
 			sinon.assert.calledOnce( spy );
 		} );
+
+		it( 'should implement the CSS transition disabling feature', () => {
+			expect( view.disableCSSTransitions ).to.be.a( 'function' );
+		} );
 	} );
 
 	describe( 'render()', () => {

+ 4 - 0
packages/ckeditor5-link/src/linkui.js

@@ -324,6 +324,8 @@ export default class LinkUI extends Plugin {
 		const editor = this.editor;
 		const linkCommand = editor.commands.get( 'link' );
 
+		this.formView.disableCSSTransitions();
+
 		this._balloon.add( {
 			view: this.formView,
 			position: this._getBalloonPositionData()
@@ -334,6 +336,8 @@ export default class LinkUI extends Plugin {
 			this.formView.urlInputView.fieldView.select();
 		}
 
+		this.formView.enableCSSTransitions();
+
 		// Make sure that each time the panel shows up, the URL field remains in sync with the value of
 		// the command. If the user typed in the input, then canceled the balloon (`urlInputView.fieldView#value` stays
 		// unaltered) and re-opened it without changing the value of the link command (e.g. because they

+ 3 - 0
packages/ckeditor5-link/src/ui/linkformview.js

@@ -15,6 +15,7 @@ import SwitchButtonView from '@ckeditor/ckeditor5-ui/src/button/switchbuttonview
 
 import LabeledFieldView from '@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview';
 import { createLabeledInputText } from '@ckeditor/ckeditor5-ui/src/labeledfield/utils';
+import injectCSSTransitionDisabling from '@ckeditor/ckeditor5-ui/src/bindings/injecttransitiondisabling';
 
 import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
@@ -151,6 +152,8 @@ export default class LinkFormView extends View {
 
 			children: this.children
 		} );
+
+		injectCSSTransitionDisabling( this );
 	}
 
 	/**

+ 12 - 0
packages/ckeditor5-link/tests/linkui.js

@@ -1107,6 +1107,18 @@ describe( 'LinkUI', () => {
 				sinon.assert.calledOnce( selectSpy );
 			} );
 
+			it( 'should disable CSS transitions before showing the form to avoid unnecessary animations' +
+				'(and then enable them again)', () => {
+				const addSpy = sinon.spy( balloon, 'add' );
+				const disableCSSTransitionsSpy = sinon.spy( formView, 'disableCSSTransitions' );
+				const enableCSSTransitionsSpy = sinon.spy( formView, 'enableCSSTransitions' );
+				const selectSpy = sinon.spy( formView.urlInputView.fieldView, 'select' );
+
+				actionsView.fire( 'edit' );
+
+				sinon.assert.callOrder( disableCSSTransitionsSpy, addSpy, selectSpy, enableCSSTransitionsSpy );
+			} );
+
 			it( 'should execute unlink command on actionsView#unlink event', () => {
 				const executeSpy = testUtils.sinon.spy( editor, 'execute' );
 

+ 4 - 0
packages/ckeditor5-link/tests/ui/linkformview.js

@@ -91,6 +91,10 @@ describe( 'LinkFormView', () => {
 				expect( view.template.children[ 0 ].get( 2 ) ).to.equal( view.cancelButtonView );
 			} );
 		} );
+
+		it( 'should implement the CSS transition disabling feature', () => {
+			expect( view.disableCSSTransitions ).to.be.a( 'function' );
+		} );
 	} );
 
 	describe( 'render()', () => {

+ 3 - 0
packages/ckeditor5-media-embed/src/mediaembedui.js

@@ -78,6 +78,8 @@ export default class MediaEmbedUI extends Plugin {
 		// default action of the drop-down is executed (i.e. the panel showed up). Otherwise, the
 		// invisible form/input cannot be focused/selected.
 		button.on( 'open', () => {
+			form.disableCSSTransitions();
+
 			// Make sure that each time the panel shows up, the URL field remains in sync with the value of
 			// the command. If the user typed in the input, then canceled (`urlInputView#fieldView#value` stays
 			// unaltered) and re-opened it without changing the value of the media command (e.g. because they
@@ -86,6 +88,7 @@ export default class MediaEmbedUI extends Plugin {
 			form.url = command.value || '';
 			form.urlInputView.fieldView.select();
 			form.focus();
+			form.enableCSSTransitions();
 		}, { priority: 'low' } );
 
 		dropdown.on( 'submit', () => {

+ 3 - 0
packages/ckeditor5-media-embed/src/ui/mediaformview.js

@@ -19,6 +19,7 @@ import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusCycler from '@ckeditor/ckeditor5-ui/src/focuscycler';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
+import injectCSSTransitionDisabling from '@ckeditor/ckeditor5-ui/src/bindings/injecttransitiondisabling';
 
 import checkIcon from '@ckeditor/ckeditor5-core/theme/icons/check.svg';
 import cancelIcon from '@ckeditor/ckeditor5-core/theme/icons/cancel.svg';
@@ -136,6 +137,8 @@ export default class MediaFormView extends View {
 			]
 		} );
 
+		injectCSSTransitionDisabling( this );
+
 		/**
 		 * The default info text for the {@link #urlInputView}.
 		 *

+ 10 - 0
packages/ckeditor5-media-embed/tests/mediaembedui.js

@@ -127,6 +127,16 @@ describe( 'MediaEmbedUI', () => {
 					button.fire( 'open' );
 					sinon.assert.calledOnce( spy );
 				} );
+
+				it( 'should disable CSS transitions to avoid unnecessary animations (and then enable them again)', () => {
+					const disableCSSTransitionsSpy = sinon.spy( form, 'disableCSSTransitions' );
+					const enableCSSTransitionsSpy = sinon.spy( form, 'enableCSSTransitions' );
+					const selectSpy = sinon.spy( form.urlInputView.fieldView, 'select' );
+
+					button.fire( 'open' );
+
+					sinon.assert.callOrder( disableCSSTransitionsSpy, selectSpy, enableCSSTransitionsSpy );
+				} );
 			} );
 		} );
 

+ 4 - 0
packages/ckeditor5-media-embed/tests/ui/mediaformview.js

@@ -77,6 +77,10 @@ describe( 'MediaFormView', () => {
 			expect( spy.calledOnce ).to.true;
 		} );
 
+		it( 'should implement the CSS transition disabling feature', () => {
+			expect( view.disableCSSTransitions ).to.be.a( 'function' );
+		} );
+
 		describe( 'url input view', () => {
 			it( 'has info text', () => {
 				expect( view.urlInputView.infoText ).to.match( /^Paste the media URL/ );

+ 72 - 0
packages/ckeditor5-ui/src/bindings/injecttransitiondisabling.js

@@ -0,0 +1,72 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module ui/bindings/injecttransitiondisabling
+ */
+
+/**
+ * A decorator that brings possibility to temporarily disable CSS transitions using
+ * {@link module:ui/view~View} methods. It is helpful when, for instance, the transitions should not happen
+ * when the view is first displayed but they should work normally in other cases.
+ *
+ * The methods to control the CSS transitions are:
+ * * `disableCSSTransitions()` – adds the `.ck-transitions-disabled` class to the
+ * {@link module:ui/view~View#element view element},
+ * * `enableCSSTransitions()` – removes the `.ck-transitions-disabled` class from the
+ * {@link module:ui/view~View#element view element}.
+ *
+ * **Note**: This helper extends the {@link module:ui/view~View#template template} and must be used **after**
+ * {@link module:ui/view~View#setTemplate} is called:
+ *
+ *		import injectCSSTransitionDisabling from '@ckeditor/ckeditor5-ui/src/bindings/injecttransitiondisabling';
+ *
+ *		class MyView extends View {
+ *			constructor() {
+ *				super();
+ *
+ *				// ...
+ *
+ *				this.setTemplate( { ... } );
+ *
+ *				// ...
+ *
+ *				injectCSSTransitionDisabling( this );
+ *
+ *				// ...
+ *			}
+ *		}
+ *
+ * The usage comes down to:
+ *
+ *		const view = new MyView();
+ *
+ *		// ...
+ *
+ *		view.disableCSSTransitions();
+ *		view.show();
+ *		view.enableCSSTransitions();
+ *
+ * @param {module:ui/view~View} view View instance that should get this functionality.
+ */
+export default function injectCSSTransitionDisabling( view ) {
+	view.set( '_isCSSTransitionsDisabled', false );
+
+	view.disableCSSTransitions = () => {
+		view._isCSSTransitionsDisabled = true;
+	};
+
+	view.enableCSSTransitions = () => {
+		view._isCSSTransitionsDisabled = false;
+	};
+
+	view.extendTemplate( {
+		attributes: {
+			class: [
+				view.bindTemplate.if( '_isCSSTransitionsDisabled', 'ck-transitions-disabled' )
+			]
+		}
+	} );
+}

+ 66 - 0
packages/ckeditor5-ui/tests/bindings/injecttransitiondisabling.js

@@ -0,0 +1,66 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import injectCSSTransitionDisabling from '../../src/bindings/injecttransitiondisabling';
+import View from '../../src/view';
+
+describe( 'injectCSSTransitionDisabling()', () => {
+	let view;
+
+	class TestView extends View {
+		constructor( ...args ) {
+			super( ...args );
+
+			this.setTemplate( {
+				tag: 'div'
+			} );
+
+			injectCSSTransitionDisabling( this );
+		}
+	}
+
+	beforeEach( () => {
+		view = new TestView();
+		view.render();
+	} );
+
+	afterEach( () => {
+		view.destroy();
+	} );
+
+	it( 'should create a protected observable property for class binding', () => {
+		expect( view._isCSSTransitionsDisabled ).to.be.false;
+	} );
+
+	it( 'should not alter the CSS class of the view until blocking is enabled', () => {
+		expect( view.element.classList ).to.be.empty;
+	} );
+
+	describe( 'disableCSSTransitions() method', () => {
+		it( 'should belong to the view', () => {
+			expect( view.disableCSSTransitions ).to.be.a( 'function' );
+		} );
+
+		it( 'should set the proper CSS class when called', () => {
+			view.disableCSSTransitions();
+
+			expect( view.element.classList.contains( 'ck-transitions-disabled' ) ).to.be.true;
+		} );
+	} );
+
+	describe( 'enableCSSTransitions() method', () => {
+		it( 'should belong to the view', () => {
+			expect( view.enableCSSTransitions ).to.be.a( 'function' );
+		} );
+
+		it( 'should remove the proper CSS class when called', () => {
+			view.disableCSSTransitions();
+			expect( view.element.classList.contains( 'ck-transitions-disabled' ) ).to.be.true;
+
+			view.enableCSSTransitions();
+			expect( view.element.classList.contains( 'ck-transitions-disabled' ) ).to.be.false;
+		} );
+	} );
+} );

+ 12 - 0
packages/ckeditor5-ui/theme/globals/_transition.css

@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * A class that disables all transitions of the element and its children.
+ */
+.ck-transitions-disabled,
+.ck-transitions-disabled * {
+	transition: none !important;
+}

+ 1 - 0
packages/ckeditor5-ui/theme/globals/globals.css

@@ -6,3 +6,4 @@
 @import "./_hidden.css";
 @import "./_reset.css";
 @import "./_zindex.css";
+@import "./_transition.css";