Without further ado, let’s look at the architecture

Still come some nonsense (everybody see officer beg don’t hit me, don’t close first, still a little dry goods)

Hello, this is merry the pig, after these days of learning outcomes, everything comes to him who waits, team boss finally passed the open examination (we are interested in can also join ha, directly add group of official documentation, old convention examination to you), members of the team have a chance to become open source wangEditor, have stronger way, Haha so share my joy with you. But before to join the team, a lot of knowledge I don’t know, such as ts (are useless, in the actual work are oneself peacetime XJB make a play), test automation (jest, etc., are also oneself XJB make a play), the team’s document very serious very detailed, eldest brother want to be familiar with all kinds of specification before I joined (including source structure, development and specification, test specification, etc.), So for the source code structure, I looked at the menu-related code (architecture as shown above), so this article was born

Menu architecture Analysis

Before analysis, we should first understand the Menu of our products

It’s never a bad idea to look at the document first. Take a screenshot of the document

What’s a Button, a DropList, a Panel?

Listen to me. You’ll get it right away. Give me a couple of examples

  • Bold function – click the button, directly trigger the effect, the selected word rub on the bold (here will not show the picture after all, click on the trigger function) – BtnMenu

  • Title function – drop down menu appears when mouse moves up, there are H1 ~ H6, the menu disappears after mouse moves away – DropListMenu

  • Insert picture – click and a box pops up, and then the user can operate foolishly – PanelMenu

So far, we know the wangEditor product. There are three types of menus, which are easy to understand

Let’s talk briefly about class inheritance

From the diagram, we can clearly see that all menu classes inherit menus of the three parent types, simply speaking

  • Bold menu – inherit BtnMenu
  • Title Menu – Inherit DropListMenu
  • Insert picture menu – Inherit PanelMenu

These three menus then inherit the largest parent, Menu

A few words about interfaces

Interfaces are a specification, and we found several of them in the architecture diagram

  • Methods must be implemented after menuactive-implementstryChangeActive
    • For example, if you have rich text with bold font and no bold font, when you click the cursor on the bold font, the bold menu will not be highlighted, but when you click on the bold font, the menu will be highlighted, so it must be for every menuimplements MenuActiveAnd,tryChangeActiveMethod name see name know meaning, big guy structure cow force!

There are a lot of details did not say, first to casually post a source code for everyone to see, analyze the source code

First take a look at the largest parent class, the source code is as follows

/ * * *@description Menu class parent *@author wangfupeng* /

import { DomElement } from '.. /.. /utils/dom-core'
import Editor from '.. /.. /editor/index'
import Panel from './Panel'

export interface MenuActive {
    /** * Change the menu activation status, whether the menu is highlighted */
    tryChangeActive(): void
}

class Menu {
    public key: string | undefined
    public $elem: DomElement
    public editor: Editor
    private _active: boolean // Whether the menu is active. For example, when a bold text is selected, the bold menu is active (that is, highlighted).

    constructor($elem: DomElement, editor: Editor) {
        this.$elem = $elem
        this.editor = editor
        this._active = false

        // Bind menu click events
        $elem.on('click'.(e: Event) = > {
            Panel.hideCurAllPanels() // Hide all current panels

            // Trigger the menu click hook
            editor.txt.eventHooks.menuClickEvents.forEach(fn= > fn())

            e.stopPropagation()
            if (editor.selection.getRange() == null) {
                return
            }
            this.clickHandler(e)
        })
    }

    /** * menu click event, subclass can be overridden *@param e event
     */
    protected clickHandler(e: Event): void {}

    /** * Activate menu, highlight */
    protected active(): void {
        this._active = true
        this.$elem.addClass('w-e-active')}/** * Deactivates, no longer highlights */
    protected unActive(): void {
        this._active = false
        this.$elem.removeClass('w-e-active')}/** * Whether to activate */
    public get isActive() {
        return this._active
    }
}

export default Menu
Copy the code

The notes are so detailed that I feel I don’t even need to explain them. The specific explanation is put in the bold menu of the sun Tzu class

The source code for the bold menu is as follows

/ * * *@description Bold *@author wangfupeng* /

import BtnMenu from '.. /menu-constructors/BtnMenu'
import $ from '.. /.. /utils/dom-core'
import Editor from '.. /.. /editor/index'
import { MenuActive } from '.. /menu-constructors/Menu'

class Bold extends BtnMenu implements MenuActive {
    constructor(editor: Editor) {
        const $elem = $(
            ` < div class = "w - e - the menu data -" title = "bold" > < I class = "w - e - icon - bold" > < / I > < / div > `
        )
        super($elem, editor)
    }

    /** * click event */
    public clickHandler(): void {
        const editor = this.editor
        const isSelectEmpty = editor.selection.isSelectionEmpty()

        if (isSelectEmpty) {
            // The selection range is empty, insert and select a "blank"
            editor.selection.createEmptyRange()
        }

        // Run bold
        editor.cmd.do('bold')

        if (isSelectEmpty) {
            // Need to fold the selection range
            editor.selection.collapseRange()
            editor.selection.restoreSelection()
        }
    }

    /** * Try to change menu activation status */
    public tryChangeActive(): void {
        const editor = this.editor
        if (editor.cmd.queryCommandState('bold')) {
            this.active()
        } else {
            this.unActive()
        }
    }
}

export default Bold
Copy the code

Ridicule: once upon a time, I felt that joining the open source team and helping to write notes is also a contribution, and then the boss said that the official army was really fierce, notes so detailed and perfect, do not give the opportunity. Ha ha, it seems to be more valuable to be a contributor, without further ado, analysis of source code

  • Import import related, do not explain, do not know the small partner spank
  • Look at the source lineclass Bold extends BtnMenu implements MenuActive, inherit BtnMenu, MenuActive
  • Constructor code, not on the definition of the bold DOM element, because of the inheritance of BtnMenu, need to call super
constructor(editor: Editor) {
    const $elem = $(
        ` < div class = "w - e - the menu data -" title = "bold" > < I class = "w - e - icon - bold" > < / I > < / div > `
    )
    super($elem, editor)
}
Copy the code
  • Click on the event source code comment is too detailed, that’s what you do after you click, subclass overrides the parent class methodclickHandler
/** * click event */
public clickHandler(): void {
    const editor = this.editor
    const isSelectEmpty = editor.selection.isSelectionEmpty()

    if (isSelectEmpty) {
        // The selection range is empty, insert and select a "blank"
        editor.selection.createEmptyRange()
    }

    // Run bold
    editor.cmd.do('bold')

    if (isSelectEmpty) {
        // Need to fold the selection range
        editor.selection.collapseRange()
        editor.selection.restoreSelection()
    }
}
Copy the code
  • Implement tryChangeActive,queryCommandStateLook at the states, if this state is active or not, if this state is not active, some of you might ask, “Where do active and unActive come from?”, “Here, here, here, here, here, here, here, here, here, here, here, here, here, here, here, here, what does the largest parent of Menu do?
/** * Activate menu, highlight */
protected active(): void {
    this._active = true
    this.$elem.addClass('w-e-active')}/** * Deactivates, no longer highlights */
protected unActive(): void {
    this._active = false
    this.$elem.removeClass('w-e-active')}Copy the code

conclusion

Is the first look at the architecture diagram, in the more think, in the more look at the source code, this article only analyzed a macro, details did not say more, at most about the implementation of a bold menu, the rest of the source code we are interested in their own to see oh ~ continue to stick big guy’s architecture diagram, convenient for everyone to see more

Today’s water article to this, after the source code analysis will continue to produce, including the development of specifications, test specifications ~ thank you for your support