diff --git a/packages/react-maplibre/src/components/MlMarker/MlMarker.tsx b/packages/react-maplibre/src/components/MlMarker/MlMarker.tsx index 0202192e..306bcecd 100644 --- a/packages/react-maplibre/src/components/MlMarker/MlMarker.tsx +++ b/packages/react-maplibre/src/components/MlMarker/MlMarker.tsx @@ -2,7 +2,18 @@ import React, { useRef, useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; import useMap from '../../hooks/useMap'; import maplibregl from 'maplibre-gl'; -import { Box } from '@mui/material'; +import { Box, Paper, IconButton } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; + +// Constants for popup styling +const POPUP_PADDING_VERTICAL = 12; +const POPUP_PADDING_HORIZONTAL = 16; +const CLOSE_BUTTON_SPACING = 4; +const SCROLLBAR_WIDTH = 16; // Typical scrollbar width +const CLOSE_BUTTON_OFFSET = SCROLLBAR_WIDTH + CLOSE_BUTTON_SPACING; +const POPUP_MIN_WIDTH = 200; +const POPUP_MAX_WIDTH = 750; +const POPUP_MAX_HEIGHT = 500; export interface MlMarkerProps { /** ID of the map to add the marker to */ @@ -27,6 +38,10 @@ export interface MlMarkerProps { contentOffset?: number; /** Whether mouse events pass through the marker content */ passEventsThrough?: boolean; + /** Whether to show a close button to remove the marker */ + showCloseButton?: boolean; + /** Callback function when the close button is clicked */ + onClose?: () => void; /** Anchor position of the marker relative to its coordinates */ anchor?: | 'top' @@ -103,16 +118,33 @@ function getBoxMargins( return m; } -const MlMarker = ({ passEventsThrough = true, contentOffset = 5, ...props }: MlMarkerProps) => { +const MlMarker = ({ + passEventsThrough = true, + contentOffset = 5, + showCloseButton = true, + ...props +}: MlMarkerProps) => { const mapHook = useMap({ mapId: props.mapId, waitForLayer: props.insertBeforeLayer, }); const [marker, setMarker] = useState(null); + const [contentWidth, setContentWidth] = useState(300); const container = useRef(null); const iframeRef = useRef(null); + const handleClose = (event: React.MouseEvent) => { + event.stopPropagation(); + if (props.onClose) { + props.onClose(); + } else { + // Default behavior: remove the marker + marker?.remove(); + container.current?.remove(); + } + }; + useEffect(() => { if (!mapHook.map) return; @@ -161,9 +193,17 @@ const MlMarker = ({ passEventsThrough = true, contentOffset = 5, ...props }: MlM function handleIframeLoad() { const iframeDoc = iframeRef.current?.contentWindow?.document; - if (iframeDoc && iframeRef.current?.parentElement) { + if (iframeDoc && iframeRef.current) { const scrollHeight = iframeDoc.documentElement.scrollHeight; - iframeRef.current.parentElement.style.height = `${scrollHeight}px`; + const scrollWidth = iframeDoc.documentElement.scrollWidth; + iframeRef.current.style.height = `${scrollHeight}px`; + + // Set width based on content, with min and max constraints + const calculatedWidth = Math.max( + POPUP_MIN_WIDTH, + Math.min(scrollWidth + POPUP_PADDING_HORIZONTAL * 2, POPUP_MAX_WIDTH) + ); + setContentWidth(calculatedWidth); } } @@ -173,41 +213,117 @@ const MlMarker = ({ passEventsThrough = true, contentOffset = 5, ...props }: MlM -