File size: 6,235 Bytes
376f172 dc06ee7 376f172 dc06ee7 3578090 376f172 dc06ee7 376f172 dc06ee7 506c5f2 dc06ee7 376f172 dc06ee7 506c5f2 dc06ee7 506c5f2 dc06ee7 506c5f2 dc06ee7 376f172 dc06ee7 376f172 dc06ee7 376f172 dc06ee7 3578090 506c5f2 dc06ee7 5856d68 dc06ee7 506c5f2 376f172 dc06ee7 506c5f2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
import { useEffect, useState, useCallback, useMemo } from 'react'
// import { MiniMap } from '@react-sigma/minimap'
import { SigmaContainer, useRegisterEvents, useSigma } from '@react-sigma/core'
import { Settings as SigmaSettings } from 'sigma/settings'
import { GraphSearchOption, OptionItem } from '@react-sigma/graph-search'
import { EdgeArrowProgram, NodePointProgram, NodeCircleProgram } from 'sigma/rendering'
import { NodeBorderProgram } from '@sigma/node-border'
import EdgeCurveProgram, { EdgeCurvedArrowProgram } from '@sigma/edge-curve'
import FocusOnNode from '@/components/FocusOnNode'
import LayoutsControl from '@/components/LayoutsControl'
import GraphControl from '@/components/GraphControl'
import ThemeToggle from '@/components/ThemeToggle'
import ZoomControl from '@/components/ZoomControl'
import FullScreenControl from '@/components/FullScreenControl'
import Settings from '@/components/Settings'
import GraphSearch from '@/components/GraphSearch'
import GraphLabels from '@/components/GraphLabels'
import PropertiesView from '@/components/PropertiesView'
import { useSettingsStore } from '@/stores/settings'
import { useGraphStore } from '@/stores/graph'
import '@react-sigma/core/lib/style.css'
import '@react-sigma/graph-search/lib/style.css'
// Sigma settings
const defaultSigmaSettings: Partial<SigmaSettings> = {
allowInvalidContainer: true,
defaultNodeType: 'default',
defaultEdgeType: 'curvedArrow',
renderEdgeLabels: false,
edgeProgramClasses: {
arrow: EdgeArrowProgram,
curvedArrow: EdgeCurvedArrowProgram,
curvedNoArrow: EdgeCurveProgram
},
nodeProgramClasses: {
default: NodeBorderProgram,
circel: NodeCircleProgram,
point: NodePointProgram
},
labelGridCellSize: 60,
labelRenderedSizeThreshold: 12,
enableEdgeEvents: true,
labelColor: {
color: '#000',
attribute: 'labelColor'
},
edgeLabelColor: {
color: '#000',
attribute: 'labelColor'
},
edgeLabelSize: 8,
labelSize: 12
// minEdgeThickness: 2
// labelFont: 'Lato, sans-serif'
}
const GraphEvents = () => {
const registerEvents = useRegisterEvents()
const sigma = useSigma()
const [draggedNode, setDraggedNode] = useState<string | null>(null)
useEffect(() => {
// Register the events
registerEvents({
downNode: (e) => {
setDraggedNode(e.node)
sigma.getGraph().setNodeAttribute(e.node, 'highlighted', true)
},
// On mouse move, if the drag mode is enabled, we change the position of the draggedNode
mousemovebody: (e) => {
if (!draggedNode) return
// Get new position of node
const pos = sigma.viewportToGraph(e)
sigma.getGraph().setNodeAttribute(draggedNode, 'x', pos.x)
sigma.getGraph().setNodeAttribute(draggedNode, 'y', pos.y)
// Prevent sigma to move camera:
e.preventSigmaDefault()
e.original.preventDefault()
e.original.stopPropagation()
},
// On mouse up, we reset the autoscale and the dragging mode
mouseup: () => {
if (draggedNode) {
setDraggedNode(null)
sigma.getGraph().removeNodeAttribute(draggedNode, 'highlighted')
}
},
// Disable the autoscale at the first down interaction
mousedown: () => {
if (!sigma.getCustomBBox()) sigma.setCustomBBox(sigma.getBBox())
}
})
}, [registerEvents, sigma, draggedNode])
return null
}
const GraphViewer = () => {
const [sigmaSettings, setSigmaSettings] = useState(defaultSigmaSettings)
const selectedNode = useGraphStore.use.selectedNode()
const focusedNode = useGraphStore.use.focusedNode()
const moveToSelectedNode = useGraphStore.use.moveToSelectedNode()
const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
const renderLabels = useSettingsStore.use.showNodeLabel()
const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
const renderEdgeLabels = useSettingsStore.use.showEdgeLabel()
useEffect(() => {
setSigmaSettings({
...defaultSigmaSettings,
enableEdgeEvents,
renderEdgeLabels,
renderLabels
})
}, [renderLabels, enableEdgeEvents, renderEdgeLabels])
const onSearchFocus = useCallback((value: GraphSearchOption | null) => {
if (value === null) useGraphStore.getState().setFocusedNode(null)
else if (value.type === 'nodes') useGraphStore.getState().setFocusedNode(value.id)
}, [])
const onSearchSelect = useCallback((value: GraphSearchOption | null) => {
if (value === null) {
useGraphStore.getState().setSelectedNode(null)
} else if (value.type === 'nodes') {
useGraphStore.getState().setSelectedNode(value.id, true)
}
}, [])
const autoFocusedNode = useMemo(() => focusedNode ?? selectedNode, [focusedNode, selectedNode])
const searchInitSelectedNode = useMemo(
(): OptionItem | null => (selectedNode ? { type: 'nodes', id: selectedNode } : null),
[selectedNode]
)
return (
<SigmaContainer settings={sigmaSettings} className="!bg-background !size-full overflow-hidden">
<GraphControl />
{enableNodeDrag && <GraphEvents />}
<FocusOnNode node={autoFocusedNode} move={moveToSelectedNode} />
<div className="absolute top-2 left-2 flex items-start gap-2">
<GraphLabels />
{showNodeSearchBar && (
<GraphSearch
value={searchInitSelectedNode}
onFocus={onSearchFocus}
onChange={onSearchSelect}
/>
)}
</div>
<div className="bg-background/60 absolute bottom-2 left-2 flex flex-col rounded-xl border-2 backdrop-blur-lg">
<Settings />
<ZoomControl />
<LayoutsControl />
<FullScreenControl />
<ThemeToggle />
</div>
{showPropertyPanel && (
<div className="absolute top-2 right-2">
<PropertiesView />
</div>
)}
{/* <div className="absolute bottom-2 right-2 flex flex-col rounded-xl border-2">
<MiniMap width="100px" height="100px" />
</div> */}
</SigmaContainer>
)
}
export default GraphViewer
|