FIameCaster / prism-react-editor

Lightweight, extensible code editor component for React apps
MIT License
2 stars 1 forks source link

How to implement theme selector inside a react app? #1

Open zagoorland opened 1 month ago

zagoorland commented 1 month ago

Hey. I'm struggling to implement theme switcher using this library. Here's my code snippet:

import { Editor } from "prism-react-editor"
import { BasicSetup } from "prism-react-editor/setups"
import "prism-react-editor/prism/languages/jsx"
import "prism-react-editor/prism/languages/tsx"
import "prism-react-editor/languages"

import "prism-react-editor/layout.css"
// import "prism-react-editor/themes/github-dark.css"
// import "prism-react-editor/themes/github-light.css"
import { loadTheme } from "prism-react-editor/themes"

// Required by the basic setup
import "prism-react-editor/search.css"
import { useEffect, useLayoutEffect } from "react"

interface CodeEditorProps {
  language: string
  theme: string
}

export default function CodeEditor({ language, theme }: CodeEditorProps) {
  useEffect(() => {
    loadTheme("github-dark")
  }, [])

  return (
    <main className="grow overflow-auto github">
      <Editor
        language={language}
        value={`const foo: string = 'bar';\nfunction greet(name: string): string {\n  return 'Hello, ' + name;\n}\nconsole.log(greet('World'));\n${"console.log(foo);\n".repeat(100)}`} // Long content to test scrolling
        style={{
          height: "100%",
          fontSize: "16px",
        }}
      >
        {(editor) => <BasicSetup editor={editor} />}
      </Editor>
    </main>
  )
}

Using this example styles are not working at all :(

Any tips how to get it working? It would be nice to have some example inside readme :)

Cheers!

FIameCaster commented 1 month ago

loadTheme() does not inject the theme's CSS into the page, it simply returns a Promise that resolves with the theme's CSS. You need to add the CSS returned to a <style> element. This could be achieved with an example like this:

export default function CodeEditor({ language, theme }: CodeEditorProps) {
  const [themeCss, setCss] = useState<string | null>(null)

  useEffect(() => {
    let cancelled = false
    loadTheme(theme).then(css => {
      if (!cancelled) setCss(css)
    })
    return () => cancelled = true
  }, [theme])

  return (
    themeCss && <main className="grow overflow-auto github">
      <style>{themeCss}</style>
      <Editor
        language={language}
        value="const foo = 'bar'"
        style={{
          height: "100%",
          fontSize: "16px",
        }}
      >
        {(editor) => <BasicSetup editor={editor} />}
      </Editor>
    </main>
  )
}

Note that you'll get multiple <style> elements with themes on the page if the CodeEditor component is used more than once. If this is the case, you should move the theme and the <style> element to a higher component that's only used once per page.

The editor also won't be visible before the theme loads. There are ways around this, but they're cumbersome. You can render the editor even if themeCss is null, but this will result in an FOUC before the theme loads. It's up to you to decide what's best.

Hope this helps!

zagoorland commented 1 month ago

It helped! It works perfect now :) There's no way I would solve it by myself, there's no much in the docs about usage of loadTheme function. Please, consider adding some example with select element and useTheme :)

Thank you :)

FIameCaster commented 1 month ago

There is an example showing the loadTheme function at the bottom of the readme and the function is documented with JSDoc, which you should be able to read in your IDE.

Please, consider adding some example with select element and useTheme :)

Adding a useTheme hook to prism-react-editor/themes is a good idea. I will consider this.