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

Merge pull request #22 from ckeditor/t/3

Other: Limited the default size of mentions panel to 10 items. Closes #3.
Aleksander Nowodzinski 6 лет назад
Родитель
Сommit
c99c894a74

+ 5 - 3
packages/ckeditor5-mention/src/mentionui.js

@@ -469,9 +469,11 @@ function createTextMatcher( marker ) {
 // Default feed callback
 // Default feed callback
 function createFeedCallback( feedItems ) {
 function createFeedCallback( feedItems ) {
 	return feedText => {
 	return feedText => {
-		const filteredItems = feedItems.filter( item => {
-			return item.toLowerCase().includes( feedText.toLowerCase() );
-		} );
+		const filteredItems = feedItems
+		// Make default mention feed case-insensitive.
+			.filter( item => item.toLowerCase().includes( feedText.toLowerCase() ) )
+			// Do not return more than 10 items.
+			.slice( 0, 10 );
 
 
 		return Promise.resolve( filteredItems );
 		return Promise.resolve( filteredItems );
 	};
 	};

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

@@ -9,6 +9,9 @@
 
 
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import ListView from '@ckeditor/ckeditor5-ui/src/list/listview';
 import ListView from '@ckeditor/ckeditor5-ui/src/list/listview';
+import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
+
+import '../../theme/mentionui.css';
 
 
 /**
 /**
  * The mention ui view.
  * The mention ui view.
@@ -16,6 +19,9 @@ import ListView from '@ckeditor/ckeditor5-ui/src/list/listview';
  * @extends module:ui/view~View
  * @extends module:ui/view~View
  */
  */
 export default class MentionsView extends View {
 export default class MentionsView extends View {
+	/**
+	 * @inheritDoc
+	 */
 	constructor( locale ) {
 	constructor( locale ) {
 		super( locale );
 		super( locale );
 
 
@@ -27,7 +33,7 @@ export default class MentionsView extends View {
 			attributes: {
 			attributes: {
 				class: [
 				class: [
 					'ck',
 					'ck',
-					'ck-mention'
+					'ck-mentions'
 				],
 				],
 
 
 				tabindex: '-1'
 				tabindex: '-1'
@@ -39,10 +45,18 @@ export default class MentionsView extends View {
 		} );
 		} );
 	}
 	}
 
 
+	/**
+	 * {@link #select Selects} the first item.
+	 */
 	selectFirst() {
 	selectFirst() {
 		this.select( 0 );
 		this.select( 0 );
 	}
 	}
 
 
+	/**
+	 * Selects next item to the currently {@link #select selected}.
+	 *
+	 * If the last item is already selected, it will select the first item.
+	 */
 	selectNext() {
 	selectNext() {
 		const item = this.selected;
 		const item = this.selected;
 
 
@@ -51,6 +65,11 @@ export default class MentionsView extends View {
 		this.select( index + 1 );
 		this.select( index + 1 );
 	}
 	}
 
 
+	/**
+	 * Selects previous item to the currently {@link #select selected}.
+	 *
+	 * If the first item is already selected, it will select the last item.
+	 */
 	selectPrevious() {
 	selectPrevious() {
 		const item = this.selected;
 		const item = this.selected;
 
 
@@ -59,6 +78,15 @@ export default class MentionsView extends View {
 		this.select( index - 1 );
 		this.select( index - 1 );
 	}
 	}
 
 
+	/**
+	 * Marks item at a given index as selected.
+	 *
+	 * Handles selection cycling when passed index is out of bounds:
+	 * - if the index is lower than 0, it will select the last item,
+	 * - if the index is higher than the last item index, it will select the first item.
+	 *
+	 * @param {Number} index Index of an item to be marked as selected.
+	 */
 	select( index ) {
 	select( index ) {
 		let indexToGet = 0;
 		let indexToGet = 0;
 
 
@@ -71,6 +99,11 @@ export default class MentionsView extends View {
 		const item = this.listView.items.get( indexToGet );
 		const item = this.listView.items.get( indexToGet );
 		item.highlight();
 		item.highlight();
 
 
+		// Scroll the mentions view to the selected element.
+		if ( !this._isItemVisibleInScrolledArea( item ) ) {
+			this.element.scrollTop = item.element.offsetTop;
+		}
+
 		if ( this.selected ) {
 		if ( this.selected ) {
 			this.selected.removeHighlight();
 			this.selected.removeHighlight();
 		}
 		}
@@ -78,7 +111,19 @@ export default class MentionsView extends View {
 		this.selected = item;
 		this.selected = item;
 	}
 	}
 
 
+	/**
+	 * Triggers the `execute` event on the {@link #select selected} item.
+	 */
 	executeSelected() {
 	executeSelected() {
 		this.selected.fire( 'execute' );
 		this.selected.fire( 'execute' );
 	}
 	}
+
+	// Checks if an item is visible in the scrollable area.
+	//
+	// The item is considered visible when:
+	// - its top boundary is inside the scrollable rect
+	// - its bottom boundary is inside the scrollable rect (the whole item must be visible)
+	_isItemVisibleInScrolledArea( item ) {
+		return new Rect( this.element ).contains( new Rect( item.element ) );
+	}
 }
 }

+ 7 - 0
packages/ckeditor5-mention/tests/manual/mention.js

@@ -30,6 +30,13 @@ ClassicEditor
 		mention: {
 		mention: {
 			feeds: [
 			feeds: [
 				{ feed: [ 'Barney', 'Lily', 'Marshall', 'Robin', 'Ted' ] },
 				{ feed: [ 'Barney', 'Lily', 'Marshall', 'Robin', 'Ted' ] },
+				{
+					marker: '#',
+					feed: [
+						'a01', 'a02', 'a03', 'a04', 'a05', 'a06', 'a07', 'a08', 'a09', 'a10',
+						'a11', 'a12', 'a13', 'a14', 'a15', 'a16', 'a17', 'a18', 'a19', 'a20'
+					]
+				}
 			]
 			]
 		}
 		}
 	} )
 	} )

+ 13 - 7
packages/ckeditor5-mention/tests/manual/mention.md

@@ -4,13 +4,19 @@ The minimal mention configuration with a static list of autocomplete feed:
 
 
 ### Configuration
 ### Configuration
 
 
-The feed:
-
-- Barney
-- Lily
-- Marshall
-- Robin
-- Ted
+The feeds:
+
+1. Static list with `@` marker:
+    - Barney
+    - Lily
+    - Marshall
+    - Robin
+    - Ted
+2. Static list of 20 items (`#` marker)
+    - a01
+    - a02
+    - ... 
+    - a20
 
 
 ### Interaction
 ### Interaction
 
 

+ 69 - 0
packages/ckeditor5-mention/tests/mentionui.js

@@ -218,6 +218,75 @@ describe( 'MentionUI', () => {
 				} );
 				} );
 		} );
 		} );
 
 
+		describe( 'static list with large set of results', () => {
+			const bigList = {
+				marker: '@',
+				feed: [
+					'a01', 'a02', 'a03', 'a04', 'a05', 'a06', 'a07', 'a08', 'a09', 'a10', 'a11', 'a12'
+				]
+			};
+
+			beforeEach( () => {
+				return createClassicTestEditor( { feeds: [ bigList ] } );
+			} );
+
+			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 );
+					} );
+			} );
+
+			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', () => {
 		describe( 'static list with default trigger', () => {
 			beforeEach( () => {
 			beforeEach( () => {
 				return createClassicTestEditor( staticConfig );
 				return createClassicTestEditor( staticConfig );

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

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