Files
gitmost/packages/editor-ext/src/lib/transclusion/transclusion-reference.ts
Philip Okugbe de60aa7e61 feat: synced blocks (transclusion) (#2163)
* feat: synced blocks (transclusion)

* fix:remove name

* make placeholders smaller

* feat: enforce strict transclusion schema

* fix: scope synced blocks to workspace, gate unsync on edit permission

* fix collab module error
2026-05-08 13:23:16 +01:00

93 lines
2.0 KiB
TypeScript

import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
export interface TransclusionReferenceOptions {
HTMLAttributes: Record<string, any>;
view: any;
}
export interface TransclusionReferenceAttributes {
sourcePageId?: string | null;
transclusionId?: string | null;
}
declare module "@tiptap/core" {
interface Commands<ReturnType> {
transclusionReference: {
insertTransclusionReference: (
attributes: TransclusionReferenceAttributes,
) => ReturnType;
};
}
}
export const TransclusionReference = Node.create<TransclusionReferenceOptions>({
name: "transclusionReference",
addOptions() {
return {
HTMLAttributes: {},
view: null,
};
},
group: "block",
atom: true,
selectable: true,
draggable: false,
addAttributes() {
return {
sourcePageId: {
default: null,
parseHTML: (el) => el.getAttribute("data-source-page-id"),
renderHTML: (attrs) =>
attrs.sourcePageId
? { "data-source-page-id": attrs.sourcePageId }
: {},
},
transclusionId: {
default: null,
parseHTML: (el) => el.getAttribute("data-transclusion-id"),
renderHTML: (attrs) =>
attrs.transclusionId
? { "data-transclusion-id": attrs.transclusionId }
: {},
},
};
},
parseHTML() {
return [{ tag: `div[data-type="${this.name}"]` }];
},
renderHTML({ HTMLAttributes }) {
return [
"div",
mergeAttributes(
{ "data-type": this.name },
this.options.HTMLAttributes,
HTMLAttributes,
),
];
},
addCommands() {
return {
insertTransclusionReference:
(attributes) =>
({ commands }) =>
commands.insertContent({
type: this.name,
attrs: attributes,
}),
};
},
addNodeView() {
if (!this.options.view) return null;
this.editor.isInitialized = true;
return ReactNodeViewRenderer(this.options.view);
},
});