directory

  • How to develop rich Text editor framework (A) Leaf, Text, Mark
  • How to develop a rich text editor framework (ii) — Understand selection
  • How to develop rich text editor framework (3) – Understand Decoration, Inline, Block, Document model
  • [How to develop rich text editor framework (4) — Implement redo, undo History]
  • How to develop a rich text editor framework (5) — Automatically validate content with Schema
  • How to Develop rich Text Editor Framework (6) — Highly extensible Plug-in System
  • How to develop rich Text Editor framework (7) — The Magic of Change
  • .

The body of the

The Leaf, Text and Mark models were introduced last time. Point, Range, Selection models.

Point

interface PointInterface {
    key: string;
    offset: number;
}
Copy the code

That’s a Point. A Range consists of two points, the starting Point and the ending Point. The key of a Point is the key of a Text Node. This key is maintained in a separate scope and is incremented. Each Node creates a key for the Node.

generate-key.ts

/** * add index * @type {Number}
 */
letn: number; /** * global key generation function ** @type {Function}
 */
letgenerate: () => string; /** * generates a key ** @return {String}
 */
function generateKey(): string {
    returngenerate(); } @param {Function} func */function setKeyGenerator(func): void { generate = func; } /** * reset key */function resetKeyGenerator(): void {
    n = 0;
    generate = () => `${n++}`; } /** * initialize */ resetKeyGenerator();export default generateKey;
export { setKeyGenerator, resetKeyGenerator };

Copy the code

Create a Text node

new Text({
    key: generateKey(),
    leaves: []
})
Copy the code

The offset of Point can be understood as the position offset relative to the text node. In my last article, I used the example of markdown.

Some are bold, some are italic.

The corresponding structure of this text is

Text({
    key: 1,
    leaves: [
        Leaf({
            text: 'Available text',
            marks: []
        }),
        Leaf({
            text: 'bold',
            marks: ['bold']
        }),
        Leaf({
            text: ',有的',
            marks: []
        }),
        Leaf({
            text: 'italics',
            marks: ['italics']})]})Copy the code

At this point, some students have a demand, need to bold before the text is also bold. So how do we get this position?

The Range and Selection

Note: Range is a Selection, and Selection is a Selection. Dragging text from front to back is different from dragging text from back to front, but the range is the same.

Interface RangeInterface {start: PointInterface, // end: PointInterface // endpoint} interface SelectionInterface {anchor: PointInterface; // focus: PointInterface; // Cursor end point}Copy the code

If the selection is from back to front, the DOM node corresponding to anchor comes after, and the node corresponding to focus comes in front.

In general, a selection corresponds to only one range.

The Selection API was originally created by Netscape and allows multiple zones (for example, allowing users to select columns from

). However, browsers other than Gecko do not implement multiple zones, and the specification also requires that selected content always have (only) one scope (allowing multiple zones can cause unnecessary compatibility problems, such as typing from multiple places at once).

Returning to our example, the range of bold words is key = 1, offset 4 ~ 6; The range of text is offset 2 to 4.

We manually selected the text, which is a native browser operation, but how to tell the frame that you selected the text on the data. In fact, the framework listens for the onSelect event and updates the selection it maintains by calling the native method window.getSelection() in the event callback to get the dom information from the starting point. (See Selection MDN)

const window = getWindow(event.target); const { value } = change; const { document, schema } = value; const native = window.getSelection(); // Native selection // If there is no range, SLATE automatically loses focusif(! native.rangeCount) { change.blur();return; } // Convert the native range to SLATE's rangelet range = findRange(native, value);
if(! range)return; . . // Create Slate's selectionlet selection = document.createSelection(range);
selection = selection.setIsFocused(true);
Copy the code

Once the text is selected, the framework also knows from its data which range you selected. Now we’re going to start bolding.

Since Slate is based on the controlled Contennteditable implementation, it intercepts any events that break the DOM structure. Slate intercepts the default behavior in onKeyDown. We find the corresponding Text node according to the selection anchor and focus generated before, and then operate data (objects and arrays that everyone knows). Finally, the new state is handed over to Slate to update the render.

That’s about it. If anything is wrong or not detailed enough, you can point it out in the comments and I’ll fix it.

Resousces

  • Selection mdn
  • Range mdn