feat: improve UI components with consistent styling and layout adjustments
This commit is contained in:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user