Add a map controller with controls
This example demonstrates how to set up a basic map with controls UI controls using the MapsGL Map Controller Provider.
Key Features
- Basic map initialization with Mapbox GL
- MapsGL controller integration
- Zoom in/out controls
- Data inspector
- Geolocation support
Implementation
The map controller uses several components working together. Here's how to implement it:
- First, create the basic map component with Mapbox initialization:
import { useState, useRef } from 'react';
export const App = () => {
const [map, setMap] = useState<mapboxgl.Map | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
// Initialize map
useEffect(() => {
mapboxgl.accessToken = 'YOUR_MAPBOX_KEY';
const mapEl = new mapboxgl.Map({
container: containerRef.current || '',
style: 'mapbox://styles/mapbox//streets-v10',
center: [-74.5, 40],
zoom: 3
});
setMap(mapEl);
}, []);
return (
<div ref={containerRef} className="w-full h-full" />
);
}
- Add the
MapsGLMapControllerProvider
to enable map interactions. Optionally, wrap your application in theAnchor.Position
component for global positioning styles:
import { MapsGLMapControllerProvider, Anchor } from '@xweather/maps-ui-sdk';
export const App = () => {
const [map, setMap] = useState<mapboxgl.Map | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
// ... map initialization ...
return (
<Anchor.Position offset={12}>
<MapsGLMapControllerProvider
accessKeys={accessKeys}
strategy="mapbox"
map={map}
>
{/* Control panel will go here */}
</MapsGLMapControllerProvider>
<div ref={containerRef} className="w-full h-full" />
</Anchor.Position>
);
}
- Create a button component for the controls with the
IconButton
component:
import { IconButton, IconProps } from '@xweather/maps-ui-sdk';
interface MapControlButtonProps {
icon: ComponentType<IconProps>;
isActive?: boolean;
onClick: () => void;
}
const MapControlButton = ({ icon, onClick, isActive }: MapControlButtonProps) => {
const classNameBase = 'rounded-md hover:text-white text-2xl text-[#C1C7CD] size-7';
const activeClass = isActive ? 'border-2 border-[#4ED9C2]' : ''; return (
<IconButton
className={`${classNameBase} ${activeClass}`}
icon={icon}
onClick={onClick}
/>
);
};
- Create a specialized
GeoLocate
button with loading state.
The GeoLocate
button is a special case that requires handling an asynchronous operation. We'll add a LoadingSpinner
to provide visual feedback while waiting for the browser's Geolocation API (opens in a new tab):
import { useState } from 'react';
import { HStack, LoadingSpinner, getCurrentLocation, GeoLocateIcon } from '@xweather/maps-ui-sdk';
const GeoLocateButton = ({ map }) => {
// Track loading state for geolocation request
const [isLoading, setIsLoading] = useState(false);
const handleGeoLocate = async () => {
try {
setIsLoading(true);
// Use the utility function to access browser geolocation
const { lat, lon } = await getCurrentLocation();
// Animate the map to the user's location
map?.flyTo({
center: [lon, lat],
essential: true,
zoom: 7
});
} catch (error) {
console.error('Error getting location:', error);
} finally {
setIsLoading(false);
}
};
return (
<HStack className="relative items-center justify-center">
{isLoading && <LoadingSpinner
className="absolute"
size={34}
/>}
<MapControlButton
icon={GeoLocateIcon}
onClick={handleGeoLocate}
/>
</HStack>
);
};
This component:
- Uses React's
useState
to track the geolocation loading state - Shows a loading spinner while waiting for the geolocation request
- Uses the
getCurrentLocation
utility which handles the geolocation request and browser permissions, returning the user's current location if successful - Animates the map to the user's location when coordinates are received
- Handles errors gracefully and ensures the loading spinner is always removed when the operation completes
- Finally, implement the control panel component by combining the
MapControlButton
andGeoLocateButton
with theuseMapsGLMapControllerContext
hook.
import { useMapsGLMapControllerContext } from '@xweather/maps-ui-sdk';
const MapControlPanel = () => {
const { map, toggleDataInspector, isDataInspectorVisible } = useMapsGLMapControllerContext();
return (
<Anchor.BottomLeft offsetY={40} className="z-10">
<VStack className="bg-black rounded-xl p-2 gap-1.5">
<MapControlButton
icon={AddIcon}
onClick={() => map?.zoomIn()}
/>
<MapControlButton
icon={SubtractIcon}
onClick={() => map?.zoomOut()}
/>
<MapControlButton
icon={TargetIcon}
onClick={() => toggleDataInspector('move', true)}
isActive={isDataInspectorVisible}
/>
<GeoLocateButton map={map} />
</VStack>
</Anchor.BottomLeft>
);
};
This component:
- Provides zoom controls for adjusting the map view
- Includes a data inspector toggle that can be activated on click or mouse move
- Uses the
isDataInspectorVisible
state to style the inspector button when active - Integrates the
GeoLocate
button we created in the previous step - Positions all controls consistently in an accessible location on the map