Sfoglia il codice sorgente

Limit the height of mentions view.

Maciej Gołaszewski 6 anni fa
parent
commit
7e053584a8

+ 48 - 1
packages/ckeditor5-mention/src/ui/mentionsview.js

@@ -10,12 +10,17 @@
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import ListView from '@ckeditor/ckeditor5-ui/src/list/listview';
 
+import '../../theme/mentionui.css';
+
 /**
  * The mention ui view.
  *
  * @extends module:ui/view~View
  */
 export default class MentionsView extends View {
+	/**
+	 * @inheritDoc
+	 */
 	constructor( locale ) {
 		super( locale );
 
@@ -27,7 +32,7 @@ export default class MentionsView extends View {
 			attributes: {
 				class: [
 					'ck',
-					'ck-mention'
+					'ck-mention-view'
 				],
 
 				tabindex: '-1'
@@ -39,10 +44,18 @@ export default class MentionsView extends View {
 		} );
 	}
 
+	/**
+	 * Selects the first item.
+	 */
 	selectFirst() {
 		this.select( 0 );
 	}
 
+	/**
+	 * Selects next item to the currently selected.
+	 *
+	 * If last item is already selected it will select first item.
+	 */
 	selectNext() {
 		const item = this.selected;
 
@@ -51,6 +64,11 @@ export default class MentionsView extends View {
 		this.select( index + 1 );
 	}
 
+	/**
+	 * Selects previous item to the currently selected.
+	 *
+	 * If first item is already selected it will select last item.
+	 */
 	selectPrevious() {
 		const item = this.selected;
 
@@ -59,6 +77,15 @@ export default class MentionsView extends View {
 		this.select( index - 1 );
 	}
 
+	/**
+	 * Marks item at given index as selected.
+	 *
+	 * Handles selection cycling when passed index is out of bounds:
+	 * - if index is lover then 0 it will select last item
+	 * - if index is higher then last item index it will select the first item.
+	 *
+	 * @param {Number} index Index of item to mark as selected.
+	 */
 	select( index ) {
 		let indexToGet = 0;
 
@@ -71,6 +98,11 @@ export default class MentionsView extends View {
 		const item = this.listView.items.get( indexToGet );
 		item.highlight();
 
+		// Scroll the mentions view to the selected element.
+		if ( !this._isItemVisibleInScrolledArea( item ) ) {
+			this.element.scrollTop = item.element.offsetTop;
+		}
+
 		if ( this.selected ) {
 			this.selected.removeHighlight();
 		}
@@ -78,7 +110,22 @@ export default class MentionsView extends View {
 		this.selected = item;
 	}
 
+	/**
+	 * Triggers "execute" event on selected item.
+	 */
 	executeSelected() {
 		this.selected.fire( 'execute' );
 	}
+
+	// Checks if item is visible in scrolled area.
+	//
+	// The item is considered visible when:
+	// - its top line is inside scrolled rect
+	// - its bottom line is inside scrolled rect (whole item must be visible)
+	_isItemVisibleInScrolledArea( item ) {
+		const isBottomLineVisible = item.element.offsetTop + item.element.clientHeight <= this.element.clientHeight;
+		const isTopLineVisible = item.element.offsetTop >= this.element.scrollTop;
+
+		return isBottomLineVisible && isTopLineVisible;
+	}
 }

+ 57 - 11
packages/ckeditor5-mention/tests/mentionui.js

@@ -218,7 +218,7 @@ describe( 'MentionUI', () => {
 				} );
 		} );
 
-		it( 'should show panel with no more then 10 items for default static feed', () => {
+		describe( 'static list with large set of results', () => {
 			const bigList = {
 				marker: '@',
 				feed: [
@@ -226,19 +226,65 @@ describe( 'MentionUI', () => {
 				]
 			};
 
-			return createClassicTestEditor( { feeds: [ bigList ] } )
-				.then( () => {
-					setData( model, '<paragraph>foo []</paragraph>' );
+			beforeEach( () => {
+				return createClassicTestEditor( { feeds: [ bigList ] } );
+			} );
 
-					model.change( writer => {
-						writer.insertText( '@', doc.selection.getFirstPosition() );
+			it( 'should show panel with no more then 10 items for default static feed', () => {
+				setData( model, '<paragraph>foo []</paragraph>' );
+
+				model.change( writer => {
+					writer.insertText( '@', doc.selection.getFirstPosition() );
+				} );
+
+				return waitForDebounce()
+					.then( () => {
+						expect( panelView.isVisible ).to.be.true;
+						expect( listView.items ).to.have.length( 10 );
 					} );
-				} )
-				.then( waitForDebounce )
-				.then( () => {
-					expect( panelView.isVisible ).to.be.true;
-					expect( listView.items ).to.have.length( 10 );
+			} );
+
+			it( 'should scroll mention panel to the selected item', () => {
+				setData( model, '<paragraph>foo []</paragraph>' );
+
+				model.change( writer => {
+					writer.insertText( '@', doc.selection.getFirstPosition() );
 				} );
+
+				return waitForDebounce()
+					.then( () => {
+						expect( panelView.isVisible ).to.be.true;
+
+						expectChildViewsIsOnState( [ true, false, false, false, false, false, false, false, false, false ] );
+
+						const arrowDownEvtData = {
+							keyCode: keyCodes.arrowdown,
+							preventDefault: sinon.spy(),
+							stopPropagation: sinon.spy()
+						};
+
+						const arrowUpEvtData = {
+							keyCode: keyCodes.arrowup,
+							preventDefault: sinon.spy(),
+							stopPropagation: sinon.spy()
+						};
+
+						fireKeyDownEvent( arrowDownEvtData );
+						expect( mentionsView.element.scrollTop ).to.equal( 0 );
+
+						expectChildViewsIsOnState( [ false, true, false, false, false, false, false, false, false, false ] );
+
+						fireKeyDownEvent( arrowUpEvtData );
+						fireKeyDownEvent( arrowUpEvtData );
+
+						expectChildViewsIsOnState( [ false, false, false, false, false, false, false, false, false, true ] );
+						expect( mentionsView.element.scrollTop ).to.be.not.equal( 0 );
+
+						fireKeyDownEvent( arrowDownEvtData );
+						expectChildViewsIsOnState( [ true, false, false, false, false, false, false, false, false, false ] );
+						expect( mentionsView.element.scrollTop ).to.equal( 0 );
+					} );
+			} );
 		} );
 
 		describe( 'static list with default trigger', () => {

+ 12 - 0
packages/ckeditor5-mention/theme/mentionui.css

@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+.ck.ck-mention-view {
+	max-height: 300px;
+
+	overflow-y: auto;
+
+	overscroll-behavior: contain;
+}