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

Introduced `selectionChangeDone` to selection observer.

Oskar Wróbel 8 лет назад
Родитель
Сommit
11dee1ce64

+ 19 - 0
packages/ckeditor5-engine/src/view/observer/selectionobserver.js

@@ -12,6 +12,7 @@
 import Observer from './observer';
 import MutationObserver from './mutationobserver';
 import log from '@ckeditor/ckeditor5-utils/src/log';
+import debounce from '@ckeditor/ckeditor5-utils/src/lib/lodash/debounce';
 
 /**
  * Selection observer class observes selection changes in the document. If selection changes on the document this
@@ -73,6 +74,15 @@ export default class SelectionObserver extends Observer {
 		 */
 		this._documents = new WeakSet();
 
+		/**
+		 * Fires `selectionChangeDone` event debounced. It use `lodash#debounce` method to delay function call.
+		 *
+		 * @private
+		 * @param {Object} data Selection change data.
+		 * @method #_fireSelectionChangeDoneDebounced
+		 */
+		this._fireSelectionChangeDoneDebounced = debounce( data => this.document.fire( 'selectionChangeDone', data ), 200 );
+
 		this._clearInfiniteLoopInterval = setInterval( () => this._clearInfiniteLoop(), 2000 );
 
 		/**
@@ -112,6 +122,14 @@ export default class SelectionObserver extends Observer {
 			this._handleSelectionChange( domDocument );
 		} );
 
+		// Call` #_fireSelectionChangeDoneDebounced` on each `selectionChange` event.
+		// This function is debounced what means that `selectionChangeDone` event will be fired only when
+		// defined int the function time will elapse since the last time the function was called.
+		// So `selectionChangeDone` will be fired when selection will stop changing.
+		this.listenTo( this.document, 'selectionChange', ( evt, data ) => {
+			this._fireSelectionChangeDoneDebounced( data );
+		} );
+
 		this._documents.add( domDocument );
 	}
 
@@ -122,6 +140,7 @@ export default class SelectionObserver extends Observer {
 		super.destroy();
 
 		clearInterval( this._clearInfiniteLoopInterval );
+		this._fireSelectionChangeDoneDebounced.cancel();
 	}
 
 	/**

+ 6 - 1
packages/ckeditor5-engine/tests/view/manual/selectionobserver.js

@@ -16,7 +16,12 @@ setData( viewDocument,
 	'<container:p>bom</container:p>' );
 
 viewDocument.on( 'selectionChange', ( evt, data ) => {
-	console.log( data );
+	console.log( 'selectionChange', data );
+	viewDocument.selection.setTo( data.newSelection );
+} );
+
+viewDocument.on( 'selectionChangeDone', ( evt, data ) => {
+	console.log( '%c selectionChangeDone ', 'background: #222; color: #bada55', data );
 	viewDocument.selection.setTo( data.newSelection );
 } );
 

+ 3 - 1
packages/ckeditor5-engine/tests/view/manual/selectionobserver.md

@@ -1,6 +1,8 @@
 ### Move selection
 
-Selection should be moved and event should be fired (see console),
+Selection should be moved and events should be fired on console:
+- `selectionChange` event should be fired after every change
+- `selectionChangeDone` event should be fired when selection stop changing
 
 ### Change document
 

+ 64 - 0
packages/ckeditor5-engine/tests/view/observer/selectionobserver.js

@@ -226,6 +226,70 @@ describe( 'SelectionObserver', () => {
 			}, 200 );
 		}, 400 );
 	} );
+
+	it( 'should fire `selectionChangeDone` event after selection stop changing', ( done ) => {
+		// Note that it's difficult to test lodash#debounce with sinon fake timers.
+		// See: https://github.com/lodash/lodash/issues/304
+
+		const spy = sinon.spy();
+
+		viewDocument.on( 'selectionChangeDone', spy );
+
+		// Fire `selectionChange` event.
+		viewDocument.fire( 'selectionChange' );
+
+		// Wait 100ms.
+		setTimeout( () => {
+			// Check if spy was called.
+			expect( spy.notCalled ).to.true;
+
+			// Fire event again.
+			viewDocument.fire( 'selectionChange' );
+
+			// Wait 100ms.
+			setTimeout( () => {
+				// Check if spy was called.
+				expect( spy.notCalled ).to.true;
+
+				// Fire event again.
+				viewDocument.fire( 'selectionChange', { foo: 'bar' } );
+
+				// Wait 210ms (debounced function should be called).
+				setTimeout( () => {
+					expect( spy.calledOnce ).to.true;
+					expect( spy.firstCall.args[ 1 ] ).to.deep.equal( { foo: 'bar' } );
+
+					done();
+				}, 210 );
+			}, 100 );
+		}, 100 );
+	} );
+
+	it( 'should not fire `selectionChangeDone` event when observer will be destroyed', ( done ) => {
+		// Note that it's difficult to test lodash#debounce with sinon fake timers.
+		// See: https://github.com/lodash/lodash/issues/304
+
+		const spy = sinon.spy();
+
+		viewDocument.on( 'selectionChangeDone', spy );
+
+		// Fire `selectionChange` event.
+		viewDocument.fire( 'selectionChange' );
+
+		// Wait 100ms.
+		setTimeout( () => {
+			// And destroy observer.
+			selectionObserver.destroy();
+
+			// Wait another 110ms.
+			setTimeout( () => {
+				// Check that event won't be called.
+				expect( spy.notCalled ).to.true;
+
+				done();
+			}, 110 );
+		}, 100 );
+	} );
 } );
 
 function changeDomSelection() {