Files
Codewalkers/packages/web/src/components/editor/PageLinkExtension.tsx
2026-02-07 00:33:12 +01:00

76 lines
1.7 KiB
TypeScript

import { Node, mergeAttributes, ReactNodeViewRenderer, NodeViewWrapper } from "@tiptap/react";
import type { NodeViewProps } from "@tiptap/react";
import { FileText } from "lucide-react";
import { usePageTitle } from "./PageTitleContext";
declare module "@tiptap/react" {
interface Commands<ReturnType> {
pageLink: {
insertPageLink: (attrs: { pageId: string }) => ReturnType;
};
}
}
function PageLinkNodeView({ node }: NodeViewProps) {
const title = usePageTitle(node.attrs.pageId);
const handleClick = (e: React.MouseEvent) => {
(e.currentTarget as HTMLElement).dispatchEvent(
new CustomEvent("page-link-click", {
bubbles: true,
detail: { pageId: node.attrs.pageId },
}),
);
};
return (
<NodeViewWrapper className="page-link-block" data-page-link={node.attrs.pageId} onClick={handleClick}>
<FileText className="h-5 w-5 shrink-0" />
<span>{title}</span>
</NodeViewWrapper>
);
}
export const PageLinkExtension = Node.create({
name: "pageLink",
group: "block",
atom: true,
addAttributes() {
return {
pageId: { default: null },
};
},
parseHTML() {
return [
{ tag: 'div[data-page-link]' },
{ tag: 'span[data-page-link]' },
];
},
renderHTML({ HTMLAttributes }) {
return [
"div",
mergeAttributes(HTMLAttributes, {
"data-page-link": HTMLAttributes.pageId,
class: "page-link-block",
}),
];
},
addCommands() {
return {
insertPageLink:
(attrs) =>
({ chain }) => {
return chain().insertContent({ type: this.name, attrs }).run();
},
};
},
addNodeView() {
return ReactNodeViewRenderer(PageLinkNodeView);
},
});