Solid adapter for ProseMirror.
You can view the example in prosemirror-adapter/examples/solid.
npm install @prosemirror-adapter/solid
import { ProsemirrorAdapterProvider } from '@prosemirror-adapter/solid'
import { YourAwesomeEditor } from 'somewhere'
export const Component = () => {
return (
<ProsemirrorAdapterProvider>
<YourAwesomeEditor />
</ProsemirrorAdapterProvider>
)
}
In this section we will implement a node view for paragraph node.
Build component for node view
import { useNodeViewContext } from '@prosemirror-adapter/solid'
const Paragraph = () => {
const { contentRef, selected } = useNodeViewContext()
return (
<div
style={{ outline: selected ? 'blue solid 1px' : 'none' }}
role="presentation"
ref={contentRef}
/>
)
}
import { useNodeViewFactory } from '@prosemirror-adapter/solid'
import type { Component } from 'solid-js'
import { Paragraph } from './Paragraph'
export const YourAwesomeEditor: Component = () => {
const nodeViewFactory = useNodeViewFactory()
const editorRef = (element: HTMLDivElement) => {
if (element.firstChild) return
createEditorView(
element,
{
paragraph: nodeViewFactory({
component: Paragraph,
as: 'div',
contentAs: 'p',
}),
},
[]
)
}
return <div className="editor" ref={editorRef} />
}
🚀 Congratulations! You have built your first solid node view with prosemirror-adapter.
In this section we will implement a plugin view that will display the size of the document.
Build component for plugin view
import { usePluginViewContext } from '@prosemirror-adapter/solid'
import { createMemo } from 'solid-js'
export function Size() {
const context = usePluginViewContext()
const size = createMemo(() => context().view.state.doc.nodeSize)
return (
<div>
Size for document:
{size()}
</div>
)
}
import { usePluginViewFactory } from '@prosemirror-adapter/solid'
import type { Component } from 'solid-js'
import { Plugin } from 'prosemirror-state'
import { Paragraph } from './Paragraph'
export const YourAwesomeEditor: Component = () => {
const pluginViewFactory = usePluginViewFactory()
const editorRef = (element: HTMLDivElement) => {
if (!element || element.firstChild) return
const editorView = new EditorView(element, {
state: EditorState.create({
schema: YourProsemirrorSchema,
plugins: [
new Plugin({
view: pluginViewFactory({
component: Size,
}),
}),
],
}),
})
}
return <div className="editor" ref={editorRef} />
}
🚀 Congratulations! You have built your first solid plugin view with prosemirror-adapter.
In this section we will implement a widget view that will add hashes for heading when selected.
Build component for widget decoration view
import { useWidgetViewContext } from '@prosemirror-adapter/solid'
import { createMemo } from 'solid-js'
export function Hashes() {
const context = useWidgetViewContext()
const level = createMemo(() => context().spec?.level)
const hashes = createMemo(() => new Array(level() || 0).fill('#').join(''))
return (
<span style={{ 'color': 'blue', 'margin-right': '6px' }}>
{hashes()}
</span>
)
}
import { useWidgetViewFactory } from '@prosemirror-adapter/solid'
import type { Component } from 'solid-js'
import { Plugin } from 'prosemirror-state'
import { Hashes } from './Hashes'
export const YourAwesomeEditor: Component = () => {
const widgetViewFactory = useWidgetViewFactory()
const editorRef = useCallback(
(element: HTMLDivElement) => {
if (!element || element.firstChild)
return
const getHashWidget = widgetViewFactory({
as: 'i',
component: Hashes,
})
const editorView = new EditorView(element, {
state: EditorState.create({
schema: YourProsemirrorSchema,
plugins: [
new Plugin({
props: {
decorations(state) {
const { $from } = state.selection
const node = $from.node()
if (node.type.name !== 'heading')
return DecorationSet.empty
const widget = getHashWidget($from.before() + 1, {
side: -1,
level: node.attrs.level,
})
return DecorationSet.create(state.doc, [widget])
},
},
}),
]
})
})
},
[widgetViewFactory],
)
return <div className="editor" ref={editorRef} />
}
🚀 Congratulations! You have built your first solid widget view with prosemirror-adapter.
type DOMSpec = string | HTMLElement | ((node: Node) => HTMLElement)
interface NodeViewFactoryOptions {
// Component
component: SolidComponent
// The DOM element to use as the root node of the node view.
as?: DOMSpec
// The DOM element that contains the content of the node.
contentAs?: DOMSpec
// Overrides: this part is equal to properties of [NodeView](https://prosemirror.net/docs/ref/#view.NodeView)
update?: (
node: Node,
decorations: readonly Decoration[],
innerDecorations: DecorationSource
) => boolean | void
ignoreMutation?: (mutation: ViewMutationRecord) => boolean | void
selectNode?: () => void
deselectNode?: () => void
setSelection?: (
anchor: number,
head: number,
root: Document | ShadowRoot
) => void
stopEvent?: (event: Event) => boolean
destroy?: () => void
// Called when the node view is updated.
onUpdate?: () => void
}
interface NodeViewContext {
// The DOM element that contains the content of the node.
contentRef: NodeViewContentRef
// The prosemirror editor view.
view: EditorView
// Get prosemirror position of current node view.
getPos: () => number | undefined
// Set node.attrs of current node.
setAttrs: (attrs: Attrs) => void
// The prosemirror node for current node.
node: ShallowRef<Node>
// The prosemirror decorations for current node.
decorations: ShallowRef<readonly Decoration[]>
// The prosemirror inner decorations for current node.
innerDecorations: ShallowRef<DecorationSource>
// Whether the node is selected.
selected: ShallowRef<boolean>
}
interface PluginViewFactoryOptions {
// Component
component: SolidComponent
// The DOM element to use as the root node of the plugin view.
// The `viewDOM` here means `EditorState.view.dom`.
// By default, it will be `EditorState.view.dom.parentElement`.
root?: (viewDOM: HTMLElement) => HTMLElement
// Overrides: this part is equal to properties of [PluginView](https://prosemirror.net/docs/ref/#state.PluginView)
update?: (view: EditorView, prevState: EditorState) => void
destroy?: () => void
}
interface PluginViewContext {
// The prosemirror editor view.
view: ShallowRef<EditorView>
// The previously prosemirror editor state.
// Will be `undefined` when the plugin view is created.
prevState: ShallowRef<EditorState | undefined>
}
type WidgetDecorationFactory = (
pos: number,
spec?: WidgetDecorationSpec
) => Decoration
interface WidgetViewFactoryOptions {
// Component
component: SolidComponent
// The DOM element to use as the root node of the widget view.
as: string | HTMLElement
}
interface WidgetViewContext {
// The prosemirror editor view.
view: EditorView
// Get the position of the widget.
getPos: () => number | undefined
// Get the [spec](https://prosemirror.net/docs/ref/#view.Decoration^widget^spec) of the widget.
spec?: WidgetDecorationSpec
}
Follow our contribution guide to learn how to contribute to prosemirror-adapter.