feat: add zoom controls and reset functionality to workflow editor toolbar

This commit is contained in:
mlogclub
2026-06-28 10:55:28 +08:00
parent af9c28033a
commit 6a0e65bc07
2 changed files with 86 additions and 0 deletions
@@ -3,6 +3,9 @@
import type { ReactNode } from "react" import type { ReactNode } from "react"
import { import {
CheckCircle2Icon, CheckCircle2Icon,
Maximize2Icon,
MinusIcon,
PlusIcon,
RotateCcwIcon, RotateCcwIcon,
SaveIcon, SaveIcon,
SendIcon, SendIcon,
@@ -11,6 +14,7 @@ import {
} from "lucide-react" } from "lucide-react"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import type { WorkflowDraftValidation } from "./workflow-utils" import type { WorkflowDraftValidation } from "./workflow-utils"
@@ -26,6 +30,11 @@ export function WorkflowEditorToolbar({
redoDisabled = false, redoDisabled = false,
onAutoLayout, onAutoLayout,
autoLayoutDisabled = false, autoLayoutDisabled = false,
zoomPercent,
onZoomIn,
onZoomOut,
onResetZoom,
onFitView,
onRestoreDefault, onRestoreDefault,
restoreDefaultDisabled = false, restoreDefaultDisabled = false,
onValidate, onValidate,
@@ -45,6 +54,11 @@ export function WorkflowEditorToolbar({
redoDisabled?: boolean redoDisabled?: boolean
onAutoLayout?: () => void onAutoLayout?: () => void
autoLayoutDisabled?: boolean autoLayoutDisabled?: boolean
zoomPercent?: string
onZoomIn?: () => void
onZoomOut?: () => void
onResetZoom?: () => void
onFitView?: () => void
onRestoreDefault?: () => void onRestoreDefault?: () => void
restoreDefaultDisabled?: boolean restoreDefaultDisabled?: boolean
onValidate?: () => void onValidate?: () => void
@@ -72,6 +86,35 @@ export function WorkflowEditorToolbar({
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
{toolbarExtra} {toolbarExtra}
<div className="flex h-7 items-center rounded-lg border bg-muted/30 p-0.5">
<WorkflowToolbarIconButton label="缩小" onClick={onZoomOut}>
<MinusIcon className="size-3.5" />
</WorkflowToolbarIconButton>
<Tooltip>
<TooltipTrigger
render={
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 w-12 px-1 text-xs tabular-nums"
aria-label="重置为 100%"
disabled={!onResetZoom}
onClick={onResetZoom}
/>
}
>
{zoomPercent ?? "100%"}
</TooltipTrigger>
<TooltipContent> 100%</TooltipContent>
</Tooltip>
<WorkflowToolbarIconButton label="放大" onClick={onZoomIn}>
<PlusIcon className="size-3.5" />
</WorkflowToolbarIconButton>
<WorkflowToolbarIconButton label="适配画布" onClick={onFitView}>
<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 px-2 text-xs" disabled={undoDisabled} onClick={onUndo}>
<Undo2Icon className="size-3.5" /> <Undo2Icon className="size-3.5" />
@@ -102,3 +145,34 @@ export function WorkflowEditorToolbar({
</div> </div>
) )
} }
function WorkflowToolbarIconButton({
label,
onClick,
children,
}: {
label: string
onClick?: () => void
children: ReactNode
}) {
return (
<Tooltip>
<TooltipTrigger
render={
<Button
type="button"
variant="ghost"
size="icon-xs"
className="size-6"
aria-label={label}
disabled={!onClick}
onClick={onClick}
/>
}
>
{children}
</TooltipTrigger>
<TooltipContent>{label}</TooltipContent>
</Tooltip>
)
}
@@ -191,6 +191,7 @@ function WorkflowEditorInner({
const workflowDocument = useService(WorkflowDocument) const workflowDocument = useService(WorkflowDocument)
const selectService = useService(WorkflowSelectService) const selectService = useService(WorkflowSelectService)
const [autoLayouting, setAutoLayouting] = useState(false) const [autoLayouting, setAutoLayouting] = useState(false)
const zoomPercent = `${Math.round(playgroundTools.zoom * 100)}%`
useEffect(() => { useEffect(() => {
const disposable = selectService.onSelectionChanged(() => { const disposable = selectService.onSelectionChanged(() => {
@@ -248,6 +249,12 @@ function WorkflowEditorInner({
} }
} }
const resetZoom = () => {
context.playground.config.updateConfig({
zoom: 1,
})
}
const closeConfigPanel = () => { const closeConfigPanel = () => {
selectService.clear() selectService.clear()
onSelectNode("") onSelectNode("")
@@ -273,6 +280,11 @@ function WorkflowEditorInner({
redoDisabled={redoDisabled} redoDisabled={redoDisabled}
onAutoLayout={() => void autoLayout()} onAutoLayout={() => void autoLayout()}
autoLayoutDisabled={autoLayouting || definition.nodes.length < 2} autoLayoutDisabled={autoLayouting || definition.nodes.length < 2}
zoomPercent={zoomPercent}
onZoomIn={() => playgroundTools.zoomin(true)}
onZoomOut={() => playgroundTools.zoomout(true)}
onResetZoom={resetZoom}
onFitView={() => playgroundTools.fitView(true)}
onRestoreDefault={onRestoreDefault} onRestoreDefault={onRestoreDefault}
restoreDefaultDisabled={restoreDefaultDisabled} restoreDefaultDisabled={restoreDefaultDisabled}
onValidate={onValidate} onValidate={onValidate}