diff --git a/src/schema.ts b/src/schema.ts index f9d6742..3d3187b 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -1,268 +1,48 @@ -import { NodeSpec, Schema, DOMOutputSpec, MarkSpec } from "prosemirror-model" - -function addIsAmgBlockAttr(nodes: { [key: string]: NodeSpec }): { - [key: string]: NodeSpec -} { - for (const [_, node] of Object.entries(nodes)) { +import { NodeSpec, Schema, SchemaSpec } from "prosemirror-model" +import { schema as base } from "prosemirror-schema-basic" +import { addListNodes } from "prosemirror-schema-list" +import OrderedMap from "orderedmap" + +/** + * Add `isAmgBlock` attr to all NodeSpecs with content. + * Use this when using a custom schema. + */ +export function addIsAmgBlockAttr( + nodes: OrderedMap, +): OrderedMap { + let copy = nodes + nodes.forEach((name, node) => { if (node.content) { - node.attrs - ? (node.attrs.isAmgBlock = { default: false }) - : (node.attrs = { isAmgBlock: { default: false } }) + copy = copy.update(name, { + ...node, + attrs: { + ...node.attrs, + isAmgBlock: { default: false }, + }, + }) } - } - return nodes + }) + return copy } -// basics -const pDOM: DOMOutputSpec = ["p", 0] -const blockquoteDOM: DOMOutputSpec = ["blockquote", 0] -const hrDOM: DOMOutputSpec = ["hr"] -const preDOM: DOMOutputSpec = ["pre", ["code", 0]] - -// marks -const emDOM: DOMOutputSpec = ["em", 0] -const strongDOM: DOMOutputSpec = ["strong", 0] -const codeDOM: DOMOutputSpec = ["code", 0] - -// lists -const olDOM: DOMOutputSpec = ["ol", 0] -const ulDOM: DOMOutputSpec = ["ul", 0] -const liDOM: DOMOutputSpec = ["li", 0] - -export const schema = new Schema({ - nodes: addIsAmgBlockAttr({ - /// NodeSpec The top level document node. - doc: { - content: "block+", - } as NodeSpec, - - /// A plain paragraph textblock. Represented in the DOM - /// as a `

` element. - paragraph: { - content: "inline*", - group: "block", - parseDOM: [{ tag: "p" }], - toDOM() { - return pDOM - }, - } as NodeSpec, - - /// A blockquote (`

`) wrapping one or more blocks. - blockquote: { - content: "block+", - group: "block", - defining: true, - parseDOM: [{ tag: "blockquote" }], - toDOM() { - return blockquoteDOM - }, - } as NodeSpec, - - /// A horizontal rule (`
`). - horizontal_rule: { - group: "block", - parseDOM: [{ tag: "hr" }], - toDOM() { - return hrDOM - }, - } as NodeSpec, - - /// A heading textblock, with a `level` attribute that - /// should hold the number 1 to 6. Parsed and serialized as `

` to - /// `

` elements. - heading: { - attrs: { level: { default: 1 } }, - content: "inline*", - group: "block", - defining: true, - parseDOM: [ - { tag: "h1", attrs: { level: 1 } }, - { tag: "h2", attrs: { level: 2 } }, - { tag: "h3", attrs: { level: 3 } }, - { tag: "h4", attrs: { level: 4 } }, - { tag: "h5", attrs: { level: 5 } }, - { tag: "h6", attrs: { level: 6 } }, - ], - toDOM(node) { - return ["h" + node.attrs.level, 0] - }, - } as NodeSpec, - - /// A code listing. Disallows marks or non-text inline - /// nodes by default. Represented as a `
` element with a
-    /// `` element inside of it.
-    code_block: {
-      content: "text*",
-      marks: "",
-      group: "block",
-      code: true,
-      defining: true,
-      parseDOM: [{ tag: "pre", preserveWhitespace: "full" }],
-      toDOM() {
-        return preDOM
-      },
-    } as NodeSpec,
-
-    /// The text node.
-    text: {
-      group: "inline",
-    } as NodeSpec,
-
-    /// An inline image (``) node. Supports `src`,
-    /// `alt`, and `href` attributes. The latter two default to the empty
-    /// string.
-    image: {
-      inline: true,
-      attrs: {
-        src: {},
-        alt: { default: null },
-        title: { default: null },
-      },
-      group: "inline",
-      draggable: true,
-      parseDOM: [
-        {
-          tag: "img[src]",
-          getAttrs(dom: HTMLElement) {
-            return {
-              src: dom.getAttribute("src"),
-              title: dom.getAttribute("title"),
-              alt: dom.getAttribute("alt"),
-            }
-          },
-        },
-      ],
-      toDOM(node) {
-        const { src, alt, title } = node.attrs
-        return ["img", { src, alt, title }]
-      },
-    } as NodeSpec,
-
-    ordered_list: {
-      group: "block",
-      content: "list_item+",
-      attrs: { order: { default: 1 } },
-      parseDOM: [
-        {
-          tag: "ol",
-          getAttrs(dom: HTMLElement) {
-            return {
-              order: dom.hasAttribute("start")
-                ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-                  +dom.getAttribute("start")!
-                : 1,
-            }
-          },
-        },
-      ],
-      toDOM(node) {
-        return node.attrs.order == 1
-          ? olDOM
-          : ["ol", { start: node.attrs.order }, 0]
-      },
-    } as NodeSpec,
-
-    bullet_list: {
-      content: "list_item+",
-      group: "block",
-      parseDOM: [{ tag: "ul" }],
-      toDOM() {
-        return ulDOM
-      },
-    },
-
-    /// A list item (`
  • `) spec. - list_item: { - content: "paragraph block*", - parseDOM: [{ tag: "li" }], - toDOM() { - return liDOM - }, - defining: true, +const basicNodes: SchemaSpec["nodes"] = addListNodes( + base.spec.nodes, + "paragraph block*", +).append({ + aside: { + content: "block+", + group: "block", + defining: true, + parseDOM: [{ tag: "aside" }], + toDOM() { + return ["aside", 0] }, + }, +}) - aside: { - content: "block+", - group: "block", - defining: true, - parseDOM: [{ tag: "aside" }], - toDOM() { - return ["aside", 0] - }, - }, - }), - marks: { - /// A link. Has `href` and `title` attributes. `title` - /// defaults to the empty string. Rendered and parsed as an `` - /// element. - link: { - attrs: { - href: {}, - title: { default: null }, - }, - inclusive: false, - parseDOM: [ - { - tag: "a[href]", - getAttrs(dom: HTMLElement) { - return { - href: dom.getAttribute("href"), - title: dom.getAttribute("title"), - } - }, - }, - ], - toDOM(node) { - const { href, title } = node.attrs - return ["a", { href, title }, 0] - }, - } as MarkSpec, - - /// An emphasis mark. Rendered as an `` element. Has parse rules - /// that also match `` and `font-style: italic`. - em: { - parseDOM: [ - { tag: "i" }, - { tag: "em" }, - { style: "font-style=italic" }, - { style: "font-style=normal", clearMark: m => m.type.name == "em" }, - ], - toDOM() { - return emDOM - }, - } as MarkSpec, +const basicMarks: SchemaSpec["marks"] = base.spec.marks - /// A strong mark. Rendered as ``, parse rules also match - /// `` and `font-weight: bold`. - strong: { - parseDOM: [ - { tag: "strong" }, - // This works around a Google Docs misbehavior where - // pasted content will be inexplicably wrapped in `` - // tags with a font-weight normal. - { - tag: "b", - getAttrs: (node: HTMLElement) => - node.style.fontWeight != "normal" && null, - }, - { style: "font-weight=400", clearMark: m => m.type.name == "strong" }, - { - style: "font-weight", - getAttrs: (value: string) => - /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null, - }, - ], - toDOM() { - return strongDOM - }, - } as MarkSpec, - - /// Code font mark. Represented as a `` element. - code: { - parseDOM: [{ tag: "code" }], - toDOM() { - return codeDOM - }, - } as MarkSpec, - }, +export const schema = new Schema({ + nodes: addIsAmgBlockAttr(basicNodes), + marks: basicMarks, })