import { useState, type ComponentType, type ReactNode } from 'react'; import { AlertCircle, Info, Loader2, SearchX, X } from 'lucide-react'; import { AnimatePresence, motion } from 'motion/react'; import { cn } from '../../lib/cn'; export type SurfaceIcon = ComponentType<{ size?: number; className?: string }>; export function PageFrame({ title, subtitle, icon: Icon, eyebrow, meta, actions, children, maxWidth = 'max-w-6xl', compactInfo = false, }: { title: string; subtitle?: string; icon?: SurfaceIcon; eyebrow?: string; meta?: ReactNode; actions?: ReactNode; children: ReactNode; maxWidth?: string; compactInfo?: boolean; }) { const [infoOpen, setInfoOpen] = useState(false); return (
{compactInfo ? ( <>
{Icon ? ( ) : null}
{eyebrow ? {eyebrow} : null} {meta ? {meta} : null}

{title}

{actions ?
{actions}
: null} {subtitle ? ( ) : null}
{infoOpen && subtitle ? (
{subtitle}
) : null}
) : (
{Icon ? ( ) : null} {eyebrow ? {eyebrow} : null} {meta ? {meta} : null}

{title}

{subtitle ?

{subtitle}

: null}
{actions ?
{actions}
: null}
)} {children}
); } export function SurfaceCard({ title, subtitle, actions, children, className, }: { title?: string; subtitle?: string; actions?: ReactNode; children: ReactNode; className?: string; }) { return (
{(title || subtitle || actions) && (
{title ?

{title}

: null} {subtitle ?

{subtitle}

: null}
{actions ?
{actions}
: null}
)} {children}
); } export function MetricTile({ label, value, unit, helper, icon: Icon, tone = 'blue', }: { label: string; value: ReactNode; unit?: string; helper?: string; icon?: SurfaceIcon; tone?: 'blue' | 'emerald' | 'amber' | 'rose' | 'slate'; }) { const toneClass = { blue: 'bg-blue-50 text-blue-600 ring-blue-100', emerald: 'bg-emerald-50 text-emerald-600 ring-emerald-100', amber: 'bg-amber-50 text-amber-600 ring-amber-100', rose: 'bg-rose-50 text-rose-600 ring-rose-100', slate: 'bg-slate-100 text-slate-600 ring-slate-200', }[tone]; return (
{label}
{Icon ? ( ) : null}
{value} {unit ? {unit} : null}
{helper ?
{helper}
: null}
); } export function SegmentedNav({ tabs, active, onChange, className, }: { tabs: readonly { id: T; label: string; icon?: SurfaceIcon }[]; active: T; onChange: (id: T) => void; className?: string; }) { return (
{tabs.map(({ id, label, icon: Icon }) => { const isActive = active === id; return ( ); })}
); } export function SkeletonBlock({ className }: { className?: string }) { return
; } export function LoadingState({ label = '数据加载中' }: { label?: string }) { return (
{label}
); } export function EmptyState({ title = '暂无数据', description = '换个筛选条件或稍后再试', }: { title?: string; description?: string; }) { return (
{title}
{description}
); } export function ErrorState({ message }: { message: string }) { return (
加载失败
{message}
); } export function FadeIn({ children, className }: { children: ReactNode; className?: string }) { return ( {children} ); }