claritykit-svelte
Version:
A comprehensive Svelte component library focused on accessibility, ADHD-optimized design, developer experience, and full SSR compatibility
176 lines (175 loc) • 6.68 kB
JavaScript
import { Node, mergeAttributes } from '@tiptap/core';
export const BlockTransclusion = Node.create({
name: 'blockTransclusion',
addOptions() {
return {
HTMLAttributes: {
class: 'block-transclusion',
},
renderTransclusion: async (blockId) => {
// This would fetch the actual block content
// For now, return a placeholder
return `<div class="transclusion-content">Content of block ${blockId}</div>`;
},
};
},
group: 'block',
atom: true,
addAttributes() {
return {
blockId: {
default: null,
parseHTML: (element) => element.getAttribute('data-block-id'),
renderHTML: (attributes) => {
if (!attributes.blockId) {
return {};
}
return {
'data-block-id': attributes.blockId,
};
},
},
preview: {
default: true,
parseHTML: (element) => element.getAttribute('data-preview') === 'true',
renderHTML: (attributes) => {
return {
'data-preview': attributes.preview ? 'true' : 'false',
};
},
},
};
},
parseHTML() {
return [
{
tag: 'div[data-block-id].block-transclusion',
getAttrs: (element) => {
const blockId = element.getAttribute('data-block-id');
const preview = element.getAttribute('data-preview') === 'true';
return blockId ? { blockId, preview } : false;
},
},
];
},
renderHTML({ HTMLAttributes }) {
return [
'div',
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
class: 'block-transclusion',
'data-type': 'block-transclusion',
role: 'region',
'aria-label': `Transcluded content from block ${HTMLAttributes['data-block-id']}`,
}),
[
'div',
{ class: 'transclusion-header' },
[
'span',
{ class: 'transclusion-indicator' },
'📎',
],
[
'span',
{ class: 'transclusion-label' },
`Block ${HTMLAttributes['data-block-id']}`,
],
],
[
'div',
{ class: 'transclusion-content' },
`Loading content from block ${HTMLAttributes['data-block-id']}...`,
],
];
},
addCommands() {
return {
insertBlockTransclusion: (attributes) => ({ commands }) => {
return commands.insertContent({
type: this.name,
attrs: attributes,
});
},
updateBlockTransclusion: (attributes) => ({ commands }) => {
return commands.updateAttributes(this.name, attributes);
},
};
},
addKeyboardShortcuts() {
return {
'Mod-Shift-t': () => {
// This would open a block selector for transclusion
return this.editor.commands.insertBlockTransclusion({ blockId: 'placeholder' });
},
};
},
addNodeView() {
return ({ node, HTMLAttributes, getPos, editor }) => {
const dom = document.createElement('div');
dom.className = 'block-transclusion-wrapper';
const blockId = node.attrs.blockId;
const preview = node.attrs.preview;
// Create the transclusion container
const container = document.createElement('div');
container.className = 'block-transclusion';
container.setAttribute('data-block-id', blockId);
container.setAttribute('data-preview', preview.toString());
// Header with indicator and controls
const header = document.createElement('div');
header.className = 'transclusion-header';
header.innerHTML = `
<span class="transclusion-indicator">📎</span>
<span class="transclusion-label">Block ${blockId}</span>
<button class="transclusion-toggle" title="Toggle preview">👁️</button>
`;
// Content area
const content = document.createElement('div');
content.className = 'transclusion-content';
content.innerHTML = 'Loading...';
// Load the transcluded content
this.options.renderTransclusion(blockId).then((html) => {
content.innerHTML = html;
});
// Toggle preview functionality
const toggleButton = header.querySelector('.transclusion-toggle');
toggleButton?.addEventListener('click', () => {
const newPreview = !preview;
if (typeof getPos === 'function') {
editor.commands.updateAttributes(this.name, { preview: newPreview });
}
});
container.appendChild(header);
if (preview) {
container.appendChild(content);
}
dom.appendChild(container);
return {
dom,
update: (updatedNode) => {
if (updatedNode.type !== this.type) {
return false;
}
// Update the node view when attributes change
const newBlockId = updatedNode.attrs.blockId;
const newPreview = updatedNode.attrs.preview;
if (newBlockId !== blockId) {
// Reload content for new block
this.options.renderTransclusion(newBlockId).then((html) => {
content.innerHTML = html;
});
}
// Update preview visibility
if (newPreview !== preview) {
if (newPreview) {
container.appendChild(content);
}
else {
content.remove();
}
}
return true;
},
};
};
},
});