Schema Definition
Schema definition for the Callout node type:
{
callout: {
content: 'block+',
group: 'block',
attrs: {
type: { default: 'info' }
},
}
}Model Representation
Example model representation:
{
type: 'callout',
attrs: { type: 'info' },
children: [
{
type: 'paragraph',
children: [{ type: 'text', text: 'Callout content' }]
}
]
}HTML Serialization
Converting model to HTML:
function serializeCallout(node) {
return '<div data-type="callout" data-callout-type="' + node.attrs.type + '">' +
serializeChildren(node.children) + '</div>';
}HTML Deserialization
Parsing HTML to model:
function parseCallout(domNode) {
return {
type: 'callout',
attrs: { type: domNode.getAttribute('data-callout-type') || 'info' },
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 callout
const callout = document.createElement('div');
callout.setAttribute('data-type', 'callout');
callout.setAttribute('data-callout-type', node.attrs.type);
callout.className = 'callout callout-' + node.attrs.type;
callout.contentEditable = 'true';
node.children.forEach(child => {
callout.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: Callout type validation
// Solution: Validate callout type
function validateCalloutType(type) {
const validTypes = ['info', 'warning', 'error', 'success'];
return validTypes.includes(type) ? type : 'info';
}
// Issue: Callout styling
// Solution: Apply type-specific styles
function styleCallout(callout, type) {
callout.className = 'callout callout-' + type;
// Apply type-specific colors, icons, etc.
}Implementation
Complete implementation example:
class CalloutNode {
constructor(attrs, children) {
this.type = 'callout';
this.attrs = { type: attrs?.type || 'info' };
this.children = children || [];
}
toDOM() {
const callout = document.createElement('div');
callout.setAttribute('data-type', 'callout');
callout.setAttribute('data-callout-type', this.attrs.type);
callout.className = 'callout callout-' + this.attrs.type;
this.children.forEach(child => {
callout.appendChild(child.toDOM());
});
return callout;
}
static fromDOM(domNode) {
return new CalloutNode(
{ type: domNode.getAttribute('data-callout-type') || 'info' },
Array.from(domNode.childNodes)
.map(node => parseNode(node))
.filter(Boolean)
);
}
}