@@ -21,12 +21,11 @@ import { keepPreviousData } from "@tanstack/react-query";
2121import { useStation } from "../hooks/use-station.js" ;
2222import { useStations } from "../hooks/use-stations.js" ;
2323import { useDebouncedCallback } from "../hooks/use-debounced-callback.js" ;
24- import { useExtremes } from "../hooks/use-extremes.js" ;
2524import { useNeapsConfig } from "../provider.js" ;
2625import { useDarkMode } from "../hooks/use-dark-mode.js" ;
2726import { useThemeColors } from "../hooks/use-theme-colors.js" ;
28- import { formatLevel , formatTime } from "../utils/format .js" ;
29- import type { StationSummary , Extreme } from "../types.js" ;
27+ import { TideConditions } from "./TideConditions .js" ;
28+ import type { StationSummary } from "../types.js" ;
3029
3130// Props that StationsMap manages internally and cannot be overridden
3231type ManagedMapProps = "onMove" | "onClick" | "interactiveLayerIds" | "style" | "cursor" ;
@@ -61,51 +60,19 @@ export interface StationsMapProps extends Omit<ComponentProps<typeof Map>, Manag
6160function stationsToGeoJSON ( stations : StationSummary [ ] ) : GeoJSON . FeatureCollection {
6261 return {
6362 type : "FeatureCollection" ,
64- features : stations . map ( ( s ) => ( {
65- type : "Feature" as const ,
66- geometry : { type : "Point" as const , coordinates : [ s . longitude , s . latitude ] } ,
67- properties : { id : s . id , name : s . name , region : s . region , country : s . country , type : s . type } ,
63+ features : stations . map ( ( { longitude, latitude, ...properties } ) => ( {
64+ type : "Feature" ,
65+ geometry : {
66+ type : "Point" ,
67+ coordinates : [ longitude , latitude ] ,
68+ } ,
69+ properties,
6870 } ) ) ,
6971 } ;
7072}
7173
72- function getNextExtreme ( extremes : Extreme [ ] ) : Extreme | null {
73- const now = new Date ( ) ;
74- return extremes . find ( ( e ) => e . time > now ) ?? null ;
75- }
76-
7774function StationPreviewCard ( { stationId } : { stationId : string } ) {
78- const config = useNeapsConfig ( ) ;
79- const now = new Date ( ) ;
80- const end = new Date ( now . getTime ( ) + 24 * 60 * 60 * 1000 ) ;
81- const { data, isLoading } = useExtremes ( {
82- id : stationId ,
83- start : now . toISOString ( ) ,
84- end : end . toISOString ( ) ,
85- } ) ;
86-
87- if ( isLoading ) {
88- return < span className = "text-xs text-(--neaps-text-muted)" > Loading...</ span > ;
89- }
90-
91- if ( ! data ) return null ;
92-
93- const next = getNextExtreme ( data . extremes ) ;
94- return (
95- < div className = "text-xs" >
96- { next && (
97- < div className = "mt-1" >
98- < span className = "text-(--neaps-text-muted)" > Next { next . high ? "High" : "Low" } : </ span >
99- < span className = "font-semibold text-(--neaps-text)" >
100- { formatLevel ( next . level , data . units ?? config . units ) } { " " }
101- < span className = "text-(--neaps-text-muted)" >
102- at { formatTime ( next . time , data . station ?. timezone ?? "UTC" , config . locale ) }
103- </ span >
104- </ span >
105- </ div >
106- ) }
107- </ div >
108- ) ;
75+ return < TideConditions id = { stationId } showDate = { false } /> ;
10976}
11077
11178export const StationsMap = forwardRef < MapRef , StationsMapProps > ( function StationsMap (
@@ -117,7 +84,7 @@ export const StationsMap = forwardRef<MapRef, StationsMapProps>(function Station
11784 focusStation,
11885 showGeolocation = true ,
11986 clustering = true ,
120- clusterMaxZoom : clusterMaxZoomProp = 14 ,
87+ clusterMaxZoom : clusterMaxZoomProp = 7 ,
12188 clusterRadius : clusterRadiusProp = 50 ,
12289 popupContent = "preview" ,
12390 children,
@@ -198,26 +165,20 @@ export const StationsMap = forwardRef<MapRef, StationsMapProps>(function Station
198165 // Station point click
199166 if ( props ?. id ) {
200167 const coords = ( feature . geometry as GeoJSON . Point ) . coordinates ;
201- const station : StationSummary = {
202- id : props . id ,
203- name : props . name ,
168+ const station = {
169+ ...props ,
204170 latitude : coords [ 1 ] ,
205171 longitude : coords [ 0 ] ,
206- region : props . region ?? "" ,
207- country : props . country ?? "" ,
208- continent : "" ,
209- timezone : "" ,
210- type : props . type ?? "reference" ,
211- } ;
172+ } as StationSummary ;
212173
213- if ( popupContent === "preview" ? ( viewState . zoom ?? 0 ) >= 10 : popupContent !== false ) {
174+ if ( popupContent !== false ) {
214175 setSelectedStation ( station ) ;
215176 }
216177
217178 onStationSelect ?.( station ) ;
218179 }
219180 } ,
220- [ onStationSelect , viewState . zoom , popupContent ] ,
181+ [ onStationSelect , popupContent ] ,
221182 ) ;
222183
223184 const handleLocateMe = useCallback ( ( ) => {
@@ -372,18 +333,29 @@ export const StationsMap = forwardRef<MapRef, StationsMapProps>(function Station
372333 < Popup
373334 longitude = { selectedStation . longitude }
374335 latitude = { selectedStation . latitude }
375- anchor = "bottom"
336+ maxWidth = "none"
337+ offset = { 10 }
376338 onClose = { ( ) => setSelectedStation ( null ) }
377339 closeOnClick = { false }
378- className = "neaps-popup"
340+ closeButton = { false }
379341 >
380- < div className = "p-2 min-w-40 " >
342+ < div className = "relative " >
381343 { typeof popupContent === "function" ? (
382344 popupContent ( selectedStation )
383345 ) : (
384346 < >
385- < div className = "font-semibold text-sm text-(--neaps-text)" >
386- { selectedStation . name }
347+ < div className = "flex gap-2 justify-between items-start mb-2" >
348+ < div className = "text-base font-semibold text-(--neaps-text)" >
349+ { selectedStation . name }
350+ </ div >
351+ < button
352+ type = "button"
353+ onClick = { ( ) => setSelectedStation ( null ) }
354+ className = "text-(--neaps-text-muted) hover:text-(--neaps-text) cursor-pointer leading-4 text-lg"
355+ aria-label = "Close popup"
356+ >
357+ ×
358+ </ button >
387359 </ div >
388360 { popupContent === "simple" ? (
389361 < div className = "text-xs text-(--neaps-text-muted)" >
0 commit comments