2

I have created an editor from composing a couple of slates examples together, namely https://www.slatejs.org/examples/richtext and https://www.slatejs.org/examples/links

However when I added these the active state for all block level nodes it doesn't work at all, meaning I can't toggle list items on and off, anchor links end up getting nested etc.

The broken code seems to be these lines, coming from isBlockActive function in all the examples:

const isBlockActive = (editor, format) => {
    const [match] = Editor.nodes(editor, {
        match: n => {
            console.log('n.type', n.type);
            return n.type === format;
        },
    });

    return !!match;
};

match is always undefined no matter where my cursor is located.

I am running latest on all the packages currently 0.58.1.

Below is my toggleBlock function I also took from the examples that uses the isBlockActive function to work out the toggling logic.

const toggleBlock = (editor, format) => {
    const isActive = isBlockActive(editor, format);
    const isList = LIST_TYPES.includes(format);

    Transforms.unwrapNodes(editor, {
        match: n => LIST_TYPES.includes(n.type),
        split: true,
    });

    Transforms.setNodes(editor, {
        type: isActive ? 'paragraph' : isList ? 'list-item' : format,
    });

    if (!isActive && isList) {
        const block = { type: format, children: [] };
        Transforms.wrapNodes(editor, block);
    }
};

Has anyone run into this problem before, perhaps the codebase is not in sync with the examples and Editor.nodes isn't recommended anymore?

All inline formatting options work as the example uses a different approach:

const isMarkActive = (editor, format) => {
    const marks = Editor.marks(editor);
    return marks ? marks[format] === true : false;
};

Also here is my toolbar and renderElement functions if it helps:

<Slate editor={editor} value={value} onChange={handleChange}>
  <div className={styles.toolbar}>
    <MarkButton format="bold" icon="bold" />
    <MarkButton format="italic" icon="italic" />
    <MarkButton format="underline" icon="underline" />
    <MarkButton format="code" icon="code" />
    <BlockButton format="heading-one" icon="h1" />
    <BlockButton format="heading-two" icon="h2" />
    <BlockButton format="heading-three" icon="h3" />
    <BlockButton format="quote" icon="quote-left" />
    <BlockButton format="numbered-list" icon="list-ol" />
    <BlockButton format="bulleted-list" icon="list-ul" />
    <BlockButton format="break" icon="horizontal-rule" />
    <LinkButton />
  </div>
  ...
const Element = ({ attributes, children, element }) => {
    switch (element.type) {
        case 'quote':
            return <blockquote {...attributes}>{children}</blockquote>;
        case 'code':
            return (
                <pre>
                    <code {...attributes}>{children}</code>
                </pre>
            );
        case 'heading-one':
            return <h1 {...attributes}>{children}</h1>;
        case 'heading-two':
            return <h2 {...attributes}>{children}</h2>;
        case 'heading-three':
            return <h3 {...attributes}>{children}</h3>;
        case 'heading-four':
            return <h4 {...attributes}>{children}</h4>;
        case 'heading-five':
            return <h5 {...attributes}>{children}</h5>;
        case 'heading-six':
            return <h6 {...attributes}>{children}</h6>;
        case 'list-item':
            return <li {...attributes}>{children}</li>;
        case 'numbered-list':
            return <ol {...attributes}>{children}</ol>;
        case 'bulleted-list':
            return <ul {...attributes}>{children}</ul>;
        case 'link':
            return (
                <a {...attributes} href={element.url}>
                    {children}
                </a>
            );
        default:
            return <p {...attributes}>{children}</p>;
    }
};
Joe Methven
  • 518
  • 4
  • 15

1 Answers1

3

Editor.nodes() returns an iterator.

You'll need to change the isBlockActive function to:

const isBlockActive = (editor, format) => {
  const nodes = Editor.nodes(editor, {
    match: n => n.type === format,
  })
  return !!nodes.next().value
}
atng
  • 56
  • 2
  • Hey @atng thanks for the answer, I got to this point and I wasn't sure why, after checking the source code, it seems that if universal flag (as an option of Editor.nodes) is set to true, it returns the iterator, otherwise it returns the value. Said that I have an app in gatsby using typescript plugin and it returns a iterator, but then this codesandbox doesn't: https://codesandbox.io/s/magical-breeze-gwtb0?file=/src/App.tsx – Jepser Bernardino Oct 20 '20 at 15:40