3

In this code, I am trying to insert a code block using react-quilljs

import React, { useState } from 'react';
import hljs from 'highlight.js';

import { useQuill } from 'react-quilljs';

import 'quill/dist/quill.snow.css'; // Add css for snow theme

export default () => {

  hljs.configure({
    languages: ['javascript', 'ruby', 'python', 'rust'],
  });

  const theme = 'snow';

  const modules = {
    toolbar: [['code-block']],
    syntax: {
      highlight: (text) => hljs.highlightAuto(text).value,
    },
   
  };

  const placeholder = 'Compose an epic...';

  const formats = ['code-block'];

  const { quill, quillRef } = useQuill({
    theme,
    modules,
    formats,
    placeholder,
  });
  

  const [content, setContent] = useState('');

  React.useEffect(() => {
    if (quill) {
      quill.on('text-change', () => {
        setContent(quill.root.innerHTML);
      });
    }
  }, [quill]);

  const submitHandler = (e) => {};

  return (
    <div style={{ width: 500, height: 300 }}>
      <div ref={quillRef} />

      <form onSubmit={submitHandler}>
        <button type='submit'>Submit</button>
      </form>
      {quill && (
        <div
          className='ql-editor'
          dangerouslySetInnerHTML={{ __html: content }}
        />
      )}
    </div>
  );
};

Using the above code, I get the following preview of the editor's content

enter image description here

There are two problems with this:

  1. There is no code syntax highlighting, as I want to achieve this using the highlihgt.js package, inside the code block inside the editor, and

  2. The code block is not displayed (with the black background and highlighting syntax when it's working) in the previewing div outside the editor.

How can I fix these two issues?

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
BlackMath
  • 932
  • 7
  • 24

4 Answers4

2

Look at the css in the editor mode. It depends on two class names ql-snow and ql-editor. You can fix this issue by wrapping it around one more div with className ql-snow.

<div className='ql-snow'> 
   <div className='ql-editor' dangerouslySetInnerHTML={{ __html: content }}> 
   <div/> 
</div>

This should work.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Krish
  • 31
  • 4
1

Your code is getting marked up by highlight.js with CSS classes:

<span class="hljs-keyword">const</span>

You are not seeing the impact of those CSS classes because you don't have a stylesheet loaded to handle them. You need to choose the theme that you want from the available styles and import the corresponding stylesheet.

import 'highlight.js/styles/darcula.css';
Linda Paiste
  • 38,446
  • 6
  • 64
  • 102
  • Thanks. That fixed the highlight problem in both the editor and the preview, but I still have the 2nd problem, which is the code block in the previewing div doesn't have a black background as it appears in the editor. How can I fix this issue? – BlackMath Apr 11 '21 at 19:54
  • I'm not sure. You need to look around in the docs for quill and highlight.js. The weird thing is that if I enter `document.querySelector('.ql-editor')?.innerHTML` in the browser console I get the formatted results. But if I put it inside the on change text function then I don't? – Linda Paiste Apr 11 '21 at 20:30
  • Weird! Also, blockquotes aren't reflected in the preview, but they appear inside the editor. – BlackMath Apr 12 '21 at 02:18
0

I got the same issue and when I used hjls what happened was that I got syntax highlighting in the editor but not in the value. If you noticed the syntax gets highlighted some seconds after you write the code block, this means that the value gets set before the syntax gets highlighted. So, I just set the value after 2 seconds using setTimeout and this solved my problem

Like this:

<ReactQuill
  theme="snow"
  value={value}
  onChange={(content) => {
    setTimeout(() => {
      setValue(content)
    }, 2000)
  }}
  modules={modules}
  formats={formats}
  bounds="#editor"
  placeholder="Write something..."
  className="text-black dark:text-white"
/>
bigfoot
  • 59
  • 2
  • 14
0

I recently implemented this logic into my project. I used React Quill for the text editor, implemented syntax highlighting to it using highlight.js, and I also used React Markdown to display the formatted content on my website. React Markdown by default works with markdown, so you need a plugin (rehype-raw) to get it to parse HTML. This is my code, from my project. Just remove some of the unnecessary stuff from here that is specific to my project and use what you need.

// PLUGINS IMPORTS //
import { Typography } from "@mui/material";
import { useEffect, useState } from "react";
import hljs from "highlight.js";
import "react-quill/dist/quill.core.css";
import "react-quill/dist/quill.snow.css";
import "highlight.js/styles/atom-one-dark.css";
import ReactQuill from "react-quill";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";

// COMPONENTS IMPORTS //
import { CreateButton } from "components/atoms";

// EXTRA IMPORTS //
import styles from "./create-post.module.css";
import { ETheme } from "types/theme";

/////////////////////////////////////////////////////////////////////////////

type CreatePostProps = {};

hljs.configure({
  // optionally configure hljs
  languages: ["javascript", "python", "c", "c++", "java", "HTML", "css", "matlab"],
});

const toolbarOptions = [
  ["bold", "italic", "underline", "strike"],
  ["blockquote", "code-block"],

  [{ list: "ordered" }, { list: "bullet" }],
  ["link"],
  [{ indent: "-1" }, { indent: "+1" }],

  [{ header: [1, 2, 3, 4, 5, 6, false] }],

  [{ align: [] }],
];
const modules = {
  syntax: {
    highlight: function (text: string) {
      return hljs.highlightAuto(text).value;
    },
  },
  toolbar: toolbarOptions,
  clipboard: {
    // toggle to add extra line breaks when pasting HTML:
    matchVisual: false,
  },
};

const formats = [
  "header",
  "font",
  "size",
  "bold",
  "italic",
  "underline",
  "strike",
  "blockquote",
  "code-block",
  "list",
  "bullet",
  "indent",
  "link",
  "align",
];

const placeholder = "Description";

const CreatePost = (props: CreatePostProps) => {
  const [markdownText, setMarkdownText] = useState("");
  useEffect(() => {
    console.log({ markdownText });
  }, [markdownText]);
  return (
    <main className={`${styles["create-post-wrapper"]}`}>
      <header className={`${styles["create-post-header"]}`}>
        <Typography variant="h6">Create a post</Typography>
      </header>
      {/* making the border a seperate div makes it easier to apply margin */}
      <div className={`${styles["border"]} ${styles["top"]}`}></div>
      <div>Choose a community</div>
      <article className={`${styles["create-post"]}`}>
        <section className={`${styles["inner-create-post"]}`}>
          <section>Title</section>
          <ReactQuill
            value={markdownText}
            onChange={value => setMarkdownText(value)}
            theme="snow"
            modules={modules}
            formats={formats}
            placeholder={placeholder}
          />
          <div className="ql-snow">
            <div className="ql-editor">
              <ReactMarkdown children={markdownText} rehypePlugins={[rehypeRaw]} />
            </div>
          </div>
          <div className={`${styles["border"]} ${styles["bottom"]}`}></div>
          <section className={`${styles["post-button"]}`}>
            <CreateButton
              theme={ETheme.LIGHT}
              buttonText="Post"
              buttonProps={{
                fullWidth: false,
              }}
            />
          </section>
        </section>
      </article>
    </main>
  );
};

export default CreatePost;

You can always add more options to toolbarOptions, but don't forget to also add them to formats if you do. Also, if you want to keep formatting anywhere else in your website, you need the two divs with these 2 classes around your markdown.

React Quill bacially saves everything into a string with HTML and classes, you import styles for those classes and it works like magic.

Elco
  • 97
  • 1
  • 7