-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
谢新根
committed
Jan 18, 2024
1 parent
30681d5
commit c898da9
Showing
14 changed files
with
1,521 additions
and
1,486 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,180 +1,183 @@ | ||
import TopologyContext from '@/contexts/topology'; | ||
import X6ReactPortalProvider from '@/contexts/x6-react-portal'; | ||
import useGraph from '@/hooks/useGraph'; | ||
import type { Topology } from '@/types/global.d'; | ||
import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; | ||
import { Graph, Model } from '@antv/x6'; | ||
import { useSize } from 'ahooks'; | ||
import { Button, Layout, Space } from 'antd'; | ||
import classNames from 'classnames'; | ||
import React, { | ||
forwardRef, | ||
useEffect, | ||
useImperativeHandle, | ||
useRef, | ||
useState, | ||
forwardRef, | ||
useEffect, | ||
useImperativeHandle, | ||
useRef, | ||
useState, | ||
} from 'react'; | ||
|
||
import classNames from 'classnames'; | ||
import { Button, Layout, Space } from 'antd'; | ||
import { MinusOutlined, PlusOutlined } from '@ant-design/icons'; | ||
import { useSize } from 'ahooks'; | ||
import { GRAPH_ZOOM } from '@/constants'; | ||
import TopologyContext from '@/contexts/topology'; | ||
import X6ReactPortalProvider from '@/contexts/x6-react-portal'; | ||
import useGraph from '@/hooks/useGraph'; | ||
import '@/index.less'; | ||
import EventBus from '@/utils/event-bus'; | ||
|
||
export type EditorProps = { | ||
iconMap: Record<string, Topology.TopologyIconProp>; | ||
value?: Model.FromJSONData; | ||
style?: React.CSSProperties; | ||
className?: string; | ||
iconMap: Record<string, Topology.TopologyIconProp>; | ||
value?: Model.FromJSONData; | ||
style?: React.CSSProperties; | ||
className?: string; | ||
}; | ||
|
||
export type EditorRef = { | ||
getInstance: () => Graph; | ||
getInstance: () => Graph; | ||
}; | ||
|
||
const zoomFitPadding = 14; | ||
|
||
const Editor: React.ForwardRefRenderFunction<EditorRef, EditorProps> = ( | ||
props, | ||
ref, | ||
props, | ||
ref, | ||
) => { | ||
const graphContainerRef = useRef<any>(); | ||
const eventBusRef = useRef<EventBus>(new EventBus()); | ||
const [zoom, setZoom] = useState(1); | ||
const graphContainerSize = useSize(graphContainerRef); | ||
const graphContainerRef = useRef<any>(); | ||
const eventBusRef = useRef<EventBus>(new EventBus()); | ||
const [zoom, setZoom] = useState(1); | ||
const graphContainerSize = useSize(graphContainerRef); | ||
|
||
const [graphInstance] = useGraph(graphContainerRef, eventBusRef.current, { | ||
graphOption: { | ||
width: graphContainerSize?.width, | ||
height: graphContainerSize?.height, | ||
// autoResize: true, | ||
grid: false, | ||
embedding: false, | ||
interacting: false, | ||
panning: true, | ||
}, | ||
pluginOption: { | ||
transform: false, | ||
selection: false, | ||
snapline: false, | ||
keyboard: false, | ||
clipboard: false, | ||
history: false, | ||
scroller: false, | ||
}, | ||
keyBoardEvent: false, | ||
graphEvent: false, | ||
}); | ||
|
||
const handleZoomToFit = () => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.zoomToFit({ | ||
padding: zoomFitPadding, | ||
const [graphInstance] = useGraph(graphContainerRef, eventBusRef.current, { | ||
graphOption: { | ||
width: graphContainerSize?.width, | ||
height: graphContainerSize?.height, | ||
// autoResize: true, | ||
grid: false, | ||
embedding: false, | ||
interacting: false, | ||
panning: true, | ||
}, | ||
pluginOption: { | ||
transform: false, | ||
selection: false, | ||
snapline: false, | ||
keyboard: false, | ||
clipboard: false, | ||
history: false, | ||
scroller: false, | ||
}, | ||
keyBoardEvent: false, | ||
graphEvent: false, | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
if (graphInstance) { | ||
graphInstance.resize( | ||
graphContainerSize?.width, | ||
graphContainerSize?.height, | ||
); | ||
graphInstance.zoomToFit({ | ||
padding: zoomFitPadding, | ||
}); | ||
} | ||
}, [graphInstance, graphContainerSize]); | ||
const handleZoomToFit = () => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.zoomToFit({ | ||
padding: zoomFitPadding, | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
if (graphInstance) { | ||
if (props.value) { | ||
graphInstance.fromJSON(props.value); | ||
} else { | ||
graphInstance.clearCells(); | ||
} | ||
} | ||
}, [graphInstance, props.value]); | ||
useEffect(() => { | ||
if (graphInstance) { | ||
graphInstance.resize( | ||
graphContainerSize?.width, | ||
graphContainerSize?.height, | ||
); | ||
graphInstance.zoomToFit({ | ||
padding: zoomFitPadding, | ||
}); | ||
} | ||
}, [graphInstance, graphContainerSize]); | ||
|
||
useEffect(() => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.on('scale', () => { | ||
const zoom = graphInstance.zoom(); | ||
let zoomValue = Math.min(zoom, GRAPH_ZOOM.max); | ||
zoomValue = Math.max(zoomValue, GRAPH_ZOOM.min); | ||
setZoom(Math.max(zoomValue)); | ||
}); | ||
}, [graphInstance]); | ||
useEffect(() => { | ||
if (graphInstance) { | ||
if (props.value) { | ||
graphInstance.fromJSON(props.value); | ||
} else { | ||
graphInstance.clearCells(); | ||
} | ||
} | ||
}, [graphInstance, props.value]); | ||
|
||
useImperativeHandle(ref, () => ({ | ||
getInstance: () => graphInstance as Graph, | ||
})); | ||
useEffect(() => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.on('scale', () => { | ||
const zoom = graphInstance.zoom(); | ||
let zoomValue = Math.min(zoom, GRAPH_ZOOM.max); | ||
zoomValue = Math.max(zoomValue, GRAPH_ZOOM.min); | ||
setZoom(Math.max(zoomValue)); | ||
}); | ||
}, [graphInstance]); | ||
|
||
const handleChangeZoom = (zoom: number, absolute = false) => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.zoom(zoom, { | ||
minScale: GRAPH_ZOOM.min, | ||
maxScale: GRAPH_ZOOM.max, | ||
absolute, | ||
}); | ||
}; | ||
useImperativeHandle(ref, () => ({ | ||
getInstance: () => graphInstance as Graph, | ||
})); | ||
|
||
const handleChangeZoom = (zoom: number, absolute = false) => { | ||
if (!graphInstance) { | ||
return; | ||
} | ||
graphInstance.zoom(zoom, { | ||
minScale: GRAPH_ZOOM.min, | ||
maxScale: GRAPH_ZOOM.max, | ||
absolute, | ||
}); | ||
}; | ||
|
||
const zoomText = Math.floor(zoom * 100) + '%'; | ||
const zoomText = Math.floor(zoom * 100) + '%'; | ||
|
||
return ( | ||
<TopologyContext.Provider | ||
value={{ | ||
graph: graphInstance, | ||
iconMap: props.iconMap, | ||
eventBus: eventBusRef.current, | ||
}} | ||
> | ||
<X6ReactPortalProvider /> | ||
<Layout | ||
style={props.style} | ||
className={classNames( | ||
'topology-designable', | ||
'topology-designable-preview', | ||
props.className, | ||
)} | ||
> | ||
<Layout.Content | ||
style={{ overflow: 'initial' }} | ||
className="topology-designable-content" | ||
return ( | ||
<TopologyContext.Provider | ||
value={{ | ||
graph: graphInstance, | ||
iconMap: props.iconMap, | ||
eventBus: eventBusRef.current, | ||
}} | ||
> | ||
<div className="canvas" style={{ width: '100%', height: '100%' }}> | ||
<div className="preview-zoom"> | ||
<Space.Compact size="small" block> | ||
<Button | ||
icon={<MinusOutlined />} | ||
onClick={() => handleChangeZoom(-0.1)} | ||
/> | ||
<Button | ||
title={`单击切换到自适应\n双击切换到100%`} | ||
onClick={handleZoomToFit} | ||
onDoubleClick={() => handleChangeZoom(1, true)} | ||
<X6ReactPortalProvider /> | ||
<Layout | ||
style={props.style} | ||
className={classNames( | ||
'topology-designable', | ||
'topology-designable-preview', | ||
props.className, | ||
)} | ||
> | ||
<Layout.Content | ||
style={{ overflow: 'initial' }} | ||
className="topology-designable-content" | ||
> | ||
{zoomText} | ||
</Button> | ||
<Button | ||
icon={<PlusOutlined />} | ||
onClick={() => handleChangeZoom(0.1)} | ||
/> | ||
</Space.Compact> | ||
</div> | ||
<div | ||
className="editor editor-preview" | ||
style={{ height: '100%' }} | ||
ref={graphContainerRef} | ||
></div> | ||
</div> | ||
</Layout.Content> | ||
</Layout> | ||
</TopologyContext.Provider> | ||
); | ||
<div | ||
className="canvas" | ||
style={{ width: '100%', height: '100%' }} | ||
> | ||
<div className="preview-zoom"> | ||
<Space.Compact size="small" block> | ||
<Button | ||
icon={<MinusOutlined />} | ||
onClick={() => handleChangeZoom(-0.1)} | ||
/> | ||
<Button | ||
title={`单击切换到自适应\n双击切换到100%`} | ||
onClick={handleZoomToFit} | ||
onDoubleClick={() => | ||
handleChangeZoom(1, true) | ||
} | ||
> | ||
{zoomText} | ||
</Button> | ||
<Button | ||
icon={<PlusOutlined />} | ||
onClick={() => handleChangeZoom(0.1)} | ||
/> | ||
</Space.Compact> | ||
</div> | ||
<div | ||
className="editor editor-preview" | ||
style={{ height: '100%' }} | ||
ref={graphContainerRef} | ||
></div> | ||
</div> | ||
</Layout.Content> | ||
</Layout> | ||
</TopologyContext.Provider> | ||
); | ||
}; | ||
|
||
export default forwardRef(Editor); |
Oops, something went wrong.