Last active
June 24, 2019 12:23
-
-
Save TeemuKoivisto/7cf3294e6dcf24e5b774b8708f7c9daa to your computer and use it in GitHub Desktop.
Updated ProseMirror + React example with TypeScript and styled-components from @esmevane https://gist.github.com/esmevane/7326b19e20a5670954b51ea8618d096d
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as React from 'react' | |
import ReactDOM from 'react-dom' | |
import { EditorState } from 'prosemirror-state' | |
import { EditorView } from 'prosemirror-view' | |
import { Node, Schema } from 'prosemirror-model' | |
import applyDevTools from 'prosemirror-dev-tools' | |
import styled from 'styled-components' | |
// Here we have the (too simple) React component which | |
// we'll be rendering content into. | |
// | |
class Underlined extends React.Component<any, any> { | |
public hole: React.RefObject<HTMLParagraphElement> | |
constructor(props: any) { | |
super(props) | |
this.hole = React.createRef() | |
} | |
// We'll put the content into what we render using | |
// this function, which appends a given node to | |
// a ref HTMLElement, if present. | |
// | |
public append(node: HTMLElement) { | |
if (this.hole) { | |
this.hole.current!.appendChild(node) | |
} | |
} | |
public render() { | |
// Just really wanted to prove I could get React AND | |
// styled-component abilities at the same time. | |
// | |
const UnderlinedText: any = styled.p` | |
text-decoration: underline; | |
` | |
// We want to render the content dom node in the styled | |
// component. So, we set its inner ref (a SC quirk). | |
// | |
return <UnderlinedText ref={this.hole} /> | |
// return <p ref={this.hole} style={{textDecoration: 'underline'}}></p> | |
} | |
} | |
// This class is our actual interactor for ProseMirror itself. | |
// It glues DOM rendering, React, and ProseMirror nodes together. | |
// | |
class Underline { | |
public dom: HTMLElement | |
public contentDOM: HTMLElement | |
public ref: React.RefObject<any> | |
constructor(public node: Node) { | |
// We'll use this to access our Underlined component's | |
// instance methods. | |
// | |
this.ref = React.createRef<any>() | |
// Here, we'll provide a container to render React into. | |
// Coincidentally, this is where ProseMirror will put its | |
// generated contentDOM. React will throw out that content | |
// once rendered, and at the same time we'll append it into | |
// the component tree, like a fancy shell game. This isn't | |
// obvious to the user, but would it be more obvious on an | |
// expensive render? | |
// | |
this.dom = document.createElement('span') | |
// Finally, we provide an element to render content into. | |
// We will be moving this node around as we need to. | |
// | |
this.contentDOM = document.createElement('span') | |
ReactDOM.render( | |
<Underlined ref={this.ref} />, | |
this.dom, | |
this.putContentDomInRef | |
) | |
} | |
public update(node: Node) { | |
return true | |
} | |
// This is the least complex part. Now we've put | |
// all of our interlocking pieces behind refs and | |
// instance properties, this becomes the callback | |
// which performs the actual shell game. | |
// | |
private putContentDomInRef = () => { | |
this.ref.current.append(this.contentDOM) | |
} | |
destroy() { | |
ReactDOM.unmountComponentAtNode(this.dom) | |
} | |
} | |
export class Editor extends React.Component<{}, {}> { | |
editorState: EditorState | |
editorView?: EditorView | |
constructor(props: {}) { | |
super(props) | |
this.editorState = EditorState.create({ | |
schema: new Schema({ | |
nodes: { | |
doc: { | |
content: 'block+' | |
}, | |
underline: { | |
group: 'block', | |
content: 'inline*', | |
parseDOM: [{ tag: 'p' }], | |
toDOM() { return ['p', 0] } | |
}, | |
text: { | |
group: 'inline' | |
}, | |
} | |
}) | |
}) | |
} | |
createEditorView = (element: HTMLDivElement | null) => { | |
if (element != null) { | |
this.editorView = new EditorView(element, { | |
nodeViews: { | |
underline: (node: Node) => new Underline(node) | |
}, | |
state: this.editorState, | |
}) | |
applyDevTools(this.editorView) | |
} | |
} | |
componentWillUnmount() { | |
if (this.editorView) { | |
this.editorView.destroy() | |
} | |
} | |
shouldComponentUpdate() { | |
return false | |
} | |
render() { | |
return <div id="editor" ref={ref => { this.createEditorView(ref) }} /> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment