浏览代码

Feature: Introduced `View#hasDomSelection`.

Szymon Cofalik 5 年之前
父节点
当前提交
2598e353d5

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

@@ -143,9 +143,13 @@ export default class SelectionObserver extends Observer {
 		// It means that the DOM selection is in some way incorrect. Ranges that were in the DOM selection could not be
 		// converted to the view. This happens when the DOM selection was moved outside of the editable element.
 		if ( newViewSelection.rangeCount == 0 ) {
+			this.view.hasDomSelection = false;
+
 			return;
 		}
 
+		this.view.hasDomSelection = true;
+
 		if ( this.selection.isEqual( newViewSelection ) && this.domConverter.isDomSelectionCorrect( domSelection ) ) {
 			return;
 		}

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

@@ -101,6 +101,14 @@ export default class View {
 		this.set( 'isRenderingInProgress', false );
 
 		/**
+		 * Informs whether the DOM selection is inside any of the DOM roots managed by the view.
+		 *
+		 * @readonly
+		 * @member {Boolean} #hasDomSelection
+		 */
+		this.set( 'hasDomSelection', false );
+
+		/**
 		 * Instance of the {@link module:engine/view/renderer~Renderer renderer}.
 		 *
 		 * @protected

+ 59 - 1
packages/ckeditor5-engine/tests/view/view/view.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
-/* globals document, console */
+/* globals document, console, setTimeout */
 
 import View from '../../../src/view/view';
 import Observer from '../../../src/view/observer/observer';
@@ -514,6 +514,64 @@ describe( 'view', () => {
 		} );
 	} );
 
+	describe( 'hasDomSelection', () => {
+		let domElement, domP, domSelection;
+
+		beforeEach( () => {
+			const viewRoot = createViewRoot( viewDocument, 'div', 'main' );
+
+			view.attachDomRoot( domRoot );
+
+			viewRoot._appendChild( new ViewElement( viewDocument, 'p' ) );
+			view.forceRender();
+
+			domElement = createElement( document, 'div', { contenteditable: 'true' } );
+			document.body.appendChild( domElement );
+
+			domSelection = document.getSelection();
+			domP = domRoot.childNodes[ 0 ];
+		} );
+
+		afterEach( () => {
+			domElement.remove();
+		} );
+
+		it( 'should be true if selection is inside a DOM root element', done => {
+			domSelection.collapse( domP, 0 );
+
+			// Wait for async selectionchange event on DOM document.
+			setTimeout( () => {
+				expect( view.hasDomSelection ).to.be.true;
+
+				done();
+			}, 100 );
+		} );
+
+		it( 'should be true if selection is inside a DOM root element - no focus', done => {
+			domSelection.collapse( domP, 0 );
+			domRoot.blur();
+
+			// Wait for async selectionchange event on DOM document.
+			setTimeout( () => {
+				expect( view.hasDomSelection ).to.be.true;
+				expect( view.document.isFocused ).to.be.false;
+
+				done();
+			}, 100 );
+		} );
+
+		it( 'should be false if selection is outside DOM root element', done => {
+			domSelection.collapse( domElement, 0 );
+
+			// Wait for async selectionchange event on DOM document.
+			setTimeout( () => {
+				expect( view.hasDomSelection ).to.be.false;
+
+				done();
+			}, 100 );
+		} );
+	} );
+
 	describe( 'forceRender()', () => {
 		it( 'disable observers, renders and enable observers', () => {
 			const observerMock = view.addObserver( ObserverMock );