Slate.js with react does not display changes

327 Views Asked by At

I am learning Slate.js text editor for react and I have problem. When I for example press button to change text to <h1> nothing is changed in textarea, but when I inspect code in Elements panel of browser I can see changes (element changing from paragraph to h1). Don't know whats wrong. Code is pretty much taken from Slate.js example (simplified a bit). Using bold,code,italic works fine (MarkButton, ToggleMark, Leaf) only Block Elements are problematic (text alignment works fine tho). Here's my component:

import { SystemUpdateOutlined } from "@mui/icons-material";
import React, { useCallback, useMemo, useState } from "react";
import { Editor, Transforms, createEditor, Element } from "slate";
import { Slate, Editable, withReact } from "slate-react";

// Define our own custom set of helpers.

const LIST_TYPES = ["numbered-list", "bulleted-list"];
const TEXT_ALIGN_TYPES = ["left", "center", "right", "justify"];

const CustomEditor = {
  //------------------------------------------------------------------------

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

  //------------------------------------------------------------------------

  isBlockActive(editor, format, blockType = "type") {
    const { selection } = editor;
    if (!selection) return false;

    const [match] = Array.from(
      Editor.nodes(editor, {
        at: Editor.unhangRange(editor, selection),
        match: (n) =>
          !Editor.isEditor(n) &&
          Element.isElement(n) &&
          n[blockType] === format,
      })
    );
    return !!match;
  },

  //------------------------------------------------------------------------

  toggleMark(editor, format) {
    const isActive = CustomEditor.isMarkActive(editor, format);
    if (isActive) {
      Editor.removeMark(editor, format);
    } else {
      Editor.addMark(editor, format, true);
    }
  },

  //------------------------------------------------------------------------

  toggleBlock(editor, format) {
    const isActive = CustomEditor.isBlockActive(
      editor,
      format,
      TEXT_ALIGN_TYPES.includes(format) ? "align" : "type"
    );
    console.log(isActive);

    const isList = LIST_TYPES.includes(format);

    Transforms.unwrapNodes(editor, {
      match: (n) =>
        !Editor.isEditor(n) &&
        Element.isElement(n) &&
        LIST_TYPES.includes(n.type) &&
        !TEXT_ALIGN_TYPES.includes(format),
      split: true,
    });
    let newProperties;
    if (TEXT_ALIGN_TYPES.includes(format)) {
      newProperties = {
        ...newProperties,
        align: isActive ? undefined : format,
      };
    } else {
      newProperties = {
        ...newProperties,
        type: isActive ? "paragraph" : isList ? "list-item" : format,
      };
    }
    //Transforms.setNodes < Element > (editor, newProperties);
    Transforms.setNodes(editor, newProperties, {
      match: (n) => Editor.isBlock(editor, n) && Element.isElement(n),
    });

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

const initialValue = [
  {
    type: "paragraph",
    children: [{ text: "A line of text in a paragraph." }],
  },
];

const SlateTextEditor = () => {
  const [editor] = useState(() => withReact(createEditor()));
  const renderElement = useCallback((props) => <BlockElement {...props} />, []);
  const renderLeaf = useCallback((props) => <Leaf {...props} />, []);

  return (
    <Slate editor={editor} initialValue={initialValue}>
      <div>
        <MarkButton editor={editor} format="bold" icon="format_bold" />
        <MarkButton editor={editor} format="italic" icon="format_italic" />
        <MarkButton
          editor={editor}
          format="underline"
          icon="format_underlined"
        />
        <MarkButton editor={editor} format="code" icon="code" />
        <BlockButton editor={editor} format="heading-one" icon="looks_one" />
        <BlockButton editor={editor} format="heading-two" icon="looks_two" />
        <BlockButton editor={editor} format="block-quote" icon="format_quote" />
        <BlockButton
          editor={editor}
          format="numbered-list"
          icon="format_list_numbered"
        />
        <BlockButton
          editor={editor}
          format="bulleted-list"
          icon="format_list_bulleted"
        />
        <BlockButton editor={editor} format="left" icon="format_align_left" />
        <BlockButton
          editor={editor}
          format="center"
          icon="format_align_center"
        />
        <BlockButton editor={editor} format="right" icon="format_align_right" />
        <BlockButton
          editor={editor}
          format="justify"
          icon="format_align_justify"
        />
      </div>

      <Editable
        renderElement={renderElement}
        renderLeaf={renderLeaf}
        spellCheck
        autoFocus
        onKeyDown={(event) => {
          if (!event.ctrlKey) {
            return;
          }

          // Replace the `onKeyDown` logic with our new commands.
          switch (event.key) {
            case "`": {
              event.preventDefault();
              CustomEditor.toggleBlock(editor, "heading-one");
              break;
            }

            case "b": {
              event.preventDefault();
              CustomEditor.toggleMark(editor);
              break;
            }
          }
        }}
      />
    </Slate>
  );
};

//Buttons ----------------------------------------------------------------

const MarkButton = ({ editor, format, icon }) => {
  return (
    <button
      onMouseDown={(event) => {
        event.preventDefault();
        CustomEditor.toggleMark(editor, format);
      }}
      title={format}
    >
      <Icon>{icon}</Icon>
    </button>
  );
};

//-------------------------------------------------------------------------

const BlockButton = ({ editor, format, icon }) => {
  return (
    <button
      onMouseDown={(event) => {
        event.preventDefault();
        CustomEditor.toggleBlock(editor, format);
      }}
      title={format}
    >
      <Icon>{icon}</Icon>
    </button>
  );
};

//-------------------------------------------------------------------------

const Icon = ({ children }) => {
  return <span className="material-icons">{children}</span>;
};

//Leaf and Block elements -------------------------------------------------------------------------

const BlockElement = ({ attributes, children, element }) => {
  const style = { textAlign: element.align };
  switch (element.type) {
    case "block-quote":
      return (
        <blockquote style={style} {...attributes}>
          {children}
        </blockquote>
      );
    case "bulleted-list":
      return (
        <ul style={style} {...attributes}>
          {children}
        </ul>
      );
    case "heading-one":
      console.log("W hedingu");
      return (
        <h1 style={style} {...attributes}>
          {children}
        </h1>
      );
    case "heading-two":
      return (
        <h2 style={style} {...attributes}>
          {children}
        </h2>
      );
    case "list-item":
      return (
        <li style={style} {...attributes}>
          {children}
        </li>
      );
    case "numbered-list":
      return (
        <ol style={style} {...attributes}>
          {children}
        </ol>
      );
    default:
      return (
        <p style={style} {...attributes}>
          {children}
        </p>
      );
  }
};

//-------------------------------------------------------------------------

const Leaf = ({ attributes, children, leaf }) => {
  if (leaf.bold) {
    children = <strong>{children}</strong>;
  }

  if (leaf.code) {
    children = <code>{children}</code>;
  }

  if (leaf.italic) {
    children = <em>{children}</em>;
  }

  if (leaf.underline) {
    children = <u>{children}</u>;
  }

  return <span {...attributes}>{children}</span>;
};

//-------------------------------------------------------------------

export default SlateTextEditor;

Browser inspect

0

There are 0 best solutions below