Mention Support in EJ2 TypeScript Markdown Editor Control

16 May 202522 minutes to read

By integrating the Mention control with the Markdown Editor, users can effortlessly mention or tag other users or objects from a suggested list. This eliminates the need to manually type out names or identifying information, improving both efficiency and accuracy.

Enabling Mention in EJ2 TypeScript Markdown Editor

To enable the Mention functionality within the Markdown Editor, set the target property of the Mention control to the ID of the textarea element inside the editor. When specifying the target, ensure that you append the suffix _editable-content to the ID. This configuration allows users to mention or tag others from the suggested list while editing text.

When a user types the @ symbol followed by a character, the Markdown Editor displays a list of suggestions. Users can select an item from the list by either clicking on it or typing the desired name.

Configuring Mention Properties

The Syncfusion Mention control provides several customizable properties to enhance the tagging experience:

  • allowSpaces - Allow to continue search action if user enter space after mention character while searching.
  • suggestionCount - Defines the maximum number of items displayed in the suggestion list.
  • itemTemplate - Customizes the appearance of items in the suggestion list.

Example: Implementing Mention in Markdown Editor

The following example demonstrates how to enable Mention support in the Markdown Editor.

import {
  createElement,
  enableRipple,
  KeyboardEventArgs,
} from '@syncfusion/ej2-base';
enableRipple(true);

import {
  RichTextEditor,
  Link,
  Image,
  MarkdownEditor,
  Table,
  Toolbar,
} from '@syncfusion/ej2-richtexteditor';
import Marked from 'marked';
import { Mention } from '@syncfusion/ej2-dropdowns';

RichTextEditor.Inject(Link, Image, MarkdownEditor, Table, Toolbar);

let textArea: HTMLTextAreaElement;
let mdsource: HTMLElement;

let markdownMention: RichTextEditor = new RichTextEditor({
  value: `Hello [@Maria](mailto:[email protected])\n\nWelcome to the mention integration with markdown editor demo. Type @ character and tag user from the suggestion list.`,
  height: 250,
  editorMode: 'Markdown',
  toolbarSettings: {
    items: [
      'Bold',
      'Italic',
      'StrikeThrough',
      '|',
      'Formats',
      'Blockquote',
      'OrderedList',
      'UnorderedList',
      'SuperScript',
      'SubScript',
      '|',
      'CreateLink',
      'Image',
      'CreateTable',
      '|',
      {
        tooltipText: 'Preview',
        template:
          '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn" aria-label="Preview Code">' +
          '<span class="e-btn-icon e-md-preview e-icons"></span></button>',
      },
      '|',
      'Undo',
      'Redo',
    ],
  },
  created: () => {
    textArea =
      markdownMention.contentModule.getEditPanel() as HTMLTextAreaElement;
    textArea.addEventListener('keyup', (e: KeyboardEventArgs) => {
      markDownConversion();
    });
    mdsource = document.getElementById('preview-code');
    mdsource.addEventListener('click', (e: MouseEvent) => {
      fullPreview();
      if ((e.currentTarget as HTMLElement).classList.contains('e-active')) {
        markdownMention.disableToolbarItem([
          'Bold',
          'Italic',
          'StrikeThrough',
          'OrderedList',
          'UnorderedList',
          'SuperScript',
          'SubScript',
          'CreateLink',
          'Image',
          'CreateTable',
          'Formats',
          'Blockquote',
          'Undo',
          'Redo',
        ]);
      } else {
        markdownMention.enableToolbarItem([
          'Bold',
          'Italic',
          'StrikeThrough',
          'OrderedList',
          'UnorderedList',
          'SuperScript',
          'SubScript',
          'CreateLink',
          'Image',
          'CreateTable',
          'Formats',
          'Blockquote',
          'Undo',
          'Redo',
        ]);
      }
    });
  },
});
markdownMention.appendTo('#markdownMention');

