重构消息通知弹窗
This commit is contained in:
parent
5e23c7b17f
commit
2dd4e11191
@ -52,6 +52,7 @@
|
||||
"@react-form-builder/designer-bundle": "^7.4.0",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@tisoap/react-flow-smart-edge": "^4.0.1",
|
||||
"@types/recharts": "^1.8.29",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@xyflow/react": "^12.8.6",
|
||||
|
||||
@ -131,6 +131,9 @@ importers:
|
||||
'@tanstack/react-virtual':
|
||||
specifier: ^3.13.12
|
||||
version: 3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@tisoap/react-flow-smart-edge':
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1(@xyflow/react@12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2)
|
||||
'@types/recharts':
|
||||
specifier: ^1.8.29
|
||||
version: 1.8.29
|
||||
@ -2045,6 +2048,14 @@ packages:
|
||||
'@tanstack/virtual-core@3.13.12':
|
||||
resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==}
|
||||
|
||||
'@tisoap/react-flow-smart-edge@4.0.1':
|
||||
resolution: {integrity: sha512-tqyQyaQFDc4QIL3Kw9UL9QVWId4cSuVNqKsDRzdPH1Mf8YMrwB8/dq/BMFEbuGaln9B3wlgeyhQx3vn6E9lpJA==}
|
||||
peerDependencies:
|
||||
'@xyflow/react': '>=12'
|
||||
react: '>=18'
|
||||
react-dom: '>=18'
|
||||
typescript: '>=5'
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||
|
||||
@ -6409,6 +6420,13 @@ snapshots:
|
||||
|
||||
'@tanstack/virtual-core@3.13.12': {}
|
||||
|
||||
'@tisoap/react-flow-smart-edge@4.0.1(@xyflow/react@12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2)':
|
||||
dependencies:
|
||||
'@xyflow/react': 12.9.0(@types/react@18.3.18)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
typescript: 5.7.2
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.26.3
|
||||
|
||||
@ -17,7 +17,7 @@ import '@xyflow/react/dist/style.css';
|
||||
|
||||
import type { FlowNode, FlowEdge } from '../types';
|
||||
import { nodeTypes } from '../nodes';
|
||||
import CustomEdge from './CustomEdge';
|
||||
import SmartEdge from './SmartEdge';
|
||||
import { generateEdgeId } from '../utils/idGenerator';
|
||||
|
||||
interface FlowCanvasProps {
|
||||
@ -48,109 +48,6 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
|
||||
const [nodes, , onNodesStateChange] = useNodesState(initialNodes);
|
||||
const [edges, setEdges, onEdgesStateChange] = useEdgesState(initialEdges);
|
||||
|
||||
// --- Auto adjust edge vertices when nodes move (preserve shape, avoid excessive length) ---
|
||||
useEffect(() => {
|
||||
// helper: get node position by id
|
||||
const getNodePos = (id: string) => {
|
||||
const n = nodes.find((nn) => nn.id === id);
|
||||
return n?.position;
|
||||
};
|
||||
|
||||
// geometry helpers
|
||||
const recomputeVertices = (
|
||||
oldVerts: Array<{ x: number; y: number }> | undefined,
|
||||
oldA: { x: number; y: number } | undefined,
|
||||
oldB: { x: number; y: number } | undefined,
|
||||
newA: { x: number; y: number },
|
||||
newB: { x: number; y: number }
|
||||
) => {
|
||||
// default three vertices placed along the baseline
|
||||
const defaultThree = () => [
|
||||
{ x: newA.x + (newB.x - newA.x) * 0.25, y: newA.y + (newB.y - newA.y) * 0.25 },
|
||||
{ x: (newA.x + newB.x) / 2, y: (newA.y + newB.y) / 2 },
|
||||
{ x: newA.x + (newB.x - newA.x) * 0.75, y: newA.y + (newB.y - newA.y) * 0.75 },
|
||||
];
|
||||
|
||||
// if no old endpoints or old vertices, return default
|
||||
if (!oldA || !oldB || !oldVerts || oldVerts.length !== 3) {
|
||||
return defaultThree();
|
||||
}
|
||||
|
||||
const projOnLine = (p: { x: number; y: number }, a: { x: number; y: number }, b: { x: number; y: number }) => {
|
||||
const dx = b.x - a.x, dy = b.y - a.y;
|
||||
const len2 = dx * dx + dy * dy || 1;
|
||||
const t = Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2));
|
||||
const px = a.x + t * dx, py = a.y + t * dy;
|
||||
const offx = p.x - px, offy = p.y - py;
|
||||
// sign: which side of baseline (using cross product sign)
|
||||
const cross = dx * (p.y - a.y) - dy * (p.x - a.x);
|
||||
const sign = cross >= 0 ? 1 : -1;
|
||||
const offMag = Math.hypot(offx, offy);
|
||||
return { t, offMag, sign };
|
||||
};
|
||||
|
||||
const mapToNew = (t: number, offMag: number, sign: number, a: { x: number; y: number }, b: { x: number; y: number }) => {
|
||||
const dx = b.x - a.x, dy = b.y - a.y;
|
||||
const len = Math.hypot(dx, dy) || 1;
|
||||
// unit normal for new baseline
|
||||
const nx = -dy / len, ny = dx / len;
|
||||
// clamp offset to avoid huge bulges (auto shrink if nodes far apart)
|
||||
const maxOffset = Math.max(20, 0.35 * len);
|
||||
const useOff = Math.min(offMag, maxOffset);
|
||||
const baseX = a.x + t * dx, baseY = a.y + t * dy;
|
||||
return { x: baseX + sign * useOff * nx, y: baseY + sign * useOff * ny };
|
||||
};
|
||||
|
||||
// Compute mapping (t, offset) from old baseline, then apply to new baseline
|
||||
const tOff = oldVerts.map((v) => projOnLine(v, oldA, oldB));
|
||||
// enforce monotonic t order to keep vertex order stable
|
||||
tOff.sort((a, b) => a.t - b.t);
|
||||
const mapped = tOff.map((m) => mapToNew(m.t, m.offMag, m.sign, newA, newB));
|
||||
// ensure exactly 3 vertices
|
||||
if (mapped.length === 3) return mapped as Array<{ x: number; y: number }>;
|
||||
// fallback
|
||||
return defaultThree();
|
||||
};
|
||||
|
||||
setEdges((eds) => {
|
||||
return eds.map((ed) => {
|
||||
const s = getNodePos(ed.source);
|
||||
const t = getNodePos(ed.target);
|
||||
if (!s || !t) return ed;
|
||||
|
||||
const dataAny = (ed as any).data || {};
|
||||
const last = dataAny._lastEndpoints as { sx: number; sy: number; tx: number; ty: number } | undefined;
|
||||
const current = { sx: s.x, sy: s.y, tx: t.x, ty: t.y };
|
||||
|
||||
// Initialize vertices if missing
|
||||
const curVerts = (dataAny.vertices as any) as Array<{ x: number; y: number }> | undefined;
|
||||
let vertices = curVerts;
|
||||
let changed = false;
|
||||
|
||||
if (!Array.isArray(vertices) || vertices.length !== 3) {
|
||||
vertices = recomputeVertices(undefined, undefined, undefined, { x: current.sx, y: current.sy }, { x: current.tx, y: current.ty });
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Recompute when endpoints moved
|
||||
if (!last || last.sx !== current.sx || last.sy !== current.sy || last.tx !== current.tx || last.ty !== current.ty) {
|
||||
vertices = recomputeVertices(vertices, last ? { x: last.sx, y: last.sy } : undefined, last ? { x: last.tx, y: last.ty } : undefined, { x: current.sx, y: current.sy }, { x: current.tx, y: current.ty });
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) return ed;
|
||||
return {
|
||||
...ed,
|
||||
data: {
|
||||
...ed.data,
|
||||
vertices,
|
||||
_lastEndpoints: current,
|
||||
},
|
||||
} as any;
|
||||
});
|
||||
});
|
||||
}, [nodes, setEdges]);
|
||||
|
||||
// 处理边重连
|
||||
const onReconnect = useCallback((oldEdge: Edge, newConnection: Connection) => {
|
||||
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
|
||||
@ -164,7 +61,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
|
||||
source: params.source!,
|
||||
target: params.target!,
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
animated: false,
|
||||
style: {
|
||||
stroke: '#94a3b8',
|
||||
strokeWidth: 2,
|
||||
@ -282,7 +179,7 @@ const FlowCanvas: React.FC<FlowCanvasProps> = ({
|
||||
onDragOver={handleDragOver}
|
||||
onMove={onViewportChange}
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={{ smoothstep: CustomEdge }}
|
||||
edgeTypes={{ smoothstep: SmartEdge }}
|
||||
isValidConnection={isValidConnection}
|
||||
defaultEdgeOptions={{
|
||||
type: 'smoothstep',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user