feat(workbench): integrate runtime data and map tooling
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import type { ComponentProps, ReactNode } from "react";
|
||||
import { AlertCircle, CheckCircle2, CircleHelp, Info, TriangleAlert } from "lucide-react";
|
||||
|
||||
import { cn } from "@/shared/ui/cn";
|
||||
|
||||
export type WorkbenchTone = "neutral" | "info" | "success" | "warning" | "danger" | "teal";
|
||||
|
||||
const sectionTone: Record<WorkbenchTone, string> = {
|
||||
neutral: "bg-slate-100 text-slate-600",
|
||||
info: "bg-blue-50 text-blue-700",
|
||||
success: "bg-emerald-50 text-emerald-700",
|
||||
warning: "bg-amber-50 text-amber-700",
|
||||
danger: "bg-rose-50 text-rose-700",
|
||||
teal: "bg-cyan-50 text-cyan-800"
|
||||
};
|
||||
|
||||
const noticeTone: Record<Exclude<WorkbenchTone, "teal">, string> = {
|
||||
neutral: "border-slate-300 bg-slate-50 text-slate-700",
|
||||
info: "border-blue-500 bg-blue-50/80 text-blue-950",
|
||||
success: "border-emerald-500 bg-emerald-50/80 text-emerald-950",
|
||||
warning: "border-amber-500 bg-amber-50/90 text-amber-950",
|
||||
danger: "border-rose-500 bg-rose-50/90 text-rose-950"
|
||||
};
|
||||
|
||||
const noticeIcon = {
|
||||
neutral: CircleHelp,
|
||||
info: Info,
|
||||
success: CheckCircle2,
|
||||
warning: TriangleAlert,
|
||||
danger: AlertCircle
|
||||
};
|
||||
|
||||
export function WorkbenchSection({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
meta,
|
||||
actions,
|
||||
tone = "neutral",
|
||||
bodyClassName,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<"section"> & {
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
meta?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
tone?: WorkbenchTone;
|
||||
bodyClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
"overflow-hidden rounded-[14px] border border-slate-200/90 bg-[var(--surface-reading)] shadow-[0_1px_2px_rgba(15,23,42,0.06)]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex min-h-14 items-start gap-3 border-b border-slate-200/75 px-3.5 py-3">
|
||||
{icon ? (
|
||||
<span className={cn("mt-0.5 grid size-7 shrink-0 place-items-center rounded-lg", sectionTone[tone])}>
|
||||
{icon}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<h3 className="text-[13px] font-semibold leading-5 text-slate-900">{title}</h3>
|
||||
{meta}
|
||||
</div>
|
||||
{description ? <p className="mt-0.5 text-xs leading-5 text-slate-500">{description}</p> : null}
|
||||
</div>
|
||||
{actions ? <div className="flex shrink-0 items-center gap-1">{actions}</div> : null}
|
||||
</div>
|
||||
<div className={cn("p-3.5", bodyClassName)}>{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkbenchNotice({
|
||||
tone = "info",
|
||||
title,
|
||||
icon,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: ComponentProps<"div"> & {
|
||||
tone?: Exclude<WorkbenchTone, "teal">;
|
||||
title?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
}) {
|
||||
const Icon = noticeIcon[tone];
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-w-0 gap-2.5 rounded-r-lg border-l-2 px-3 py-2.5 text-xs leading-5",
|
||||
noticeTone[tone],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="mt-0.5 grid size-4 shrink-0 place-items-center" aria-hidden="true">
|
||||
{icon ?? <Icon size={15} />}
|
||||
</span>
|
||||
<div className="min-w-0 flex-1 break-words">
|
||||
{title ? <p className="font-semibold">{title}</p> : null}
|
||||
<div className={cn(title && "mt-0.5")}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkbenchEmptyState({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
action,
|
||||
className
|
||||
}: {
|
||||
icon?: ReactNode;
|
||||
title: ReactNode;
|
||||
description?: ReactNode;
|
||||
action?: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className={cn("rounded-xl border border-dashed border-slate-300 bg-slate-50/70 px-4 py-5 text-center", className)}>
|
||||
{icon ? <span className="mx-auto grid size-8 place-items-center rounded-full bg-white text-slate-500 shadow-xs">{icon}</span> : null}
|
||||
<p className={cn("text-xs font-semibold text-slate-700", icon && "mt-2")}>{title}</p>
|
||||
{description ? <p className="mx-auto mt-1 max-w-72 text-[11px] leading-5 text-slate-500">{description}</p> : null}
|
||||
{action ? <div className="mt-3 flex justify-center">{action}</div> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user