(null);
+ const [tooltipPosition, setTooltipPosition] = useState({x, y});
+
+ const baseOffset = {
+ x: 16,
+ y: -8,
+ };
+
+ useEffect(() => {
+ if (tooltipRef.current != null) {
+ const tooltipWidth = tooltipRef.current.offsetWidth;
+
+ let newX = x + baseOffset.x;
+ let newY = y + baseOffset.y;
+
+ if (newX + tooltipWidth > containerWidth) {
+ newX = x - tooltipWidth - baseOffset.x;
+ }
+
+ if (newY < 0) {
+ newY = y + Math.abs(baseOffset.y);
+ }
+
+ setTooltipPosition({x: newX, y: newY});
+ }
+ }, [x, y, containerWidth, baseOffset.x, baseOffset.y]);
+
+ return (
+
+
+
+
Timestamp:
+
+ {formatDateTimeDownToMS(timestamp, timezone)}
+
+
+
+
+
Value:
+
+ {valueFormatter(value, 'nanoseconds', 2)}
+
+
+
+
+ );
+}
diff --git a/ui/packages/shared/profile/src/MetricsGraphStrips/AreaGraph/index.tsx b/ui/packages/shared/profile/src/MetricsGraphStrips/AreaGraph/index.tsx
index 44b915280d4..556ccfd036f 100644
--- a/ui/packages/shared/profile/src/MetricsGraphStrips/AreaGraph/index.tsx
+++ b/ui/packages/shared/profile/src/MetricsGraphStrips/AreaGraph/index.tsx
@@ -18,6 +18,7 @@ import cx from 'classnames';
import * as d3 from 'd3';
import {NumberDuo} from '../../utils';
+import {Tooltip} from './Tooltip';
export interface DataPoint {
timestamp: number;
@@ -143,7 +144,7 @@ const ZoomWindow = ({
(undefined);
const [dragStart, setDragStart] = useState
(undefined);
const [isHoveringDragHandle, setIsHoveringDragHandle] = useState(false);
+ const [hoverData, setHoverData] = useState<{timestamp: number; value: number} | null>(null);
+ const [isMouseOverGraph, setIsMouseOverGraph] = useState(false);
const isDragging = dragStart !== undefined;
// Declare the x (horizontal position) scale.
@@ -245,12 +248,39 @@ export const AreaGraph = ({
{
- const [x, y] = d3.pointer(e);
- setMousePosition([x, y]);
+ const [xPos, yPos] = d3.pointer(e);
+
+ if (
+ xPos >= marginLeft &&
+ xPos <= width - marginRight &&
+ yPos >= marginTop &&
+ yPos <= height - marginBottom
+ ) {
+ setMousePosition([xPos, yPos]);
+
+ // Find the closest data point
+ if (!isHoveringDragHandle && !isDragging) {
+ const xDate = x.invert(xPos);
+ const bisect = d3.bisector((d: DataPoint) => d.timestamp).left;
+ const index = bisect(data, xDate.getTime());
+ const dataPoint = data[index];
+ if (dataPoint !== undefined) {
+ setHoverData(dataPoint);
+ }
+ }
+ } else {
+ setMousePosition(undefined);
+ setHoverData(null);
+ }
+ }}
+ onMouseEnter={() => {
+ setIsMouseOverGraph(true);
}}
onMouseLeave={() => {
+ setIsMouseOverGraph(false);
setMousePosition(undefined);
setDragStart(undefined);
+ setHoverData(null);
}}
onMouseDown={e => {
// only left mouse button
@@ -313,6 +343,21 @@ export const AreaGraph = ({
})}
>
+ {/* Update Tooltip conditional render */}
+ {mousePosition !== undefined &&
+ hoverData !== null &&
+ !isDragging &&
+ !isHoveringDragHandle &&
+ isMouseOverGraph && (
+
+ )}
+
diff --git a/ui/packages/shared/profile/src/MetricsGraphStrips/index.tsx b/ui/packages/shared/profile/src/MetricsGraphStrips/index.tsx
index 62e9b2f60e3..fffc403efa4 100644
--- a/ui/packages/shared/profile/src/MetricsGraphStrips/index.tsx
+++ b/ui/packages/shared/profile/src/MetricsGraphStrips/index.tsx
@@ -95,7 +95,10 @@ export const MetricsGraphStrips = ({
return (
{
const newCollapsedIndices = [...collapsedIndices];
if (collapsedIndices.includes(i)) {
diff --git a/ui/packages/shared/utilities/src/index.ts b/ui/packages/shared/utilities/src/index.ts
index 8be80d93979..813f96e1548 100644
--- a/ui/packages/shared/utilities/src/index.ts
+++ b/ui/packages/shared/utilities/src/index.ts
@@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {format} from 'date-fns-tz';
import colors from 'tailwindcss/colors';
import {Label} from '@parca/client';
@@ -376,3 +377,9 @@ export const isUrlEncoded = (str: string): boolean => {
return false; // Invalid encoding
}
};
+
+export function formatDateTimeDownToMS(timestamp: Date, timezone?: string): string {
+ return timezone !== undefined
+ ? format(timestamp, "yyyy:MM:dd'T'HH:mm:ss.SSS", {timeZone: timezone})
+ : format(timestamp, "yyyy:MM:dd'T'HH:mm:ss.SSS", {timeZone: 'UTC'});
+}