From 54e2468c7a306e787188f515c555c0c5508a0cba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 23 Nov 2025 10:57:29 +0000 Subject: [PATCH] Refactor: Replace custom toolbar with Flowgram-style tools component Following Flowgram demo structure: - Created tools component directory with index, styles, zoom-select, add-node-dropdown - Implemented FloatingTools positioned at bottom-left (like Flowgram demo) - Added undo/redo buttons with history integration - Added zoom selector dropdown (50%, 100%, 150%, 200%) - Added fit-view button for canvas fitting - Added Add Node dropdown with all node types - Updated FlowgramEditor to use new tools component - Removed old toolbar from editor - Added 6 new translation keys (zh-CN + en-US) - Following exact Flowgram UI patterns with styled-components Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- src/locales/en-US.json | 10 +- src/locales/zh-CN.json | 10 +- .../scenario/flowgram/FlowgramEditor.tsx | 99 +------------------ .../components/tools/add-node-dropdown.tsx | 93 +++++++++++++++++ .../flowgram/components/tools/index.tsx | 67 +++++++++++++ .../flowgram/components/tools/styles.tsx | 54 ++++++++++ .../flowgram/components/tools/zoom-select.tsx | 65 ++++++++++++ src/pages/scenario/flowgram/editor.less | 40 -------- 8 files changed, 297 insertions(+), 141 deletions(-) create mode 100644 src/pages/scenario/flowgram/components/tools/add-node-dropdown.tsx create mode 100644 src/pages/scenario/flowgram/components/tools/index.tsx create mode 100644 src/pages/scenario/flowgram/components/tools/styles.tsx create mode 100644 src/pages/scenario/flowgram/components/tools/zoom-select.tsx diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 9567abe3..93e8d054 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -617,5 +617,11 @@ "放大": "Zoom In", "缩小": "Zoom Out", "适应画布": "Fit to Canvas", - "条件": "Condition" -} + "条件": "Condition", + "scenario_add_node": "Add Node", + "scenario_http_node": "HTTP Request", + "scenario_script_node": "Script Execution", + "scenario_condition_node": "Condition", + "scenario_delay_node": "Delay", + "scenario_loop_node": "Loop" +} \ No newline at end of file diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 5237c583..fe5f3647 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -617,5 +617,11 @@ "放大": "放大", "缩小": "缩小", "适应画布": "适应画布", - "条件": "条件" -} + "条件": "条件", + "scenario_add_node": "添加节点", + "scenario_http_node": "HTTP 请求", + "scenario_script_node": "脚本执行", + "scenario_condition_node": "条件判断", + "scenario_delay_node": "延迟", + "scenario_loop_node": "循环" +} \ No newline at end of file diff --git a/src/pages/scenario/flowgram/FlowgramEditor.tsx b/src/pages/scenario/flowgram/FlowgramEditor.tsx index 707412e0..307bcf38 100644 --- a/src/pages/scenario/flowgram/FlowgramEditor.tsx +++ b/src/pages/scenario/flowgram/FlowgramEditor.tsx @@ -1,22 +1,10 @@ import React, { useEffect, useImperativeHandle, forwardRef } from 'react'; -import { Button, Tooltip } from 'antd'; -import { - PlusOutlined, - ZoomInOutlined, - ZoomOutOutlined, - FullscreenOutlined, - ApiOutlined, - CodeOutlined, - BranchesOutlined, - ClockCircleOutlined, - SyncOutlined, -} from '@ant-design/icons'; import { EditorRenderer, FreeLayoutEditorProvider } from '@flowgram.ai/free-layout-editor'; import { DockedPanelLayer } from '@flowgram.ai/panel-manager-plugin'; import '@flowgram.ai/free-layout-editor/index.css'; -import intl from 'react-intl-universal'; import { nodeRegistries } from './nodes'; import { useEditorProps } from './hooks/use-editor-props'; +import { FlowgramTools } from './components/tools'; import './editor.less'; export interface FlowgramEditorProps { @@ -53,96 +41,13 @@ const FlowgramEditor = forwardRef( } }, [onChange]); - const handleAddNode = (nodeType: string) => { - // This will be implemented to add nodes via Flowgram API - console.log('Add node:', nodeType); - }; - - const handleZoom = (direction: 'in' | 'out' | 'fit') => { - // This will be implemented to control zoom via Flowgram API - console.log('Zoom:', direction); - }; - return (
-
-
- {intl.get('新建节点')}: - - - - - - - - - - - - - - - -
-
- {intl.get('视图')}: - -
-
+
diff --git a/src/pages/scenario/flowgram/components/tools/add-node-dropdown.tsx b/src/pages/scenario/flowgram/components/tools/add-node-dropdown.tsx new file mode 100644 index 00000000..7bd5931f --- /dev/null +++ b/src/pages/scenario/flowgram/components/tools/add-node-dropdown.tsx @@ -0,0 +1,93 @@ +/** + * Add Node dropdown component + */ + +import React from 'react'; +import { Button, Dropdown, Menu } from 'antd'; +import { + PlusOutlined, + ApiOutlined, + CodeOutlined, + BranchesOutlined, + ClockCircleOutlined, + SyncOutlined, +} from '@ant-design/icons'; +import { usePlayground } from '@flowgram.ai/free-layout-editor'; +import { nanoid } from 'nanoid'; +import intl from 'react-intl-universal'; + +export const AddNodeDropdown: React.FC = () => { + const playground = usePlayground(); + + const handleAddNode = (type: string) => { + // Get center of viewport + const viewport = playground.viewport.getViewport(); + const centerX = viewport.x + viewport.width / 2; + const centerY = viewport.y + viewport.height / 2; + + // Add node at center + const nodeId = nanoid(); + playground.nodeService.createNode({ + id: nodeId, + type, + x: centerX - 140, // Offset to center the node (280px width / 2) + y: centerY - 60, // Offset to center the node (120px height / 2) + width: type === 'start' || type === 'end' ? 120 : 280, + height: type === 'delay' || type === 'loop' ? 100 : 120, + }); + }; + + const menu = ( + , + onClick: () => handleAddNode('http'), + }, + { + key: 'script', + label: intl.get('scenario_script_node'), + icon: , + onClick: () => handleAddNode('script'), + }, + { + key: 'condition', + label: intl.get('scenario_condition_node'), + icon: , + onClick: () => handleAddNode('condition'), + }, + { + key: 'delay', + label: intl.get('scenario_delay_node'), + icon: , + onClick: () => handleAddNode('delay'), + }, + { + key: 'loop', + label: intl.get('scenario_loop_node'), + icon: , + onClick: () => handleAddNode('loop'), + }, + ]} + /> + ); + + return ( + + + + ); +}; diff --git a/src/pages/scenario/flowgram/components/tools/index.tsx b/src/pages/scenario/flowgram/components/tools/index.tsx new file mode 100644 index 00000000..b739aedc --- /dev/null +++ b/src/pages/scenario/flowgram/components/tools/index.tsx @@ -0,0 +1,67 @@ +/** + * Flowgram Tools Component + * Based on: https://github.com/bytedance/flowgram.ai/tree/main/apps/demo-free-layout/src/components/tools + */ + +import React, { useState, useEffect } from 'react'; +import { Button, Tooltip, Divider } from 'antd'; +import { + UndoOutlined, + RedoOutlined, + PlusOutlined, + ZoomInOutlined, + ZoomOutOutlined, + FullscreenOutlined, +} from '@ant-design/icons'; +import { useClientContext } from '@flowgram.ai/free-layout-editor'; +import { ZoomSelect } from './zoom-select'; +import { AddNodeDropdown } from './add-node-dropdown'; +import { ToolContainer, ToolSection } from './styles'; + +export const FlowgramTools: React.FC = () => { + const { history, playground } = useClientContext(); + const [canUndo, setCanUndo] = useState(false); + const [canRedo, setCanRedo] = useState(false); + + useEffect(() => { + const disposable = history.undoRedoService.onChange(() => { + setCanUndo(history.canUndo()); + setCanRedo(history.canRedo()); + }); + return () => disposable.dispose(); + }, [history]); + + return ( + + + + +