11import {
22 useCallback ,
33 useMemo ,
4+ useState ,
45} from 'react' ;
56import { NumberInput } from '@ifrc-go/ui' ;
67import { useTranslation } from '@ifrc-go/ui/hooks' ;
@@ -10,15 +11,19 @@ import {
1011 isNotDefined ,
1112} from '@togglecorp/fujs' ;
1213import {
14+ MapCenter ,
1315 MapContainer ,
1416 MapLayer ,
1517 MapSource ,
1618} from '@togglecorp/re-map' ;
1719import { type ObjectError } from '@togglecorp/toggle-form' ;
1820import getBbox from '@turf/bbox' ;
1921import {
22+ type AnySourceData ,
2023 type CircleLayer ,
2124 type FillLayer ,
25+ type FitBoundsOptions ,
26+ type FlyToOptions ,
2227 type LngLat ,
2328 type Map ,
2429 type MapboxGeoJSONFeature ,
@@ -35,15 +40,34 @@ import {
3540import { localUnitMapStyle } from '#utils/map' ;
3641
3742import ActiveCountryBaseMapLayer from '../ActiveCountryBaseMapLayer' ;
43+ import LocationSearchInput , { type LocationSearchResult } from '../LocationSearchInput' ;
3844
3945import i18n from './i18n.json' ;
4046import styles from './styles.module.css' ;
4147
48+ const centerOptions = {
49+ zoom : 16 ,
50+ duration : 1000 ,
51+ } satisfies FlyToOptions ;
52+
53+ const geoJsonSourceOptions = {
54+ type : 'geojson' ,
55+ } satisfies AnySourceData ;
56+
4257interface GeoPoint {
4358 lng : number ;
4459 lat : number
4560}
4661
62+ const fitBoundsOptions = {
63+ padding : {
64+ left : 20 ,
65+ top : 20 ,
66+ bottom : 50 ,
67+ right : 20 ,
68+ } ,
69+ } satisfies FitBoundsOptions ;
70+
4771type Value = Partial < GeoPoint > ;
4872
4973interface Props < NAME > extends BaseMapProps {
@@ -93,17 +117,6 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
93117 const countryDetails = useCountry ( { id : country ?? - 1 } ) ;
94118 const strings = useTranslation ( i18n ) ;
95119
96- const bounds = useMemo (
97- ( ) => {
98- if ( isNotDefined ( countryDetails ) ) {
99- return undefined ;
100- }
101-
102- return getBbox ( countryDetails . bbox ) ;
103- } ,
104- [ countryDetails ] ,
105- ) ;
106-
107120 const pointGeoJson = useMemo < GeoJSON . Feature | undefined > (
108121 ( ) => {
109122 if ( isNotDefined ( value ) || isNotDefined ( value . lng ) || isNotDefined ( value . lat ) ) {
@@ -192,6 +205,27 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
192205 [ value , onChange , name ] ,
193206 ) ;
194207
208+ const bounds = useMemo (
209+ ( ) => {
210+ if ( isNotDefined ( countryDetails ) ) {
211+ return undefined ;
212+ }
213+
214+ return getBbox ( countryDetails . bbox ) ;
215+ } ,
216+ [ countryDetails ] ,
217+ ) ;
218+
219+ const [ searchResult , setSearchResult ] = useState < LocationSearchResult | undefined > ( ) ;
220+
221+ const center = useMemo ( ( ) => {
222+ if ( isNotDefined ( searchResult ) ) {
223+ return undefined ;
224+ }
225+
226+ return [ + searchResult . lon , + searchResult . lat ] satisfies [ number , number ] ;
227+ } , [ searchResult ] ) ;
228+
195229 return (
196230 < div className = { _cs ( styles . baseMapPointInput , className ) } >
197231 < div className = { styles . locationInputs } >
@@ -232,12 +266,21 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
232266 />
233267 </ DiffWrapper >
234268 </ div >
269+ { isDefined ( countryDetails ) && (
270+ < div className = { styles . locationSearch } >
271+ < LocationSearchInput
272+ countryIso = { countryDetails . iso }
273+ onResultSelect = { setSearchResult }
274+ />
275+ </ div >
276+ ) }
235277 < BaseMap
236278 // eslint-disable-next-line react/jsx-props-no-spreading
237279 { ...otherProps }
238280 mapOptions = { {
239281 zoom : 18 ,
240282 bounds,
283+ fitBoundsOptions,
241284 ...mapOptions ,
242285 } }
243286 mapStyle = { mapStyle }
@@ -264,14 +307,20 @@ function BaseMapPointInput<NAME extends string>(props: Props<NAME>) {
264307 < MapSource
265308 sourceKey = "selected-point"
266309 geoJson = { pointGeoJson }
267- sourceOptions = { { type : 'geojson' } }
310+ sourceOptions = { geoJsonSourceOptions }
268311 >
269312 < MapLayer
270313 layerKey = "point-circle"
271314 layerOptions = { circleLayerOptions }
272315 />
273316 </ MapSource >
274317 ) }
318+ { center && (
319+ < MapCenter
320+ center = { center }
321+ centerOptions = { centerOptions }
322+ />
323+ ) }
275324 { children }
276325 </ BaseMap >
277326 </ div >
0 commit comments