const emailData: MentionUser[] = [
  {
    name: 'Selma Rose',
    initial: 'SR',
    email: '[email protected]',
    color: '#FAFDFF',
    bgColor: '#01579B',
  },
  {
    name: 'Maria',
    initial: 'MA',
    email: '[email protected]',
    color: '#004378',
    bgColor: '#ADDBFF',
  },
  {
    name: 'Russo Kay',
    initial: 'RK',
    email: '[email protected]',
    color: '#F9DEDC',
    bgColor: '#8C1D18',
  },
  {
    name: 'Robert',
    initial: 'RO',
    email: '[email protected]',
    color: '#FFD6F7',
    bgColor: '#37003A',
  },
  {
    name: 'Camden Kate',
    initial: 'CK',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#464ECF',
  },
  {
    name: 'Garth',
    initial: 'GA',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#008861',
  },
  {
    name: 'Andrew James',
    initial: 'AJ',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#53CA17',
  },
  {
    name: 'Olivia',
    initial: 'OL',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#8C1D18',
  },
  {
    name: 'Sophia',
    initial: 'SO',
    email: '[email protected]',
    color: '#000000',
    bgColor: '#D0BCFF',
  },
  {
    name: 'Margaret',
    initial: 'MA',
    email: '[email protected]',
    color: '#000000',
    bgColor: '#F2B8B5',
  },
  {
    name: 'Ursula Ann',
    initial: 'UA',
    email: '[email protected]',
    color: '#000000',
    bgColor: '#47ACFB',
  },
  {
    name: 'Laura Grace',
    initial: 'LG',
    email: '[email protected]',
    color: '#000000',
    bgColor: '#FFE088',
  },
  {
    name: 'Albert',
    initial: 'AL',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#00335B',
  },
  {
    name: 'William',
    initial: 'WA',
    email: '[email protected]',
    color: '#FFFFFF',
    bgColor: '#163E02',
  },
];
const mention: Mention = new Mention({
  dataSource: emailData as unknown as { [key: string]: Object }[],
  fields: { text: 'name' },
  displayTemplate: '[@${name}](mailto:${email})',
  itemTemplate: '#editorMentionListTemplate',
  popupWidth: '250px',
  popupHeight: '200px',
  sortOrder: 'Ascending',
  target: markdownMention.inputElement,
  allowSpaces: true,
});
mention.appendTo('#editorMention');

function markDownConversion(): void {
  if (mdsource.classList.contains('e-active')) {
    let id: string = markdownMention.getID() + 'html-view';
    let htmlPreview: HTMLElement = markdownMention.element.querySelector(
      '#' + id
    );
    htmlPreview.innerHTML = Marked(
      (markdownMention.contentModule.getEditPanel() as HTMLTextAreaElement)
        .value
    );
  }
}
function fullPreview(): void {
  let id: string = markdownMention.getID() + 'html-preview';
  let htmlPreview: HTMLElement = markdownMention.element.querySelector(
    '#' + id
  );
  let previewTextArea: HTMLElement = markdownMention.element.querySelector(
    '.e-rte-content'
  ) as HTMLElement;
  if (mdsource.classList.contains('e-active')) {
    mdsource.classList.remove('e-active');
    mdsource.parentElement.title = 'Preview';
    textArea.style.display = 'block';
    htmlPreview.style.display = 'none';
    previewTextArea.style.overflow = 'hidden';
  } else {
    mdsource.classList.add('e-active');
    if (!htmlPreview) {
      htmlPreview = createElement('div', {
        className: 'e-content e-pre-source',
      });
      htmlPreview.id = id;
      textArea.parentNode.appendChild(htmlPreview);
      previewTextArea.style.overflow = 'auto';
    }
    if (previewTextArea.style.overflow === 'hidden') {
      previewTextArea.style.overflow = 'auto';
    }
    textArea.style.display = 'none';
    htmlPreview.style.display = 'block';
    htmlPreview.innerHTML = Marked(
      (markdownMention.contentModule.getEditPanel() as HTMLTextAreaElement)
        .value
    );
    mdsource.parentElement.title = 'Code View';
  }
}
interface MentionUser {
  name: string;
  initial: string;
  email: string;
  color: string;
  bgColor: string;
}
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Essential JS 2 Rich Text Editor</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="description" content="Typescript UI Controls" />
    <meta name="author" content="Syncfusion" />
    <link href="index.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-base/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-richtexteditor/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-inputs/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-lists/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-navigations/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-popups/styles/material.css" rel="stylesheet" />
     <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-buttons/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
    <link href="https://cdn.syncfusion.com/ej2/31.2.12/ej2-dropdowns/styles/material.css" rel="stylesheet"/>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
    <script src="systemjs.config.js"></script>
   
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>

