question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

TOAST UI Editor `3.0.0-alpha.0` is released!

See original GitHub issue

TOAST UI Editor new alpha version is released!

TOAST UI Editor 3.0.0-alpha.0 is released. As previously shared on the roadmap, v3.0 has improved many things, including model abstraction, custom syntax support, and plug-in systems. To this end, all new structural changes were made, and it took quite a long time. In this process, we wrote all new code using typescript to gain the advantage of static type checking and prevent potential bugs. Here is the summary of some of the biggest changes in v3.0.👇

Model Abstraction(with ProseMirror)

we select a Prosmirror for Model Abstraction. Because Prosmirror provides the schema to customize and provides DOM manipulation as a high-level API, it has greatly aided the maintenance of editor code and the possibility of various feature extensions.

  • Based on markdown, internal node schema were created and managed, and structurally command, keymap, and so on, making it easier to understand features and rendering structures.
  • We could remove a large amount of pre/post processing code for the DOM manipulation in the WYSIWYG editor.
  • This structural integration of core module has also eliminated many dependencies, resulting in a significant reduction in bundle capacity.(approximately 100 kb)
  • It is possible to support syntax such as custom blocks, which in turn allows plugin system to be provided as a more concise option.

Below is an example of ListItem node. We define nodes in the form of abstracted model, and we can easily read related commands and keymaps in same domain.

export class ListItem extends NodeSchema {
  get name() {
    return 'listItem';
  }

  get schema() {
    return {
      content: 'paragraph listGroup*',
      attrs: {
        task: { default: false },
        checked: { default: false },
        rawHTML: { default: null },
      },
      defining: true,
      parseDOM: [
        // ...
      ],
      toDOM({ attrs }: ProsemirrorNode): DOMOutputSpecArray {
        // ...

        return [
          'li',
          {
            class: classNames.join(' '),
            'data-task': task,
            ...(checked && { 'data-task-checked': checked }),
          },
          0,
        ];
      },
    };
  }

  commands(): EditorCommand {
    // ...
  }

  keymaps() {
    return {
      Enter: this.commands()(),
    };
  }
}

Plugin Improvement

The previous method of creating plugins by accessing the editor’s internal properties or by directly manipulating DOM complicates the code and creates high level of coupling between the editor and the plugin. We removed all of these dependencies and changed the plugin to work with only a few specific information of the editor. In doing so, the plugin separated to operate with minimal information about the editor. In particular, codes that customize the toolbar or manipulate WYSIWYG’s nodes can be defined much more concisely in plug-ins. If you look at the newly changed color picker or merge table plugin, you can see that it is much simpler and more readable.

Below is an example of a color picker plugin. The option format is more concise and declarative than before.

export default function colorSyntaxPlugin(
  context: PluginContext,
  options: PluginOptions = {}
): PluginInfo {
  // ...
  return {
    markdownCommands: {
      color: ({ selectedColor }, { tr, selection, schema }, dispatch) => {
        // ...
      },
    },
    wysiwygCommands: {
      color: ({ selectedColor }, { tr, selection, schema }, dispatch) => {
        // ...
      },
    },
    toolbarItems: [
      {
        groupIndex: 0,
        itemIndex: 3,
        item: toolbarItem,
      },
    ],
    toHTMLRenderers: {
      htmlInline: {
        span(node: MdLikeNode, { entering }: Context) {
          return entering
            ? { type: 'openTag', tagName: 'span', attributes: node.attrs }
            : { type: 'closeTag', tagName: 'span' };
        },
      },
    },
  };
}

UI structure

The editor’s UI was often directly exposed to the outside, and internally it was very difficult to manage due to unnecessary inheritance structures. To improve this, we introduce a declarative UI that is being adopted by modern front-end frameworks like Vue, React. The spaghetti codes inside have been reduced much more concisely due to our declarative codes based on very small virtual DOM. I and my colleagues, who are already somewhat familiar with Vue, React, were able to easily expand the UI in this structure, which gave us quite a good experience.

Below is an example of a Link popup UI.

export class LinkPopupBody extends Component<Props> {
  // ...

  mounted() {
    this.initialize();
  }

  render() {
    return html`
      <div>
        <label for="toastuiLinkUrlInput">${i18n.get('URL')}</label>
        <input
          id="toastuiLinkUrlInput"
          type="text"
          ref=${(el: HTMLInputElement) => (this.refs.url = el)}
        />
        <label for="toastuiLinkTextInput">${i18n.get('Link text')}</label>
        <input
          id="toastuiLinkTextInput"
          type="text"
          ref=${(el: HTMLInputElement) => (this.refs.text = el)}
        />
        <div class="${cls('button-container')}">
          <button type="button" class="${cls('close-button')}" onClick=${this.props.hidePopup}>
            ${i18n.get('Cancel')}
          </button>
          <button type="button" class="${cls('ok-button')}" onClick=${this.execCommand}>
            ${i18n.get('OK')}
          </button>
        </div>
      </div>
    `;
  }
}

Document

Currently, documents have not yet been prepared for our new alpha version. We will update the migration guide and tutorial guide sequentially. First, a description of custom block and widget nodes can be seen briefly as below.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:25
  • Comments:11 (5 by maintainers)

github_iconTop GitHub Comments

6reactions
LKHcodingcommented, Jun 8, 2021

Is both editor and viewer available in Next.js(ssr) with this update? I’m really looking forward to it.

2reactions
js87zzcommented, Jun 4, 2021

@stfenjobs @ats1999 React wrapper will probably be out on official release(maybe next week or week after next). Thanks!

Read more comments on GitHub >

github_iconTop Results From Across the Web

[Release News] TOAST UI Editor 3.0 is here!
Let's take a look at the TOAST UI Editor 3.0's new changes. What Changed With 3.0? Core Module Replacement ➔ Lighter Editor. The...
Read more >
@toast-ui/editor-plugin-uml - npm
TOAST UI Editor : UML Plugin. Latest version: 3.0.1, last published: a year ago. Start using @toast-ui/editor-plugin-uml in your project by ...
Read more >
@toast-ui/editor NPM | npm.io
Check @toast-ui/editor 3.2.1 package - Last release 3.2.1 with MIT licence at our NPM packages aggregator and search engine.
Read more >
Editor | TOAST UI :: Make Your Web Delicious!
TOAST UI Editor provides Markdown mode and WYSIWYG mode. Depending on the type of use you want like production of Markdown or maybe...
Read more >
Unity Hub Release Notes - Unity
Download the Unity Hub to install Unity Editor versions and manage your ... 3.0.0. Jan. 10, 2022. What's new. The release of Hub...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found