feat(network-map): add jitter-based label placement and clamp function for improved edge label positioning
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m20s

This commit is contained in:
2026-02-24 23:39:52 +07:00
parent d657b03abc
commit b76cbc15cb
+43 -15
View File
@@ -119,6 +119,20 @@ function anchorPoint(pos, side, slotOffset = 0) {
return { x: pos.x + slotOffset, y: pos.y - NODE_HEIGHT / 2 }; return { x: pos.x + slotOffset, y: pos.y - NODE_HEIGHT / 2 };
} }
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
function hash01(str) {
let h = 2166136261;
const s = String(str || '');
for (let i = 0; i < s.length; i += 1) {
h ^= s.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return (h >>> 0) / 4294967295;
}
export default function NetworkMapUnifi({ export default function NetworkMapUnifi({
servers = [], servers = [],
connections = [], connections = [],
@@ -442,15 +456,24 @@ export default function NetworkMapUnifi({
return `M ${sx} ${sy} L ${sOutX} ${sOutY} Q ${cpx} ${cpy} ${eOutX} ${eOutY} L ${ex} ${ey}`; return `M ${sx} ${sy} L ${sOutX} ${sOutY} Q ${cpx} ${cpy} ${eOutX} ${eOutY} L ${ex} ${ey}`;
}, [computeCurvePoints]); }, [computeCurvePoints]);
/** Точка для подписи: середина ребра + смещение по перпендикуляру (чтобы подпись была у ребра, а не уезжала) */ /** Точка подписи на кривой с небольшим jitter и ограничением в пределах карты */
const labelPlacement = useCallback((e, level = 0) => { const labelPlacement = useCallback((e, level = 0) => {
const { mx, my, perpX, perpY } = computeCurvePoints(e); const geom = computeCurvePoints(e);
const baseOffset = 18; const jitter = (hash01(e.edgeKey) - 0.5) * 0.24;
const stepOffset = 26; const t = clamp(0.42 + jitter, 0.26, 0.74);
const x = mx + perpX * (baseOffset + level * stepOffset); const omt = 1 - t;
const y = my + perpY * (baseOffset + level * stepOffset); const xCurve = omt * omt * geom.sOutX + 2 * omt * t * geom.cpx + t * t * geom.eOutX;
return { x, y }; const yCurve = omt * omt * geom.sOutY + 2 * omt * t * geom.cpy + t * t * geom.eOutY;
}, [computeCurvePoints]); const baseOffset = 16;
const stepOffset = 18;
const x = xCurve + geom.perpX * (baseOffset + level * stepOffset);
const y = yCurve + geom.perpY * (baseOffset + level * stepOffset);
const margin = 22;
return {
x: clamp(x, margin, Math.max(margin, size.w - margin)),
y: clamp(y, margin, Math.max(margin, size.h - margin)),
};
}, [computeCurvePoints, size.w, size.h]);
if (servers.length === 0) return null; if (servers.length === 0) return null;
@@ -500,11 +523,15 @@ export default function NetworkMapUnifi({
const used = []; const used = [];
const LABEL_W = 118; const LABEL_W = 118;
const LABEL_H = 22; const LABEL_H = 22;
const denseMode = routedEdges.length > 14;
return routedEdges.map((e, idx) => { return routedEdges.map((e, idx) => {
const dimFactor = const dimFactor =
hoveredEdgeKey && hoveredEdgeKey !== e.edgeKey ? 0.25 : 1; hoveredEdgeKey && hoveredEdgeKey !== e.edgeKey ? 0.16 : 1;
const strokeWidth = hoveredEdgeKey === e.edgeKey ? 3 : 2; const strokeWidth = hoveredEdgeKey === e.edgeKey ? 3 : 1.8;
const edgeColor = getTunnelColor(e.tunnelType); const edgeColor = getTunnelColor(e.tunnelType);
const isFocused =
hoveredEdgeKey === e.edgeKey || selectedEdge?.edgeKey === e.edgeKey;
const baseOpacity = denseMode ? 0.62 : 0.82;
return ( return (
<g <g
key={e.edgeKey || `${e.from}-${e.to}-${idx}`} key={e.edgeKey || `${e.from}-${e.to}-${idx}`}
@@ -521,12 +548,13 @@ export default function NetworkMapUnifi({
stroke={edgeColor} stroke={edgeColor}
strokeWidth={strokeWidth} strokeWidth={strokeWidth}
strokeLinecap="round" strokeLinecap="round"
opacity={dimFactor} opacity={baseOpacity * dimFactor}
filter="url(#edgeGlow)" filter="url(#edgeGlow)"
/> />
{(() => { {(() => {
if (!e.edgeLabel) return null; const showLabel = e.edgeLabel && (!denseMode || isFocused || e.stale);
const minDist = 70; if (!showLabel) return null;
const minDist = denseMode ? 56 : 70;
let level = 0; let level = 0;
let x; let y; let x; let y;
while (level < 10) { while (level < 10) {
@@ -557,9 +585,9 @@ export default function NetworkMapUnifi({
width={w} width={w}
height={h} height={h}
rx={7} rx={7}
fill="rgba(2,6,23,0.9)" fill={isFocused ? 'rgba(2,6,23,0.94)' : 'rgba(2,6,23,0.85)'}
stroke={isStale ? staleColor : edgeColor} stroke={isStale ? staleColor : edgeColor}
strokeWidth={1} strokeWidth={isFocused ? 1.3 : 1}
strokeDasharray={isStale ? '2,2' : 'none'} strokeDasharray={isStale ? '2,2' : 'none'}
/> />
{isStale && ( {isStale && (