diff --git a/readme.md b/readme.md
index 31bf033..94ca9e9 100644
--- a/readme.md
+++ b/readme.md
@@ -87,6 +87,85 @@ in other words :
{ id: string, plateNumber: number, name: string, path: string }[]
```
+### County Map Feature
+
+When enabled, clicking on a city displays a popup showing the county (ilçe) map of that city. This feature:
+- Must be explicitly enabled via `showCountyMapOnClick` prop
+- Requires county data to be imported separately to keep bundle size small when not used
+- Can be customized via `countyMapWrapper` prop
+- Supports all the same interaction handlers as the main map
+
+#### Basic Usage
+
+```javascript
+import TurkeyMap from 'turkey-map-react';
+import { istanbulCounties, ankaraCounties } from 'turkey-map-react/lib/data/counties';
+
+// Import only the county data you need
+const countyData = {
+ istanbul: istanbulCounties,
+ ankara: ankaraCounties
+};
+
+
+```
+
+#### Handling County Events
+
+```javascript
+ {
+ console.log(`${county.name} in ${city.name} was clicked!`);
+ }}
+/>
+```
+
+#### Custom County Map Wrapper
+
+```javascript
+const customCountyMapWrapper = (countyMapPopup, city, countyData) => (
+
+
Custom Header for {city.name}
+ {countyMapPopup}
+
+);
+
+
+```
+
+#### Providing Custom County Data
+
+County data must follow the `CountyData` type structure:
+
+```javascript
+import { CountyData } from 'turkey-map-react';
+
+const myCustomCountyData: CountyData = {
+ cityId: "mycity",
+ cityName: "My City",
+ counties: [
+ { id: "county1", name: "County 1", path: "M 0 0 L 100 0..." },
+ { id: "county2", name: "County 2", path: "M 100 0 L 200 0..." }
+ ]
+};
+
+
+```
+
+**Note on Sample Data:** The built-in county data for Istanbul, Ankara, and Izmir uses placeholder rectangular paths for demonstration purposes only. These are NOT actual geographic boundaries. For production use, you should provide real geographic SVG data for county boundaries. Consider using geographic data sources like GADM or OpenStreetMap for accurate county shapes.
+
## API
### Types
@@ -94,6 +173,8 @@ in other words :
| Type | Description |
| :---------------- | :-------------------------------------------------------------------------------------- |
| *CityType* | { **id**: *string*, **plateNumber**: *number*, **name**: *string*, **path**: *string* } |
+| *CountyType* | { **id**: *string*, **name**: *string*, **path**: *string* } |
+| *CountyData* | { **cityId**: *string*, **cityName**: *string*, **counties**: *CountyType*[] } |
| *ViewBoxType* | { **top**: *number*, **left**: *number*, **width**: *number*, **height**: *number* } |
| *CustomStyleType* | { **idleColor**: *string*, **hoverColor**: *string* } |
@@ -111,6 +192,10 @@ in other words :
| cityWrapper | City DOMs are wrapped by provided component. | ( **cityComponent**: *JSX.Element*, **city** : *CityType* ) => *JSX.Element* | *Unwrapped city* | 1.0.0 |
| onHover | Event when a city hovered on the map. | ( **city** : *CityType* ) => *void* | - | 1.0.0 |
| onClick | Event when a city clicked on the map | ( **city** : *CityType* ) => *void* | - | 1.0.0 |
+| showCountyMapOnClick | Enables county map popup when a city is clicked | *boolean* | *false* | 3.0.0 |
+| countyData | County data for cities (import only what you need) | *Record* | *undefined* | 3.0.0 |
+| countyMapWrapper | Custom wrapper for county map popup | ( **popup**: *JSX.Element*, **city**: *CityType*, **data**: *CountyData* ) => *JSX.Element* | *Default popup* | 3.0.0 |
+| onCountyClick | Event when a county is clicked in the county map | ( **county**: *CountyType*, **city**: *CityType* ) => *void* | - | 3.0.0 |
### Styling
diff --git a/src/CountyMapPopup.tsx b/src/CountyMapPopup.tsx
new file mode 100644
index 0000000..e7fa7b3
--- /dev/null
+++ b/src/CountyMapPopup.tsx
@@ -0,0 +1,255 @@
+import React, { MouseEventHandler, useCallback, useState } from 'react';
+import { Property } from 'csstype';
+
+export type CountyType = {
+ id: string;
+ name: string;
+ path: string;
+};
+
+export type CountyData = {
+ cityId: string;
+ cityName: string;
+ counties: CountyType[];
+};
+
+interface ICountyMapPopupProps {
+ countyData: CountyData;
+ onClose: () => void;
+ customStyle?: { idleColor: string, hoverColor: string };
+ hoverable?: boolean;
+ showTooltip?: boolean;
+ tooltipText?: string;
+ onCountyHover?: (county: CountyType) => void;
+ onCountyClick?: (county: CountyType) => void;
+}
+
+const CountyMapPopup: React.FC = ({
+ countyData,
+ onClose,
+ customStyle = { idleColor: "#444", hoverColor: "#dc3522" },
+ hoverable = true,
+ showTooltip = false,
+ tooltipText,
+ onCountyHover,
+ onCountyClick
+}) => {
+ const [hoveredCountyName, setHoveredCountyName] = useState(undefined);
+ const [tooltipStyle, setTooltipStyle] = useState<{
+ left: number,
+ top: number,
+ visibility?: Property.Visibility,
+ animation?: Property.Animation
+ }>({ left: 0, top: 0, visibility: "hidden" });
+
+ // Handle ESC key to close popup
+ React.useEffect(() => {
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') {
+ onClose();
+ }
+ };
+
+ document.addEventListener('keydown', handleKeyDown);
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [onClose]);
+
+ const handleMouseEvent = useCallback((
+ event: React.MouseEvent,
+ callback: (county: CountyType) => void
+ ) => {
+ const element = event.target as Element;
+
+ if (element.tagName === 'path') {
+ const parent = element.parentNode as Element;
+
+ const countyId = parent.getAttribute('id') ?? "";
+ const countyPath = element.getAttribute("d") ?? "";
+ const countyName: string = parent.getAttribute('data-ilceadi') ?? "";
+ const county: CountyType = { id: countyId, name: countyName, path: countyPath };
+
+ if (callback && typeof callback === 'function') {
+ callback(county);
+ }
+ }
+ }, []);
+
+ const handleHover = useCallback((county: CountyType) => {
+ setHoveredCountyName(county.name);
+ if (onCountyHover) {
+ onCountyHover(county);
+ }
+ }, [onCountyHover]);
+
+ const handleOnHover = useCallback((event: React.MouseEvent): void => {
+ handleMouseEvent(event, handleHover);
+ }, [handleMouseEvent, handleHover]);
+
+ const handleOnClick = useCallback((event: React.MouseEvent): void => {
+ if (onCountyClick) {
+ handleMouseEvent(event, onCountyClick);
+ }
+ }, [onCountyClick, handleMouseEvent]);
+
+ const handleOnMouseMove: MouseEventHandler = useCallback((event) => {
+ setTooltipStyle(prevState => ({
+ ...prevState,
+ left: event.pageX + 16,
+ top: event.pageY - 32
+ }));
+ }, []);
+
+ const handleOnMouseEnter = useCallback((event: React.MouseEvent): void => {
+ const target = event.currentTarget;
+ const path = target.querySelector('path');
+ if (path) {
+ path.style.fill = customStyle.hoverColor;
+ }
+ if (!showTooltip) return;
+
+ setTooltipStyle(prevState => ({
+ ...prevState,
+ animation: undefined,
+ visibility: "visible"
+ }));
+ }, [customStyle.hoverColor, showTooltip]);
+
+ const handleOnMouseLeave = useCallback((event: React.MouseEvent): void => {
+ const target = event.currentTarget;
+ const path = target.querySelector('path');
+ if (path) {
+ path.style.fill = customStyle.idleColor;
+ }
+ if (!showTooltip) return;
+
+ setTooltipStyle(prevState => ({
+ ...prevState,
+ visibility: undefined,
+ animation: `0.1s county_react_map_tooltip_fade_out forwards ease-out`,
+ }));
+ }, [customStyle.idleColor, showTooltip]);
+
+ const handleOverlayClick = useCallback((event: React.MouseEvent) => {
+ if (event.target === event.currentTarget) {
+ onClose();
+ }
+ }, [onClose]);
+
+ return (
+