project init
This commit is contained in:
70
apps/web/src/components/editor/lesson-content-viewer.tsx
Normal file
70
apps/web/src/components/editor/lesson-content-viewer.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
'use client';
|
||||
|
||||
import { useEditor, EditorContent } from '@tiptap/react';
|
||||
import StarterKit from '@tiptap/starter-kit';
|
||||
import Underline from '@tiptap/extension-underline';
|
||||
import Link from '@tiptap/extension-link';
|
||||
import Image from '@tiptap/extension-image';
|
||||
import mermaid from 'mermaid';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
|
||||
const emptyDoc = { type: 'doc', content: [] };
|
||||
|
||||
interface LessonContentViewerProps {
|
||||
content: Record<string, unknown> | null;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function LessonContentViewer({ content, className }: LessonContentViewerProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit.configure({
|
||||
heading: { levels: [1, 2, 3] },
|
||||
codeBlock: {
|
||||
HTMLAttributes: (node: { attrs: { language?: string } }) =>
|
||||
node.attrs.language === 'mermaid'
|
||||
? { class: 'mermaid rounded-lg p-4 bg-muted min-h-[80px]', 'data-language': 'mermaid' }
|
||||
: { class: 'rounded-lg bg-muted p-4 font-mono text-sm', 'data-language': node.attrs.language || '' },
|
||||
},
|
||||
}),
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: true,
|
||||
HTMLAttributes: { class: 'text-primary underline underline-offset-2' },
|
||||
}),
|
||||
Image.configure({ HTMLAttributes: { class: 'rounded-lg max-w-full h-auto' } }),
|
||||
],
|
||||
content: content ?? emptyDoc,
|
||||
editable: false,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class:
|
||||
'prose prose-lg dark:prose-invert max-w-none focus:outline-none leading-relaxed [&_.ProseMirror]:outline-none [&_.ProseMirror]:text-foreground [&_.ProseMirror_p]:leading-7 [&_.ProseMirror_h1]:text-3xl [&_.ProseMirror_h2]:text-2xl [&_.ProseMirror_h3]:text-xl [&_.ProseMirror_pre]:rounded-lg [&_.ProseMirror_pre]:bg-muted [&_.ProseMirror_pre]:p-4 [&_.ProseMirror_pre]:font-mono [&_.ProseMirror_pre]:text-sm [&_.ProseMirror_pre]:border [&_.ProseMirror_blockquote]:border-primary [&_.ProseMirror_blockquote]:bg-muted/30',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editor && content) {
|
||||
editor.commands.setContent(content);
|
||||
}
|
||||
}, [content, editor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current || !content) return;
|
||||
const mermaidNodes = containerRef.current.querySelectorAll('pre[data-language="mermaid"]');
|
||||
if (mermaidNodes.length === 0) return;
|
||||
mermaid.run({ nodes: Array.from(mermaidNodes), suppressErrors: true }).catch(() => {});
|
||||
}, [content]);
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className={className}>
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user