@@ -15,6 +15,7 @@ function Places(): JSX.Element {
1515 const [ selectedLayers , setSelectedLayers ] = useState < string [ ] > ( Object . values ( PlaceType ) )
1616 const [ places , setPlaces ] = useState < PlaceItem [ ] > ( [ ] )
1717 const [ selectedPlace , setSelectedPlace ] = useState < PlaceItem | null > ( null )
18+ const [ isInitialized , setIsInitialized ] = useState ( false )
1819 const placesRef = useRef < PlaceItem [ ] > ( [ ] )
1920
2021 // Receive places data from PlacesMap component
@@ -32,7 +33,7 @@ function Places(): JSX.Element {
3233 setTimeout ( ( ) => {
3334 const place = placesRef . current . find ( ( p ) => p . id === placeId )
3435 if ( place ) {
35- setSelectedPlace ( place )
36+ handlePlaceSelect ( place )
3637 }
3738 } , 500 )
3839 }
@@ -42,14 +43,64 @@ function Places(): JSX.Element {
4243 return ( ) => window . removeEventListener ( 'hogmap:places-updated' , handlePlaceAdded as EventListener )
4344 } , [ ] )
4445
45- // Show place detail overlay when a marker is clicked
46- const handlePlaceClick = useCallback ( ( placeId : number ) => {
47- const place = placesRef . current . find ( ( p ) => p . id === placeId )
48- if ( place ) {
49- setSelectedPlace ( place )
46+ // Select a place and update URL hash
47+ const handlePlaceSelect = useCallback ( ( place : PlaceItem , updateHash = true ) => {
48+ setSelectedPlace ( place )
49+
50+ if ( updateHash ) {
51+ window . history . replaceState ( null , '' , `#placeId=${ place . id } ` )
5052 }
5153 } , [ ] )
5254
55+ // Show place detail overlay when a marker is clicked
56+ const handlePlaceClick = useCallback (
57+ ( placeId : number ) => {
58+ const place = placesRef . current . find ( ( p ) => p . id === placeId )
59+ if ( place ) {
60+ handlePlaceSelect ( place )
61+ }
62+ } ,
63+ [ handlePlaceSelect ]
64+ )
65+
66+ // Close place detail and clear URL hash
67+ const handleClosePlace = useCallback ( ( ) => {
68+ setSelectedPlace ( null )
69+ window . history . replaceState ( null , '' , window . location . pathname )
70+ } , [ ] )
71+
72+ // Initialize from URL hash on page load
73+ useEffect ( ( ) => {
74+ if ( ! isInitialized && places . length > 0 ) {
75+ const hash = window . location . hash
76+ const match = hash . match ( / # p l a c e I d = ( \d + ) / )
77+
78+ if ( match ) {
79+ const placeId = parseInt ( match [ 1 ] , 10 )
80+ const place = places . find ( ( p ) => p . id === placeId )
81+
82+ if ( place ) {
83+ // Select place without updating hash (since we're reading from it)
84+ handlePlaceSelect ( place , false )
85+ }
86+ }
87+
88+ setIsInitialized ( true )
89+ }
90+ } , [ places , isInitialized , handlePlaceSelect ] )
91+
92+ // Handle ESC key to close detail panel
93+ useEffect ( ( ) => {
94+ const handleKeyDown = ( e : KeyboardEvent ) => {
95+ if ( e . key === 'Escape' && selectedPlace ) {
96+ handleClosePlace ( )
97+ }
98+ }
99+
100+ window . addEventListener ( 'keydown' , handleKeyDown )
101+ return ( ) => window . removeEventListener ( 'keydown' , handleKeyDown )
102+ } , [ selectedPlace , handleClosePlace ] )
103+
53104 // Count places by type
54105 const placesByType = places . reduce ( ( acc , place ) => {
55106 const type = place . type || PlaceType . COFFEE
@@ -156,9 +207,7 @@ function Places(): JSX.Element {
156207
157208 < div className = "flex-1 relative h-full border-primary border-t @xl:border-t-0" >
158209 < AnimatePresence >
159- { selectedPlace && (
160- < PlaceDetail place = { selectedPlace } onClose = { ( ) => setSelectedPlace ( null ) } />
161- ) }
210+ { selectedPlace && < PlaceDetail place = { selectedPlace } onClose = { handleClosePlace } /> }
162211 </ AnimatePresence >
163212
164213 < PlacesMap
0 commit comments