UNPKG

@milkdown/preset-commonmark

Version:

The commonmark preset of [milkdown](https://milkdown.dev/).

135 lines (116 loc) 3.45 kB
import type { Node as ProseNode } from '@milkdown/prose/model' import { expectDomTypeError } from '@milkdown/exception' import { toggleMark } from '@milkdown/prose/commands' import { TextSelection } from '@milkdown/prose/state' import { $command, $markAttr, $markSchema } from '@milkdown/utils' import { withMeta } from '../__internal__' /// HTML attributes for the link mark. export const linkAttr = $markAttr('link') withMeta(linkAttr, { displayName: 'Attr<link>', group: 'Link', }) /// Link mark schema. export const linkSchema = $markSchema('link', (ctx) => ({ attrs: { href: { validate: 'string' }, title: { default: null, validate: 'string|null' }, }, parseDOM: [ { tag: 'a[href]', getAttrs: (dom) => { if (!(dom instanceof HTMLElement)) throw expectDomTypeError(dom) return { href: dom.getAttribute('href'), title: dom.getAttribute('title'), } }, }, ], toDOM: (mark) => ['a', { ...ctx.get(linkAttr.key)(mark), ...mark.attrs }], parseMarkdown: { match: (node) => node.type === 'link', runner: (state, node, markType) => { const url = node.url as string const title = node.title as string state.openMark(markType, { href: url, title }) state.next(node.children) state.closeMark(markType) }, }, toMarkdown: { match: (mark) => mark.type.name === 'link', runner: (state, mark) => { state.withMark(mark, 'link', undefined, { title: mark.attrs.title, url: mark.attrs.href, }) }, }, })) withMeta(linkSchema.mark, { displayName: 'MarkSchema<link>', group: 'Link', }) /// @internal export interface UpdateLinkCommandPayload { href?: string title?: string } /// A command to toggle the link mark. /// You can pass the `href` and `title` to the link. export const toggleLinkCommand = $command( 'ToggleLink', (ctx) => (payload: UpdateLinkCommandPayload = {}) => toggleMark(linkSchema.type(ctx), payload) ) withMeta(toggleLinkCommand, { displayName: 'Command<toggleLinkCommand>', group: 'Link', }) /// A command to update the link mark. /// You can pass the `href` and `title` to update the link. export const updateLinkCommand = $command( 'UpdateLink', (ctx) => (payload: UpdateLinkCommandPayload = {}) => (state, dispatch) => { if (!dispatch) return false let node: ProseNode | undefined let pos = -1 const { selection } = state const { from, to } = selection state.doc.nodesBetween(from, from === to ? to + 1 : to, (n, p) => { if (linkSchema.type(ctx).isInSet(n.marks)) { node = n pos = p return false } return undefined }) if (!node) return false const mark = node.marks.find(({ type }) => type === linkSchema.type(ctx)) if (!mark) return false const start = pos const end = pos + node.nodeSize const { tr } = state const linkMark = linkSchema .type(ctx) .create({ ...mark.attrs, ...payload }) if (!linkMark) return false dispatch( tr .removeMark(start, end, mark) .addMark(start, end, linkMark) .setSelection(new TextSelection(tr.selection.$anchor)) .scrollIntoView() ) return true } ) withMeta(updateLinkCommand, { displayName: 'Command<updateLinkCommand>', group: 'Link', })