ianstormtaylor / slate

A completely customizable framework for building rich text editors. (Currently in beta.)
http://slatejs.org
MIT License
30.02k stars 3.26k forks source link

Hardcoded text node inside normalizeNode method #4701

Open NEWESTERS opened 3 years ago

NEWESTERS commented 3 years ago

Description There is following blocks of code inside editor's normalizeNode method:

// Ensure that block and inline nodes have at least one text child.
if (Element.isElement(node) && node.children.length === 0) {
    const child = { text: '' }; // <-- Problem here
    Transforms.insertNodes(editor, child, {
        at: path.concat(0),
        voids: true,
    });
    return;
}

and

// Ensure that inline nodes are surrounded by text nodes.
if (editor.isInline(child)) {
    if (prev == null || !Text.isText(prev)) {
        const newChild = { text: '' }; // <-- Problem here
        Transforms.insertNodes(editor, newChild, {
            at: path.concat(n),
            voids: true,
        });
        n++;
    } else if (isLast) {
        const newChild = { text: '' }; // <-- Problem here
        Transforms.insertNodes(editor, newChild, {
            at: path.concat(n + 1),
            voids: true,
        });
        n++;
    }
}

Text node structure is hardcoded with only one text field, but i want to add some properties and declare interface like this:

interface Text {
      text: string;
      bold: boolean;
}

This is correct according to docs (https://docs.slatejs.org/concepts/02-nodes#text).

Here i expect bold property to contain true or false, but sometimes i receive undefined. It's okay for boolean, but i might want to create non-boolean property:

interface Text {
      text: string;
      bold: boolean;
      fontFamily: string; // <- single source of truth for default font family
}

This is not feature request but bug, because of logical conflict between example in docs and real implementation.

Expectation I expect ability to override text nodes creation inside Slate, for example:

function createCustomTextNode(text: string): Text {
      return { text, bold: false, fontFamily: "Helvetica" }
}

function withCustomTextNode(editor: Editor): Editor {
      editor.createTextNode = createCustomTextNode;
      return editor;
}

And Slate methods should use this method instead of hardcoded values.

Environment

Workaround I've found current workaround for this problem with overriding editor's apply method:

import { Editor, Text } from 'slate';

function createCustomTextNode(text: string): Text {
      return { text, bold: false, fontFamily: "Helvetica" }
}

export function withCustomTextNode(editor: Editor): Editor {
    const { apply } = editor;

    editor.apply = (operation) => {
        if (
            operation.type === 'insert_node' &&
            Text.isText(operation.node) &&
            operation.node.text.length === 0
        ) {
            apply({
                ...operation,
                node: createCustomTextNode("")
            });
        } else {
            apply(operation);
        }
    };

    return editor;
}
PangYiMing commented 2 years ago

emm.. that's right,You should override the editor's apply method. I think can close the bug.