Files
Genarrative/src/components/common/LegalDocumentModal.tsx
kdletters 1ad25e30f8 收口前端平台组件库能力
新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件
迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome
补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
2026-06-10 10:24:18 +08:00

127 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { FileText } from 'lucide-react';
import type { PlatformTheme } from '../../../packages/shared/src/contracts/runtime';
import type {
LegalDocument,
LegalDocumentBlock,
} from './legalDocuments';
import { PlatformActionButton } from './PlatformActionButton';
import { UnifiedModal } from './UnifiedModal';
type LegalDocumentModalProps = {
document: LegalDocument | null;
open: boolean;
platformTheme?: PlatformTheme;
zIndexClassName?: string;
onClose: () => void;
};
function LegalRichText({ text }: { text: string }) {
const parts = text.split(/(\*\*[^*]+\*\*)/gu);
return (
<>
{parts.map((part, index) => {
const boldMatch = /^\*\*([^*]+)\*\*$/u.exec(part);
if (boldMatch) {
return (
<strong
key={`${index}:${part}`}
className="font-black text-[var(--platform-text-strong)]"
>
{boldMatch[1]}
</strong>
);
}
return part;
})}
</>
);
}
function LegalDocumentBodyBlock({
block,
}: {
block: LegalDocumentBlock;
}) {
if (block.type === 'heading') {
const className =
block.level === 2
? 'mt-5 text-base font-black leading-7 text-[var(--platform-text-strong)] first:mt-0'
: 'mt-4 text-sm font-black leading-6 text-[var(--platform-text-strong)] first:mt-0';
return <div className={className}>{block.text}</div>;
}
if (block.type === 'list') {
return (
<ul className="mt-3 list-disc space-y-2 pl-5 text-sm leading-7 text-[var(--platform-text-base)]">
{block.items.map((item, index) => (
<li key={`${index}:${item}`}>
<LegalRichText text={item} />
</li>
))}
</ul>
);
}
return (
<p className="mt-3 whitespace-pre-line text-sm leading-7 text-[var(--platform-text-base)]">
<LegalRichText text={block.text} />
</p>
);
}
export function LegalDocumentModal({
document,
open,
platformTheme,
zIndexClassName,
onClose,
}: LegalDocumentModalProps) {
return (
<UnifiedModal
open={open && Boolean(document)}
title={document?.title ?? '法律信息'}
onClose={onClose}
size="md"
closeLabel="关闭法律信息"
zIndexClassName={zIndexClassName ?? 'z-[150]'}
overlayClassName={`platform-theme ${
platformTheme ? `platform-theme--${platformTheme}` : ''
}`}
panelClassName="platform-remap-surface rounded-t-[1.4rem] sm:rounded-[1.4rem]"
headerClassName="items-center"
bodyClassName="px-4 py-0 sm:px-5"
footerClassName="justify-stretch sm:justify-end"
footer={
<PlatformActionButton
onClick={onClose}
tone="secondary"
fullWidth
className="min-h-0 rounded-[0.9rem] sm:w-auto"
>
</PlatformActionButton>
}
>
<div className="max-h-[min(64vh,34rem)] overflow-y-auto py-4 pr-1">
<div className="mb-4 flex items-center gap-2 text-[var(--platform-cool-text)]">
<FileText className="h-4 w-4" />
<span className="text-xs font-black tracking-[0.2em]">
LEGAL
</span>
</div>
{document?.blocks.map((block, index) => (
<LegalDocumentBodyBlock
// 中文注释:法律文本没有稳定段落 id用序号只限定在静态文档渲染列表内。
key={`${block.type}:${index}`}
block={block}
/>
))}
</div>
</UnifiedModal>
);
}