chat-with-mentions.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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 console, window, document */
  6. import ClassicEditor from '@ckeditor/ckeditor5-build-classic/src/ckeditor';
  7. import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
  8. import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough';
  9. import Mention from '@ckeditor/ckeditor5-mention/src/mention';
  10. import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';
  11. ClassicEditor
  12. .create( document.querySelector( '.chat__editor' ), {
  13. cloudServices: CS_CONFIG,
  14. extraPlugins: [ Mention, MentionLinks, Underline, Strikethrough ],
  15. toolbar: {
  16. items: [
  17. 'bold', 'italic', 'underline', 'strikethrough', '|', 'link', '|', 'undo', 'redo'
  18. ],
  19. viewportTopOffset: window.getViewportTopOffsetConfig()
  20. },
  21. mention: {
  22. feeds: [
  23. {
  24. marker: '@',
  25. feed: [
  26. { id: '@cflores', avatar: 'm_1', name: 'Charles Flores' },
  27. { id: '@gjackson', avatar: 'm_2', name: 'Gerald Jackson' },
  28. { id: '@wreed', avatar: 'm_3', name: 'Wayne Reed' },
  29. { id: '@lgarcia', avatar: 'm_4', name: 'Louis Garcia' },
  30. { id: '@rwilson', avatar: 'm_5', name: 'Roy Wilson' },
  31. { id: '@mnelson', avatar: 'm_6', name: 'Matthew Nelson' },
  32. { id: '@rwilliams', avatar: 'm_7', name: 'Randy Williams' },
  33. { id: '@ajohnson', avatar: 'm_8', name: 'Albert Johnson' },
  34. { id: '@sroberts', avatar: 'm_9', name: 'Steve Roberts' },
  35. { id: '@kevans', avatar: 'm_10', name: 'Kevin Evans' },
  36. { id: '@mwilson', avatar: 'w_1', name: 'Mildred Wilson' },
  37. { id: '@mnelson', avatar: 'w_2', name: 'Melissa Nelson' },
  38. { id: '@kallen', avatar: 'w_3', name: 'Kathleen Allen' },
  39. { id: '@myoung', avatar: 'w_4', name: 'Mary Young' },
  40. { id: '@arogers', avatar: 'w_5', name: 'Ashley Rogers' },
  41. { id: '@dgriffin', avatar: 'w_6', name: 'Debra Griffin' },
  42. { id: '@dwilliams', avatar: 'w_7', name: 'Denise Williams' },
  43. { id: '@ajames', avatar: 'w_8', name: 'Amy James' },
  44. { id: '@randerson', avatar: 'w_9', name: 'Ruby Anderson' },
  45. { id: '@wlee', avatar: 'w_10', name: 'Wanda Lee' }
  46. ],
  47. itemRenderer: customItemRenderer
  48. },
  49. {
  50. marker: '#',
  51. feed: [
  52. '#american', '#asian', '#baking', '#breakfast', '#cake', '#caribbean',
  53. '#chinese', '#chocolate', '#cooking', '#dairy', '#delicious', '#delish',
  54. '#dessert', '#desserts', '#dinner', '#eat', '#eating', '#eggs', '#fish',
  55. '#food', '#foodgasm', '#foodie', '#foodporn', '#foods', '#french', '#fresh',
  56. '#fusion', '#glutenfree', '#greek', '#grilling', '#halal', '#homemade',
  57. '#hot', '#hungry', '#icecream', '#indian', '#italian', '#japanese', '#keto',
  58. '#korean', '#lactosefree', '#lunch', '#meat', '#mediterranean', '#mexican',
  59. '#moroccan', '#nom', '#nomnom', '#paleo', '#poultry', '#snack', '#spanish',
  60. '#sugarfree', '#sweet', '#sweettooth', '#tasty', '#thai', '#vegan',
  61. '#vegetarian', '#vietnamese', '#yum', '#yummy'
  62. ]
  63. }
  64. ]
  65. }
  66. } )
  67. .then( editor => {
  68. window.editor = editor;
  69. // Clone the first message in the chat when "Send" is clicked, fill it with new data
  70. // and append to the chat list.
  71. document.querySelector( '.chat-send' ).addEventListener( 'click', () => {
  72. const clone = document.querySelector( '.chat__posts li' ).cloneNode( true );
  73. clone.classList.add( 'new-post' );
  74. clone.querySelector( 'img' ).src = '../../assets/img/m_0.jpg';
  75. clone.querySelector( 'strong' ).textContent = 'CKEditor User';
  76. const mailtoUser = clone.querySelector( '.chat__posts__post__mailto-user' );
  77. mailtoUser.textContent = '@ckeditor';
  78. mailtoUser.href = 'mailto:info@cksource.com';
  79. clone.querySelector( '.chat__posts__post__time' ).textContent = 'just now';
  80. clone.querySelector( '.chat__posts__post__content' ).innerHTML = editor.getData();
  81. document.querySelector( '.chat__posts' ).appendChild( clone );
  82. } );
  83. } )
  84. .catch( err => {
  85. console.error( err.stack );
  86. } );
  87. /*
  88. * This plugin customizes the way mentions are handled in the editor model and data.
  89. * Instead of a classic <span class="mention"></span>,
  90. */
  91. function MentionLinks( editor ) {
  92. // The upcast converter will convert a view
  93. //
  94. // <a href="..." class="mention" data-mention="...">...</a>
  95. //
  96. // element to the model "mention" text attribute.
  97. editor.conversion.for( 'upcast' ).elementToAttribute( {
  98. view: {
  99. name: 'a',
  100. key: 'data-mention',
  101. classes: 'mention',
  102. attributes: {
  103. href: true
  104. }
  105. },
  106. model: {
  107. key: 'mention',
  108. value: viewItem => editor.plugins.get( 'Mention' ).toMentionAttribute( viewItem )
  109. },
  110. converterPriority: 'high'
  111. } );
  112. // Downcast the model "mention" text attribute to a view
  113. //
  114. // <a href="..." class="mention" data-mention="...">...</a>
  115. //
  116. // element.
  117. editor.conversion.for( 'downcast' ).attributeToElement( {
  118. model: 'mention',
  119. view: ( modelAttributeValue, { writer } ) => {
  120. // Do not convert empty attributes (lack of value means no mention).
  121. if ( !modelAttributeValue ) {
  122. return;
  123. }
  124. let href;
  125. // User mentions are downcasted as mailto: links. Tags become normal URLs.
  126. if ( modelAttributeValue.id[ 0 ] === '@' ) {
  127. href = `mailto:${ modelAttributeValue.id.slice( 1 ) }@example.com`;
  128. } else {
  129. href = `https://example.com/social/${ modelAttributeValue.id.slice( 1 ) }`;
  130. }
  131. return writer.createAttributeElement( 'a', {
  132. class: 'mention',
  133. 'data-mention': modelAttributeValue.id,
  134. href
  135. }, {
  136. // Make mention attribute to be wrapped by other attribute elements.
  137. priority: 20,
  138. // Prevent merging mentions together.
  139. id: modelAttributeValue.uid
  140. } );
  141. },
  142. converterPriority: 'high'
  143. } );
  144. }
  145. /*
  146. * Customizes the way the list of user suggestions is displayed.
  147. * Each user has an @id, a name and an avatar.
  148. */
  149. function customItemRenderer( item ) {
  150. const itemElement = document.createElement( 'span' );
  151. const avatar = document.createElement( 'img' );
  152. const userNameElement = document.createElement( 'span' );
  153. const fullNameElement = document.createElement( 'span' );
  154. itemElement.classList.add( 'mention__item' );
  155. avatar.src = `../../assets/img/${ item.avatar }.jpg`;
  156. userNameElement.classList.add( 'mention__item__user-name' );
  157. userNameElement.textContent = item.id;
  158. fullNameElement.classList.add( 'mention__item__full-name' );
  159. fullNameElement.textContent = item.name;
  160. itemElement.appendChild( avatar );
  161. itemElement.appendChild( userNameElement );
  162. itemElement.appendChild( fullNameElement );
  163. return itemElement;
  164. }