improve edges
This commit is contained in:
@@ -17,6 +17,7 @@ import ConfigNode from './components/graph/ConfigNode'
|
||||
import FunctionNode from './components/graph/FunctionNode'
|
||||
import RenderingNode from './components/graph/RenderingNode'
|
||||
import VariableNode from './components/graph/VariableNode'
|
||||
import { AnimatedEdge } from './components/graph/AnimatedEdge'
|
||||
import FlowContext from './lib/flowContext'
|
||||
import { useTheme } from './lib/themeContext'
|
||||
import {
|
||||
@@ -53,7 +54,9 @@ const initialNodes: Node[] = [
|
||||
},
|
||||
].map((n) => ({ ...n, id: n.id ?? genId() }))
|
||||
|
||||
const initialEdges: Edge[] = [{ id: 'e-config-render', source: 'config-1', target: 'render-1' }]
|
||||
const initialEdges: Edge[] = [
|
||||
{ id: 'e-config-render', source: 'config-1', target: 'render-1', type: 'animated' },
|
||||
]
|
||||
|
||||
export default function App() {
|
||||
const { theme, toggleTheme } = useTheme()
|
||||
@@ -70,6 +73,8 @@ export default function App() {
|
||||
[]
|
||||
)
|
||||
|
||||
const edgeTypes = React.useMemo(() => ({ animated: AnimatedEdge }), [])
|
||||
|
||||
const onConnect = React.useCallback(
|
||||
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
|
||||
[setEdges]
|
||||
@@ -172,6 +177,8 @@ export default function App() {
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
defaultEdgeOptions={{ type: 'animated' }}
|
||||
colorMode={theme as ColorMode}
|
||||
snapToGrid
|
||||
snapGrid={SNAP_GRID}
|
||||
|
||||
99
src/components/graph/AnimatedEdge.tsx
Normal file
99
src/components/graph/AnimatedEdge.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
BaseEdge,
|
||||
getBezierPath,
|
||||
type EdgeProps,
|
||||
} from '@xyflow/react'
|
||||
|
||||
const EDGE_STROKE_WIDTH = 2
|
||||
const DOT_MARKER_R = 1.5
|
||||
|
||||
export function AnimatedEdge({
|
||||
id,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
style,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
labelBgPadding,
|
||||
labelBgBorderRadius,
|
||||
labelX,
|
||||
labelY,
|
||||
interactionWidth,
|
||||
}: EdgeProps) {
|
||||
const [edgePath] = getBezierPath({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
})
|
||||
|
||||
const startId = `animated-edge-dot-start-${id}`
|
||||
const endId = `animated-edge-dot-end-${id}`
|
||||
|
||||
return (
|
||||
<>
|
||||
<defs>
|
||||
<marker
|
||||
id={startId}
|
||||
markerWidth={DOT_MARKER_R * 2}
|
||||
markerHeight={DOT_MARKER_R * 2}
|
||||
refX={DOT_MARKER_R + 5}
|
||||
refY={DOT_MARKER_R}
|
||||
orient="auto"
|
||||
>
|
||||
<circle
|
||||
r={DOT_MARKER_R}
|
||||
cx={DOT_MARKER_R}
|
||||
cy={DOT_MARKER_R}
|
||||
className="fill-primary stroke-background"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</marker>
|
||||
<marker
|
||||
id={endId}
|
||||
markerWidth={DOT_MARKER_R * 2}
|
||||
markerHeight={DOT_MARKER_R * 2}
|
||||
refX={DOT_MARKER_R - 5}
|
||||
refY={DOT_MARKER_R}
|
||||
orient="auto"
|
||||
>
|
||||
<circle
|
||||
r={DOT_MARKER_R}
|
||||
cx={DOT_MARKER_R}
|
||||
cy={DOT_MARKER_R}
|
||||
className="fill-primary stroke-background"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</marker>
|
||||
</defs>
|
||||
<BaseEdge
|
||||
path={edgePath}
|
||||
markerStart={`url(#${startId})`}
|
||||
markerEnd={`url(#${endId})`}
|
||||
style={{
|
||||
strokeWidth: EDGE_STROKE_WIDTH,
|
||||
...style,
|
||||
}}
|
||||
className="animated-edge-path"
|
||||
label={label}
|
||||
labelStyle={labelStyle}
|
||||
labelShowBg={labelShowBg}
|
||||
labelBgStyle={labelBgStyle}
|
||||
labelBgPadding={labelBgPadding}
|
||||
labelBgBorderRadius={labelBgBorderRadius}
|
||||
labelX={labelX}
|
||||
labelY={labelY}
|
||||
interactionWidth={interactionWidth}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -32,6 +32,25 @@ body {
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* Animated React Flow edges: thicker stroke + path animation */
|
||||
.react-flow__edge path.animated-edge-path,
|
||||
.animated-edge-path {
|
||||
stroke-width: 4;
|
||||
stroke: hsl(var(--foreground) / 0.6);
|
||||
fill: none;
|
||||
stroke-dasharray: 8 6;
|
||||
animation: edge-flow 0.6s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes edge-flow {
|
||||
from {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
to {
|
||||
stroke-dashoffset: 14;
|
||||
}
|
||||
}
|
||||
|
||||
/* Small helper for monospace pre output */
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, 'Roboto Mono', 'Courier New', monospace;
|
||||
|
||||
Reference in New Issue
Block a user