3
3
import 'maplibre-gl/dist/maplibre-gl.css' ;
4
4
5
5
import 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' ;
8
14
9
15
import { IData } from './IData' ;
10
16
import { IMaplibreWorldMapProps } from './IMaplibreWorldMapProps' ;
@@ -15,9 +21,12 @@ import strings from 'ControlStrings';
15
21
import { useCleanMapStyle } from './useCleanMapStyle' ;
16
22
17
23
const 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 } ` ,
21
30
demo : `https://demotiles.maplibre.org/style.json` , // Free demo style (no key required)
22
31
} ;
23
32
@@ -96,18 +105,21 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
96
105
const [ initialViewState ] = useState ( { longitude : 0 , latitude : 20 , zoom : 1 } ) ;
97
106
98
107
// 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
+ ) ;
111
123
112
124
// Reset to initial view when search is cleared
113
125
const resetToInitialView = useCallback ( ( ) => {
@@ -135,37 +147,41 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
135
147
} , [ data , fitPadding , initialViewState ] ) ;
136
148
137
149
// 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 ) ;
140
153
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
+ }
147
160
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 ) ;
152
166
153
- return searchValue ?. toLowerCase ( ) . includes ( term . toLowerCase ( ) ) ;
154
- } ) ;
167
+ return searchValue ?. toLowerCase ( ) . includes ( term . toLowerCase ( ) ) ;
168
+ } ) ;
155
169
156
- setFilteredData ( filtered ) ;
157
- search ?. onSearchChange ?.( term , filtered ) ;
170
+ setFilteredData ( filtered ) ;
171
+ search ?. onSearchChange ?.( term , filtered ) ;
158
172
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
+ ) ;
169
185
170
186
// Handle search clear
171
187
const handleSearchClear = useCallback ( ( ) => {
@@ -260,7 +276,11 @@ export const MaplibreWorldMap: React.FC<IMaplibreWorldMapProps> = ({
260
276
/>
261
277
{ searchTerm && (
262
278
< 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 }
264
284
</ div >
265
285
) }
266
286
</ div >
0 commit comments