<body>
    <div id='loader'>Loading....</div>
    <div id='container'>
        <div id="editorMention"></div>
        <div id="markdownMention">  
    </div>
    <style>
        .e-richtexteditor textarea.e-content {
            float: left;
        }
        .e-richtexteditor .e-rte-content {
            overflow: hidden;
        }
        .e-icon-btn.e-active .e-md-preview::before {
            content: '\e350';
        }
        .e-icon-btn .e-md-preview::before {
            content: '\e345';
        }
            .bootstrap4 .e-icon-btn.e-active .e-md-preview::before {
            content: '\e790';
        }

        .bootstrap4 .e-icon-btn .e-md-preview::before {
            content: '\e787';
        }

        .tailwind .e-icon-btn.e-active .e-md-preview::before,
        .tailwind-dark .e-icon-btn.e-active .e-md-preview::before,
        .tailwind3 .e-icon-btn.e-active .e-md-preview::before,
        .tailwind3-dark .e-icon-btn.e-active .e-md-preview::before,
        .fluent .e-icon-btn.e-active .e-md-preview::before,
        .fluent-dark .e-icon-btn.e-active .e-md-preview::before,
        .fluent2 .e-icon-btn.e-active .e-md-preview::before,
        .fluent2-dark .e-icon-btn.e-active .e-md-preview::before,
        .fluent2-highcontrast .e-icon-btn.e-active .e-md-preview::before,
        .bootstrap5 .e-icon-btn.e-active .e-md-preview::before,
        .bootstrap5-dark .e-icon-btn.e-active .e-md-preview::before,
        .material3 .e-icon-btn.e-active .e-md-preview::before,
        .material3-dark .e-icon-btn.e-active .e-md-preview::before {
            content: '\e80e';
        }

        .tailwind .e-icon-btn .e-md-preview::before,
        .tailwind-dark .e-icon-btn .e-md-preview::before,
        .tailwind3 .e-icon-btn .e-md-preview::before,
        .tailwind3-dark .e-icon-btn .e-md-preview::before,
        .bootstrap5 .e-icon-btn .e-md-preview::before,
        .bootstrap5-dark .e-icon-btn .e-md-preview::before,
        .fluent .e-icon-btn .e-md-preview::before,
        .fluent-dark .e-icon-btn .e-md-preview::before,
        .fluent2 .e-icon-btn .e-md-preview::before,
        .fluent2-dark .e-icon-btn .e-md-preview::before,
        .fluent2-highcontrast .e-icon-btn .e-md-preview::before,
        .material3 .e-icon-btn .e-md-preview::before,
        .material3-dark .e-icon-btn .e-md-preview::before {
            content: '\e7de';
        }

        .editor-mention-item-template {
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: row;
            align-items: center;
        }

        .em-content {
            display: flex;
            flex-direction: column;
            justify-content: center;
        }

        .em-avatar {
            width: 32px;
            height: 32px;
            text-align: center;
            border-radius: 50%;
            font-size: 12px;
            font-weight: 500;
            text-indent: 0px;
            line-height: 13px;
            color: #fff;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .em-name {
            color: rgb(16, 24, 40);
            font-size: 14px;
            font-weight: 400;
            line-height: 14px;
            margin-bottom: 5px;
        }

        .em-email {
            color: gray;
            font-size: 12px;
            font-weight: 400;
            line-height: 14px;
        }

        #markdownMention_editable-content_popup li {
            padding: 10px;
            height: 60px;
        }

        body[class*="-dark"] .em-name {
            color: #fff !important;
        }
    </style>
</body>

</html>