Browse Source

Feature: Introduced ViewDocument#layoutChanged event.

Oskar Wróbel 7 năm trước cách đây
mục cha
commit
c7b14a6a16

+ 6 - 0
packages/ckeditor5-engine/src/view/document.js

@@ -140,6 +140,12 @@ export default class Document {
 			}
 		} while ( wasFixed );
 	}
+
+	/**
+	 * @TODO.
+	 *
+	 * @event module:engine/view/document~Document#event:layoutChanged
+	 */
 }
 
 mix( Document, ObservableMixin );

+ 13 - 0
packages/ckeditor5-engine/src/view/view.js

@@ -22,6 +22,7 @@ import CompositionObserver from './observer/compositionobserver';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import log from '@ckeditor/ckeditor5-utils/src/log';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
+import throttle from '@ckeditor/ckeditor5-utils/src/lib/lodash/throttle';
 import { scrollViewportToShowTarget } from '@ckeditor/ckeditor5-utils/src/dom/scroll';
 import { injectUiElementHandling } from './uielement';
 import { injectQuirksHandling } from './filler';
@@ -134,6 +135,14 @@ export default class View {
 		 */
 		this._writer = new Writer( this.document );
 
+		/**
+		 * Fires throttled {@link module:engine/view/document~Document#event:layoutChanged} event.
+		 *
+		 * @protected
+		 * @type {Function}
+		 */
+		this._throttledLayoutChange = throttle( () => this.document.fire( 'layoutChanged' ), 200 );
+
 		// Add default observers.
 		this.addObserver( MutationObserver );
 		this.addObserver( SelectionObserver );
@@ -149,6 +158,9 @@ export default class View {
 		// Use 'normal' priority so that rendering is performed as first when using that priority.
 		this.on( 'render', () => {
 			this._render();
+
+			// Informs that layout has changed after render.
+			this._throttledLayoutChange();
 		} );
 	}
 
@@ -378,6 +390,7 @@ export default class View {
 		}
 
 		this.stopListening();
+		this._throttledLayoutChange.cancel();
 	}
 
 	/**

+ 51 - 0
packages/ckeditor5-engine/tests/view/view/view.js

@@ -45,6 +45,7 @@ describe( 'view', () => {
 				this.enable = sinon.spy();
 				this.disable = sinon.spy();
 				this.observe = sinon.spy();
+				this.destroy = sinon.spy();
 			}
 		};
 
@@ -379,6 +380,26 @@ describe( 'view', () => {
 
 			sinon.assert.callOrder( observerMock.disable, renderStub, observerMock.enable );
 		} );
+
+		it( 'should fire throttled view.document.layoutChanged event after each render', () => {
+			const spy = sinon.spy();
+
+			view.document.on( 'layoutChanged', spy );
+
+			view.render();
+
+			sinon.assert.calledOnce( spy );
+
+			view.render();
+
+			// Still once because of throttle.
+			sinon.assert.calledOnce( spy );
+
+			view._throttledLayoutChange.flush();
+
+			// Twice after flushing throttled event.
+			sinon.assert.calledTwice( spy );
+		} );
 	} );
 
 	describe( 'view and DOM integration', () => {
@@ -588,6 +609,36 @@ describe( 'view', () => {
 		} );
 	} );
 
+	describe( 'destroy()', () => {
+		it( 'should destroy all observers', () => {
+			const observerMock = view.addObserver( ObserverMock );
+
+			view.destroy();
+
+			sinon.assert.calledOnce( observerMock.destroy );
+		} );
+
+		it( 'should cancel throttled layoutChanged event', () => {
+			const view = new View();
+			const spy = sinon.spy();
+
+			view.document.on( 'layoutChanged', spy );
+
+			view.render();
+
+			sinon.assert.calledOnce( spy );
+
+			view.render();
+
+			view.destroy();
+
+			view._throttledLayoutChange.flush();
+
+			// Still once because second call was canceled.
+			sinon.assert.calledOnce( spy );
+		} );
+	} );
+
 	function createRoot( name, rootName, viewDoc ) {
 		const viewRoot = new RootEditableElement( name );