website-01/components/front/ContentView.tsx

83 lines
2.6 KiB
TypeScript
Raw Normal View History

2026-06-22 14:43:46 +08:00
'use client';
import ReactMarkdown, { type Components } from 'react-markdown';
import { isHTMLContent } from '@/lib/content';
interface ContentViewProps {
content: string | null | undefined;
className?: string;
/**
*
* - 'html'dangerouslySetInnerHTML
* - 'markdown' react-markdown
* 退 isHTMLContent
*/
format?: 'html' | 'markdown';
}
const mdComponents: Components = {
pre: ({ children }) => (
<pre className="my-4 overflow-x-auto rounded-md bg-gray-900 p-4 text-sm text-gray-100">
{children}
</pre>
),
code: ({ className: cls, children }) => {
// 行内 code无 language- 前缀)→ 灰底圆角;块级 code 由 <pre> 包裹
const isBlock = typeof cls === 'string' && cls.includes('language-');
if (isBlock) {
return <code className={cls}>{children}</code>;
}
return (
<code className="rounded bg-gray-100 px-1.5 py-0.5 text-sm text-pink-600">
{children}
</code>
);
},
table: ({ children }) => (
<table className="my-4 w-full border-collapse border border-gray-300 text-sm">
{children}
</table>
),
th: ({ children }) => (
<th className="border border-gray-300 bg-gray-50 px-3 py-2 text-left font-medium">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-gray-300 px-3 py-2">{children}</td>
),
hr: () => <hr className="my-6 border-gray-200" />,
};
/**
*
* - HTML RichEditor dangerouslySetInnerHTML
* - Markdown .md react-markdown React
* `.prose-rich`
*
* MD suppressHydrationWarning react-markdown
* SSR/HTML hydration
*/
export function ContentView({ content, className, format }: ContentViewProps) {
if (!content?.trim()) return null;
const wrapperClass = className ? `prose-rich ${className}` : 'prose-rich';
const isHtml =
format === 'html' ? true : format === 'markdown' ? false : isHTMLContent(content);
if (isHtml) {
return (
<div
className={wrapperClass}
dangerouslySetInnerHTML={{ __html: content }}
/>
);
}
return (
<div className={wrapperClass} suppressHydrationWarning>
<ReactMarkdown components={mdComponents}>{content}</ReactMarkdown>
</div>
);
}