console.info

I am small spot, a rich text editor, today we talk about the basic components of this editor: components! In my world: Everything is a Component. From a large article to a small character, it is a component.


thinking

Three months ago, before alfy, my creator, wrote his first line of code, he thought about this question: How do you describe an article? Or: What constitutes an essay? Everyone has their own answer to this question: text, paragraphs, pictures, headings, tables, lists, etc. But software development requires rigorous logic, and further thinking leads to some questions:

  1. Text is composed of paragraphs and headings, not of articles.
  2. Lists and tables are what articles are made of, but what are they made of? The paragraph? The title? What about lists nested lists?
  3. About pictures: emojis and other pictures placed side by side with text, and occupying a row of pictures, obviously not the same category;
  4. Headings and paragraphs may look like two different types, but they behave the same in addition to their basic styles.

In software development, there is a rule:

Any complex problem can be broken down into small and simple components.

Therefore, before writing down the first line of code for small spots, ALfy took these problems, sorted out and extracted similar content, and finally came to the following type diagram, and thus concluded that the article is also a component, composed of content blocks.

While there are so many components in the image, there are two camps, abstract and representational, depending on whether they are abstract or not.

Let’s talk about abstract components.


Abstract component

Nearly half of these components are abstract components, and since a concrete component is a concrete representation of an abstract component, the meaning of an abstract component can be easily understood once the abstract component is fully understood.

Component

Component is the cornerstone of all components. Like the tinder in Transformers, Component is the basic building block of all components. Component gives them the following capabilities:

  1. Manage component style information;
  2. Manage the history stack when component content changes;

Also, as the base class for all components, Component specifies methods that the Component must implement, or properties that must be explicit:

  1. Determine the component type,typeProperties;
  2. implementationrenderMethod that specifies how the component should render itself;

Implementation: Component.

Inline

Inline represents a block within a line and is the smallest part of the line that the cursor can operate on: characters, emojis, and formulas (in the implementation) are derived from Inline classes.

The Inline class manages the relationship between Inline blocks and content blocks.

Implementation: Inline.

Block

A Block stands for a Block of content and is the smallest unit that makes up a collection. An article is a collection of blocks, as are lists and tables, and lists and tables are derived from blocks, so the structure of nested lists can be easily represented.

So what are the basic operations that content blocks need to do?

  1. Manage parent component information;
  2. Implement different content blocks (Block) between;
  3. Interaction with parent components: add, delete, change, check, etc.
  4. Interaction with sibling components, such as merging itself into the previous component (triggering deletion at the front of the component) or receiving the latter;

Depending on the division of labor, the Block component can be derived into three categories: PlainText, Collection, and Media.

Concrete implementation: Block.

PlainText

PlainText is a PlainText component whose content is plain characters that behave the same as the Code editor, deriving the Code component.

Implementation: PlainText.

Media

Media is a multimedia component and a concrete component that can generate content blocks of pictures, videos, and audio, realizing all the methods specified by Block.

Concrete implementation: Media.

Collection

A Collection stands for a Collection, which is a container for a series of components and controls the rendering of its children.

The main job of a container component is to add, delete, modify and check its children.

Depending on the type of child component, a collection component can be split into an Inline collection (ContentCollection) and a collection of blocks (StructureCollection).

Specific implementation: Collection.

ContentCollection

ContentCollection is a collection of Inline components that contain a string of text, emoticon images, and Inline formulas.

Derive header, paragraph, and table item components depending on the usage scenario.

Concrete implementation: ContentCollection.

StructureCollection

StructureCollection is a collection of Block components, and a complete article, list, etc., can be shown through coordination and nesting of StructureCollection with blocks.

Derive lists, tables, articles, and other components based on the scenario used.

Implementation: StructureCollection.


The concrete component

After step by step abstraction, abstract component derived from the concrete component has no need to write too much code to achieve the corresponding function, but there is one of the most important method, must be their own implementation: render.

The Render function is defined in the Component and is how the Component renders.

So how do we render? Simply generate Html? The goal is to generate articles in any environment, including but not limited to Markdown and Html, but there is only one render method, how to generate variable content?

Content generator, it’s time!


Content generator

There is a good saying in Taoist philosophy: to change should change!

Corresponding to the world of small spot, the structure of the article is unchanged, but the generated content is changed. Then how to generate different content with the same content?

Since components can’t render different results, why not outsource rendering to a professional team?

Check out the following code:

class XXX extends Component {
  render() {
    return getContentBuilder().buildArticle(
      this.id,
      this.conent, // represents the content of the component
      this.decorate.getStyle(), // Component style information
      this.decorate.getData() // The information carried by the component); }}Copy the code

Get the generator with getContentBuilder, tell the generator to get an Article, and then throw the contents of your own property at the generator. I don’t care what happens, wave your hand, and lie down and drink your tea!

**ps: ** Why not just throw the component at the generator? We all know that JS is a highly dynamic language, as long as the original object is obtained, you can play with this object, in order to ensure that the component is not modified, in order to maintain love and justice, the necessary content to generate components to the generator can! Peace in the world ~

Here you might be asking: why get the generator using the getContentBuilder method, and why not pass the generator as a parameter?

Yes, of course, yes, but imagine, if I need to render a Article, the actual call is the Article component, the other components call the render function in a hierarchical recursive way, of course, passing the generator into each component in turn can solve this problem, but the coupling is too high, not good for development and maintenance. In order to completely shut off the internal logic and rendering behavior of the component, it would be a bad idea to outsource the fetch generator again.

So there are actually two outsourcing (proxy) actions:

  1. Outsource the rendering action of the component to the generator, i.ebuildArticleThe part;
  2. Outsource the action of getting the generator to a namedgetContentBuilderMethods;

Now that we’re using double outsourcing, what’s the benefit of such a complicated concept? Let me tell you:

  1. Strictly separate the internal logic of the component and rendering, the structure of the article belongs to the structure, rendering belongs to the presentation, if there is a problem with rendering, it directly goes to the generator to find the problem;
  2. The same code can produce different results becausegetContentBuilderIs a method that can return different generators;
  3. The schema is fixed, the code is concise, and it is highly decoupled.
  4. A generator is also a class that allows developers to modify the presentation of a component by class inheritance, overloading, and so on, without paying attention to the internal logic of the component;

Several generators have been implemented:

  1. ContentBuilder: used to generate editableHtmlStructure, which is the core class of the editor, isHtmlEditable core;
  2. HtmlBuilder: Used to generate static stateHtmlText, generated text is not editable, plain textHtml
  3. MarkdownBuilder: Used to generate static stateMarkdownText;
  4. BaseBuilder: an abstract class of generators in which any generator must inherit and implement all methods;

The last

About the component part, that’s about it, too much detail can be interpreted as a difficulty, so that’s it for today. How the components are composed and rendered is not the most important, because ALfy has already done it. The most important thing is that two points about software development are mentioned in the article:

  1. Any complex problem can be broken down into small and simple components.
  2. How to be best with nothing? Agency is always the best answer!

Small spot is powerful because of clever use of these two points oh! I believe that if you can thoroughly get the essence of these two points, dealing with daily problems will be more handy oh ~

Ok, that’s the end of the class. I hope you can use it moreSpot code editorOh! Love you guys

I am Specky, I bring my own salt!