@bqtran/react-sidebar-v2 v1.1.0-1
React-Sidebar-v2

This is a React friendly port of Turbo87's sidebar-v2 map control for React-Leaflet & MapLibre GL JS.
A big shoutout to Andreas Riedmüller for creating the my-component-library scaffolding that this project leverages. You can read his excellent overview article here.
NOTE: These components are in a pre-release stage, so the API will change drastically as it is being refined.
Features
- Recreates all the original sidebar-v2 functionality (markup, events) in React (leverages the original CSS only)
- Includes the nifty map autopan functionality found in Norwin's leaflet-sidebar-v2.
- Includes both components for React-Leaflet and MapLibre GL JS libraries.
- Sidebar Context Provider for state manipulation from other React components
- Native Lucide Icons Integration
- TypeScript declarations
Quick Start
- Add
<SidebarProvider>to a root component. This example uses 'maplibre' as type, but use 'leaflet' if that is your library of choice.
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import { BrowserRouter as Router } from 'react-router-dom';
import {StrictMode} from "react";
import {SidebarProvider} from "@bqtran/react-sidebar-v2";
createRoot(document.getElementById('root')!).render(
<StrictMode>
<SidebarProvider type="maplibre">
<Router>
<App />
</Router>
</SidebarProvider>
</StrictMode>
)- Add either
ReactLeafletSidebarorMaplibreSidebarto your map as a basic example is shown below:
React-Leaflet Example
import "leaflet/dist/leaflet.css"
import {ReactLeafletSidebar} from "@bqtran/react-sidebar-v2";
import {MapContainer, TileLayer} from 'react-leaflet'
export default function LeafletMap() {
return <div style={{flex:"1 1 auto", height:"100vh", width:"100vw"}} >
<MapContainer center={[29.648, -95.579]} zoom={13} scrollWheelZoom={false} zoomControl={false} style={{height:"100vh", width:"100vw"}}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<ReactLeafletSidebar position="left" autopan={true} tabs={[
{
id: "menu",
title:"Menu",
icon:"Menu",
position:"top",
disabled:false,
content:<p>Menu Content</p>
},
{
id:"settings",
title:"Settings",
icon:"Settings",
position:"bottom",
disabled:false,
content:<p>Settings Content</p>
}
]}/>
</MapContainer>
</div>
}Note:
ReactLeafletSidebarneeds to be a child component ofMapContainerfor React-Leaflet's contexts to work properly.MapLibre GL JS Example
import {MapLibreSidebar} from "@bqtran/react-sidebar-v2"; import maplibre from "maplibre-gl" import "maplibre-gl/dist/maplibre-gl.css" import {useEffect, useState} from "react"
export default function MapLibre() { const map, setMap = useState<maplibre.Map|undefined>(undefined);
useEffect(() => { if(map == undefined) { setMap(new maplibre.Map({ container: "map", zoom: 2, style: 'https://demotiles.maplibre.org/style.json' })); } }, []);
return <div style={{flex: "1 1 auto", height: "100vh", width: "100vw"}}>
<div id="map" style={{height: "100vh", width: "100vw"}}/>
{map && <MapLibreSidebar map={map} position="left" autopan={true} tabs={[
{
id: "menu",
title: "Menu",
icon: "Menu",
position: "top",
disabled: false,
content: <p>Menu Content</p>
},
{
id: "settings",
title: "Settings",
icon: "Settings",
position: "bottom",
disabled: false,
content: <p>Settings Content</p>
}
]} />
}
</div>}
>Note: The `Map` component needs to exist before it can be passed to `MapLibreSidebar`, so useState is used for this purpose.
3. The sidebar should now be showing up on your map of choice.
## Advanced Usage
A Sidebar Context is provided to expose the Sidebar's API to other React components for state information and manipulation.
You can use this to dynamically style the Sidebar according to the current state. Below is an example that applies/removes
round corners to the sidebar depending on if a tab is expanded or collapsed:
```javascript
import "leaflet/dist/leaflet.css"
import {ReactLeafletSidebar, SidebarContext, SidebarContextType} from "@bqtran/react-sidebar-v2";
import {MapContainer, TileLayer} from 'react-leaflet'
import {useContext} from "react";
export default function LeafletMap() {
const {collapsed} = useContext(SidebarContext) as SidebarContextType;
return <div style={{flex:"1 1 auto", height:"100vh", width:"100vw"}} >
<MapContainer center={[29.648, -95.579]} zoom={13} scrollWheelZoom={false} zoomControl={false} style={{height:"100vh", width:"100vw"}}>
<TileLayer
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<ReactLeafletSidebar autopan={true} position="left" tabsClassName={`${collapsed ? 'rounded-lg':'rounded-l-lg'}`} contentsClassName="rounded-r-lg" tabs={[
{
id: "menu",
title:"Menu",
icon:"Menu",
position:"top",
disabled:false,
content:<p>Menu Content</p>,
tabClassName: collapsed ? 'rounded-t-lg': 'rounded-tl-lg'
},
{
id:"settings",
title:"Settings",
icon:"Settings",
position:"bottom",
disabled:false,
content:<p>Settings Content</p>
}
]}/>
</MapContainer>
</div>
}API
SidebarProvider Context Provider Properties
type: String - Sidebar Type. Values: 'maplibre', 'leaflet'
SidebarContext Context Provider Consumables
innerRef: React.Ref - Reference to SidebaractiveTab/setActiveTab: String/React.SetStateAction - get/set current active tab IDcollapsed/setCollapsed: Boolean/React.SetStateAction - get/set Sidebar's expanded/collapsed stateposition/setPosition: String/React.SetStateAction - get/set Sidebar control's orientation on mapsidebarTabs/setSidebarTabs: TabType[]/React.SetStateAction - get/set Sidebar tabstoggleTab: String - enable/disable tab by IDtype: String - get current Sidebar's configured type
ReactLeafletSidebar / MapLibreSidebar Component Properties
className: String (Optional) - Sidebar container stylingtabsClassName: String (Optional) - Sidebar tab container stylingcontentsClassName: String (Optional) - Sidebar tab content container stylingposition: String - Sidebar control placement. Values: 'left', 'right'autopan: Boolean - Pan map on Sidebar expand/collapsetabs: Tab[]- Array of Sidebar tabsid: String - tab unique identifiertabClassName: String (Optional) - individual tab stylingcontentClassName: String (Optional) - individual tab content stylingtitle: String/Component - tab header text or componenticon: String - icon name from Lucide Icon collectionposition: String - fix tab icon to 'top' or 'bottom'. Values: 'top', 'bottom'disabled: Boolean - disable/enable sidebar tabcontent: String/Component - tab content
ReactLeafletSidebar / MapLibreSidebar Events
"closing": (tab_id) - close tab event triggered"opening": (tab_id) - open tab event triggered"content": (tab_id) - content event triggered