33import 'maplibre-gl/dist/maplibre-gl.css' ;
44
55import Map , { MapRef , StyleSpecification } from 'react-map-gl/maplibre' ;
6- import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
7- import { SearchBox , Subtitle1 , Text , } from '@fluentui/react-components' ;
6+ import React , {
7+ useCallback ,
8+ useEffect ,
9+ useMemo ,
10+ useRef ,
11+ useState ,
12+ } from 'react' ;
13+ import { SearchBox , Subtitle1 , Text } from '@fluentui/react-components' ;
814
915import { IData } from './IData' ;
1016import { IMaplibreWorldMapProps } from './IMaplibreWorldMapProps' ;
@@ -15,9 +21,12 @@ import strings from 'ControlStrings';
1521import { useCleanMapStyle } from './useCleanMapStyle' ;
1622
1723const MULTI_STYLE_URLS = {
18- satellite : ( key : string ) => `https://api.maptiler.com/maps/satellite/style.json?key=${ key } ` ,
19- streets : ( key : string ) => `https://api.maptiler.com/maps/streets/style.json?key=${ key } ` ,
20- topo : ( key : string ) => `https://api.maptiler.com/maps/topo-v2/style.json?key=${ key } ` ,
24+ satellite : ( key : string ) =>
25+ `https://api.maptiler.com/maps/satellite/style.json?key=${ key } ` ,
26+ streets : ( key : string ) =>
27+ `https://api.maptiler.com/maps/streets/style.json?key=${ key } ` ,
28+ topo : ( key : string ) =>
29+ `https://api.maptiler.com/maps/topo-v2/style.json?key=${ key } ` ,
2130 demo : `https://demotiles.maplibre.org/style.json` , // Free demo style (no key required)
2231} ;
2332
@@ -96,18 +105,21 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
96105 const [ initialViewState ] = useState ( { longitude : 0 , latitude : 20 , zoom : 1 } ) ;
97106
98107 // Search configuration with defaults
99- const searchConfig = useMemo ( ( ) => ( {
100- enabled : search ?. enabled ?? true ,
101- placeholder : search ?. placeholder ?? strings . worldMapSearchLocations ,
102- searchField : search ?. searchField ?? strings . worldMapSearchField ,
103- zoomLevel : search ?. zoomLevel ?? 8 ,
104- position : {
105- top : '10px' ,
106- left : '10px' ,
107- ...search ?. position ,
108- } ,
109- ...search ,
110- } ) , [ search ] ) ;
108+ const searchConfig = useMemo (
109+ ( ) => ( {
110+ enabled : search ?. enabled ?? true ,
111+ placeholder : search ?. placeholder ?? strings . worldMapSearchLocations ,
112+ searchField : search ?. searchField ?? strings . worldMapSearchField ,
113+ zoomLevel : search ?. zoomLevel ?? 8 ,
114+ position : {
115+ top : '10px' ,
116+ left : '10px' ,
117+ ...search ?. position ,
118+ } ,
119+ ...search ,
120+ } ) ,
121+ [ search ]
122+ ) ;
111123
112124 // Reset to initial view when search is cleared
113125 const resetToInitialView = useCallback ( ( ) => {
@@ -135,37 +147,41 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
135147 } , [ data , fitPadding , initialViewState ] ) ;
136148
137149 // Filter data based on search term
138- const handleSearch = useCallback ( ( term : string ) => {
139- setSearchTerm ( term ) ;
150+ const handleSearch = useCallback (
151+ ( term : string ) => {
152+ setSearchTerm ( term ) ;
140153
141- if ( ! term . trim ( ) ) {
142- setFilteredData ( data ) ;
143- search ?. onSearchChange ?.( term , data ) ;
144- resetToInitialView ( ) ;
145- return ;
146- }
154+ if ( ! term . trim ( ) ) {
155+ setFilteredData ( data ) ;
156+ search ?. onSearchChange ?.( term , data ) ;
157+ resetToInitialView ( ) ;
158+ return ;
159+ }
147160
148- const filtered = data . filter ( ( item ) => {
149- const searchValue = typeof searchConfig . searchField === 'function'
150- ? searchConfig . searchField ( item )
151- : item [ searchConfig . searchField as keyof IData ] as string ;
161+ const filtered = data . filter ( ( item ) => {
162+ const searchValue =
163+ typeof searchConfig . searchField === 'function'
164+ ? searchConfig . searchField ( item )
165+ : ( item [ searchConfig . searchField as keyof IData ] as string ) ;
152166
153- return searchValue ?. toLowerCase ( ) . includes ( term . toLowerCase ( ) ) ;
154- } ) ;
167+ return searchValue ?. toLowerCase ( ) . includes ( term . toLowerCase ( ) ) ;
168+ } ) ;
155169
156- setFilteredData ( filtered ) ;
157- search ?. onSearchChange ?.( term , filtered ) ;
170+ setFilteredData ( filtered ) ;
171+ search ?. onSearchChange ?.( term , filtered ) ;
158172
159- // Auto-zoom to first result
160- if ( filtered . length > 0 && mapRef . current ) {
161- const firstResult = filtered [ 0 ] ;
162- mapRef . current . getMap ( ) . flyTo ( {
163- center : firstResult . coordinates ,
164- zoom : searchConfig . zoomLevel ,
165- duration : 1000 ,
166- } ) ;
167- }
168- } , [ data , search , searchConfig , resetToInitialView ] ) ;
173+ // Auto-zoom to first result
174+ if ( filtered . length > 0 && mapRef . current ) {
175+ const firstResult = filtered [ 0 ] ;
176+ mapRef . current . getMap ( ) . flyTo ( {
177+ center : firstResult . coordinates ,
178+ zoom : searchConfig . zoomLevel ,
179+ duration : 1000 ,
180+ } ) ;
181+ }
182+ } ,
183+ [ data , search , searchConfig , resetToInitialView ]
184+ ) ;
169185
170186 // Handle search clear
171187 const handleSearchClear = useCallback ( ( ) => {
@@ -260,7 +276,11 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
260276 />
261277 { searchTerm && (
262278 < div className = { styles . searchResults } >
263- { filteredData . length } { filteredData . length === 1 ? strings . worldMapLocationLabel : strings . worldMapLocationPluralLabel } { strings . worldMapFoundLabel }
279+ { filteredData . length } { ' ' }
280+ { filteredData . length === 1
281+ ? strings . worldMapLocationLabel
282+ : strings . worldMapLocationPluralLabel } { ' ' }
283+ { strings . worldMapFoundLabel }
264284 </ div >
265285 ) }
266286 </ div >
0 commit comments