Custom Block Node Type

Category: Custom • Detailed implementation guide with view integration notes

Schema Definition

Schema definition for the Custom Block node type:

{
  customBlock: {
    content: 'block*',
    group: 'block',
    attrs: {
      type: { default: '' },
      data: { default: '{}' }
    },
  }
}

Model Representation

Example model representation:

{
  type: 'customBlock',
  attrs: {
    type: 'my-custom-block',
    data: JSON.stringify({ custom: 'data' })
  },
  children: []
}

HTML Serialization

Converting model to HTML:

function serializeCustomBlock(node) {
  return '<div data-type="custom-block" data-block-type="' + node.attrs.type + 
         '" data-block-data="' + escapeHtml(node.attrs.data) + '">' + 
         serializeChildren(node.children) + '</div>';
}

HTML Deserialization

Parsing HTML to model:

function parseCustomBlock(domNode) {
  return {
    type: 'customBlock',
    attrs: {
      type: domNode.getAttribute('data-block-type') || '',
      data: domNode.getAttribute('data-block-data') || '{}'
    },
    children: Array.from(domNode.childNodes)
      .map(node => parseNode(node))
      .filter(Boolean)
  };
}

View Integration

View Integration Notes: Pay special attention to contenteditable behavior, selection handling, and event management when implementing this node type in your view layer.

View integration code:

// Rendering custom block
const customBlock = document.createElement('div');
customBlock.setAttribute('data-type', 'custom-block');
customBlock.setAttribute('data-block-type', node.attrs.type);
customBlock.setAttribute('data-block-data', node.attrs.data);
customBlock.contentEditable = 'true';
node.children.forEach(child => {
  customBlock.appendChild(renderNode(child));
});

Common Issues

Common Pitfalls: These are issues frequently encountered when implementing this node type. Review carefully before implementation.

Common issues and solutions:

// Issue: Custom block type registration
// Solution: Register custom block renderers
const customBlockRenderers = {
  'my-custom-block': (node, data) => {
    // Custom rendering logic
  }
};

// Issue: Custom block data validation
// Solution: Validate custom block data
function validateCustomBlockData(data, type) {
  // Validate based on block type
  return true;
}

Implementation

Complete implementation example:

class CustomBlockNode {
  constructor(attrs, children) {
    this.type = 'customBlock';
    this.attrs = {
      type: attrs?.type || '',
      data: attrs?.data || '{}'
    };
    this.children = children || [];
  }
  
  toDOM() {
    const customBlock = document.createElement('div');
    customBlock.setAttribute('data-type', 'custom-block');
    customBlock.setAttribute('data-block-type', this.attrs.type);
    customBlock.setAttribute('data-block-data', this.attrs.data);
    this.children.forEach(child => {
      customBlock.appendChild(child.toDOM());
    });
    return customBlock;
  }
  
  static fromDOM(domNode) {
    return new CustomBlockNode(
      {
        type: domNode.getAttribute('data-block-type') || '',
        data: domNode.getAttribute('data-block-data') || '{}'
      },
      Array.from(domNode.childNodes)
        .map(node => parseNode(node))
        .filter(Boolean)
    );
  }
}