From ed6e295537cb200c864cf12a75f0750157b9288b Mon Sep 17 00:00:00 2001 From: Matt Rabe Date: Sat, 24 Jan 2026 09:31:37 -1000 Subject: [PATCH] Prevent histogram popovers from overflowing the screen. Closes #148 --- apps/mobile/src/components/Popover.tsx | 54 +++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/apps/mobile/src/components/Popover.tsx b/apps/mobile/src/components/Popover.tsx index 23688dd..18a929e 100644 --- a/apps/mobile/src/components/Popover.tsx +++ b/apps/mobile/src/components/Popover.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useMemo, useState } from 'react' import { Dimensions, Modal, Pressable, StyleSheet, Text, View } from 'react-native' import { palette } from '../styles' @@ -20,6 +20,11 @@ export type PopoverContent = { title: string, } +const POPOVER_PADDING = 10 +const POPOVER_OFFSET_Y = 100 +const POPOVER_ESTIMATED_WIDTH = 300 // maxWidth from styles +const POPOVER_ESTIMATED_HEIGHT = 250 + export function Popover({ content, isVisible, @@ -27,6 +32,43 @@ export function Popover({ }: PopoverProps) { const screenHeight = Dimensions.get('window').height const screenWidth = Dimensions.get('window').width + const [popoverWidth, setPopoverWidth] = useState(POPOVER_ESTIMATED_WIDTH) + const [popoverHeight, setPopoverHeight] = useState(POPOVER_ESTIMATED_HEIGHT) + + // Reset dimensions when content changes to ensure accurate positioning + useEffect(() => { + if (content) { + setPopoverWidth(POPOVER_ESTIMATED_WIDTH) + setPopoverHeight(POPOVER_ESTIMATED_HEIGHT) + } + }, [content]) + + // Calculate position to ensure popover never overflows the screen + const position = useMemo(() => { + if (!content) return { top: 0, left: 0 } + + // Calculate horizontal position + // Try to center the popover horizontally relative to the click position + const preferredLeft = content.clickPosition.x - popoverWidth / 2 + + // Ensure popover doesn't overflow on the right + const maxLeft = screenWidth - popoverWidth - POPOVER_PADDING + // Ensure popover doesn't overflow on the left + const minLeft = POPOVER_PADDING + + // Clamp the left position + const left = Math.max(minLeft, Math.min(preferredLeft, maxLeft)) + + // Calculate vertical position + const preferredTop = content.clickPosition.y - POPOVER_OFFSET_Y + const maxTop = screenHeight - popoverHeight - POPOVER_PADDING + const minTop = POPOVER_PADDING + + // Clamp the top position + const top = Math.max(minTop, Math.min(preferredTop, maxTop)) + + return { top, left } + }, [content, popoverWidth, popoverHeight, screenWidth, screenHeight]) return ( { + const { width, height } = e.nativeEvent.layout + setPopoverWidth(width) + setPopoverHeight(height) + }} onPress={(e) => e.stopPropagation()} >