Re_Figma_Code/src/components/common/FormattedDescription.tsx

78 lines
3.5 KiB
TypeScript

import * as React from "react";
import { cn } from "@/components/ui/utils";
interface FormattedDescriptionProps {
content: string;
className?: string;
}
/**
* FormattedDescription Component
*
* Renders HTML content with proper styling for lists, tables, and other formatted content.
* Use this component to display descriptions that may contain HTML formatting.
*/
export function FormattedDescription({ content, className }: FormattedDescriptionProps) {
const processedContent = React.useMemo(() => {
if (!content) return '';
// Wrap tables that aren't already wrapped in a scrollable container using regex
// Match <table> tags that aren't already inside a .table-wrapper
let processed = content;
// Pattern to match table tags that aren't already wrapped
const tablePattern = /<table[^>]*>[\s\S]*?<\/table>/gi;
processed = processed.replace(tablePattern, (match) => {
// Check if this table is already wrapped
if (match.includes('table-wrapper')) {
return match;
}
// Wrap the table in a scrollable container
return `<div class="table-wrapper">${match}</div>`;
});
return processed;
}, [content]);
if (!content) return null;
return (
<div
className={cn(
"text-sm text-gray-700 max-w-none",
// Horizontal scrolling for smaller screens
"overflow-x-auto",
"md:overflow-x-visible",
// Lists
"[&_ul]:list-disc [&_ul]:ml-6 [&_ul]:my-2 [&_ul]:list-outside",
"[&_ol]:list-decimal [&_ol]:ml-6 [&_ol]:my-2 [&_ol]:list-outside",
"[&_li]:my-1 [&_li]:pl-2",
// Table wrapper for scrolling
"[&_.table-wrapper]:overflow-x-auto [&_.table-wrapper]:max-w-full [&_.table-wrapper]:my-2 [&_.table-wrapper]:-mx-2 [&_.table-wrapper]:px-2",
"[&_.table-wrapper_table]:border-collapse [&_.table-wrapper_table]:border [&_.table-wrapper_table]:border-gray-300 [&_.table-wrapper_table]:min-w-full",
"[&_.table-wrapper_table_td]:border [&_.table-wrapper_table_td]:border-gray-300 [&_.table-wrapper_table_td]:px-3 [&_.table-wrapper_table_td]:py-2 [&_.table-wrapper_table_td]:text-sm [&_.table-wrapper_table_td]:whitespace-nowrap",
"[&_.table-wrapper_table_th]:border [&_.table-wrapper_table_th]:border-gray-300 [&_.table-wrapper_table_th]:px-3 [&_.table-wrapper_table_th]:py-2 [&_.table-wrapper_table_th]:bg-gray-50 [&_.table-wrapper_table_th]:font-semibold [&_.table-wrapper_table_th]:text-sm [&_.table-wrapper_table_th]:text-left [&_.table-wrapper_table_th]:whitespace-nowrap",
"[&_.table-wrapper_table_tr:nth-child(even)]:bg-gray-50",
// Direct table styles (fallback for tables not wrapped)
"[&_table]:border-collapse [&_table]:my-2 [&_table]:border [&_table]:border-gray-300",
"[&_table_td]:border [&_table_td]:border-gray-300 [&_table_td]:px-3 [&_table_td]:py-2 [&_table_td]:text-sm",
"[&_table_th]:border [&_table_th]:border-gray-300 [&_table_th]:px-3 [&_table_th]:py-2 [&_table_th]:bg-gray-50 [&_table_th]:font-semibold [&_table_th]:text-sm [&_table_th]:text-left",
"[&_table_tr:nth-child(even)]:bg-gray-50",
// Text formatting
"[&_p]:my-1 [&_p]:leading-relaxed",
"[&_strong]:font-bold",
"[&_em]:italic",
"[&_u]:underline",
"[&_h1]:text-xl [&_h1]:font-bold [&_h1]:my-2",
"[&_h2]:text-lg [&_h2]:font-semibold [&_h2]:my-2",
"[&_h3]:text-base [&_h3]:font-semibold [&_h3]:my-1",
className
)}
dangerouslySetInnerHTML={{ __html: processedContent }}
/>
);
}