feat: improve UI components with consistent styling and layout adjustments
This commit is contained in:
@@ -86,31 +86,8 @@ export function useFlowgramEditorProps({
|
|||||||
plugins: () => [
|
plugins: () => [
|
||||||
createMinimapPlugin({
|
createMinimapPlugin({
|
||||||
disableLayer: true,
|
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">
|
<div className="flex w-full flex-col">
|
||||||
<Field<string> name="title">
|
<Field<string> name="title">
|
||||||
{({ field }) => (
|
{({ 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}
|
{field.value || fallbackTitle}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ function ConditionNodeForm({
|
|||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col">
|
||||||
<Field<string> name="title">
|
<Field<string> name="title">
|
||||||
{({ field }) => (
|
{({ 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}
|
{field.value || fallbackTitle}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -129,16 +129,16 @@ function ConditionNodeForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-4 py-3">
|
<div className="px-3 py-2.5">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{branches.map((branch, index) => (
|
{branches.map((branch, index) => (
|
||||||
<div
|
<div
|
||||||
key={branch.id}
|
key={branch.id}
|
||||||
className={[
|
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
|
selectedBranch?.nodeId === nodeId && selectedBranch.branchId === branch.id
|
||||||
? "bg-[#dfe4ff] text-[#3327b9] ring-1 ring-inset ring-[#4e40e5]/45 before:w-1"
|
? "border-[var(--g-selection-background)] bg-muted"
|
||||||
: "bg-[#eef1f7] hover:bg-[#e6eaff]",
|
: "border-transparent bg-muted/60 hover:bg-muted",
|
||||||
].join(" ")}
|
].join(" ")}
|
||||||
onPointerDownCapture={(event) => {
|
onPointerDownCapture={(event) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@@ -154,7 +154,7 @@ function ConditionNodeForm({
|
|||||||
<span className="min-w-0 flex-1 truncate font-medium text-foreground/90">
|
<span className="min-w-0 flex-1 truncate font-medium text-foreground/90">
|
||||||
{branch.name || (branch.default ? "默认分支" : branch.id)}
|
{branch.name || (branch.default ? "默认分支" : branch.id)}
|
||||||
</span>
|
</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"}
|
{branch.default ? "else" : index === 0 ? "if" : "elseif"}
|
||||||
</span>
|
</span>
|
||||||
{branch.default ? null : (
|
{branch.default ? null : (
|
||||||
|
|||||||
@@ -25,13 +25,13 @@ export function FlowgramNodeRenderer(props: WorkflowNodeProps) {
|
|||||||
<WorkflowNodeRenderer
|
<WorkflowNodeRenderer
|
||||||
node={props.node}
|
node={props.node}
|
||||||
className={cn(
|
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",
|
"w-[320px] overflow-visible rounded-md border bg-background shadow-sm transition-colors",
|
||||||
selected ? "border-[#4e40e5]" : "border-[rgba(6,7,9,0.15)]"
|
selected ? "border-[var(--g-selection-background)]" : "border-border"
|
||||||
)}
|
)}
|
||||||
style={{ padding: 0 }}
|
style={{ padding: 0 }}
|
||||||
portPrimaryColor="#4e40e5"
|
portPrimaryColor="var(--g-selection-background)"
|
||||||
portSecondaryColor="#d0d5dd"
|
portSecondaryColor="#c9cdd4"
|
||||||
portBackgroundColor="#fff"
|
portBackgroundColor="hsl(var(--background))"
|
||||||
>
|
>
|
||||||
{form?.render()}
|
{form?.render()}
|
||||||
</WorkflowNodeRenderer>
|
</WorkflowNodeRenderer>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export function WorkflowConfigPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pointer-events-none absolute inset-y-3 right-3 z-50 flex w-[360px] max-w-[calc(100%-1.5rem)]">
|
<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="flex shrink-0 items-start justify-between gap-3 border-b px-4 py-3">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="text-sm font-medium">{selectedBranchItem ? "条件属性" : "属性"}</div>
|
<div className="text-sm font-medium">{selectedBranchItem ? "条件属性" : "属性"}</div>
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ export function WorkflowEditorToolbar({
|
|||||||
publishDisabled?: boolean
|
publishDisabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
return (
|
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-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 items-center gap-2">
|
<div className="flex min-w-0 shrink items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1 rounded-sm px-2 py-1 text-xs",
|
"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" />
|
<CheckCircle2Icon className="size-3" />
|
||||||
@@ -84,9 +84,9 @@ export function WorkflowEditorToolbar({
|
|||||||
{nodeCount} 个节点 / {edgeCount} 条连线
|
{nodeCount} 个节点 / {edgeCount} 条连线
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex min-w-0 items-center gap-1 overflow-x-auto">
|
||||||
{toolbarExtra}
|
{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}>
|
<WorkflowToolbarIconButton label="缩小" onClick={onZoomOut}>
|
||||||
<MinusIcon className="size-3.5" />
|
<MinusIcon className="size-3.5" />
|
||||||
</WorkflowToolbarIconButton>
|
</WorkflowToolbarIconButton>
|
||||||
@@ -115,29 +115,29 @@ export function WorkflowEditorToolbar({
|
|||||||
<Maximize2Icon className="size-3.5" />
|
<Maximize2Icon className="size-3.5" />
|
||||||
</WorkflowToolbarIconButton>
|
</WorkflowToolbarIconButton>
|
||||||
</div>
|
</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" />
|
<Undo2Icon className="size-3.5" />
|
||||||
撤销
|
撤销
|
||||||
</Button>
|
</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>
|
||||||
<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" />
|
<SparklesIcon className="size-3.5" />
|
||||||
自动布局
|
自动布局
|
||||||
</Button>
|
</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" />
|
<RotateCcwIcon className="size-3.5" />
|
||||||
恢复默认
|
恢复默认
|
||||||
</Button>
|
</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>
|
||||||
<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" />
|
<SaveIcon className="size-3.5" />
|
||||||
保存
|
保存
|
||||||
</Button>
|
</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" />
|
<SendIcon className="size-3.5" />
|
||||||
发布
|
发布
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
} from "@flowgram.ai/free-layout-editor"
|
} from "@flowgram.ai/free-layout-editor"
|
||||||
|
|
||||||
import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin"
|
import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin"
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
import { useFlowgramEditorProps } from "./flowgram-editor-provider"
|
import { useFlowgramEditorProps } from "./flowgram-editor-provider"
|
||||||
import {
|
import {
|
||||||
@@ -264,11 +265,16 @@ function WorkflowEditorInner({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-workflow-editor-root
|
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} />
|
<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
|
<WorkflowEditorToolbar
|
||||||
validation={validation}
|
validation={validation}
|
||||||
nodeCount={definition.nodes.length}
|
nodeCount={definition.nodes.length}
|
||||||
@@ -294,11 +300,10 @@ function WorkflowEditorInner({
|
|||||||
onPublish={onPublish}
|
onPublish={onPublish}
|
||||||
publishDisabled={publishDisabled}
|
publishDisabled={publishDisabled}
|
||||||
/>
|
/>
|
||||||
<div className="relative min-h-0 flex-1 overflow-hidden">
|
|
||||||
<EditorRenderer className="h-full w-full" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<EditorRenderer className="h-full w-full" />
|
||||||
|
|
||||||
<WorkflowConfigPanel
|
<WorkflowConfigPanel
|
||||||
definition={definition}
|
definition={definition}
|
||||||
nodeSpecs={nodeSpecs}
|
nodeSpecs={nodeSpecs}
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ export function WorkflowNodePalette({
|
|||||||
onAddNode: (spec: AIWorkflowNodeSpec) => void
|
onAddNode: (spec: AIWorkflowNodeSpec) => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
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="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>
|
||||||
<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">
|
<div className="space-y-1">
|
||||||
{nodeSpecs.map((spec) => (
|
{nodeSpecs.map((spec) => (
|
||||||
<button
|
<button
|
||||||
key={spec.type}
|
key={spec.type}
|
||||||
type="button"
|
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)}
|
onClick={() => onAddNode(spec)}
|
||||||
>
|
>
|
||||||
<PlusIcon className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
|
<PlusIcon className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
|
||||||
|
|||||||
Reference in New Issue
Block a user