diff --git a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts index 9b97c670b9c1..5008353f9103 100644 --- a/frontend/src/lib/uPlotLib/getUplotChartOptions.ts +++ b/frontend/src/lib/uPlotLib/getUplotChartOptions.ts @@ -529,9 +529,13 @@ export const getUPlotChartOptions = ({ // Simple tooltip events thElement.addEventListener('mouseenter', showTooltip); thElement.addEventListener('mouseleave', hideTooltip); - } - seriesEl.addEventListener('click', () => { - if (stackChart) { + + // Add click handlers for marker and text separately + const currentMarker = thElement.querySelector('.u-marker'); + const textElement = thElement.querySelector('.legend-text'); + + // Helper function to handle stack chart logic + const handleStackChart = (): void => { setHiddenGraph((prev) => { if (isUndefined(prev)) { return { [index]: true }; @@ -541,30 +545,71 @@ export const getUPlotChartOptions = ({ } return { [index]: true }; }); - } - if (graphsVisibilityStates) { - setGraphsVisibilityStates?.((prev) => { - const newGraphVisibilityStates = [...prev]; - if ( - newGraphVisibilityStates[index + 1] && - newGraphVisibilityStates.every((value, i) => - i === index + 1 ? value : !value, - ) - ) { - newGraphVisibilityStates.fill(true); - } else { - newGraphVisibilityStates.fill(false); - newGraphVisibilityStates[index + 1] = true; + }; + + // Marker click handler - checkbox behavior (toggle individual series) + if (currentMarker) { + currentMarker.addEventListener('click', (e) => { + e.stopPropagation(); // Prevent event bubbling to text handler + + if (stackChart) { + handleStackChart(); + } + if (graphsVisibilityStates) { + setGraphsVisibilityStates?.((prev) => { + const newGraphVisibilityStates = [...prev]; + // Toggle the specific series visibility (checkbox behavior) + newGraphVisibilityStates[index + 1] = !newGraphVisibilityStates[ + index + 1 + ]; + + saveLegendEntriesToLocalStorage({ + options: self, + graphVisibilityState: newGraphVisibilityStates, + name: id || '', + }); + return newGraphVisibilityStates; + }); } - saveLegendEntriesToLocalStorage({ - options: self, - graphVisibilityState: newGraphVisibilityStates, - name: id || '', - }); - return newGraphVisibilityStates; }); } - }); + + // Text click handler - show only/show all behavior (existing behavior) + if (textElement) { + textElement.addEventListener('click', (e) => { + e.stopPropagation(); // Prevent event bubbling + + if (stackChart) { + handleStackChart(); + } + if (graphsVisibilityStates) { + setGraphsVisibilityStates?.((prev) => { + const newGraphVisibilityStates = [...prev]; + // Show only this series / show all behavior + if ( + newGraphVisibilityStates[index + 1] && + newGraphVisibilityStates.every((value, i) => + i === index + 1 ? value : !value, + ) + ) { + // If only this series is visible, show all + newGraphVisibilityStates.fill(true); + } else { + // Otherwise, show only this series + newGraphVisibilityStates.fill(false); + newGraphVisibilityStates[index + 1] = true; + } + saveLegendEntriesToLocalStorage({ + options: self, + graphVisibilityState: newGraphVisibilityStates, + name: id || '', + }); + return newGraphVisibilityStates; + }); + } + }); + } + } }); } }, diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 14783825b369..3af7f6fa7b95 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -85,7 +85,7 @@ body { tbody { display: flex; flex-wrap: wrap; - gap: 2px 2px; + gap: 1px 2px; align-items: center; justify-content: flex-start; width: 100%; @@ -103,7 +103,7 @@ body { flex-wrap: nowrap; align-items: stretch; justify-content: flex-start; - gap: 1px; + gap: 2px; } tr.u-series { @@ -116,7 +116,6 @@ body { font-size: 12px; font-weight: 600; justify-content: flex-start; - padding: 8px 12px; cursor: pointer; position: relative; min-width: 0; @@ -124,11 +123,23 @@ body { .u-marker { border-radius: 50%; - min-width: 10px; - min-height: 10px; - width: 10px; - height: 10px; + min-width: 11px; + min-height: 11px; + width: 11px; + height: 11px; flex-shrink: 0; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + + &:hover { + transform: scale(1.2); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3); + } + + &:active { + transform: scale(0.9); + } } // Text container for proper ellipsis @@ -138,6 +149,7 @@ body { white-space: nowrap; min-width: 0; flex: 1; + padding-bottom: 2px; } // Tooltip styling @@ -159,6 +171,27 @@ body { &:hover { opacity: 0.7; } + + .u-marker { + opacity: 0.3; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 12px; + height: 2px; + background: #ff4444; + transform: translate(-50%, -50%) rotate(45deg); + border-radius: 1px; + } + + &:hover { + opacity: 0.6; + } + } } } @@ -204,11 +237,23 @@ body { .u-marker { border-radius: 50%; - min-width: 10px; - min-height: 10px; - width: 10px; - height: 10px; + min-width: 11px; + min-height: 11px; + width: 11px; + height: 11px; flex-shrink: 0; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + + &:hover { + transform: scale(1.2); + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3); + } + + &:active { + transform: scale(0.9); + } } .legend-text { @@ -217,6 +262,7 @@ body { white-space: nowrap; min-width: 0; flex: 1; + padding-bottom: 2px; } // Tooltip styling @@ -234,6 +280,27 @@ body { &:hover { opacity: 0.7; } + + .u-marker { + opacity: 0.3; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 12px; + height: 2px; + background: #ff4444; + transform: translate(-50%, -50%) rotate(45deg); + border-radius: 1px; + } + + &:hover { + opacity: 0.6; + } + } } } @@ -499,6 +566,18 @@ body { background: rgba(0, 0, 0, 0.08); opacity: 0.7; } + + .u-marker { + opacity: 0.3; + + &::after { + background: #cc3333; + } + + &:hover { + opacity: 0.6; + } + } } } @@ -525,6 +604,18 @@ body { background: rgba(0, 0, 0, 0.08); opacity: 0.7; } + + .u-marker { + opacity: 0.3; + + &::after { + background: #cc3333; + } + + &:hover { + opacity: 0.6; + } + } } }