prosemirror-flat-list
Version:
Powerful list support for ProseMirror
75 lines (67 loc) • 1.96 kB
text/typescript
import {
type DOMOutputSpec,
DOMSerializer,
type Fragment,
type Node as ProsemirrorNode,
type Schema,
} from 'prosemirror-model'
import { listToDOM } from '../schema/to-dom'
/**
* A custom DOM serializer class that can serialize flat list nodes into native
* HTML list elements (i.e. `<ul>` and `<ol>`).
*
* @public @group Plugins
*/
export class ListDOMSerializer extends DOMSerializer {
static override nodesFromSchema(schema: Schema): {
[node: string]: (node: ProsemirrorNode) => DOMOutputSpec
} {
const nodes = DOMSerializer.nodesFromSchema(schema)
return {
...nodes,
list: (node) =>
listToDOM({ node, nativeList: true, getMarkers: () => null }),
}
}
static override fromSchema(schema: Schema): ListDOMSerializer {
return (
(schema.cached.listDomSerializer as ListDOMSerializer) ||
(schema.cached.listDomSerializer = new ListDOMSerializer(
this.nodesFromSchema(schema),
this.marksFromSchema(schema),
))
)
}
override serializeFragment(
fragment: Fragment,
options?: { document?: Document },
target?: HTMLElement | DocumentFragment,
): HTMLElement | DocumentFragment {
const dom = super.serializeFragment(fragment, options, target)
return joinListElements(dom)
}
}
/**
* Merge adjacent <ul> elements or adjacent <ol> elements into a single list element.
*
* @public
*/
export function joinListElements<T extends Element | DocumentFragment>(
parent: T,
): T {
for (let i = 0; i < parent.childNodes.length; i++) {
const child = parent.children.item(i)
if (!child) continue
if (child.tagName === 'UL' || child.tagName === 'OL') {
let next: Element | null = null
while (
((next = child.nextElementSibling), next?.tagName === child.tagName)
) {
child.append(...Array.from(next.children))
next.remove()
}
}
joinListElements(child)
}
return parent
}