import ReactECharts from "echarts-for-react"; import type { EChartsOption } from "echarts"; import { motion, useReducedMotion } from "framer-motion"; import type { ComponentType, ReactNode } from "react"; export type MacTone = | "blue" | "emerald" | "amber" | "rose" | "violet" | "slate"; export interface MacAction { label: string; onClick?: () => void; href?: string; } const toneClasses: Record = { amber: { dot: "bg-amber-500", ring: "ring-amber-200", soft: "bg-amber-50 text-amber-700", text: "text-amber-700", }, blue: { dot: "bg-blue-500", ring: "ring-blue-200", soft: "bg-blue-50 text-blue-700", text: "text-blue-700", }, emerald: { dot: "bg-emerald-500", ring: "ring-emerald-200", soft: "bg-emerald-50 text-emerald-700", text: "text-emerald-700", }, rose: { dot: "bg-rose-500", ring: "ring-rose-200", soft: "bg-rose-50 text-rose-700", text: "text-rose-700", }, slate: { dot: "bg-slate-500", ring: "ring-slate-200", soft: "bg-slate-100 text-slate-700", text: "text-slate-700", }, violet: { dot: "bg-violet-500", ring: "ring-violet-200", soft: "bg-violet-50 text-violet-700", text: "text-violet-700", }, }; export function cn(...values: Array) { return values.filter(Boolean).join(" "); } function useEntranceMotion() { const reducedMotion = useReducedMotion(); if (reducedMotion) return {}; return { animate: { opacity: 1, y: 0 }, initial: { opacity: 0, y: 10 }, transition: { duration: 0.32, ease: "easeOut" as const }, }; } function MacChartEmpty({ height }: { height: number }) { return (
暂无图表数据
); } export function MacPageHeader({ actions, description, eyebrow, meta, title, }: { actions?: ReactNode; description?: ReactNode; eyebrow?: ReactNode; meta?: ReactNode; title: ReactNode; }) { return (
{eyebrow ? (
{eyebrow}
) : null}

{title}

{description ? (

{description}

) : null} {meta ?
{meta}
: null}
{actions ?
{actions}
: null}
); } export function MacPanel({ children, className, title, description, action, }: { children: ReactNode; className?: string; title?: ReactNode; description?: ReactNode; action?: ReactNode; }) { return (
{title || description || action ? (
{title ?

{title}

: null} {description ? (

{description}

) : null}
{action ?
{action}
: null}
) : null} {children}
); } export function MacMetricCard({ caption, icon: Icon, label, tone = "blue", value, }: { caption?: ReactNode; icon?: ComponentType<{ size?: string | number; className?: string }>; label: ReactNode; tone?: MacTone; value: ReactNode; }) { const classes = toneClasses[tone]; return (
{Icon ? : }
{label}
{value}
{caption ?
{caption}
: null}
); } export function MacDashboardMetricCard({ caption, delta, icon: Icon, label, tone = "blue", value, }: { caption?: ReactNode; delta?: ReactNode; icon?: ComponentType<{ className?: string; size?: string | number; strokeWidth?: string | number; }>; label: ReactNode; tone?: MacTone; value: ReactNode; }) { const classes = toneClasses[tone]; const motionProps = useEntranceMotion(); return (
{Icon ? : }
{label}
{value}
{caption ? {caption} : null} {delta ? {delta} : null}
); } export function MacChartPanel({ action, children, className, title, }: { action?: ReactNode; children: ReactNode; className?: string; title: ReactNode; }) { const motionProps = useEntranceMotion(); return (

{title}

{action ?
{action}
: null}
{children}
); } export type MacTrendSeries = { color: string; data: number[]; name: string; }; export function MacTrendLineChart({ height = 286, labels, series, }: { height?: number; labels: string[]; series: MacTrendSeries[]; }) { if (!labels.length || !series.length) return ; const option: EChartsOption = { color: series.map(item => item.color), grid: { bottom: 34, left: 44, right: 18, top: 34 }, legend: { icon: "circle", itemHeight: 7, itemWidth: 7, left: 0, textStyle: { color: "#64748b", fontSize: 12 }, top: 0, }, tooltip: { trigger: "axis", backgroundColor: "rgba(15, 23, 42, 0.92)", borderColor: "transparent", textStyle: { color: "#fff" }, }, xAxis: { type: "category", boundaryGap: false, data: labels, axisLine: { lineStyle: { color: "#e2e8f0" } }, axisTick: { show: false }, axisLabel: { color: "#64748b", fontSize: 11 }, }, yAxis: { type: "value", axisLabel: { color: "#64748b", fontSize: 11 }, splitLine: { lineStyle: { color: "#eef2f7" } }, }, series: series.map(item => ({ name: item.name, type: "line", smooth: true, symbol: "circle", symbolSize: 7, data: item.data, lineStyle: { width: 3 }, areaStyle: { opacity: 0.08 }, })), }; return ( ); } export type MacDonutItem = { color: string; label: string; value: number; }; export function MacDonutChart({ centerLabel, centerValue, height = 220, items, }: { centerLabel: string; centerValue: string; height?: number; items: MacDonutItem[]; }) { if (!items.length) return ; const option: EChartsOption = { color: items.map(item => item.color), graphic: [ { type: "text", left: "center", top: "42%", style: { fill: "#0f172a", fontSize: 24, fontWeight: 700, text: centerValue, align: "center", }, }, { type: "text", left: "center", top: "55%", style: { fill: "#64748b", fontSize: 12, text: centerLabel, align: "center", }, }, ], series: [ { type: "pie", radius: ["62%", "82%"], center: ["50%", "50%"], avoidLabelOverlap: true, label: { show: false }, labelLine: { show: false }, data: items.map(item => ({ name: item.label, value: item.value })), }, ], tooltip: { trigger: "item", backgroundColor: "rgba(15, 23, 42, 0.92)", borderColor: "transparent", textStyle: { color: "#fff" }, }, }; return ( ); } export function MacTodoItem({ icon, meta, time, title, tone = "blue", }: { icon?: ReactNode; meta?: ReactNode; time?: ReactNode; title: ReactNode; tone?: MacTone; }) { const classes = toneClasses[tone]; return (
{icon || }
{title}
{meta ?
{meta}
: null}
{time ?
{time}
: null}
); } export function MacActivityItem({ icon, subtitle, time, title, tone = "blue", }: { icon?: ReactNode; subtitle?: ReactNode; time?: ReactNode; title: ReactNode; tone?: MacTone; }) { const classes = toneClasses[tone]; return (
{icon || }
{title}
{subtitle ?
{subtitle}
: null}
{time ?
{time}
: null}
); } export function MacEnvironmentRow({ label, status, value, }: { label: ReactNode; status?: "success" | "warning" | "default"; value: ReactNode; }) { const statusClass = status === "success" ? "text-emerald-600" : status === "warning" ? "text-amber-600" : "text-slate-600"; return (
{label}
{value}
); } export function MacStatusPill({ children, tone = "slate", }: { children: ReactNode; tone?: MacTone; }) { const classes = toneClasses[tone]; return ( {children} ); } export function MacStatePage({ actions, description, fullScreen = false, icon, status, title, }: { actions?: ReactNode; description?: ReactNode; fullScreen?: boolean; icon?: ReactNode; status?: ReactNode; title: ReactNode; }) { return (
{status ?
{status}
: null}
{icon || "OX"}

{title}

{description ? (

{description}

) : null} {actions ?
{actions}
: null}
); } export function MacPrimaryButton({ children, className, onClick, type = "button", }: { children: ReactNode; className?: string; onClick?: () => void; type?: "button" | "submit"; }) { return ( ); } export function MacSecondaryButton({ children, className, onClick, type = "button", }: { children: ReactNode; className?: string; onClick?: () => void; type?: "button" | "submit"; }) { return ( ); } export function MacListItem({ children, icon, tone = "blue", }: { children: ReactNode; icon?: ReactNode; tone?: MacTone; }) { const classes = toneClasses[tone]; return (
{icon || }
{children}
); } export function MacDiagnosticPanel({ data, title = "详细信息", }: { data: unknown; title?: string; }) { return (
{title} 展开查看完整内容
        {JSON.stringify(data, null, 2)}
      
); } export function MacTrendBars({ values, tone = "blue", }: { values: number[]; tone?: MacTone; }) { const color = tone === "emerald" ? "bg-emerald-500" : tone === "amber" ? "bg-amber-500" : tone === "rose" ? "bg-rose-500" : tone === "violet" ? "bg-violet-500" : "bg-blue-500"; const max = Math.max(...values, 1); return (
{values.map((value, index) => (
))}
); }