Solid package for ProseMirror Adapter
npm install @prosemirror-adapter/solidSolid adapter for ProseMirror.
You can view the example in prosemirror-adapter/examples/solid.

``bash`
npm install @prosemirror-adapter/solid
`tsx
import { ProsemirrorAdapterProvider } from '@prosemirror-adapter/solid'
import { YourAwesomeEditor } from 'somewhere'
export function Component() {
return (
)
}
`
In this section we will implement a node view for paragraph node.
#### Build component for node view
`tsx
import { useNodeViewContext } from '@prosemirror-adapter/solid'
function Paragraph() {
const { contentRef, selected } = useNodeViewContext()
return
#### Bind node view components with prosemirror
`tsx
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
}
`š Congratulations! You have built your first solid node view with prosemirror-adapter.
$3
In this section we will implement a mark view for links that changes color periodically.
#### Build component for mark view
`tsx
import { useMarkViewContext } from '@prosemirror-adapter/solid'
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'const colors = [
'#f06292',
'#ba68c8',
'#9575cd',
'#7986cb',
'#64b5f6',
'#4fc3f7',
'#4dd0e1',
'#4db6ac',
'#81c784',
'#aed581',
'#ffb74d',
'#ffa726',
'#ff8a65',
'#d4e157',
'#ffd54f',
'#ffecb3',
]
function pickRandomColor() {
return colors[Math.floor(Math.random() * colors.length)]
}
export function Link() {
const [color, setColor] = createSignal(colors[0])
const context = useMarkViewContext()
const href = createMemo(() => context().mark.attrs.href as string)
const title = createMemo(() => context().mark.attrs.title as string | null)
createEffect(() => {
const interval = setInterval(() => {
setColor(pickRandomColor())
}, 1000)
return onCleanup(() => clearInterval(interval))
})
return (
href={href()}
title={title() || undefined}
ref={context().contentRef}
style={{ color: color(), transition: 'color 1s ease-in-out' }}
>
)
}
`#### Bind mark view components with prosemirror
`tsx
import { useMarkViewFactory } from '@prosemirror-adapter/solid'
import { Plugin } from 'prosemirror-state'
import { Link } from './Link'export function Editor() {
const markViewFactory = useMarkViewFactory()
const editorRef = (element: HTMLElement) => {
if (!element || element.firstChild) return
const editorView = new EditorView(element, {
state: EditorState.create({
schema: YourProsemirrorSchema,
plugins: [
new Plugin({
props: {
markViews: {
link: markViewFactory({
component: Link,
}),
},
},
}),
],
}),
})
}
return
}
`š Congratulations! You have built your first solid mark view with prosemirror-adapter.
$3
In this section we will implement a plugin view that will display the size of the document.
#### Build component for plugin view
`tsx
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 (
Size for document:
{size()}
)
}
`#### Bind plugin view components with prosemirror
`tsx
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
}
`š Congratulations! You have built your first solid plugin view with prosemirror-adapter.
$3
In this section we will implement a widget view that will add hashes for heading when selected.
#### Build component for widget decoration view
`tsx
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 {hashes()}
}
`#### Bind widget view components with prosemirror
`tsx
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
}
`š Congratulations! You have built your first solid widget view with prosemirror-adapter.
API
$3
#### useNodeViewFactory: () => (options: NodeViewFactoryOptions) => NodeView
`ts
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
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
}
`#### useNodeViewContext: () => NodeViewContext
`ts
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
// The prosemirror decorations for current node.
decorations: ShallowRef
// The prosemirror inner decorations for current node.
innerDecorations: ShallowRef
// Whether the node is selected.
selected: ShallowRef
}
`
$3
#### useMarkViewFactory: () => (options: MarkViewFactoryOptions) => MarkView
`ts
type MarkViewDOMSpec = string | HTMLElement | ((mark: Mark) => HTMLElement)interface MarkViewFactoryOptions {
// Component
component: Component
// The DOM element to use as the root node of the mark view
as?: MarkViewDOMSpec
// The DOM element that contains the content of the mark
contentAs?: MarkViewDOMSpec
// Called when the mark view is destroyed
destroy?: () => void
}
`#### useMarkViewContext: () => MarkViewContext
`ts
interface MarkViewContext {
// The DOM element that contains the content of the mark
contentRef: (element: HTMLElement) => void // The prosemirror editor view
view: EditorView
// The prosemirror mark for current mark view
mark: Mark
// Whether the mark is inline
inline: boolean
}
`
$3
#### usePluginViewFactory: () => (options: PluginViewFactoryOptions) => PluginView
`ts
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
update?: (view: EditorView, prevState: EditorState) => void
destroy?: () => void
}
`#### usePluginViewContext: () => PluginViewContext
`ts
interface PluginViewContext {
// The prosemirror editor view.
view: ShallowRef // The previously prosemirror editor state.
// Will be
undefined when the plugin view is created.
prevState: ShallowRef
}
`
$3
#### useWidgetViewFactory: () => (options: WidgetViewFactoryOptions) => WidgetDecorationFactory
`ts
type WidgetDecorationFactory = (pos: number, spec?: WidgetDecorationSpec) => Decorationinterface WidgetViewFactoryOptions {
// Component
component: SolidComponent
// The DOM element to use as the root node of the widget view.
as: string | HTMLElement
}
`#### useWidgetViewContext: () => WidgetViewContext
`ts
interface WidgetViewContext {
// The prosemirror editor view.
view: EditorView // Get the position of the widget.
getPos: () => number | undefined
// Get the spec of the widget.
spec?: WidgetDecorationSpec
}
``Follow our contribution guide to learn how to contribute to prosemirror-adapter.