Procházet zdrojové kódy

Whitelist some characters to enable mention UI after them.

Maciej Gołaszewski před 6 roky
rodič
revize
90100e8d32

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

@@ -24,6 +24,8 @@ import MentionListItemView from './ui/mentionlistitemview';
 
 const VERTICAL_SPACING = 3;
 
+const IGNORED_CHARACTERS = ' ([{\'"';
+
 /**
  * The mention UI feature.
  *
@@ -523,7 +525,7 @@ function getBalloonPanelPositions( positionName ) {
 function createPattern( marker, minimumCharacters ) {
 	const numberOfCharacters = minimumCharacters == 0 ? '*' : `{${ minimumCharacters },}`;
 
-	return `(^| )(\\${ marker })([_a-zA-Z0-9À-ž]${ numberOfCharacters }?)$`;
+	return `(^| |${ IGNORED_CHARACTERS.split( '' ).join( '|\\' ) })(\\${ marker })([_a-zA-Z0-9À-ž]${ numberOfCharacters }?)$`;
 }
 
 // Creates a test callback for marker to be used in text watcher instance.

+ 95 - 2
packages/ckeditor5-mention/tests/manual/mention.js

@@ -12,16 +12,109 @@ import Mention from '../../src/mention';
 import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
 import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
 import Font from '@ckeditor/ckeditor5-font/src/font';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+
+import { toWidget, viewToModelPositionOutsideModelElement } from '@ckeditor/ckeditor5-widget/src/utils';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+class InlineWidget extends Plugin {
+	constructor( editor ) {
+		super( editor );
+
+		editor.model.schema.register( 'placeholder', {
+			allowWhere: '$text',
+			isObject: true,
+			isInline: true,
+			allowAttributes: [ 'type' ]
+		} );
+
+		editor.conversion.for( 'editingDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: ( modelItem, viewWriter ) => {
+				const widgetElement = createPlaceholderView( modelItem, viewWriter );
+
+				return toWidget( widgetElement, viewWriter );
+			}
+		} );
+
+		editor.conversion.for( 'dataDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: createPlaceholderView
+		} );
+
+		editor.conversion.for( 'upcast' ).elementToElement( {
+			view: 'placeholder',
+			model: ( viewElement, modelWriter ) => {
+				let type = 'general';
+
+				if ( viewElement.childCount ) {
+					const text = viewElement.getChild( 0 );
+
+					if ( text.is( 'text' ) ) {
+						type = text.data.slice( 1, -1 );
+					}
+				}
+
+				return modelWriter.createElement( 'placeholder', { type } );
+			}
+		} );
+
+		editor.editing.mapper.on(
+			'viewToModelPosition',
+			viewToModelPositionOutsideModelElement( editor.model, viewElement => viewElement.name == 'placeholder' )
+		);
+
+		this._createToolbarButton();
+
+		function createPlaceholderView( modelItem, viewWriter ) {
+			const widgetElement = viewWriter.createContainerElement( 'placeholder' );
+			const viewText = viewWriter.createText( '{' + modelItem.getAttribute( 'type' ) + '}' );
+
+			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewText );
+
+			return widgetElement;
+		}
+	}
+
+	_createToolbarButton() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		editor.ui.componentFactory.add( 'placeholder', locale => {
+			const buttonView = new ButtonView( locale );
+
+			buttonView.set( {
+				label: t( 'Insert placeholder' ),
+				tooltip: true,
+				withText: true
+			} );
+
+			this.listenTo( buttonView, 'execute', () => {
+				const model = editor.model;
+
+				model.change( writer => {
+					const placeholder = writer.createElement( 'placeholder', { type: 'placeholder' } );
+
+					model.insertContent( placeholder );
+
+					writer.setSelection( placeholder, 'on' );
+				} );
+			} );
+
+			return buttonView;
+		} );
+	}
+}
 
 ClassicEditor
 	.create( global.document.querySelector( '#editor' ), {
-		plugins: [ ArticlePluginSet, Underline, Font, Mention ],
+		plugins: [ ArticlePluginSet, Underline, Font, Mention, InlineWidget ],
 		toolbar: [
 			'heading',
 			'|', 'bulletedList', 'numberedList', 'blockQuote',
 			'|', 'bold', 'italic', 'underline', 'link',
 			'|', 'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor',
-			'|', 'insertTable',
+			'|', 'insertTable', 'placeholder',
 			'|', 'undo', 'redo'
 		],
 		image: {

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

@@ -502,6 +502,27 @@ describe( 'MentionUI', () => {
 					} );
 			} );
 
+			for ( const character of [ '(', '\'', '"', '[' ] ) {
+				it( `should show panel for matched marker after a "${ character }" character`, () => {
+					setData( model, '<paragraph>[] foo</paragraph>' );
+
+					model.change( writer => {
+						writer.insertText( character, doc.selection.getFirstPosition() );
+					} );
+
+					model.change( writer => {
+						writer.insertText( '@', doc.selection.getFirstPosition() );
+					} );
+
+					return waitForDebounce()
+						.then( () => {
+							expect( panelView.isVisible ).to.be.true;
+							expect( editor.model.markers.has( 'mention' ) ).to.be.true;
+							expect( mentionsView.items ).to.have.length( 5 );
+						} );
+				} );
+			}
+
 			it( 'should not show panel for marker in the middle of other word', () => {
 				setData( model, '<paragraph>foo[]</paragraph>' );