mention-customization.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* globals ClassicEditor, console, window, document, setTimeout */
  6. import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';
  7. ClassicEditor
  8. .create( document.querySelector( '#snippet-mention-customization' ), {
  9. cloudServices: CS_CONFIG,
  10. extraPlugins: [ MentionCustomization ],
  11. toolbar: {
  12. items: [
  13. 'heading', '|', 'bold', 'italic', '|', 'undo', 'redo'
  14. ],
  15. viewportTopOffset: window.getViewportTopOffsetConfig()
  16. },
  17. mention: {
  18. feeds: [
  19. {
  20. marker: '@',
  21. feed: getFeedItems,
  22. itemRenderer: customItemRenderer
  23. }
  24. ]
  25. }
  26. } )
  27. .then( editor => {
  28. window.editor = editor;
  29. } )
  30. .catch( err => {
  31. console.error( err.stack );
  32. } );
  33. function MentionCustomization( editor ) {
  34. // The upcast converter will convert view <a class="mention" href="" data-user-id="">
  35. // elements to the model 'mention' text attribute.
  36. editor.conversion.for( 'upcast' ).elementToAttribute( {
  37. view: {
  38. name: 'a',
  39. key: 'data-mention',
  40. classes: 'mention',
  41. attributes: {
  42. href: true,
  43. 'data-user-id': true
  44. }
  45. },
  46. model: {
  47. key: 'mention',
  48. value: viewItem => {
  49. // The mention feature expects that the mention attribute value
  50. // in the model is a plain object with a set of additional attributes.
  51. // In order to create a proper object use the toMentionAttribute() helper method:
  52. const mentionAttribute = editor.plugins.get( 'Mention' ).toMentionAttribute( viewItem, {
  53. // Add any other properties that you need.
  54. link: viewItem.getAttribute( 'href' ),
  55. userId: viewItem.getAttribute( 'data-user-id' )
  56. } );
  57. return mentionAttribute;
  58. }
  59. },
  60. converterPriority: 'high'
  61. } );
  62. // Downcast the model 'mention' text attribute to a view <a> element.
  63. editor.conversion.for( 'downcast' ).attributeToElement( {
  64. model: 'mention',
  65. view: ( modelAttributeValue, { writer } ) => {
  66. // Do not convert empty attributes (lack of value means no mention).
  67. if ( !modelAttributeValue ) {
  68. return;
  69. }
  70. return writer.createAttributeElement( 'a', {
  71. class: 'mention',
  72. 'data-mention': modelAttributeValue.id,
  73. 'data-user-id': modelAttributeValue.userId,
  74. 'href': modelAttributeValue.link
  75. }, {
  76. // Make mention attribute to be wrapped by other attribute elements.
  77. priority: 20,
  78. // Prevent merging mentions together.
  79. id: modelAttributeValue.uid
  80. } );
  81. },
  82. converterPriority: 'high'
  83. } );
  84. }
  85. const items = [
  86. { id: '@swarley', userId: '1', name: 'Barney Stinson', link: 'https://www.imdb.com/title/tt0460649/characters/nm0000439' },
  87. { id: '@lilypad', userId: '2', name: 'Lily Aldrin', link: 'https://www.imdb.com/title/tt0460649/characters/nm0004989' },
  88. { id: '@marshmallow', userId: '3', name: 'Marshall Eriksen', link: 'https://www.imdb.com/title/tt0460649/characters/nm0781981' },
  89. { id: '@rsparkles', userId: '4', name: 'Robin Scherbatsky', link: 'https://www.imdb.com/title/tt0460649/characters/nm1130627' },
  90. { id: '@tdog', userId: '5', name: 'Ted Mosby', link: 'https://www.imdb.com/title/tt0460649/characters/nm1102140' }
  91. ];
  92. function getFeedItems( queryText ) {
  93. // As an example of an asynchronous action, return a promise
  94. // that resolves after a 100ms timeout.
  95. // This can be a server request or any sort of delayed action.
  96. return new Promise( resolve => {
  97. setTimeout( () => {
  98. const itemsToDisplay = items
  99. // Filter out the full list of all items to only those matching the query text.
  100. .filter( isItemMatching )
  101. // Return 10 items max - needed for generic queries when the list may contain hundreds of elements.
  102. .slice( 0, 10 );
  103. resolve( itemsToDisplay );
  104. }, 100 );
  105. } );
  106. // Filtering function - it uses the `name` and `username` properties of an item to find a match.
  107. function isItemMatching( item ) {
  108. // Make the search case-insensitive.
  109. const searchString = queryText.toLowerCase();
  110. // Include an item in the search results if the name or username includes the current user input.
  111. return (
  112. item.name.toLowerCase().includes( searchString ) ||
  113. item.id.toLowerCase().includes( searchString )
  114. );
  115. }
  116. }
  117. function customItemRenderer( item ) {
  118. const itemElement = document.createElement( 'span' );
  119. itemElement.classList.add( 'custom-item' );
  120. itemElement.id = `mention-list-item-id-${ item.userId }`;
  121. itemElement.textContent = `${ item.name } `;
  122. const usernameElement = document.createElement( 'span' );
  123. usernameElement.classList.add( 'custom-item-username' );
  124. usernameElement.textContent = item.id;
  125. itemElement.appendChild( usernameElement );
  126. return itemElement;
  127. }