Fix eye button tap on touch devices in details panel

On touch devices, tapping an eye button in the details panel would
trigger chart mouseout → showDefaultView() which rebuilt the DOM
before the click event fired. Added pointerdown/pointerup guards
on the details box to prevent the race condition.

Also added @media (pointer: coarse) to always show eye buttons
on touch devices regardless of viewport width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Anton Volnuhin 2026-02-08 01:01:00 +03:00
parent 7e87a03b90
commit bda882d89a

20
app.js
View File

@ -3244,6 +3244,19 @@ function setupHoverEvents(sunburstData, contextName = null) {
// Track if we're inside a section to handle sector exit properly // Track if we're inside a section to handle sector exit properly
let isInsideSection = false; let isInsideSection = false;
// Prevent details panel reset when user is interacting with it (touch devices)
let isInteractingWithDetails = false;
const detailsBox = document.getElementById('details-box');
if (detailsBox) {
detailsBox.addEventListener('pointerdown', () => {
isInteractingWithDetails = true;
});
// Clear after a short delay to allow click handlers to fire
detailsBox.addEventListener('pointerup', () => {
setTimeout(() => { isInteractingWithDetails = false; }, 300);
});
}
// Add general mousemove event listener to detect when outside chart circle // Add general mousemove event listener to detect when outside chart circle
chartDom.addEventListener('mousemove', function(e) { chartDom.addEventListener('mousemove', function(e) {
// Get mouse position relative to chart container // Get mouse position relative to chart container
@ -3256,7 +3269,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
lastMouseY = mouseY; lastMouseY = mouseY;
// If not inside the chart circle and we were inside a section, show default view // If not inside the chart circle and we were inside a section, show default view
if (!isInsideChart(mouseX, mouseY) && isInsideSection) { if (!isInsideChart(mouseX, mouseY) && isInsideSection && !isInteractingWithDetails) {
isInsideSection = false; isInsideSection = false;
// Reset details immediately when leaving a section // Reset details immediately when leaving a section
showDefaultView(); showDefaultView();
@ -3403,7 +3416,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
hideChartEyeButton(); hideChartEyeButton();
// Reset details with a small delay to allow mouseover of next sector to fire first // Reset details with a small delay to allow mouseover of next sector to fire first
setTimeout(() => { setTimeout(() => {
if (!isOverEyeButton && !isInsideSection) { if (!isOverEyeButton && !isInsideSection && !isInteractingWithDetails) {
showDefaultView(); showDefaultView();
} }
}, 50); }, 50);
@ -3416,6 +3429,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
if (e.relatedTarget === chartEyeBtn || (e.relatedTarget && chartEyeBtn.contains(e.relatedTarget))) { if (e.relatedTarget === chartEyeBtn || (e.relatedTarget && chartEyeBtn.contains(e.relatedTarget))) {
return; return;
} }
if (isInteractingWithDetails) return;
isInsideSection = false; isInsideSection = false;
showDefaultView(); showDefaultView();
}); });
@ -3424,7 +3438,7 @@ function setupHoverEvents(sunburstData, contextName = null) {
myChart.on('downplay', function(params) { myChart.on('downplay', function(params) {
// Reset to default view when a section is no longer emphasized (unless hovering eye button or still in section) // Reset to default view when a section is no longer emphasized (unless hovering eye button or still in section)
setTimeout(() => { setTimeout(() => {
if (!isOverEyeButton && !isInsideSection) { if (!isOverEyeButton && !isInsideSection && !isInteractingWithDetails) {
showDefaultView(); showDefaultView();
} }
}, 50); }, 50);