Fix proximity radar tooltip flicker on hover

Separate SVG translate positioning from CSS hover scale by nesting
device elements in two groups, preventing the CSS transform from
overriding the position and causing rapid mouseenter/mouseleave cycling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-04 23:40:14 +00:00
parent 725d95c079
commit 9dd8849b21

View File

@@ -211,19 +211,21 @@ const ProximityRadar = (function() {
const hitAreaSize = Math.max(dotSize * 2, 15);
return `
<g class="radar-device ${pulseClass}${isSelected ? ' selected' : ''}" data-device-key="${escapeAttr(device.device_key)}"
transform="translate(${x}, ${y})" style="cursor: pointer;">
<!-- Invisible hit area to prevent hover flicker -->
<circle class="radar-device-hitarea" r="${hitAreaSize}" fill="transparent" />
${isSelected ? `<circle r="${dotSize + 8}" fill="none" stroke="#00d4ff" stroke-width="2" stroke-opacity="0.8">
<animate attributeName="r" values="${dotSize + 6};${dotSize + 10};${dotSize + 6}" dur="1.5s" repeatCount="indefinite"/>
<animate attributeName="stroke-opacity" values="0.8;0.4;0.8" dur="1.5s" repeatCount="indefinite"/>
</circle>` : ''}
<circle r="${dotSize}" fill="${color}"
fill-opacity="${isSelected ? 1 : 0.4 + confidence * 0.5}"
stroke="${isSelected ? '#00d4ff' : color}" stroke-width="${isSelected ? 2 : 1}" />
${device.is_new && !isSelected ? `<circle r="${dotSize + 3}" fill="none" stroke="#3b82f6" stroke-width="1" stroke-dasharray="2,2" />` : ''}
<title>${escapeHtml(device.name || device.address)} (${device.rssi_current || '--'} dBm)</title>
<g transform="translate(${x}, ${y})">
<g class="radar-device ${pulseClass}${isSelected ? ' selected' : ''}" data-device-key="${escapeAttr(device.device_key)}"
style="cursor: pointer;">
<!-- Invisible hit area to prevent hover flicker -->
<circle class="radar-device-hitarea" r="${hitAreaSize}" fill="transparent" />
${isSelected ? `<circle r="${dotSize + 8}" fill="none" stroke="#00d4ff" stroke-width="2" stroke-opacity="0.8">
<animate attributeName="r" values="${dotSize + 6};${dotSize + 10};${dotSize + 6}" dur="1.5s" repeatCount="indefinite"/>
<animate attributeName="stroke-opacity" values="0.8;0.4;0.8" dur="1.5s" repeatCount="indefinite"/>
</circle>` : ''}
<circle r="${dotSize}" fill="${color}"
fill-opacity="${isSelected ? 1 : 0.4 + confidence * 0.5}"
stroke="${isSelected ? '#00d4ff' : color}" stroke-width="${isSelected ? 2 : 1}" />
${device.is_new && !isSelected ? `<circle r="${dotSize + 3}" fill="none" stroke="#3b82f6" stroke-width="1" stroke-dasharray="2,2" />` : ''}
<title>${escapeHtml(device.name || device.address)} (${device.rssi_current || '--'} dBm)</title>
</g>
</g>
`;
}).join('');