If you have any thoughts on my blog or articles and you want to let me know, you can either post a comment below(public) or tell me via this i_kkkp@163.com

Perfect Implementation of a Latex Rich Text Formula Editor Based on Vue3 and MathJax Rendering

Introduction

In a project, there was a need for a rich text editor with support for formula editing. However, many rich text editors lacked robust support for formula editing. To address this, I developed a small plugin using easy-formula-editor and wangeditor. It is a Latex rich text formula editor based on Vue3 and MathJax rendering. It supports easy formula editing for users with zero experience, allows customization of the editor’s configuration and style, supports re-editing of formulas, and can be used as a standalone plugin or integrated with rich text editors.

  • Easy formula editing for users with zero experience
  • Customizable editor configuration and style
  • Support for re-editing formulas
  • Can be used as a standalone plugin or integrated with rich text editors

MathJax

Installation and Usage

NPM

npm i easy-formula-editor

or

import formulaEditor from "easy-formula-editor";
const editor = new formulaEditor();
editor.create('#test');

CDN

<script type="text/javascript" src="../dist/formula-editor.min.js"></script>
<script type="text/javascript">
  const editor = new formulaEditor();
  editor.create('#test');
</script>

Export

// Latex formula
editor.latex.text()

// HTML formula
editor.$textSvgElem.html()

Extending Rich Text Editor Menu Bar

Registering Menus

[Note] It is recommended to use a global approach to register menus. If there are multiple editors with different custom menus, use the instance method to register menus.

Global Approach

// Menu key, each menu must be unique
const menuKey = 'alertMenuKey' 

// Register the menu
E.registerMenu(menuKey, AlertMenu)

const editor = new E('#div1')
editor.create()

const editor2 = new E('#div2')
editor2.create()

Instance Approach

// Menu key, each menu must be unique
const menuKey = 'alertMenuKey' 
const menuKey2 = 'alertMenuKey2'

const editor = new E('#div1')
// Register the menu
editor.menus.extend(menuKey, AlertMenu)

// Add the menu to editor.config.menus    const menuKey = 'alertMenuKey' 
// Menu order can also be adjusted through configuration, refer to the documentation on "Configuring Menus"    editor.config.menus.push(menuKey)
editor.config.menus = editor.config.menus.concat(menuKey)

// After registering the menu, create the editor, order matters!!
editor.create()

const editor2 = new E('#div2')
editor2.menus.extend(menuKey2, AlertMenu)
editor2.config.menus.push(menuKey2)
editor2.create()

Real Project Integration Example

import E from "wangeditor";
import formulaEditor from "easy-formula-editor";
import createPanelConf from "./create-panel-conf";

const { PanelMenu, Panel } = E;

class AlertMenu extends PanelMenu {
  constructor(editor) {
    // The data-title attribute indicates a tooltip for the button when the mouse hovers over it
    const $elem = E.$(
      `<div class="w-e-menu" data-title="Math Formula">
        <span>Formula</span>
      </div>`
    );
    super($elem, editor);
  }

  /**
   * Menu click event
   */
  clickHandler() {
    const formula = new formulaEditor();
    const conf = createPanelConf(this.editor, formula);
    const panel = new Panel(this, conf);
    panel.create();

    formula.create("#edit-content");
  }

  tryChangeActive() {}
}

const menuKey = "alertMenuKey";

// Register the menu
E.registerMenu(menuKey, AlertMenu);

export default E;
//create-panel-conf.ts
export default function (wangEditor, formulaEditor) {
    const btnOkId = 'btn-ok'
  
    /**
     * Insert formula
     */
    function insertFomule() {
      const formula = formulaEditor.latex.text()
      // Ensure that there are spaces on both sides when inserting into wangEditor, otherwise it may lead to focus issues
      wangEditor.txt.append('<p>'+formula+'</p>')
      return true
    }
  
    // Tabs configuration
    const tabsConf = [
      {
        // Tab title
        title: "Insert Math Formula",
        // Template
        tpl: `<div>
                <div id="edit-content"></div>
                <div class="w-e-button-container">
                  <button type="button" id="${btnOkId}" class="right">Insert</button>
                </div>
              </div>`,
        // Event bindings
        events: [
          // Insert formula
          {
            selector: '#' + btnOkId,
            type: 'click',
            fn: insertFomule,
            bindEnter: true,
          },
        ],
      }, // Tab end
    ]
  
    return {
        width: 660,
        height: 0,
        // Panel can contain multiple tabs
        tabs: tabsConf, // Tabs end
      }
}

Using the above code, you can add a formula editor menu to the rich text editor:

<template>
  <div class="formula-container">
    <v-card elevation="0" class="formula-card" title="Output Area" subtitle="Output">
      <div id="formula" class="formula-content">
        {{ renderedFormula ? `$${renderedFormula}$` : '' }}
      </div>
    </v-card>
    <div class="editor-area">
      <div id="wang-editor" class="editor"></div>
    </div>
  </div>
</template>


<script setup>
import E from "../utils/formula-menu-conf";
import { ref, onMounted, nextTick, defineProps, watchEffect } from "vue";

// Define props
const props = defineProps({
  initMessage: {
    type: String,
    default: "",
  }
});

watchEffect(() => {
  props.initMessage;
});

const editor = ref(null);
const renderedFormula = ref("");

function convert() {
  MathJax.texReset();
  MathJax.typesetClear();
  MathJax.typesetPromise();
}

function updateFormula() {
  renderedFormula.value = editor.value.txt.text();
  nextTick(convert);
}

onMounted(() => {
  editor.value = new E("#wang-editor");
  editor.value.config.height = 360;
  editor.value.config.menus = ['head', 'bold', 'underline', 'strikeThrough','emoticon', 'undo', 'redo'];
  editor.value.config.onchange = updateFormula;
  editor.value.config.onchangeTimeout = 500;
  editor.value.create();
  editor.value.txt.html(props.initMessage);
});


</script>


This setup allows seamless integration of the formula editor with a rich text editor, as depicted in the following image:

![MathJax](/img/mathjax/mathjax-2.png)

Here's a link to the official documentation for WangEditor: [www.wangeditor.com/v4](https://www.wangeditor.com/v4)

Feel free to reach out if you have any further questions or need additional clarification.
Documenting the Process of Submitting a PR to ElementUI Getting Started with React - Part 3

Comments