ethio-map-kit
v0.1.5
Published
Type-safe React components for an interactive Ethiopia regional SVG map.
Maintainers
Readme
Ethio Map Kit
React components for building interactive Ethiopia map visualizations.
Ethio Map Kit includes a regional Ethiopia map, smooth region selection, light/dark themes, terrain and heat layers, marker support, service stats, and TypeScript types.
Install
npm install ethio-map-kitImport the stylesheet once:
import "ethio-map-kit/styles.css";Quick Start
import {
EthiopiaMap,
defaultRegionData,
defaultRegionServiceStats,
defaultLocations,
} from "ethio-map-kit";
import "ethio-map-kit/styles.css";
export function App() {
return (
<EthiopiaMap
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
locations={defaultLocations}
theme="light"
layer="regions"
showLabels
onRegionSelect={({ regionId, region }) => {
console.log(regionId, region);
}}
/>
);
}Terrain Layer
<EthiopiaMap
data={defaultRegionData}
layer="terrain"
theme="dark"
/>The bundled terrain image is a transparent WebP, so it works in both light and dark mode.
Custom Data
import type { RegionRecord, RegionDatum } from "ethio-map-kit";
const data: RegionRecord<RegionDatum> = {
...defaultRegionData,
amhara: {
name: "Amhara",
value: 78,
trend: "+12%",
copy: "High regional index with several sample cities.",
},
};
<EthiopiaMap data={data} />;Common Props
| Prop | Type | Description |
| --- | --- | --- |
| data | RegionRecord<RegionDatum> | Region values and copy keyed by region ID. |
| serviceStats | RegionServiceStats | Optional service indicators for each region. |
| locations | LatLngLocation[] | Optional marker/location data. |
| theme | "light" \| "dark" | Map theme. |
| layer | "regions" \| "heat" \| "terrain" | Active visual layer. |
| defaultLayer | "regions" \| "heat" \| "terrain" | Initial visual layer for uncontrolled maps. |
| onLayerChange | (layer) => void | Called when the built-in layer control requests a layer change. |
| viewMode | "map" \| "bars" | Map view or bar view. |
| selectionAnimation | "pulse" \| "static" \| "none" \| "outline" | Selected-region animation style. |
| showLabels | boolean | Show or hide region labels. |
| showMarkers | boolean | Show or hide location markers. |
| showInMapStats | boolean | Show or hide the selected region service overlay inside the map surface. |
| regionColors | PartialRegionRecord<string> | Override region colors. |
| selectedRegionId | EthiopiaRegionId \| null | Controlled selected region. |
| defaultSelectedRegionId | EthiopiaRegionId \| null | Initial selected region. |
| onRegionSelect | (selection) => void | Called when a region is selected or cleared. |
| className | string | Class applied to the map surface in the all-in-one EthiopiaMap. |
| style | React.CSSProperties | Inline style applied to the map surface in the all-in-one EthiopiaMap. Useful for height and width. |
| terrainImageUrl | string | Custom terrain image URL. |
| showLayerControl | boolean | Show a bottom-left Normal/Terrain map toggle inside the map surface. |
| layerControlClassName | string | Extra class for the built-in layer toggle. |
| layerControlLabels | { regions?: string; terrain?: string } | Custom labels for the built-in layer toggle. |
In-Map Layer Toggle
Use showLayerControl when you want a small Normal/Terrain switch inside the map, similar to common map viewers.
<EthiopiaMap
data={defaultRegionData}
defaultLayer="regions"
showLayerControl
/>For a controlled map, keep your own layer state and handle onLayerChange:
const [layer, setLayer] = useState<EthiopiaMapLayer>("regions");
<EthiopiaMap
data={defaultRegionData}
layer={layer}
onLayerChange={setLayer}
showLayerControl
/>;Sizing The Map
The map surface fills its own container and has a default minimum height. Use className, style, or your layout wrapper to control the rendered size.
<EthiopiaMap
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
className="w-full"
style={{ width: "100%", height: 640, minHeight: 640 }}
/>With composable components, size EthiopiaMapSurface directly:
<EthiopiaMapProvider data={defaultRegionData}>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
<EthiopiaMapSurface
className="w-full"
style={{ height: 620, minHeight: 620 }}
/>
<EthiopiaSelectedRegionCard />
</div>
</EthiopiaMapProvider>Composable Components
For custom layouts, wrap components with EthiopiaMapProvider.
import {
EthiopiaMapProvider,
EthiopiaMapSurface,
EthiopiaMapLegend,
EthiopiaSelectedRegionCard,
EthiopiaRegionServicesCard,
EthiopiaRegionServicesPanel,
EthiopiaLocationsPanel,
defaultRegionData,
defaultRegionServiceStats,
} from "ethio-map-kit";
export function Dashboard() {
return (
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
>
<EthiopiaMapSurface />
<EthiopiaMapLegend />
<EthiopiaSelectedRegionCard />
<EthiopiaRegionServicesCard />
<EthiopiaLocationsPanel />
</EthiopiaMapProvider>
);
}Service Stats Layouts
Service stats can appear inside the map surface, outside the map, or in a standalone details page. Use either the in-map overlay or the external card unless you intentionally want both.
In-map overlay only:
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
showInMapStats
>
<EthiopiaMapSurface />
<EthiopiaSelectedRegionCard />
</EthiopiaMapProvider>External service card only:
<EthiopiaMapProvider
data={defaultRegionData}
serviceStats={defaultRegionServiceStats}
showInMapStats={false}
>
<div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_320px]">
<EthiopiaMapSurface className="w-full" style={{ height: 620 }} />
<EthiopiaRegionServicesCard />
</div>
</EthiopiaMapProvider>Customize the in-map overlay and its scroll list from EthiopiaMapSurface:
<EthiopiaMapSurface
inMapStatsClassName="my-map-stats"
inMapStatsStyle={{ maxHeight: 320 }}
inMapStatsListClassName="my-map-stats-list"
inMapStatsListStyle={{ maxHeight: 240 }}
/>Opt in to incremental row rendering when a region has many indicators:
<EthiopiaRegionServicesCard
incrementalRows={{
enabled: true,
initialCount: 8,
step: 8,
thresholdPx: 240,
}}
/>The same option works on the in-map overlay:
<EthiopiaMapSurface
incrementalRows={{
enabled: true,
initialCount: 8,
step: 8,
thresholdPx: 320,
showMoreFallback: false,
animateNewRows: false,
sentinelVisible: false,
}}
/>incrementalRows is client-side only. Pass the full service list, and the component reveals more rows from that already-loaded data as the user scrolls. For compact in-map overlays, keep animateNewRows false and use a larger thresholdPx so rows appear before the user reaches the end of the visible list.
For server-rendered detail pages, fetch data in your app and pass it directly to the context-free panel. The package does not include Prisma, Next.js caching, route generation, ISR, or database fetching.
const services = await getCachedMapsData("afar", 2021, true);
return (
<EthiopiaRegionServicesPanel
regionName="Afar"
regionValue={34}
services={services.afar ?? []}
/>
);Exports
EthiopiaMapEthiopiaMapProviderEthiopiaMapSurfaceEthiopiaMapLegendEthiopiaSelectedRegionCardEthiopiaRegionServicesCardEthiopiaRegionServicesPanelEthiopiaLocationsPaneluseEthiopiaMapuseSelectedRegionIddefaultRegionDatadefaultRegionServiceStatsdefaultLocationsETHIOPIA_REGION_IDSETHIOPIA_AUTO_TOUR_SEQUENCE
Region IDs
tigray
afar
amhara
benishangul-gumuz
gambela
south-west-ethiopia
central-ethiopia
sidama
south-ethiopia
oromia
addis-ababa
somali
dire-dawa
harari