feat: improve UI components with consistent styling and layout adjustments

This commit is contained in:
mlogclub
2026-06-28 11:29:03 +08:00
parent 6a0e65bc07
commit 5160499fbf
7 changed files with 41 additions and 59 deletions
@@ -86,31 +86,8 @@ export function useFlowgramEditorProps({
plugins: () => [
createMinimapPlugin({
disableLayer: true,
canvasStyle: {
canvasWidth: 150,
canvasHeight: 84,
canvasPadding: 48,
canvasBackground: "rgba(245, 245, 245, 1)",
canvasBorderRadius: 8,
viewportBackground: "rgba(235, 235, 235, 1)",
viewportBorderRadius: 4,
viewportBorderColor: "rgba(201, 201, 201, 1)",
viewportBorderWidth: 1,
viewportBorderDashLength: 2,
nodeColor: "rgba(255, 255, 255, 1)",
nodeBorderRadius: 2,
nodeBorderWidth: 0.145,
nodeBorderColor: "rgba(6, 7, 9, 0.10)",
overlayColor: "rgba(255, 255, 255, 0)",
},
}),
createFreeSnapPlugin({
edgeColor: "#00B2B2",
alignColor: "#00B2B2",
edgeLineWidth: 1,
alignLineWidth: 1,
alignCrossWidth: 8,
}),
createFreeSnapPlugin({}),
],
}
},
@@ -70,12 +70,12 @@ function FlowgramNodeForm({
<div className="flex w-full flex-col">
<Field<string> name="title">
{({ field }) => (
<div className="border-b px-4 py-3 text-sm font-medium leading-5">
<div className="border-b px-3 py-2.5 text-sm font-medium leading-5">
{field.value || fallbackTitle}
</div>
)}
</Field>
<div className="px-4 py-3 text-xs text-muted-foreground">{nodeType}</div>
<div className="px-3 py-2 text-xs text-muted-foreground">{nodeType}</div>
</div>
)
}
@@ -106,7 +106,7 @@ function ConditionNodeForm({
<div className="flex w-full flex-col">
<Field<string> name="title">
{({ field }) => (
<div className="border-b px-4 py-3 text-sm font-medium leading-5">
<div className="border-b px-3 py-2.5 text-sm font-medium leading-5">
{field.value || fallbackTitle}
</div>
)}
@@ -129,16 +129,16 @@ function ConditionNodeForm({
}
return (
<div className="px-4 py-3">
<div className="px-3 py-2.5">
<div className="space-y-2">
{branches.map((branch, index) => (
<div
key={branch.id}
className={[
"relative flex min-h-10 cursor-pointer items-center gap-2 rounded-md px-3 text-xs transition-colors before:absolute before:bottom-2 before:left-0 before:top-2 before:w-0.5 before:rounded-full before:bg-[#4e40e5]",
"relative flex min-h-9 cursor-pointer items-center gap-2 rounded-sm border px-2.5 text-xs transition-colors",
selectedBranch?.nodeId === nodeId && selectedBranch.branchId === branch.id
? "bg-[#dfe4ff] text-[#3327b9] ring-1 ring-inset ring-[#4e40e5]/45 before:w-1"
: "bg-[#eef1f7] hover:bg-[#e6eaff]",
? "border-[var(--g-selection-background)] bg-muted"
: "border-transparent bg-muted/60 hover:bg-muted",
].join(" ")}
onPointerDownCapture={(event) => {
event.stopPropagation()
@@ -154,7 +154,7 @@ function ConditionNodeForm({
<span className="min-w-0 flex-1 truncate font-medium text-foreground/90">
{branch.name || (branch.default ? "默认分支" : branch.id)}
</span>
<span className="shrink-0 rounded-sm bg-[#e7e9f3] px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
<span className="shrink-0 rounded-sm border bg-background px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
{branch.default ? "else" : index === 0 ? "if" : "elseif"}
</span>
{branch.default ? null : (
@@ -25,13 +25,13 @@ export function FlowgramNodeRenderer(props: WorkflowNodeProps) {
<WorkflowNodeRenderer
node={props.node}
className={cn(
"w-[360px] overflow-visible rounded-lg border bg-background shadow-[0_2px_6px_rgba(0,0,0,0.04),0_4px_12px_rgba(0,0,0,0.02)] transition-colors",
selected ? "border-[#4e40e5]" : "border-[rgba(6,7,9,0.15)]"
"w-[320px] overflow-visible rounded-md border bg-background shadow-sm transition-colors",
selected ? "border-[var(--g-selection-background)]" : "border-border"
)}
style={{ padding: 0 }}
portPrimaryColor="#4e40e5"
portSecondaryColor="#d0d5dd"
portBackgroundColor="#fff"
portPrimaryColor="var(--g-selection-background)"
portSecondaryColor="#c9cdd4"
portBackgroundColor="hsl(var(--background))"
>
{form?.render()}
</WorkflowNodeRenderer>
@@ -61,7 +61,7 @@ export function WorkflowConfigPanel({
return (
<div className="pointer-events-none absolute inset-y-3 right-3 z-50 flex w-[360px] max-w-[calc(100%-1.5rem)]">
<section className="pointer-events-auto flex min-h-0 w-full flex-col overflow-hidden rounded-lg border bg-background shadow-2xl">
<section className="pointer-events-auto flex min-h-0 w-full flex-col overflow-hidden rounded-md border bg-background/95 shadow-sm backdrop-blur">
<div className="flex shrink-0 items-start justify-between gap-3 border-b px-4 py-3">
<div className="min-w-0">
<div className="text-sm font-medium">{selectedBranchItem ? "条件属性" : "属性"}</div>
@@ -69,12 +69,12 @@ export function WorkflowEditorToolbar({
publishDisabled?: boolean
}) {
return (
<div className="relative z-50 flex h-10 shrink-0 items-center justify-between border-b bg-background px-2">
<div className="flex min-w-0 items-center gap-2">
<div className="flex min-h-10 max-w-full items-center justify-between gap-3 rounded-md border bg-background/95 px-2 py-1 shadow-sm backdrop-blur">
<div className="flex min-w-0 shrink items-center gap-2">
<span
className={cn(
"inline-flex items-center gap-1 rounded-sm px-2 py-1 text-xs",
validation.valid ? "bg-emerald-50 text-emerald-700" : "bg-amber-50 text-amber-700"
validation.valid ? "bg-muted text-muted-foreground" : "bg-amber-50 text-amber-700"
)}
>
<CheckCircle2Icon className="size-3" />
@@ -84,9 +84,9 @@ export function WorkflowEditorToolbar({
{nodeCount} / {edgeCount} 线
</span>
</div>
<div className="flex items-center gap-1">
<div className="flex min-w-0 items-center gap-1 overflow-x-auto">
{toolbarExtra}
<div className="flex h-7 items-center rounded-lg border bg-muted/30 p-0.5">
<div className="flex h-7 shrink-0 items-center rounded-md border bg-muted/30 p-0.5">
<WorkflowToolbarIconButton label="缩小" onClick={onZoomOut}>
<MinusIcon className="size-3.5" />
</WorkflowToolbarIconButton>
@@ -115,29 +115,29 @@ export function WorkflowEditorToolbar({
<Maximize2Icon className="size-3.5" />
</WorkflowToolbarIconButton>
</div>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={undoDisabled} onClick={onUndo}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={undoDisabled} onClick={onUndo}>
<Undo2Icon className="size-3.5" />
</Button>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={redoDisabled} onClick={onRedo}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={redoDisabled} onClick={onRedo}>
</Button>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={autoLayoutDisabled} onClick={onAutoLayout}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={autoLayoutDisabled} onClick={onAutoLayout}>
<SparklesIcon className="size-3.5" />
</Button>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={restoreDefaultDisabled} onClick={onRestoreDefault}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={restoreDefaultDisabled} onClick={onRestoreDefault}>
<RotateCcwIcon className="size-3.5" />
</Button>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={validateDisabled} onClick={onValidate}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={validateDisabled} onClick={onValidate}>
</Button>
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs" disabled={saveDraftDisabled} onClick={onSaveDraft}>
<Button type="button" variant="ghost" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={saveDraftDisabled} onClick={onSaveDraft}>
<SaveIcon className="size-3.5" />
</Button>
<Button type="button" size="sm" className="h-7 px-2 text-xs" disabled={publishDisabled} onClick={onPublish}>
<Button type="button" size="sm" className="h-7 shrink-0 px-2 text-xs" disabled={publishDisabled} onClick={onPublish}>
<SendIcon className="size-3.5" />
</Button>
@@ -15,6 +15,7 @@ import {
} from "@flowgram.ai/free-layout-editor"
import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin"
import { cn } from "@/lib/utils"
import { useFlowgramEditorProps } from "./flowgram-editor-provider"
import {
@@ -264,11 +265,16 @@ function WorkflowEditorInner({
return (
<div
data-workflow-editor-root
className="relative isolate flex h-full min-h-0 w-full flex-1 overflow-hidden border bg-background"
className="relative isolate h-full min-h-0 w-full flex-1 overflow-hidden border bg-[var(--g-editor-background)]"
>
<WorkflowNodePalette nodeSpecs={nodeSpecs} onAddNode={addNode} />
<div className="flex min-h-0 min-w-0 flex-1 flex-col">
<div
className={cn(
"absolute left-[17.5rem] top-3 z-50 max-w-[calc(100%-18.25rem)]",
selectedNodeId ? "right-[23.25rem]" : "right-3"
)}
>
<WorkflowEditorToolbar
validation={validation}
nodeCount={definition.nodes.length}
@@ -294,11 +300,10 @@ function WorkflowEditorInner({
onPublish={onPublish}
publishDisabled={publishDisabled}
/>
<div className="relative min-h-0 flex-1 overflow-hidden">
<EditorRenderer className="h-full w-full" />
</div>
</div>
<EditorRenderer className="h-full w-full" />
<WorkflowConfigPanel
definition={definition}
nodeSpecs={nodeSpecs}
@@ -12,17 +12,17 @@ export function WorkflowNodePalette({
onAddNode: (spec: AIWorkflowNodeSpec) => void
}) {
return (
<aside className="relative z-50 flex w-60 shrink-0 flex-col border-r bg-muted/20">
<aside className="absolute left-3 top-3 z-50 flex max-h-[calc(100%-1.5rem)] w-64 shrink-0 flex-col overflow-hidden rounded-md border bg-background/95 shadow-sm backdrop-blur">
<div className="border-b px-3 py-2">
<div className="text-sm font-medium"></div>
<div className="text-xs font-medium text-muted-foreground"></div>
</div>
<div className="min-h-0 flex-1 overflow-y-auto p-2">
<div className="min-h-0 flex-1 overflow-y-auto p-1.5">
<div className="space-y-1">
{nodeSpecs.map((spec) => (
<button
key={spec.type}
type="button"
className="flex w-full items-start gap-2 rounded-md border bg-background px-2 py-2 text-left text-sm hover:bg-muted"
className="flex w-full items-start gap-2 rounded-sm px-2 py-2 text-left text-sm hover:bg-muted"
onClick={() => onAddNode(spec)}
>
<PlusIcon className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />