mirror of
https://github.com/whyour/qinglong.git
synced 2025-12-14 16:05:39 +08:00
Fix: Refactor tools plugin following Flowgram demo patterns
Comprehensive fixes based on feedback: 1. Fixed zoom: use playgroundTools.zoom for reading, playgroundTools.config.updateZoom for setting 2. Created useAddNode hook following Flowgram pattern for proper node addition 3. Added Minimap component to DemoTools (removed from useEditorProps plugin) 4. Fixed createHistoryNodePlugin to accept opts parameter 5. Updated createPanelManagerPlugin to create custom tools panel 6. Created NodePanel component following Flowgram demo pattern 7. Updated createFreeNodePanelPlugin with renderer parameter 8. All components now follow official Flowgram.ai patterns exactly Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
a1b21e81f6
commit
eccda4da1a
22
src/pages/scenario/flowgram/components/minimap.tsx
Normal file
22
src/pages/scenario/flowgram/components/minimap.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Minimap component for workflow editor
|
||||||
|
* Following Flowgram demo pattern from:
|
||||||
|
* https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/tools/minimap.tsx
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Minimap as FlowgramMinimap } from '@flowgram.ai/minimap-plugin';
|
||||||
|
|
||||||
|
export const Minimap: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="demo-tools-minimap">
|
||||||
|
<FlowgramMinimap
|
||||||
|
style={{
|
||||||
|
width: '150px',
|
||||||
|
height: '100px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
93
src/pages/scenario/flowgram/components/node-panel/index.tsx
Normal file
93
src/pages/scenario/flowgram/components/node-panel/index.tsx
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* NodePanel component for node type selection
|
||||||
|
* Following Flowgram demo pattern from:
|
||||||
|
* https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/node-panel/index.tsx
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { NodePanelProps } from '@flowgram.ai/free-node-panel-plugin';
|
||||||
|
import { Card, Row, Col } from 'antd';
|
||||||
|
import {
|
||||||
|
ApiOutlined,
|
||||||
|
CodeOutlined,
|
||||||
|
BranchesOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
SyncOutlined,
|
||||||
|
PlayCircleOutlined,
|
||||||
|
StopOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
import { NODE_TYPES } from '../../nodes/constants';
|
||||||
|
import './styles.less';
|
||||||
|
|
||||||
|
export const NodePanel: React.FC<NodePanelProps> = ({ onSelect, onClose }) => {
|
||||||
|
const nodeTypes = [
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.START,
|
||||||
|
label: intl.get('scenario_start'),
|
||||||
|
icon: <PlayCircleOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.HTTP,
|
||||||
|
label: intl.get('scenario_http_node'),
|
||||||
|
icon: <ApiOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.SCRIPT,
|
||||||
|
label: intl.get('scenario_script_node'),
|
||||||
|
icon: <CodeOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.CONDITION,
|
||||||
|
label: intl.get('scenario_condition_node'),
|
||||||
|
icon: <BranchesOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.DELAY,
|
||||||
|
label: intl.get('scenario_delay_node'),
|
||||||
|
icon: <ClockCircleOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.LOOP,
|
||||||
|
label: intl.get('scenario_loop_node'),
|
||||||
|
icon: <SyncOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NODE_TYPES.END,
|
||||||
|
label: intl.get('scenario_end'),
|
||||||
|
icon: <StopOutlined style={{ fontSize: 24 }} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleNodeClick = (type: string) => {
|
||||||
|
onSelect?.({
|
||||||
|
nodeType: type,
|
||||||
|
nodeJSON: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="node-panel">
|
||||||
|
<div className="node-panel-header">
|
||||||
|
<span>{intl.get('scenario_add_node')}</span>
|
||||||
|
</div>
|
||||||
|
<div className="node-panel-content">
|
||||||
|
<Row gutter={[8, 8]}>
|
||||||
|
{nodeTypes.map((node) => (
|
||||||
|
<Col span={12} key={node.type}>
|
||||||
|
<Card
|
||||||
|
hoverable
|
||||||
|
className="node-panel-card"
|
||||||
|
onClick={() => handleNodeClick(node.type)}
|
||||||
|
>
|
||||||
|
<div className="node-panel-card-content">
|
||||||
|
{node.icon}
|
||||||
|
<div className="node-panel-card-label">{node.label}</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
.node-panel {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
min-width: 320px;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
.node-panel-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-panel-content {
|
||||||
|
padding: 12px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-panel-card {
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-panel-card-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.node-panel-card-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
.node-panel-card-content {
|
||||||
|
color: #1890ff;
|
||||||
|
.node-panel-card-label {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode support
|
||||||
|
[data-prefers-color='dark'] {
|
||||||
|
.node-panel {
|
||||||
|
background: #1f1f1f;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
|
||||||
|
|
||||||
|
.node-panel-header {
|
||||||
|
border-bottom-color: #303030;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-panel-card {
|
||||||
|
background: #141414;
|
||||||
|
border-color: #303030;
|
||||||
|
|
||||||
|
.node-panel-card-label {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #177ddc;
|
||||||
|
.node-panel-card-label {
|
||||||
|
color: #177ddc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/pages/scenario/flowgram/hooks/use-add-node.ts
Normal file
110
src/pages/scenario/flowgram/hooks/use-add-node.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/**
|
||||||
|
* Hook for adding new workflow nodes
|
||||||
|
* Following Flowgram demo pattern from:
|
||||||
|
* https://github.com/bytedance/flowgram.ai/blob/main/apps/demo-free-layout/src/components/add-node/use-add-node.ts
|
||||||
|
*/
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
useService,
|
||||||
|
WorkflowDocument,
|
||||||
|
usePlayground,
|
||||||
|
PositionSchema,
|
||||||
|
WorkflowNodeEntity,
|
||||||
|
WorkflowSelectService,
|
||||||
|
getAntiOverlapPosition,
|
||||||
|
WorkflowNodeMeta,
|
||||||
|
FlowNodeBaseType,
|
||||||
|
} from '@flowgram.ai/free-layout-editor';
|
||||||
|
import { NodePanelResult, WorkflowNodePanelService } from '@flowgram.ai/free-node-panel-plugin';
|
||||||
|
|
||||||
|
// Hook to get panel position from mouse event
|
||||||
|
const useGetPanelPosition = () => {
|
||||||
|
const playground = usePlayground();
|
||||||
|
return useCallback(
|
||||||
|
(targetBoundingRect: DOMRect): PositionSchema =>
|
||||||
|
playground.config.getPosFromMouseEvent({
|
||||||
|
clientX: targetBoundingRect.left + 64,
|
||||||
|
clientY: targetBoundingRect.top - 7,
|
||||||
|
}),
|
||||||
|
[playground]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hook to handle node selection
|
||||||
|
const useSelectNode = () => {
|
||||||
|
const selectService = useService(WorkflowSelectService);
|
||||||
|
return useCallback(
|
||||||
|
(node?: WorkflowNodeEntity) => {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectService.selectNode(node);
|
||||||
|
},
|
||||||
|
[selectService]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContainerNode = (selectService: WorkflowSelectService) => {
|
||||||
|
const { activatedNode } = selectService;
|
||||||
|
if (!activatedNode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { isContainer } = activatedNode.getNodeMeta<WorkflowNodeMeta>();
|
||||||
|
if (isContainer) {
|
||||||
|
return activatedNode;
|
||||||
|
}
|
||||||
|
const parentNode = activatedNode.parent;
|
||||||
|
if (!parentNode || parentNode.flowNodeType === FlowNodeBaseType.ROOT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return parentNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Main hook for adding new nodes
|
||||||
|
export const useAddNode = () => {
|
||||||
|
const workflowDocument = useService(WorkflowDocument);
|
||||||
|
const nodePanelService = useService<WorkflowNodePanelService>(WorkflowNodePanelService);
|
||||||
|
const selectService = useService(WorkflowSelectService);
|
||||||
|
const playground = usePlayground();
|
||||||
|
const getPanelPosition = useGetPanelPosition();
|
||||||
|
const select = useSelectNode();
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
async (targetBoundingRect: DOMRect): Promise<void> => {
|
||||||
|
const panelPosition = getPanelPosition(targetBoundingRect);
|
||||||
|
const containerNode = getContainerNode(selectService);
|
||||||
|
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
nodePanelService.callNodePanel({
|
||||||
|
position: panelPosition,
|
||||||
|
enableMultiAdd: true,
|
||||||
|
containerNode,
|
||||||
|
panelProps: {},
|
||||||
|
onSelect: async (panelParams?: NodePanelResult) => {
|
||||||
|
if (!panelParams) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { nodeType, nodeJSON } = panelParams;
|
||||||
|
const position = Boolean(containerNode)
|
||||||
|
? getAntiOverlapPosition(workflowDocument, {
|
||||||
|
x: 0,
|
||||||
|
y: 200,
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const node: WorkflowNodeEntity = workflowDocument.createWorkflowNodeByType(
|
||||||
|
nodeType,
|
||||||
|
position,
|
||||||
|
containerNode?.id,
|
||||||
|
nodeJSON
|
||||||
|
);
|
||||||
|
|
||||||
|
select(node);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[workflowDocument, nodePanelService, selectService, playground, getPanelPosition, select]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -3,11 +3,11 @@ import { FreeLayoutProps } from '@flowgram.ai/free-layout-editor';
|
||||||
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
|
import { createFreeSnapPlugin } from '@flowgram.ai/free-snap-plugin';
|
||||||
import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
|
import { createFreeLinesPlugin } from '@flowgram.ai/free-lines-plugin';
|
||||||
import { createFreeNodePanelPlugin } from '@flowgram.ai/free-node-panel-plugin';
|
import { createFreeNodePanelPlugin } from '@flowgram.ai/free-node-panel-plugin';
|
||||||
import { createMinimapPlugin } from '@flowgram.ai/minimap-plugin';
|
|
||||||
import { createPanelManagerPlugin } from '@flowgram.ai/panel-manager-plugin';
|
import { createPanelManagerPlugin } from '@flowgram.ai/panel-manager-plugin';
|
||||||
import { createHistoryNodePlugin } from '@flowgram.ai/history-node-plugin';
|
import { createHistoryNodePlugin } from '@flowgram.ai/history-node-plugin';
|
||||||
import { FlowNodeRegistry } from '../nodes/http';
|
import { FlowNodeRegistry } from '../nodes/http';
|
||||||
import { createToolsPlugin } from '../plugins/tools-plugin';
|
import { createToolsPlugin } from '../plugins/tools-plugin';
|
||||||
|
import { NodePanel } from '../components/node-panel';
|
||||||
|
|
||||||
export function useEditorProps(
|
export function useEditorProps(
|
||||||
initialData: any,
|
initialData: any,
|
||||||
|
|
@ -26,16 +26,10 @@ export function useEditorProps(
|
||||||
plugins: () => [
|
plugins: () => [
|
||||||
createFreeSnapPlugin({}),
|
createFreeSnapPlugin({}),
|
||||||
createFreeLinesPlugin({}),
|
createFreeLinesPlugin({}),
|
||||||
createFreeNodePanelPlugin({}),
|
createFreeNodePanelPlugin({
|
||||||
createHistoryNodePlugin(),
|
renderer: NodePanel,
|
||||||
createMinimapPlugin({
|
|
||||||
style: {
|
|
||||||
width: '150px',
|
|
||||||
height: '100px',
|
|
||||||
bottom: '20px',
|
|
||||||
right: '20px',
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
|
createHistoryNodePlugin({}),
|
||||||
createPanelManagerPlugin({
|
createPanelManagerPlugin({
|
||||||
factories: [],
|
factories: [],
|
||||||
layerProps: {},
|
layerProps: {},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
usePlaygroundTools,
|
usePlaygroundTools,
|
||||||
useHistoryService,
|
useHistoryService,
|
||||||
useNodeService,
|
|
||||||
} from '@flowgram.ai/free-layout-editor';
|
} from '@flowgram.ai/free-layout-editor';
|
||||||
import {
|
import {
|
||||||
DesktopOutlined,
|
DesktopOutlined,
|
||||||
|
|
@ -21,13 +20,15 @@ import {
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Dropdown, Menu, Button, Tooltip } from 'antd';
|
import { Dropdown, Menu, Button, Tooltip } from 'antd';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
import { Minimap } from '../../components/minimap';
|
||||||
|
import { useAddNode } from '../../hooks/use-add-node';
|
||||||
import { NODE_TYPES } from '../../nodes/constants';
|
import { NODE_TYPES } from '../../nodes/constants';
|
||||||
import './styles.less';
|
import './styles.less';
|
||||||
|
|
||||||
export const DemoTools: React.FC = () => {
|
export const DemoTools: React.FC = () => {
|
||||||
const playgroundTools = usePlaygroundTools();
|
const playgroundTools = usePlaygroundTools();
|
||||||
const historyService = useHistoryService();
|
const historyService = useHistoryService();
|
||||||
const nodeService = useNodeService();
|
const addNode = useAddNode();
|
||||||
|
|
||||||
const [zoom, setZoom] = useState(100);
|
const [zoom, setZoom] = useState(100);
|
||||||
const [canUndo, setCanUndo] = useState(false);
|
const [canUndo, setCanUndo] = useState(false);
|
||||||
|
|
@ -37,7 +38,8 @@ export const DemoTools: React.FC = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (playgroundTools) {
|
if (playgroundTools) {
|
||||||
const updateZoom = () => {
|
const updateZoom = () => {
|
||||||
const currentZoom = playgroundTools.getZoom();
|
// Use playgroundTools.zoom for reading (Feedback #1)
|
||||||
|
const currentZoom = playgroundTools.zoom;
|
||||||
setZoom(Math.round(currentZoom * 100));
|
setZoom(Math.round(currentZoom * 100));
|
||||||
};
|
};
|
||||||
updateZoom();
|
updateZoom();
|
||||||
|
|
@ -62,22 +64,23 @@ export const DemoTools: React.FC = () => {
|
||||||
}, [historyService]);
|
}, [historyService]);
|
||||||
|
|
||||||
const handleZoom = (value: number) => {
|
const handleZoom = (value: number) => {
|
||||||
if (playgroundTools) {
|
if (playgroundTools?.config) {
|
||||||
playgroundTools.setZoom(value / 100);
|
// Use playgroundTools.config.updateZoom for writing (Feedback #1)
|
||||||
|
playgroundTools.config.updateZoom(value / 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleZoomIn = () => {
|
const handleZoomIn = () => {
|
||||||
if (playgroundTools) {
|
if (playgroundTools?.config) {
|
||||||
const current = playgroundTools.getZoom();
|
const current = playgroundTools.zoom;
|
||||||
playgroundTools.setZoom(Math.min(current + 0.1, 2));
|
playgroundTools.config.updateZoom(Math.min(current + 0.1, 2));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleZoomOut = () => {
|
const handleZoomOut = () => {
|
||||||
if (playgroundTools) {
|
if (playgroundTools?.config) {
|
||||||
const current = playgroundTools.getZoom();
|
const current = playgroundTools.zoom;
|
||||||
playgroundTools.setZoom(Math.max(current - 0.1, 0.5));
|
playgroundTools.config.updateZoom(Math.max(current - 0.1, 0.5));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -99,14 +102,10 @@ export const DemoTools: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddNode = (type: string) => {
|
// Use useAddNode hook for node addition (Feedback #2)
|
||||||
if (nodeService && playgroundTools?.viewport) {
|
const handleAddNodeClick = async (event: React.MouseEvent<HTMLElement>) => {
|
||||||
const center = playgroundTools.viewport.getCenter();
|
const target = event.currentTarget.getBoundingClientRect();
|
||||||
nodeService.createNode({
|
await addNode(target);
|
||||||
type,
|
|
||||||
position: center,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const zoomMenu = (
|
const zoomMenu = (
|
||||||
|
|
@ -123,19 +122,6 @@ export const DemoTools: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const addNodeMenu = (
|
|
||||||
<Menu
|
|
||||||
onClick={({ key }) => handleAddNode(key)}
|
|
||||||
items={[
|
|
||||||
{ key: NODE_TYPES.HTTP, label: intl.get('scenario_http_node'), icon: <PlusOutlined /> },
|
|
||||||
{ key: NODE_TYPES.SCRIPT, label: intl.get('scenario_script_node'), icon: <PlusOutlined /> },
|
|
||||||
{ key: NODE_TYPES.CONDITION, label: intl.get('scenario_condition_node'), icon: <PlusOutlined /> },
|
|
||||||
{ key: NODE_TYPES.DELAY, label: intl.get('scenario_delay_node'), icon: <PlusOutlined /> },
|
|
||||||
{ key: NODE_TYPES.LOOP, label: intl.get('scenario_loop_node'), icon: <PlusOutlined /> },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="demo-tools">
|
<div className="demo-tools">
|
||||||
<div className="demo-tools-section">
|
<div className="demo-tools-section">
|
||||||
|
|
@ -234,6 +220,11 @@ export const DemoTools: React.FC = () => {
|
||||||
|
|
||||||
<div className="demo-tools-divider" />
|
<div className="demo-tools-divider" />
|
||||||
|
|
||||||
|
{/* Minimap component added to toolbar (Feedback #3) */}
|
||||||
|
<Minimap />
|
||||||
|
|
||||||
|
<div className="demo-tools-divider" />
|
||||||
|
|
||||||
<Tooltip title={intl.get('scenario_alerts')}>
|
<Tooltip title={intl.get('scenario_alerts')}>
|
||||||
<Button
|
<Button
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -242,15 +233,14 @@ export const DemoTools: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Dropdown overlay={addNodeMenu} trigger={['click']}>
|
<Button
|
||||||
<Button
|
type="primary"
|
||||||
type="primary"
|
icon={<PlusOutlined />}
|
||||||
icon={<PlusOutlined />}
|
className="demo-tools-add-button"
|
||||||
className="demo-tools-add-button"
|
onClick={handleAddNodeClick}
|
||||||
>
|
>
|
||||||
{intl.get('scenario_add_node')}
|
{intl.get('scenario_add_node')}
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,13 @@
|
||||||
margin: 0 4px;
|
margin: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minimap container in toolbar (Feedback #3)
|
||||||
|
.demo-tools-minimap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.demo-tools-add-button {
|
.demo-tools-add-button {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user