|
|
@@ -46,6 +46,10 @@ ClassicEditor
|
|
|
|
|
|
The minimal configuration of a mention requires defining a feed and optionally a marker (if not using the default `@` character).
|
|
|
|
|
|
+Below is an example of fully customize mention feature.
|
|
|
+
|
|
|
+{@snippet features/mention-customization}
|
|
|
+
|
|
|
### Providing the feed
|
|
|
|
|
|
The {@link module:mention/mention~MentionFeed#feed} can be provided as:
|
|
|
@@ -63,18 +67,18 @@ The mention feature does not limit items displayed in the mention suggestion lis
|
|
|
|
|
|
```js
|
|
|
const items = [
|
|
|
- { id: '1', name: 'Barney Stinson', username: 'swarley' },
|
|
|
- { id: '2', name: 'Lily Aldrin', username: 'lilypad' },
|
|
|
- { id: '3', name: 'Marshall Eriksen', username: 'marshmallow' },
|
|
|
- { id: '4', name: 'Robin Scherbatsky', username: 'rsparkles' },
|
|
|
- { id: '5', name: 'Ted Mosby', username: 'tdog' }
|
|
|
- ];
|
|
|
+ { id: '1', name: 'Barney Stinson', username: 'swarley', link: 'https://www.imdb.com/title/tt0460649/characters/nm0000439' },
|
|
|
+ { id: '2', name: 'Lily Aldrin', username: 'lilypad', link: 'https://www.imdb.com/title/tt0460649/characters/nm0004989' },
|
|
|
+ { id: '3', name: 'Marshall Eriksen', username: 'marshmallow', link: 'https://www.imdb.com/title/tt0460649/characters/nm0781981' },
|
|
|
+ { id: '4', name: 'Robin Scherbatsky', username: 'rsparkles', link: 'https://www.imdb.com/title/tt0460649/characters/nm1130627' },
|
|
|
+ { id: '5', name: 'Ted Mosby', username: 'tdog', link: 'https://www.imdb.com/title/tt0460649/characters/nm1102140' }
|
|
|
+];
|
|
|
|
|
|
function getFeedItems( feedText ) {
|
|
|
- // As an example of asynchronous action return a promise that resolves after a 100ms timeout.
|
|
|
+ // As an example of asynchronous action return a promise that resolves after a 100ms timeout.
|
|
|
return new Promise( resolve => {
|
|
|
setTimeout( () => {
|
|
|
- resolve( items.filter( isItemMathing ) );
|
|
|
+ resolve( items.filter( isItemMatching ) );
|
|
|
}, 100 );
|
|
|
} );
|
|
|
|
|
|
@@ -85,13 +89,12 @@ function getFeedItems( feedText ) {
|
|
|
|
|
|
// Include an item in the search results if name or username includes the current user input.
|
|
|
return textIncludesSearchSting( item.name, searchString ) || textIncludesSearchSting( item.username, searchString );
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
function textIncludesSearchSting( text, searchString ) {
|
|
|
return text.toLowerCase().includes( searchString );
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
```
|
|
|
|
|
|
### Customizing the auto-complete list
|
|
|
@@ -115,7 +118,7 @@ function customItemRenderer( item ) {
|
|
|
|
|
|
### Customizing the output
|
|
|
|
|
|
-In order to have full control over the markup generated by the editor you can overwrite the conversion process. To do that you must specify both {@link module:engine/conversion/upcastdisatcher upcast} and {@link module:engine/conversion/downcastdisatcher downcast} converters.
|
|
|
+In order to have full control over the markup generated by the editor you can overwrite the conversion process. To do that you must specify both {@link module:engine/conversion/upcastdisatcher~UpcastDispatcher upcast} and {@link module:engine/conversion/downcastdisatcher~DowncastDispatcher downcast} converters.
|
|
|
|
|
|
```js
|
|
|
import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
|
|
|
@@ -123,79 +126,88 @@ import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
|
|
|
// The link plugin using highest priority in conversion pipeline.
|
|
|
const HIGHER_THEN_HIGHEST = priorities.highest + 50;
|
|
|
|
|
|
-// The upcast converter will convert <a class="mention"> elements to the model 'mention' attribute.
|
|
|
-editor.conversion.for( 'upcast' ).elementToAttribute( {
|
|
|
- view: {
|
|
|
- name: 'a',
|
|
|
- key: 'data-mention',
|
|
|
- classes: 'mention',
|
|
|
- attributes: {
|
|
|
- href: true
|
|
|
+ClassicEditor
|
|
|
+ .create( document.querySelector( '#editor' ), {
|
|
|
+ plugins: [ Mention, CustomMention, ... ], // Add custom mention plugin function.
|
|
|
+ mention: {
|
|
|
+ // configuration...
|
|
|
}
|
|
|
- },
|
|
|
- model: {
|
|
|
- key: 'mention',
|
|
|
- value: viewItem => {
|
|
|
- // Optionally: do not convert partial mentions.
|
|
|
- if( !isFullMention(viewItem)){
|
|
|
- return;
|
|
|
+ } )
|
|
|
+ .then( ... )
|
|
|
+ .catch( ... );
|
|
|
+
|
|
|
+function CustomMention( editor ) {
|
|
|
+ // The upcast converter will convert <a class="mention"> elements to the model 'mention' attribute.
|
|
|
+ editor.conversion.for( 'upcast' ).elementToAttribute( {
|
|
|
+ view: {
|
|
|
+ name: 'a',
|
|
|
+ key: 'data-mention',
|
|
|
+ classes: 'mention',
|
|
|
+ attributes: {
|
|
|
+ href: true,
|
|
|
+ 'data-user-id': true
|
|
|
}
|
|
|
-
|
|
|
- // The mention feature expects that mention attribute value in the model is a plain object:
|
|
|
- const mentionValue = {
|
|
|
- // The name attribute is required by mention editing.
|
|
|
- name: viewItem.getAttribute( 'data-mention' ),
|
|
|
- // Add any other properties as required.
|
|
|
- link: viewItem.getAttribute( 'href' ),
|
|
|
- id: viewItem.getAttribute( 'data-user-id' )
|
|
|
- };
|
|
|
-
|
|
|
- return mentionValue;
|
|
|
- }
|
|
|
- },
|
|
|
- converterPriority: HIGHER_THEN_HIGHEST
|
|
|
-} );
|
|
|
+ },
|
|
|
+ model: {
|
|
|
+ key: 'mention',
|
|
|
+ value: viewItem => {
|
|
|
+ // Optionally: do not convert partial mentions.
|
|
|
+ if ( !isFullMention( viewItem ) ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // The mention feature expects that mention attribute value in the model is a plain object:
|
|
|
+ const mentionValue = {
|
|
|
+ // The name attribute is required by mention editing.
|
|
|
+ name: viewItem.getAttribute( 'data-mention' ),
|
|
|
+ // Add any other properties as required.
|
|
|
+ link: viewItem.getAttribute( 'href' ),
|
|
|
+ id: viewItem.getAttribute( 'data-user-id' )
|
|
|
+ };
|
|
|
+
|
|
|
+ return mentionValue;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ converterPriority: HIGHER_THEN_HIGHEST
|
|
|
+ } );
|
|
|
|
|
|
-function isFullMention( viewElement ) {
|
|
|
- const textNode = viewElement.getChild( 0 );
|
|
|
+ function isFullMention( viewElement ) {
|
|
|
+ const textNode = viewElement.getChild( 0 );
|
|
|
+ const dataMention = viewElement.getAttribute( 'data-mention' );
|
|
|
|
|
|
- // Do not parse empty mentions.
|
|
|
- if ( !textNode || !textNode.is( 'text' ) ) {
|
|
|
- return;
|
|
|
- }
|
|
|
+ // Do not parse empty mentions.
|
|
|
+ if ( !textNode || !textNode.is( 'text' ) ) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- const mentionString = textNode.data;
|
|
|
+ const mentionString = textNode.data;
|
|
|
|
|
|
- // Assume that mention is set as marker + mention name.
|
|
|
- const marker = mentionString.slice( 0, 1 );
|
|
|
- const name = mentionString.slice( 1 );
|
|
|
+ // Assume that mention is set as marker + mention name.
|
|
|
+ const name = mentionString.slice( 1 );
|
|
|
|
|
|
- // Do not upcast partial mentions - might come from copy-paste of partially selected mention.
|
|
|
- if ( name != dataMention ) {
|
|
|
- return;
|
|
|
+ // Do not upcast partial mentions - might come from copy-paste of partially selected mention.
|
|
|
+ return name == dataMention;
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
-
|
|
|
-// Don't forget to define a downcast converter as well:
|
|
|
-
|
|
|
-editor.conversion.for( 'downcast' ).attributeToElement( {
|
|
|
- model: 'mention',
|
|
|
- view: ( modelAttributeValue, viewWriter ) => {
|
|
|
- if ( !modelAttributeValue ) {
|
|
|
- // Do not convert empty attributes.
|
|
|
- return;
|
|
|
- }
|
|
|
+ // Don't forget to define a downcast converter as well:
|
|
|
+ editor.conversion.for( 'downcast' ).attributeToElement( {
|
|
|
+ model: 'mention',
|
|
|
+ view: ( modelAttributeValue, viewWriter ) => {
|
|
|
+ if ( !modelAttributeValue ) {
|
|
|
+ // Do not convert empty attributes.
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- return viewWriter.createAttributeElement( 'a', {
|
|
|
- class: 'mention',
|
|
|
- 'data-mention': modelAttributeValue.name,
|
|
|
- 'data-user-id': modelAttributeValue.id,
|
|
|
- 'href': modelAttributeValue.link
|
|
|
- } );
|
|
|
- },
|
|
|
- converterPriority: HIGHER_THEN_HIGHEST
|
|
|
-} );
|
|
|
+ return viewWriter.createAttributeElement( 'a', {
|
|
|
+ class: 'mention',
|
|
|
+ 'data-mention': modelAttributeValue.name,
|
|
|
+ 'data-user-id': modelAttributeValue.id,
|
|
|
+ 'href': modelAttributeValue.link
|
|
|
+ } );
|
|
|
+ },
|
|
|
+ converterPriority: HIGHER_THEN_HIGHEST
|
|
|
+ } );
|
|
|
+}
|
|
|
```
|
|
|
|
|
|
## Common API
|