ProseMirror Schema

Each document of the Editor conforms to a specific schema which describes the nodes and the marks that the document may contain.

The nodes conform to the HTML elements such as paragraphs, headings, lists, tables, images, and so on. The marks conform to the inline formatting options such as Bold, Italic, FontSize, and so on. By modifying the default, built-in schema, you can add additional types of nodes and marks as well as edit and remove the existing ones.

Setting a custom schema is possible only once - upon the initialization of the Editor component. Further changes to the object the schema input is bound to will not be reflected, and the Editor will continue using the initially provided schema (or the built-in one if no custom schema is initally provided).

Modifying the Default Schema

To customize the existing default Editor schema:

  1. Import schema (the default Editor schema) and Schema (the ProseMirror Schema class) from the @progress/kendo-angular-editor package:

        import { schema, Schema } from '@progress/kendo-angular-editor';
  2. Customize the existing nodes using the relevant properties and methods from the NodeSpec and OrderedMap APIs. For example, add an attribute to the existing paragraph node:

        const paragraph = { ...schema.spec.nodes.get('paragraph') };
        paragraph.attrs['dir'] = { default: null };
        let nodes = schema.spec.nodes.update('paragraph', paragraph);
    
  3. Create new nodes, and add them to the existing schema. For example:

        // NodeSpec interface
        // http://prosemirror.net/docs/ref/#model.NodeSpec
        const iframe = {
            attrs: {
                src: { default: null },
                style: { default: null }
            },
            group: 'block',
            selectable: false,
            parseDOM: [ {
                tag: 'iframe',
                getAttrs: (dom) => ({
                    src: dom.getAttribute('src'),
                    style: dom.getAttribute('style')
                })
            } ],
            toDOM: node => {
                const attrs = {
                    src: node.attrs.src,
                    style: node.attrs.style,
                    frameborder: '0',
                    allowfullscreen: 'true'
                };
                return [ 'iframe', attrs ];
            }
        };
    
        // Append the new node.
        nodes = schema.spec.nodes.addToEnd('iframe', iframe);
  4. Add new marks in a similar fashion - create the new marks, and add them to the existing collection using the OrderedMap API, e.g.:

        // Append a new mark representing the <s> formatting tag.
        // MarkSpec interface
        // https://prosemirror.net/docs/ref/#model.MarkSpec
        const tagMark = tag => ({
            [tag]: {
                name: tag,
                inclusive: true,
                parseDOM: [ { tag } ],
                toDOM: () => [ tag, 0 ]
            }
        });
        const mark = tagMark('s');
        let marks = schema.spec.marks.append(mark);
  5. Create the customized schema with the modified and newly added nodes and marks, and bind the Editor's schema input:

        // template
        <kendo-editor [schema]="mySchema" ...></kendo-editor>
    
        // TS Class code
        public mySchema = new Schema({
            marks,
            nodes
        });

The following example demonstrates how to:

  • Add a new node to the nodes object of the schema.
  • Add a new mark to the marks object of the schema.
  • Add a custom attribute to the existing nodes.
Example
View Source
Edit In Stackblitz  
Change Theme:

Creating a Custom Schema

To create a custom schema from scratch:

  1. Define all marks, following the ProseMirror MarkSpec interface:

        // https://prosemirror.net/docs/ref/#model.MarkSpec
        const tagMark = tag => {
            return {
                [tag]: {
                    name: tag,
                    inclusive: true,
                    parseDOM: [ { tag: tag } ],
                    toDOM: () => [ tag, 0 ]
                }
            };
        };
    
        const marks = {
            link: {
                attrs: {
                    ...commonAttributes(),
                    href: { default: null },
                    target: { default: null },
                    title: { default: null }
                },
                inclusive: false,
                parseDOM: [ { tag: 'a', getAttrs: getAttributes } ],
                toDOM(node) { return [ 'a', getAttrs(node.attrs), 0 ]; }
            },
    
            ...tagMark('strong'),
            ...tagMark('b'),
            ...tagMark('em'),
            ...tagMark('i'),
            ...tagMark('u'),
            ...tagMark('del'),
            ...tagMark('sub'),
            ...tagMark('sup'),
            ...tagMark('code'),
    
            style: {
                attrs: {
                    ...commonAttributes()
                },
                parseDOM: [ {
                    tag: 'span',
                    getAttrs: getAttributes
                } ],
                toDOM: node => (hasAttrs(node.attrs) ?
                    [ 'span', getAttrs(node.attrs), 0 ] : [ 'span', 0 ])
            }
        };
  2. Define all nodes, following the ProseMirror NodeSpec interface:

        const nodes = {
            // The top level document node.
            doc: {
                content: 'block+'
            },
    
            paragraph: {
                content: 'inline*',
                group: 'block',
                attrs: {
                    ...commonAttributes()
                },
                parseDOM: [ {
                    tag: 'p',
                    getAttrs: getAttributes
                } ],
                toDOM: node => hasAttrs(node.attrs) ? [ 'p', getAttrs(node.attrs), 0 ] : [ 'p', 0 ]
            },
    
            div: {
                content: 'block*',
                group: 'block',
                attrs: {
                    ...commonAttributes()
                },
                parseDOM: [ {
                    tag: 'div',
                    getAttrs: getAttributes
                } ],
                toDOM: node => hasAttrs(node.attrs) ? [ 'div', getAttrs(node.attrs), 0 ] : [ 'div', 0 ]
            },
            // ...more nodes
        }
  3. Create the custom schema using the ProseMirror Schema class constructor, and the nodes and marks, defined in the previous steps:

        import { Schema } from '@progress/kendo-angular-editor';
    
        const mySchema = new Schema({ nodes, marks });
  4. Bind the Editor schema input to the newly created schema.

The following example demonstrates how to implement a custom Editor schema.

Example
View Source
Edit In Stackblitz  
Change Theme: