@geekles007/motion-map-components 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +234 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +291 -0
- package/dist/index.d.ts +291 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +119 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tondji
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# React Map Components
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@geekles/react-map-components)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
Modern, lightweight React components for interactive maps built on top of Leaflet.
|
|
8
|
+
|
|
9
|
+
## β¨ Features
|
|
10
|
+
|
|
11
|
+
- πΊοΈ **Easy to use** - Simple, declarative API for creating maps
|
|
12
|
+
- π¦ **Lightweight** - Tree-shakeable, only import what you need
|
|
13
|
+
- π¨ **Customizable** - Full control over styling and behavior
|
|
14
|
+
- π± **Responsive** - Works great on desktop and mobile
|
|
15
|
+
- π§ **TypeScript** - Full type support out of the box
|
|
16
|
+
- βοΈ **React 18/19** - Built for modern React with hooks
|
|
17
|
+
|
|
18
|
+
## π¦ Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# npm
|
|
22
|
+
npm install @geekles/react-map-components
|
|
23
|
+
|
|
24
|
+
# yarn
|
|
25
|
+
yarn add @geekles/react-map-components
|
|
26
|
+
|
|
27
|
+
# pnpm
|
|
28
|
+
pnpm add @geekles/react-map-components
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## π Quick Start
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { MapContainer, TileLayer, Marker, Popup } from '@geekles/react-map-components'
|
|
35
|
+
import '@geekles/react-map-components/styles.css'
|
|
36
|
+
|
|
37
|
+
function App() {
|
|
38
|
+
return (
|
|
39
|
+
<MapContainer
|
|
40
|
+
center={[51.505, -0.09]}
|
|
41
|
+
zoom={13}
|
|
42
|
+
style={{ height: '400px', width: '100%' }}
|
|
43
|
+
>
|
|
44
|
+
<TileLayer
|
|
45
|
+
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
46
|
+
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
47
|
+
/>
|
|
48
|
+
<Marker position={[51.505, -0.09]}>
|
|
49
|
+
<Popup>
|
|
50
|
+
Hello! I'm a popup.
|
|
51
|
+
</Popup>
|
|
52
|
+
</Marker>
|
|
53
|
+
</MapContainer>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## π Components
|
|
59
|
+
|
|
60
|
+
### MapContainer
|
|
61
|
+
|
|
62
|
+
The main container component for your map.
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<MapContainer
|
|
66
|
+
center={[lat, lng]}
|
|
67
|
+
zoom={13}
|
|
68
|
+
minZoom={1}
|
|
69
|
+
maxZoom={18}
|
|
70
|
+
scrollWheelZoom={true}
|
|
71
|
+
style={{ height: '100vh' }}
|
|
72
|
+
>
|
|
73
|
+
{/* children */}
|
|
74
|
+
</MapContainer>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### TileLayer
|
|
78
|
+
|
|
79
|
+
Add tile layers to your map.
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<TileLayer
|
|
83
|
+
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
84
|
+
attribution="Β© OpenStreetMap"
|
|
85
|
+
maxZoom={19}
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Marker
|
|
90
|
+
|
|
91
|
+
Add markers to your map.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<Marker
|
|
95
|
+
position={[51.505, -0.09]}
|
|
96
|
+
draggable={false}
|
|
97
|
+
eventHandlers={{
|
|
98
|
+
click: () => console.log('Marker clicked!')
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Popup
|
|
104
|
+
|
|
105
|
+
Add popups to markers or the map.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<Marker position={[51.505, -0.09]}>
|
|
109
|
+
<Popup>
|
|
110
|
+
<h3>My Location</h3>
|
|
111
|
+
<p>This is where I am!</p>
|
|
112
|
+
</Popup>
|
|
113
|
+
</Marker>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### GeoJSON
|
|
117
|
+
|
|
118
|
+
Render GeoJSON data on your map.
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<GeoJSON
|
|
122
|
+
data={geojsonData}
|
|
123
|
+
style={{ color: 'blue', weight: 2 }}
|
|
124
|
+
onEachFeature={(feature, layer) => {
|
|
125
|
+
layer.bindPopup(feature.properties.name)
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## πͺ Hooks
|
|
131
|
+
|
|
132
|
+
### useMap
|
|
133
|
+
|
|
134
|
+
Access the map instance from any child component.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { useMap } from '@geekles/react-map-components'
|
|
138
|
+
|
|
139
|
+
function MyComponent() {
|
|
140
|
+
const map = useMap()
|
|
141
|
+
|
|
142
|
+
const flyToLocation = () => {
|
|
143
|
+
map.flyTo([51.505, -0.09], 14)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return <button onClick={flyToLocation}>Go to London</button>
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### useMapEvents
|
|
151
|
+
|
|
152
|
+
Subscribe to map events.
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { useMapEvents } from '@geekles/react-map-components'
|
|
156
|
+
|
|
157
|
+
function LocationLogger() {
|
|
158
|
+
useMapEvents({
|
|
159
|
+
click: (e) => {
|
|
160
|
+
console.log('Clicked at:', e.latlng)
|
|
161
|
+
},
|
|
162
|
+
zoomend: () => {
|
|
163
|
+
console.log('Zoom changed')
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
return null
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## π¨ Styling
|
|
172
|
+
|
|
173
|
+
Import the default styles:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import '@geekles/react-map-components/styles.css'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Or customize with your own CSS:
|
|
180
|
+
|
|
181
|
+
```css
|
|
182
|
+
.leaflet-container {
|
|
183
|
+
height: 100%;
|
|
184
|
+
width: 100%;
|
|
185
|
+
border-radius: 8px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.leaflet-popup-content-wrapper {
|
|
189
|
+
border-radius: 12px;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## π§ TypeScript
|
|
194
|
+
|
|
195
|
+
All components are fully typed. Import types as needed:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import type {
|
|
199
|
+
MapContainerProps,
|
|
200
|
+
MarkerProps,
|
|
201
|
+
LatLngExpression
|
|
202
|
+
} from '@geekles/react-map-components'
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## π€ Contributing
|
|
206
|
+
|
|
207
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Clone the repo
|
|
211
|
+
git clone https://github.com/geekles/react-map-components.git
|
|
212
|
+
cd react-map-components
|
|
213
|
+
|
|
214
|
+
# Install dependencies
|
|
215
|
+
pnpm install
|
|
216
|
+
|
|
217
|
+
# Start Storybook for development
|
|
218
|
+
pnpm dev
|
|
219
|
+
|
|
220
|
+
# Run tests
|
|
221
|
+
pnpm test
|
|
222
|
+
|
|
223
|
+
# Build the library
|
|
224
|
+
pnpm build
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## π License
|
|
228
|
+
|
|
229
|
+
MIT Β© [geekles](https://github.com/geekles)
|
|
230
|
+
|
|
231
|
+
## π Acknowledgments
|
|
232
|
+
|
|
233
|
+
- Built on top of [Leaflet](https://leafletjs.com/)
|
|
234
|
+
- Inspired by [react-leaflet](https://react-leaflet.js.org/)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
'use strict';var react=require('react'),x=require('leaflet'),jsxRuntime=require('react/jsx-runtime'),reactDom=require('react-dom');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var x__default=/*#__PURE__*/_interopDefault(x);var g=react.createContext(null);function E({children:e}){let[t,o]=react.useState(null);return jsxRuntime.jsx(g.Provider,{value:{map:t,setMap:o},children:e})}function L(){let e=react.useContext(g);if(!e)throw new Error("useMapContext must be used within a MapProvider or MapContainer");return e}x__default.default.Icon&&x__default.default.Icon.Default&&x__default.default.Icon.Default.prototype&&delete x__default.default.Icon.Default.prototype._getIconUrl;x__default.default.Icon.Default.mergeOptions({iconRetinaUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png",iconUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png",shadowUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png"});function F({center:e,zoom:t,minZoom:o,maxZoom:u,scrollWheelZoom:n=true,dragging:i=true,doubleClickZoom:s=true,zoomControl:f=true,style:r,className:a,children:p,whenReady:l,options:c,setMapReady:y}){let d=react.useRef(null),{setMap:M}=L(),P=react.useRef(null);return react.useEffect(()=>{if(!d.current||P.current)return;let C=x__default.default.map(d.current,{center:e,zoom:t,minZoom:o,maxZoom:u,scrollWheelZoom:n,dragging:i,doubleClickZoom:s,zoomControl:f,...c});return P.current=C,M(C),y(true),l&&l(C),()=>{C.remove(),P.current=null,M(null),y(false);}},[]),react.useEffect(()=>{P.current&&P.current.setView(e,t);},[e,t]),jsxRuntime.jsx("div",{ref:d,style:{height:"100%",width:"100%",...r},className:a,children:p})}function T(e){let[t,o]=react.useState(false);return jsxRuntime.jsx(E,{children:jsxRuntime.jsx(F,{...e,setMapReady:o,children:t?e.children:null})})}function m(){let{map:e}=L();if(!e)throw new Error("Map instance is not available. Make sure this hook is used within a MapContainer and the map has been initialized.");return e}function J({url:e,attribution:t,maxZoom:o,minZoom:u,opacity:n,zIndex:i,options:s}){let f=m(),r=react.useRef(null);return react.useEffect(()=>{if(!f)return;let a=x__default.default.tileLayer(e,{attribution:t,maxZoom:o,minZoom:u,opacity:n,zIndex:i,...s});return a.addTo(f),r.current=a,()=>{a.remove(),r.current=null;}},[f,e,t,o,u,n,i,s]),null}var N=react.createContext({marker:null});function v(){let{marker:e}=react.useContext(N);return e}function b({position:e,draggable:t=false,opacity:o,alt:u,eventHandlers:n,children:i,options:s}){let f=m(),[r,a]=react.useState(null);return react.useEffect(()=>{if(!f)return;let p=x__default.default.marker(e,{draggable:t,opacity:o,alt:u,...s});return p.addTo(f),a(p),()=>{p.remove(),a(null);}},[f]),react.useEffect(()=>{r&&r.setLatLng(e);},[e,r]),react.useEffect(()=>{r&&(t?r.dragging?.enable():r.dragging?.disable());},[t,r]),react.useEffect(()=>{r&&o!==void 0&&r.setOpacity(o);},[o,r]),react.useEffect(()=>{if(!r||!n)return;let p=Object.keys(n);return p.forEach(l=>{let c=n[l];c&&r.on(l,c);}),()=>{p.forEach(l=>{let c=n[l];c&&r.off(l,c);});}},[n,r]),jsxRuntime.jsx(N.Provider,{value:{marker:r},children:i})}function w({position:e,children:t,maxWidth:o=300,minWidth:u=50,autoClose:n=true,closeOnClick:i=true,className:s,options:f}){let r=m(),a=v(),p=react.useRef(null),[l,c]=react.useState(null);return react.useEffect(()=>{if(!r)return;let y=document.createElement("div");c(y);let d={maxWidth:o,minWidth:u,autoClose:n,closeOnClick:i,className:s,...f},M=!a&&e?x__default.default.popup(d).setLatLng(e):x__default.default.popup(d);return M.setContent(y),p.current=M,a&&a.bindPopup(M),()=>{a&&a.unbindPopup(),M.remove(),p.current=null,c(null);}},[r,a]),react.useEffect(()=>{p.current&&l&&r&&(a||e)&&p.current.openOn(r);},[l,r,a,e]),react.useEffect(()=>{p.current&&e&&!a&&r&&(p.current.setLatLng(e),p.current.isOpen()||p.current.openOn(r));},[e,a,r]),l?reactDom.createPortal(t,l):null}function D({data:e,style:t,onEachFeature:o,pointToLayer:u,filter:n,options:i}){let s=m(),f=react.useRef(null);return react.useEffect(()=>{if(!s||!e)return;let r=x__default.default.geoJSON(e,{style:t,onEachFeature:o,pointToLayer:u,filter:n,...i});return r.addTo(s),f.current=r,()=>{r.remove(),f.current=null;}},[s,e,t,o,u,n,i]),react.useEffect(()=>{f.current&&e&&(f.current.clearLayers(),f.current.addData(e));},[e]),null}function oe(e){let t=m();react.useEffect(()=>{if(!t||!e)return;let o=Object.keys(e);return o.forEach(u=>{let n=e[u];n&&t.on(u,n);}),()=>{o.forEach(u=>{let n=e[u];n&&t.off(u,n);});}},[t,e]);}
|
|
2
|
+
exports.GeoJSON=D;exports.MapContainer=T;exports.MapProvider=E;exports.Marker=b;exports.Popup=w;exports.TileLayer=J;exports.useMap=m;exports.useMapContext=L;exports.useMapEvents=oe;exports.useMarker=v;//# sourceMappingURL=index.cjs.map
|
|
3
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/MapContext.tsx","../src/components/MapContainer/MapContainer.tsx","../src/hooks/useMap.ts","../src/components/TileLayer/TileLayer.tsx","../src/components/Marker/Marker.tsx","../src/components/Popup/Popup.tsx","../src/components/GeoJSON/GeoJSON.tsx","../src/hooks/useMapEvents.ts"],"names":["MapContext","createContext","MapProvider","children","map","setMap","useState","jsx","useMapContext","context","useContext","L","MapContainerInner","center","zoom","minZoom","maxZoom","scrollWheelZoom","dragging","doubleClickZoom","zoomControl","style","className","whenReady","options","setMapReady","containerRef","useRef","mapRef","useEffect","MapContainer","props","mapReady","useMap","TileLayer","url","attribution","opacity","zIndex","tileLayerRef","tileLayer","MarkerContext","useMarker","marker","Marker","position","draggable","alt","eventHandlers","setMarker","markerInstance","eventNames","eventName","handler","Popup","maxWidth","minWidth","autoClose","closeOnClick","popupRef","container","setContainer","containerElement","popupOptions","popup","createPortal","GeoJSON","data","onEachFeature","pointToLayer","filter","geoJsonRef","geoJson","useMapEvents","handlers"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createContext, useContext, useState, type ReactNode } from 'react'\nimport type { Map } from 'leaflet'\nimport type { MapContextValue } from '../types'\n\nconst MapContext = createContext<MapContextValue | null>(null)\n\nexport interface MapProviderProps {\n children: ReactNode\n}\n\n/**\n * Provider component for sharing map instance across components\n */\nexport function MapProvider({ children }: MapProviderProps) {\n const [map, setMap] = useState<Map | null>(null)\n\n return (\n <MapContext.Provider value={{ map, setMap }}>\n {children}\n </MapContext.Provider>\n )\n}\n\n/**\n * Hook to access the map context\n * @throws Error if used outside of MapProvider\n */\nexport function useMapContext(): MapContextValue {\n const context = useContext(MapContext)\n \n if (!context) {\n throw new Error('useMapContext must be used within a MapProvider or MapContainer')\n }\n \n return context\n}\n\nexport { MapContext }\n","import { useEffect, useRef, useState } from 'react'\nimport L from 'leaflet'\nimport type { Map } from 'leaflet'\nimport { MapProvider, useMapContext } from '../../context/MapContext'\nimport type { MapContainerProps } from '../../types'\n\n// Fix for default marker icons in Leaflet with bundlers\n// import 'leaflet/dist/leaflet.css' // Commented out for testing\n\n// Fix for default marker icons in Leaflet with bundlers\nif (L.Icon && L.Icon.Default && L.Icon.Default.prototype) {\n delete (L.Icon.Default.prototype as any)._getIconUrl\n}\nL.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png',\n iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png',\n shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',\n})\n\ninterface MapContainerInnerProps extends MapContainerProps {\n setMapReady: (ready: boolean) => void\n}\n\nfunction MapContainerInner({\n center,\n zoom,\n minZoom,\n maxZoom,\n scrollWheelZoom = true,\n dragging = true,\n doubleClickZoom = true,\n zoomControl = true,\n style,\n className,\n children,\n whenReady,\n options,\n setMapReady,\n}: MapContainerInnerProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const { setMap } = useMapContext()\n const mapRef = useRef<Map | null>(null)\n\n useEffect(() => {\n if (!containerRef.current || mapRef.current) return\n\n // Create the map\n const map = L.map(containerRef.current, {\n center,\n zoom,\n minZoom,\n maxZoom,\n scrollWheelZoom,\n dragging,\n doubleClickZoom,\n zoomControl,\n ...options,\n })\n\n mapRef.current = map\n setMap(map)\n setMapReady(true)\n\n // Call whenReady callback\n if (whenReady) {\n whenReady(map)\n }\n\n // Cleanup\n return () => {\n map.remove()\n mapRef.current = null\n setMap(null)\n setMapReady(false)\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update view when center or zoom changes\n useEffect(() => {\n if (mapRef.current) {\n mapRef.current.setView(center, zoom)\n }\n }, [center, zoom])\n\n return (\n <div\n ref={containerRef}\n style={{\n height: '100%',\n width: '100%',\n ...style,\n }}\n className={className}\n >\n {children}\n </div>\n )\n}\n\n/**\n * Main container component for creating a Leaflet map\n * \n * @example\n * ```tsx\n * <MapContainer\n * center={[51.505, -0.09]}\n * zoom={13}\n * style={{ height: '400px' }}\n * >\n * <TileLayer url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\" />\n * <Marker position={[51.505, -0.09]} />\n * </MapContainer>\n * ```\n */\nexport function MapContainer(props: MapContainerProps) {\n const [mapReady, setMapReady] = useState(false)\n\n return (\n <MapProvider>\n <MapContainerInner {...props} setMapReady={setMapReady}>\n {mapReady ? props.children : null}\n </MapContainerInner>\n </MapProvider>\n )\n}\n","import type { Map } from 'leaflet'\nimport { useMapContext } from '../context/MapContext'\n\n/**\n * Hook to access the Leaflet map instance\n * Must be used within a MapContainer or MapProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const map = useMap()\n * \n * const flyToLocation = () => {\n * map.flyTo([51.505, -0.09], 14)\n * }\n * \n * return <button onClick={flyToLocation}>Go to London</button>\n * }\n * ```\n * \n * @returns The Leaflet map instance\n * @throws Error if map is not yet initialized or used outside MapContainer\n */\nexport function useMap(): Map {\n const { map } = useMapContext()\n \n if (!map) {\n throw new Error(\n 'Map instance is not available. Make sure this hook is used within a MapContainer ' +\n 'and the map has been initialized.'\n )\n }\n \n return map\n}\n","import { useEffect, useRef } from 'react'\nimport L from 'leaflet'\nimport type { TileLayer as LeafletTileLayer } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { TileLayerProps } from '../../types'\n\n/**\n * Component for adding tile layers to the map\n * \n * @example\n * ```tsx\n * <TileLayer\n * url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n * attribution='© OpenStreetMap contributors'\n * maxZoom={19}\n * />\n * ```\n */\nexport function TileLayer({\n url,\n attribution,\n maxZoom,\n minZoom,\n opacity,\n zIndex,\n options,\n}: TileLayerProps) {\n const map = useMap()\n const tileLayerRef = useRef<LeafletTileLayer | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create tile layer\n const tileLayer = L.tileLayer(url, {\n attribution,\n maxZoom,\n minZoom,\n opacity,\n zIndex,\n ...options,\n })\n\n tileLayer.addTo(map)\n tileLayerRef.current = tileLayer\n\n // Cleanup\n return () => {\n tileLayer.remove()\n tileLayerRef.current = null\n }\n }, [map, url, attribution, maxZoom, minZoom, opacity, zIndex, options])\n\n return null\n}\n","import { useEffect, useState, createContext, useContext } from 'react'\nimport L from 'leaflet'\nimport type { Marker as LeafletMarker } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { MarkerProps } from '../../types'\n\n// Context for sharing marker with Popup\ninterface MarkerContextValue {\n marker: LeafletMarker | null\n}\n\nconst MarkerContext = createContext<MarkerContextValue>({ marker: null })\n\nexport function useMarker(): LeafletMarker | null {\n const { marker } = useContext(MarkerContext)\n return marker\n}\n\n/**\n * Component for adding markers to the map\n * \n * @example\n * ```tsx\n * <Marker\n * position={[51.505, -0.09]}\n * draggable={false}\n * eventHandlers={{\n * click: () => console.log('Marker clicked!')\n * }}\n * >\n * <Popup>Hello!</Popup>\n * </Marker>\n * ```\n */\nexport function Marker({\n position,\n draggable = false,\n opacity,\n alt,\n eventHandlers,\n children,\n options,\n}: MarkerProps) {\n const map = useMap()\n const [marker, setMarker] = useState<LeafletMarker | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create marker\n const markerInstance = L.marker(position, {\n draggable,\n opacity,\n alt,\n ...options,\n })\n\n markerInstance.addTo(map)\n setMarker(markerInstance)\n\n // Cleanup\n return () => {\n markerInstance.remove()\n setMarker(null)\n }\n }, [map]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update position\n useEffect(() => {\n if (marker) {\n marker.setLatLng(position)\n }\n }, [position, marker])\n\n // Update draggable option\n useEffect(() => {\n if (marker) {\n if (draggable) {\n marker.dragging?.enable()\n } else {\n marker.dragging?.disable()\n }\n }\n }, [draggable, marker])\n\n // Update opacity\n useEffect(() => {\n if (marker && opacity !== undefined) {\n marker.setOpacity(opacity)\n }\n }, [opacity, marker])\n\n // Attach event handlers\n useEffect(() => {\n if (!marker || !eventHandlers) return\n\n const eventNames = Object.keys(eventHandlers) as Array<keyof typeof eventHandlers>\n \n eventNames.forEach(eventName => {\n const handler = eventHandlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n marker.on(eventName, handler)\n }\n })\n\n return () => {\n eventNames.forEach(eventName => {\n const handler = eventHandlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n marker.off(eventName, handler)\n }\n })\n }\n }, [eventHandlers, marker])\n\n return (\n <MarkerContext.Provider value={{ marker }}>\n {children}\n </MarkerContext.Provider>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport L from 'leaflet'\nimport type { Popup as LeafletPopup } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport { useMarker } from '../Marker/Marker'\nimport type { PopupProps } from '../../types'\n\n/**\n * Component for adding popups to markers or the map\n * \n * When used inside a Marker, it automatically binds to that marker.\n * When used standalone, you must provide a position.\n * \n * @example\n * ```tsx\n * // Inside a Marker\n * <Marker position={[51.505, -0.09]}>\n * <Popup>\n * <h3>My Location</h3>\n * <p>This is where I am!</p>\n * </Popup>\n * </Marker>\n * \n * // Standalone\n * <Popup position={[51.505, -0.09]}>\n * Hello from this location!\n * </Popup>\n * ```\n */\nexport function Popup({\n position,\n children,\n maxWidth = 300,\n minWidth = 50,\n autoClose = true,\n closeOnClick = true,\n className,\n options,\n}: PopupProps) {\n const map = useMap()\n const marker = useMarker()\n const popupRef = useRef<LeafletPopup | null>(null)\n const [container, setContainer] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create container for React portal\n const containerElement = document.createElement('div')\n setContainer(containerElement)\n\n // Create popup with position if standalone\n const popupOptions = {\n maxWidth,\n minWidth,\n autoClose,\n closeOnClick,\n className,\n ...options,\n }\n\n const popup = !marker && position \n ? L.popup(popupOptions).setLatLng(position)\n : L.popup(popupOptions)\n\n popup.setContent(containerElement)\n popupRef.current = popup\n\n // Bind to marker (don't open yet)\n if (marker) {\n marker.bindPopup(popup)\n }\n\n // Cleanup\n return () => {\n if (marker) {\n marker.unbindPopup()\n }\n popup.remove()\n popupRef.current = null\n setContainer(null)\n }\n }, [map, marker]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Open popup after container is ready\n useEffect(() => {\n if (popupRef.current && container && map) {\n if (marker) {\n // For marker-bound popups, open them (in real usage, they'd open on click)\n popupRef.current.openOn(map)\n } else if (position) {\n // For standalone popups, open at position\n popupRef.current.openOn(map)\n }\n }\n }, [container, map, marker, position])\n\n // Update position for standalone popups\n useEffect(() => {\n if (popupRef.current && position && !marker && map) {\n popupRef.current.setLatLng(position)\n if (!popupRef.current.isOpen()) {\n popupRef.current.openOn(map)\n }\n }\n }, [position, marker, map])\n\n // Render children into popup container using portal\n if (!container) {\n return null\n }\n\n return createPortal(children, container)\n}\n","import { useEffect, useRef } from 'react'\nimport L from 'leaflet'\nimport type { GeoJSON as LeafletGeoJSON } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { GeoJSONProps } from '../../types'\n\n/**\n * Component for rendering GeoJSON data on the map\n * \n * @example\n * ```tsx\n * const geojsonData = {\n * type: 'FeatureCollection',\n * features: [\n * {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-0.09, 51.505] },\n * properties: { name: 'London' }\n * }\n * ]\n * }\n * \n * <GeoJSON\n * data={geojsonData}\n * style={{ color: 'blue', weight: 2 }}\n * onEachFeature={(feature, layer) => {\n * layer.bindPopup(feature.properties.name)\n * }}\n * />\n * ```\n */\nexport function GeoJSON({\n data,\n style,\n onEachFeature,\n pointToLayer,\n filter,\n options,\n}: GeoJSONProps) {\n const map = useMap()\n const geoJsonRef = useRef<LeafletGeoJSON | null>(null)\n\n useEffect(() => {\n if (!map || !data) return\n\n // Create GeoJSON layer\n const geoJson = L.geoJSON(data, {\n style,\n onEachFeature,\n pointToLayer,\n filter,\n ...options,\n })\n\n geoJson.addTo(map)\n geoJsonRef.current = geoJson\n\n // Cleanup\n return () => {\n geoJson.remove()\n geoJsonRef.current = null\n }\n }, [map, data, style, onEachFeature, pointToLayer, filter, options])\n\n // Update data when it changes\n useEffect(() => {\n if (geoJsonRef.current && data) {\n geoJsonRef.current.clearLayers()\n geoJsonRef.current.addData(data)\n }\n }, [data])\n\n return null\n}\n","import { useEffect } from 'react'\nimport type { LeafletEventHandlerFnMap } from 'leaflet'\nimport { useMap } from './useMap'\n\n/**\n * Hook to subscribe to Leaflet map events\n * Automatically cleans up event listeners on unmount\n * \n * @example\n * ```tsx\n * function LocationLogger() {\n * useMapEvents({\n * click: (e) => {\n * console.log('Clicked at:', e.latlng)\n * },\n * zoomend: () => {\n * console.log('Zoom changed')\n * },\n * moveend: () => {\n * console.log('Map moved')\n * }\n * })\n * \n * return null\n * }\n * ```\n * \n * @param handlers - Object mapping event names to handler functions\n */\nexport function useMapEvents(handlers: LeafletEventHandlerFnMap): void {\n const map = useMap()\n\n useEffect(() => {\n if (!map || !handlers) return\n\n // Register all event handlers\n const eventNames = Object.keys(handlers) as Array<keyof LeafletEventHandlerFnMap>\n \n eventNames.forEach(eventName => {\n const handler = handlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n map.on(eventName, handler)\n }\n })\n\n // Cleanup: remove all event handlers\n return () => {\n eventNames.forEach(eventName => {\n const handler = handlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n map.off(eventName, handler)\n }\n })\n }\n }, [map, handlers])\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { LatLngExpression, Map, MapOptions, TileLayerOptions, LeafletEventHandlerFnMap, MarkerOptions, PopupOptions, GeoJSONOptions, Marker as Marker$1 } from 'leaflet';
|
|
3
|
+
export { LatLngExpression, Map as LeafletMap } from 'leaflet';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the MapContainer component
|
|
9
|
+
*/
|
|
10
|
+
interface MapContainerProps {
|
|
11
|
+
/** Initial center position [lat, lng] */
|
|
12
|
+
center: LatLngExpression;
|
|
13
|
+
/** Initial zoom level */
|
|
14
|
+
zoom: number;
|
|
15
|
+
/** Minimum zoom level */
|
|
16
|
+
minZoom?: number;
|
|
17
|
+
/** Maximum zoom level */
|
|
18
|
+
maxZoom?: number;
|
|
19
|
+
/** Enable scroll wheel zoom */
|
|
20
|
+
scrollWheelZoom?: boolean;
|
|
21
|
+
/** Enable dragging */
|
|
22
|
+
dragging?: boolean;
|
|
23
|
+
/** Enable double click zoom */
|
|
24
|
+
doubleClickZoom?: boolean;
|
|
25
|
+
/** Enable zoom control */
|
|
26
|
+
zoomControl?: boolean;
|
|
27
|
+
/** Custom CSS styles */
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
/** Custom CSS class */
|
|
30
|
+
className?: string;
|
|
31
|
+
/** Child components */
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
/** Callback when map is ready */
|
|
34
|
+
whenReady?: (map: Map) => void;
|
|
35
|
+
/** Additional Leaflet map options */
|
|
36
|
+
options?: Omit<MapOptions, 'center' | 'zoom'>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Props for the TileLayer component
|
|
40
|
+
*/
|
|
41
|
+
interface TileLayerProps {
|
|
42
|
+
/** Tile URL template */
|
|
43
|
+
url: string;
|
|
44
|
+
/** Attribution text */
|
|
45
|
+
attribution?: string;
|
|
46
|
+
/** Maximum zoom level */
|
|
47
|
+
maxZoom?: number;
|
|
48
|
+
/** Minimum zoom level */
|
|
49
|
+
minZoom?: number;
|
|
50
|
+
/** Tile opacity (0-1) */
|
|
51
|
+
opacity?: number;
|
|
52
|
+
/** Z-index */
|
|
53
|
+
zIndex?: number;
|
|
54
|
+
/** Additional Leaflet tile layer options */
|
|
55
|
+
options?: Omit<TileLayerOptions, 'attribution' | 'maxZoom' | 'minZoom'>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Props for the Marker component
|
|
59
|
+
*/
|
|
60
|
+
interface MarkerProps {
|
|
61
|
+
/** Marker position [lat, lng] */
|
|
62
|
+
position: LatLngExpression;
|
|
63
|
+
/** Enable dragging */
|
|
64
|
+
draggable?: boolean;
|
|
65
|
+
/** Marker opacity (0-1) */
|
|
66
|
+
opacity?: number;
|
|
67
|
+
/** Alt text for accessibility */
|
|
68
|
+
alt?: string;
|
|
69
|
+
/** Event handlers */
|
|
70
|
+
eventHandlers?: LeafletEventHandlerFnMap;
|
|
71
|
+
/** Child components (usually Popup) */
|
|
72
|
+
children?: ReactNode;
|
|
73
|
+
/** Additional Leaflet marker options */
|
|
74
|
+
options?: Omit<MarkerOptions, 'draggable' | 'opacity' | 'alt'>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Props for the Popup component
|
|
78
|
+
*/
|
|
79
|
+
interface PopupProps {
|
|
80
|
+
/** Popup position (optional if used inside Marker) */
|
|
81
|
+
position?: LatLngExpression;
|
|
82
|
+
/** Popup content */
|
|
83
|
+
children: ReactNode;
|
|
84
|
+
/** Maximum width */
|
|
85
|
+
maxWidth?: number;
|
|
86
|
+
/** Minimum width */
|
|
87
|
+
minWidth?: number;
|
|
88
|
+
/** Auto close on map click */
|
|
89
|
+
autoClose?: boolean;
|
|
90
|
+
/** Close on click */
|
|
91
|
+
closeOnClick?: boolean;
|
|
92
|
+
/** Custom CSS class */
|
|
93
|
+
className?: string;
|
|
94
|
+
/** Additional Leaflet popup options */
|
|
95
|
+
options?: Omit<PopupOptions, 'maxWidth' | 'minWidth' | 'autoClose' | 'closeOnClick' | 'className'>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Props for the GeoJSON component
|
|
99
|
+
*/
|
|
100
|
+
interface GeoJSONProps {
|
|
101
|
+
/** GeoJSON data */
|
|
102
|
+
data: GeoJSON.GeoJsonObject;
|
|
103
|
+
/** Style for features */
|
|
104
|
+
style?: GeoJSONOptions['style'];
|
|
105
|
+
/** Callback for each feature */
|
|
106
|
+
onEachFeature?: GeoJSONOptions['onEachFeature'];
|
|
107
|
+
/** Point to layer callback */
|
|
108
|
+
pointToLayer?: GeoJSONOptions['pointToLayer'];
|
|
109
|
+
/** Filter function */
|
|
110
|
+
filter?: GeoJSONOptions['filter'];
|
|
111
|
+
/** Additional Leaflet GeoJSON options */
|
|
112
|
+
options?: Omit<GeoJSONOptions, 'style' | 'onEachFeature' | 'pointToLayer' | 'filter'>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Map context value
|
|
116
|
+
*/
|
|
117
|
+
interface MapContextValue {
|
|
118
|
+
/** Leaflet map instance */
|
|
119
|
+
map: Map | null;
|
|
120
|
+
/** Set map instance */
|
|
121
|
+
setMap: (map: Map | null) => void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Event handlers map type
|
|
125
|
+
*/
|
|
126
|
+
type MapEventHandlers = LeafletEventHandlerFnMap;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Main container component for creating a Leaflet map
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* <MapContainer
|
|
134
|
+
* center={[51.505, -0.09]}
|
|
135
|
+
* zoom={13}
|
|
136
|
+
* style={{ height: '400px' }}
|
|
137
|
+
* >
|
|
138
|
+
* <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
|
139
|
+
* <Marker position={[51.505, -0.09]} />
|
|
140
|
+
* </MapContainer>
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function MapContainer(props: MapContainerProps): react_jsx_runtime.JSX.Element;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Component for adding tile layers to the map
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```tsx
|
|
150
|
+
* <TileLayer
|
|
151
|
+
* url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
152
|
+
* attribution='© OpenStreetMap contributors'
|
|
153
|
+
* maxZoom={19}
|
|
154
|
+
* />
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare function TileLayer({ url, attribution, maxZoom, minZoom, opacity, zIndex, options, }: TileLayerProps): null;
|
|
158
|
+
|
|
159
|
+
declare function useMarker(): Marker$1 | null;
|
|
160
|
+
/**
|
|
161
|
+
* Component for adding markers to the map
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* <Marker
|
|
166
|
+
* position={[51.505, -0.09]}
|
|
167
|
+
* draggable={false}
|
|
168
|
+
* eventHandlers={{
|
|
169
|
+
* click: () => console.log('Marker clicked!')
|
|
170
|
+
* }}
|
|
171
|
+
* >
|
|
172
|
+
* <Popup>Hello!</Popup>
|
|
173
|
+
* </Marker>
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function Marker({ position, draggable, opacity, alt, eventHandlers, children, options, }: MarkerProps): react_jsx_runtime.JSX.Element;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Component for adding popups to markers or the map
|
|
180
|
+
*
|
|
181
|
+
* When used inside a Marker, it automatically binds to that marker.
|
|
182
|
+
* When used standalone, you must provide a position.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```tsx
|
|
186
|
+
* // Inside a Marker
|
|
187
|
+
* <Marker position={[51.505, -0.09]}>
|
|
188
|
+
* <Popup>
|
|
189
|
+
* <h3>My Location</h3>
|
|
190
|
+
* <p>This is where I am!</p>
|
|
191
|
+
* </Popup>
|
|
192
|
+
* </Marker>
|
|
193
|
+
*
|
|
194
|
+
* // Standalone
|
|
195
|
+
* <Popup position={[51.505, -0.09]}>
|
|
196
|
+
* Hello from this location!
|
|
197
|
+
* </Popup>
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare function Popup({ position, children, maxWidth, minWidth, autoClose, closeOnClick, className, options, }: PopupProps): react.ReactPortal | null;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Component for rendering GeoJSON data on the map
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```tsx
|
|
207
|
+
* const geojsonData = {
|
|
208
|
+
* type: 'FeatureCollection',
|
|
209
|
+
* features: [
|
|
210
|
+
* {
|
|
211
|
+
* type: 'Feature',
|
|
212
|
+
* geometry: { type: 'Point', coordinates: [-0.09, 51.505] },
|
|
213
|
+
* properties: { name: 'London' }
|
|
214
|
+
* }
|
|
215
|
+
* ]
|
|
216
|
+
* }
|
|
217
|
+
*
|
|
218
|
+
* <GeoJSON
|
|
219
|
+
* data={geojsonData}
|
|
220
|
+
* style={{ color: 'blue', weight: 2 }}
|
|
221
|
+
* onEachFeature={(feature, layer) => {
|
|
222
|
+
* layer.bindPopup(feature.properties.name)
|
|
223
|
+
* }}
|
|
224
|
+
* />
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function GeoJSON$1({ data, style, onEachFeature, pointToLayer, filter, options, }: GeoJSONProps): null;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Hook to access the Leaflet map instance
|
|
231
|
+
* Must be used within a MapContainer or MapProvider
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```tsx
|
|
235
|
+
* function MyComponent() {
|
|
236
|
+
* const map = useMap()
|
|
237
|
+
*
|
|
238
|
+
* const flyToLocation = () => {
|
|
239
|
+
* map.flyTo([51.505, -0.09], 14)
|
|
240
|
+
* }
|
|
241
|
+
*
|
|
242
|
+
* return <button onClick={flyToLocation}>Go to London</button>
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @returns The Leaflet map instance
|
|
247
|
+
* @throws Error if map is not yet initialized or used outside MapContainer
|
|
248
|
+
*/
|
|
249
|
+
declare function useMap(): Map;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Hook to subscribe to Leaflet map events
|
|
253
|
+
* Automatically cleans up event listeners on unmount
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```tsx
|
|
257
|
+
* function LocationLogger() {
|
|
258
|
+
* useMapEvents({
|
|
259
|
+
* click: (e) => {
|
|
260
|
+
* console.log('Clicked at:', e.latlng)
|
|
261
|
+
* },
|
|
262
|
+
* zoomend: () => {
|
|
263
|
+
* console.log('Zoom changed')
|
|
264
|
+
* },
|
|
265
|
+
* moveend: () => {
|
|
266
|
+
* console.log('Map moved')
|
|
267
|
+
* }
|
|
268
|
+
* })
|
|
269
|
+
*
|
|
270
|
+
* return null
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*
|
|
274
|
+
* @param handlers - Object mapping event names to handler functions
|
|
275
|
+
*/
|
|
276
|
+
declare function useMapEvents(handlers: LeafletEventHandlerFnMap): void;
|
|
277
|
+
|
|
278
|
+
interface MapProviderProps {
|
|
279
|
+
children: ReactNode;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Provider component for sharing map instance across components
|
|
283
|
+
*/
|
|
284
|
+
declare function MapProvider({ children }: MapProviderProps): react_jsx_runtime.JSX.Element;
|
|
285
|
+
/**
|
|
286
|
+
* Hook to access the map context
|
|
287
|
+
* @throws Error if used outside of MapProvider
|
|
288
|
+
*/
|
|
289
|
+
declare function useMapContext(): MapContextValue;
|
|
290
|
+
|
|
291
|
+
export { GeoJSON$1 as GeoJSON, type GeoJSONProps, MapContainer, type MapContainerProps, type MapContextValue, type MapEventHandlers, MapProvider, Marker, type MarkerProps, Popup, type PopupProps, TileLayer, type TileLayerProps, useMap, useMapContext, useMapEvents, useMarker };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { LatLngExpression, Map, MapOptions, TileLayerOptions, LeafletEventHandlerFnMap, MarkerOptions, PopupOptions, GeoJSONOptions, Marker as Marker$1 } from 'leaflet';
|
|
3
|
+
export { LatLngExpression, Map as LeafletMap } from 'leaflet';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for the MapContainer component
|
|
9
|
+
*/
|
|
10
|
+
interface MapContainerProps {
|
|
11
|
+
/** Initial center position [lat, lng] */
|
|
12
|
+
center: LatLngExpression;
|
|
13
|
+
/** Initial zoom level */
|
|
14
|
+
zoom: number;
|
|
15
|
+
/** Minimum zoom level */
|
|
16
|
+
minZoom?: number;
|
|
17
|
+
/** Maximum zoom level */
|
|
18
|
+
maxZoom?: number;
|
|
19
|
+
/** Enable scroll wheel zoom */
|
|
20
|
+
scrollWheelZoom?: boolean;
|
|
21
|
+
/** Enable dragging */
|
|
22
|
+
dragging?: boolean;
|
|
23
|
+
/** Enable double click zoom */
|
|
24
|
+
doubleClickZoom?: boolean;
|
|
25
|
+
/** Enable zoom control */
|
|
26
|
+
zoomControl?: boolean;
|
|
27
|
+
/** Custom CSS styles */
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
/** Custom CSS class */
|
|
30
|
+
className?: string;
|
|
31
|
+
/** Child components */
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
/** Callback when map is ready */
|
|
34
|
+
whenReady?: (map: Map) => void;
|
|
35
|
+
/** Additional Leaflet map options */
|
|
36
|
+
options?: Omit<MapOptions, 'center' | 'zoom'>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Props for the TileLayer component
|
|
40
|
+
*/
|
|
41
|
+
interface TileLayerProps {
|
|
42
|
+
/** Tile URL template */
|
|
43
|
+
url: string;
|
|
44
|
+
/** Attribution text */
|
|
45
|
+
attribution?: string;
|
|
46
|
+
/** Maximum zoom level */
|
|
47
|
+
maxZoom?: number;
|
|
48
|
+
/** Minimum zoom level */
|
|
49
|
+
minZoom?: number;
|
|
50
|
+
/** Tile opacity (0-1) */
|
|
51
|
+
opacity?: number;
|
|
52
|
+
/** Z-index */
|
|
53
|
+
zIndex?: number;
|
|
54
|
+
/** Additional Leaflet tile layer options */
|
|
55
|
+
options?: Omit<TileLayerOptions, 'attribution' | 'maxZoom' | 'minZoom'>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Props for the Marker component
|
|
59
|
+
*/
|
|
60
|
+
interface MarkerProps {
|
|
61
|
+
/** Marker position [lat, lng] */
|
|
62
|
+
position: LatLngExpression;
|
|
63
|
+
/** Enable dragging */
|
|
64
|
+
draggable?: boolean;
|
|
65
|
+
/** Marker opacity (0-1) */
|
|
66
|
+
opacity?: number;
|
|
67
|
+
/** Alt text for accessibility */
|
|
68
|
+
alt?: string;
|
|
69
|
+
/** Event handlers */
|
|
70
|
+
eventHandlers?: LeafletEventHandlerFnMap;
|
|
71
|
+
/** Child components (usually Popup) */
|
|
72
|
+
children?: ReactNode;
|
|
73
|
+
/** Additional Leaflet marker options */
|
|
74
|
+
options?: Omit<MarkerOptions, 'draggable' | 'opacity' | 'alt'>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Props for the Popup component
|
|
78
|
+
*/
|
|
79
|
+
interface PopupProps {
|
|
80
|
+
/** Popup position (optional if used inside Marker) */
|
|
81
|
+
position?: LatLngExpression;
|
|
82
|
+
/** Popup content */
|
|
83
|
+
children: ReactNode;
|
|
84
|
+
/** Maximum width */
|
|
85
|
+
maxWidth?: number;
|
|
86
|
+
/** Minimum width */
|
|
87
|
+
minWidth?: number;
|
|
88
|
+
/** Auto close on map click */
|
|
89
|
+
autoClose?: boolean;
|
|
90
|
+
/** Close on click */
|
|
91
|
+
closeOnClick?: boolean;
|
|
92
|
+
/** Custom CSS class */
|
|
93
|
+
className?: string;
|
|
94
|
+
/** Additional Leaflet popup options */
|
|
95
|
+
options?: Omit<PopupOptions, 'maxWidth' | 'minWidth' | 'autoClose' | 'closeOnClick' | 'className'>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Props for the GeoJSON component
|
|
99
|
+
*/
|
|
100
|
+
interface GeoJSONProps {
|
|
101
|
+
/** GeoJSON data */
|
|
102
|
+
data: GeoJSON.GeoJsonObject;
|
|
103
|
+
/** Style for features */
|
|
104
|
+
style?: GeoJSONOptions['style'];
|
|
105
|
+
/** Callback for each feature */
|
|
106
|
+
onEachFeature?: GeoJSONOptions['onEachFeature'];
|
|
107
|
+
/** Point to layer callback */
|
|
108
|
+
pointToLayer?: GeoJSONOptions['pointToLayer'];
|
|
109
|
+
/** Filter function */
|
|
110
|
+
filter?: GeoJSONOptions['filter'];
|
|
111
|
+
/** Additional Leaflet GeoJSON options */
|
|
112
|
+
options?: Omit<GeoJSONOptions, 'style' | 'onEachFeature' | 'pointToLayer' | 'filter'>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Map context value
|
|
116
|
+
*/
|
|
117
|
+
interface MapContextValue {
|
|
118
|
+
/** Leaflet map instance */
|
|
119
|
+
map: Map | null;
|
|
120
|
+
/** Set map instance */
|
|
121
|
+
setMap: (map: Map | null) => void;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Event handlers map type
|
|
125
|
+
*/
|
|
126
|
+
type MapEventHandlers = LeafletEventHandlerFnMap;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Main container component for creating a Leaflet map
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* <MapContainer
|
|
134
|
+
* center={[51.505, -0.09]}
|
|
135
|
+
* zoom={13}
|
|
136
|
+
* style={{ height: '400px' }}
|
|
137
|
+
* >
|
|
138
|
+
* <TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
|
139
|
+
* <Marker position={[51.505, -0.09]} />
|
|
140
|
+
* </MapContainer>
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function MapContainer(props: MapContainerProps): react_jsx_runtime.JSX.Element;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Component for adding tile layers to the map
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```tsx
|
|
150
|
+
* <TileLayer
|
|
151
|
+
* url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
152
|
+
* attribution='© OpenStreetMap contributors'
|
|
153
|
+
* maxZoom={19}
|
|
154
|
+
* />
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
declare function TileLayer({ url, attribution, maxZoom, minZoom, opacity, zIndex, options, }: TileLayerProps): null;
|
|
158
|
+
|
|
159
|
+
declare function useMarker(): Marker$1 | null;
|
|
160
|
+
/**
|
|
161
|
+
* Component for adding markers to the map
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* <Marker
|
|
166
|
+
* position={[51.505, -0.09]}
|
|
167
|
+
* draggable={false}
|
|
168
|
+
* eventHandlers={{
|
|
169
|
+
* click: () => console.log('Marker clicked!')
|
|
170
|
+
* }}
|
|
171
|
+
* >
|
|
172
|
+
* <Popup>Hello!</Popup>
|
|
173
|
+
* </Marker>
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function Marker({ position, draggable, opacity, alt, eventHandlers, children, options, }: MarkerProps): react_jsx_runtime.JSX.Element;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Component for adding popups to markers or the map
|
|
180
|
+
*
|
|
181
|
+
* When used inside a Marker, it automatically binds to that marker.
|
|
182
|
+
* When used standalone, you must provide a position.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```tsx
|
|
186
|
+
* // Inside a Marker
|
|
187
|
+
* <Marker position={[51.505, -0.09]}>
|
|
188
|
+
* <Popup>
|
|
189
|
+
* <h3>My Location</h3>
|
|
190
|
+
* <p>This is where I am!</p>
|
|
191
|
+
* </Popup>
|
|
192
|
+
* </Marker>
|
|
193
|
+
*
|
|
194
|
+
* // Standalone
|
|
195
|
+
* <Popup position={[51.505, -0.09]}>
|
|
196
|
+
* Hello from this location!
|
|
197
|
+
* </Popup>
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare function Popup({ position, children, maxWidth, minWidth, autoClose, closeOnClick, className, options, }: PopupProps): react.ReactPortal | null;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Component for rendering GeoJSON data on the map
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```tsx
|
|
207
|
+
* const geojsonData = {
|
|
208
|
+
* type: 'FeatureCollection',
|
|
209
|
+
* features: [
|
|
210
|
+
* {
|
|
211
|
+
* type: 'Feature',
|
|
212
|
+
* geometry: { type: 'Point', coordinates: [-0.09, 51.505] },
|
|
213
|
+
* properties: { name: 'London' }
|
|
214
|
+
* }
|
|
215
|
+
* ]
|
|
216
|
+
* }
|
|
217
|
+
*
|
|
218
|
+
* <GeoJSON
|
|
219
|
+
* data={geojsonData}
|
|
220
|
+
* style={{ color: 'blue', weight: 2 }}
|
|
221
|
+
* onEachFeature={(feature, layer) => {
|
|
222
|
+
* layer.bindPopup(feature.properties.name)
|
|
223
|
+
* }}
|
|
224
|
+
* />
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
declare function GeoJSON$1({ data, style, onEachFeature, pointToLayer, filter, options, }: GeoJSONProps): null;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Hook to access the Leaflet map instance
|
|
231
|
+
* Must be used within a MapContainer or MapProvider
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```tsx
|
|
235
|
+
* function MyComponent() {
|
|
236
|
+
* const map = useMap()
|
|
237
|
+
*
|
|
238
|
+
* const flyToLocation = () => {
|
|
239
|
+
* map.flyTo([51.505, -0.09], 14)
|
|
240
|
+
* }
|
|
241
|
+
*
|
|
242
|
+
* return <button onClick={flyToLocation}>Go to London</button>
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* @returns The Leaflet map instance
|
|
247
|
+
* @throws Error if map is not yet initialized or used outside MapContainer
|
|
248
|
+
*/
|
|
249
|
+
declare function useMap(): Map;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Hook to subscribe to Leaflet map events
|
|
253
|
+
* Automatically cleans up event listeners on unmount
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```tsx
|
|
257
|
+
* function LocationLogger() {
|
|
258
|
+
* useMapEvents({
|
|
259
|
+
* click: (e) => {
|
|
260
|
+
* console.log('Clicked at:', e.latlng)
|
|
261
|
+
* },
|
|
262
|
+
* zoomend: () => {
|
|
263
|
+
* console.log('Zoom changed')
|
|
264
|
+
* },
|
|
265
|
+
* moveend: () => {
|
|
266
|
+
* console.log('Map moved')
|
|
267
|
+
* }
|
|
268
|
+
* })
|
|
269
|
+
*
|
|
270
|
+
* return null
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*
|
|
274
|
+
* @param handlers - Object mapping event names to handler functions
|
|
275
|
+
*/
|
|
276
|
+
declare function useMapEvents(handlers: LeafletEventHandlerFnMap): void;
|
|
277
|
+
|
|
278
|
+
interface MapProviderProps {
|
|
279
|
+
children: ReactNode;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Provider component for sharing map instance across components
|
|
283
|
+
*/
|
|
284
|
+
declare function MapProvider({ children }: MapProviderProps): react_jsx_runtime.JSX.Element;
|
|
285
|
+
/**
|
|
286
|
+
* Hook to access the map context
|
|
287
|
+
* @throws Error if used outside of MapProvider
|
|
288
|
+
*/
|
|
289
|
+
declare function useMapContext(): MapContextValue;
|
|
290
|
+
|
|
291
|
+
export { GeoJSON$1 as GeoJSON, type GeoJSONProps, MapContainer, type MapContainerProps, type MapContextValue, type MapEventHandlers, MapProvider, Marker, type MarkerProps, Popup, type PopupProps, TileLayer, type TileLayerProps, useMap, useMapContext, useMapEvents, useMarker };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {createContext,useState,useContext,useRef,useEffect}from'react';import x from'leaflet';import {jsx}from'react/jsx-runtime';import {createPortal}from'react-dom';var g=createContext(null);function E({children:e}){let[t,o]=useState(null);return jsx(g.Provider,{value:{map:t,setMap:o},children:e})}function L(){let e=useContext(g);if(!e)throw new Error("useMapContext must be used within a MapProvider or MapContainer");return e}x.Icon&&x.Icon.Default&&x.Icon.Default.prototype&&delete x.Icon.Default.prototype._getIconUrl;x.Icon.Default.mergeOptions({iconRetinaUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png",iconUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png",shadowUrl:"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png"});function F({center:e,zoom:t,minZoom:o,maxZoom:u,scrollWheelZoom:n=true,dragging:i=true,doubleClickZoom:s=true,zoomControl:f=true,style:r,className:a,children:p,whenReady:l,options:c,setMapReady:y}){let d=useRef(null),{setMap:M}=L(),P=useRef(null);return useEffect(()=>{if(!d.current||P.current)return;let C=x.map(d.current,{center:e,zoom:t,minZoom:o,maxZoom:u,scrollWheelZoom:n,dragging:i,doubleClickZoom:s,zoomControl:f,...c});return P.current=C,M(C),y(true),l&&l(C),()=>{C.remove(),P.current=null,M(null),y(false);}},[]),useEffect(()=>{P.current&&P.current.setView(e,t);},[e,t]),jsx("div",{ref:d,style:{height:"100%",width:"100%",...r},className:a,children:p})}function T(e){let[t,o]=useState(false);return jsx(E,{children:jsx(F,{...e,setMapReady:o,children:t?e.children:null})})}function m(){let{map:e}=L();if(!e)throw new Error("Map instance is not available. Make sure this hook is used within a MapContainer and the map has been initialized.");return e}function J({url:e,attribution:t,maxZoom:o,minZoom:u,opacity:n,zIndex:i,options:s}){let f=m(),r=useRef(null);return useEffect(()=>{if(!f)return;let a=x.tileLayer(e,{attribution:t,maxZoom:o,minZoom:u,opacity:n,zIndex:i,...s});return a.addTo(f),r.current=a,()=>{a.remove(),r.current=null;}},[f,e,t,o,u,n,i,s]),null}var N=createContext({marker:null});function v(){let{marker:e}=useContext(N);return e}function b({position:e,draggable:t=false,opacity:o,alt:u,eventHandlers:n,children:i,options:s}){let f=m(),[r,a]=useState(null);return useEffect(()=>{if(!f)return;let p=x.marker(e,{draggable:t,opacity:o,alt:u,...s});return p.addTo(f),a(p),()=>{p.remove(),a(null);}},[f]),useEffect(()=>{r&&r.setLatLng(e);},[e,r]),useEffect(()=>{r&&(t?r.dragging?.enable():r.dragging?.disable());},[t,r]),useEffect(()=>{r&&o!==void 0&&r.setOpacity(o);},[o,r]),useEffect(()=>{if(!r||!n)return;let p=Object.keys(n);return p.forEach(l=>{let c=n[l];c&&r.on(l,c);}),()=>{p.forEach(l=>{let c=n[l];c&&r.off(l,c);});}},[n,r]),jsx(N.Provider,{value:{marker:r},children:i})}function w({position:e,children:t,maxWidth:o=300,minWidth:u=50,autoClose:n=true,closeOnClick:i=true,className:s,options:f}){let r=m(),a=v(),p=useRef(null),[l,c]=useState(null);return useEffect(()=>{if(!r)return;let y=document.createElement("div");c(y);let d={maxWidth:o,minWidth:u,autoClose:n,closeOnClick:i,className:s,...f},M=!a&&e?x.popup(d).setLatLng(e):x.popup(d);return M.setContent(y),p.current=M,a&&a.bindPopup(M),()=>{a&&a.unbindPopup(),M.remove(),p.current=null,c(null);}},[r,a]),useEffect(()=>{p.current&&l&&r&&(a||e)&&p.current.openOn(r);},[l,r,a,e]),useEffect(()=>{p.current&&e&&!a&&r&&(p.current.setLatLng(e),p.current.isOpen()||p.current.openOn(r));},[e,a,r]),l?createPortal(t,l):null}function D({data:e,style:t,onEachFeature:o,pointToLayer:u,filter:n,options:i}){let s=m(),f=useRef(null);return useEffect(()=>{if(!s||!e)return;let r=x.geoJSON(e,{style:t,onEachFeature:o,pointToLayer:u,filter:n,...i});return r.addTo(s),f.current=r,()=>{r.remove(),f.current=null;}},[s,e,t,o,u,n,i]),useEffect(()=>{f.current&&e&&(f.current.clearLayers(),f.current.addData(e));},[e]),null}function oe(e){let t=m();useEffect(()=>{if(!t||!e)return;let o=Object.keys(e);return o.forEach(u=>{let n=e[u];n&&t.on(u,n);}),()=>{o.forEach(u=>{let n=e[u];n&&t.off(u,n);});}},[t,e]);}
|
|
2
|
+
export{D as GeoJSON,T as MapContainer,E as MapProvider,b as Marker,w as Popup,J as TileLayer,m as useMap,L as useMapContext,oe as useMapEvents,v as useMarker};//# sourceMappingURL=index.js.map
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/MapContext.tsx","../src/components/MapContainer/MapContainer.tsx","../src/hooks/useMap.ts","../src/components/TileLayer/TileLayer.tsx","../src/components/Marker/Marker.tsx","../src/components/Popup/Popup.tsx","../src/components/GeoJSON/GeoJSON.tsx","../src/hooks/useMapEvents.ts"],"names":["MapContext","createContext","MapProvider","children","map","setMap","useState","jsx","useMapContext","context","useContext","L","MapContainerInner","center","zoom","minZoom","maxZoom","scrollWheelZoom","dragging","doubleClickZoom","zoomControl","style","className","whenReady","options","setMapReady","containerRef","useRef","mapRef","useEffect","MapContainer","props","mapReady","useMap","TileLayer","url","attribution","opacity","zIndex","tileLayerRef","tileLayer","MarkerContext","useMarker","marker","Marker","position","draggable","alt","eventHandlers","setMarker","markerInstance","eventNames","eventName","handler","Popup","maxWidth","minWidth","autoClose","closeOnClick","popupRef","container","setContainer","containerElement","popupOptions","popup","createPortal","GeoJSON","data","onEachFeature","pointToLayer","filter","geoJsonRef","geoJson","useMapEvents","handlers"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext, useState, type ReactNode } from 'react'\nimport type { Map } from 'leaflet'\nimport type { MapContextValue } from '../types'\n\nconst MapContext = createContext<MapContextValue | null>(null)\n\nexport interface MapProviderProps {\n children: ReactNode\n}\n\n/**\n * Provider component for sharing map instance across components\n */\nexport function MapProvider({ children }: MapProviderProps) {\n const [map, setMap] = useState<Map | null>(null)\n\n return (\n <MapContext.Provider value={{ map, setMap }}>\n {children}\n </MapContext.Provider>\n )\n}\n\n/**\n * Hook to access the map context\n * @throws Error if used outside of MapProvider\n */\nexport function useMapContext(): MapContextValue {\n const context = useContext(MapContext)\n \n if (!context) {\n throw new Error('useMapContext must be used within a MapProvider or MapContainer')\n }\n \n return context\n}\n\nexport { MapContext }\n","import { useEffect, useRef, useState } from 'react'\nimport L from 'leaflet'\nimport type { Map } from 'leaflet'\nimport { MapProvider, useMapContext } from '../../context/MapContext'\nimport type { MapContainerProps } from '../../types'\n\n// Fix for default marker icons in Leaflet with bundlers\n// import 'leaflet/dist/leaflet.css' // Commented out for testing\n\n// Fix for default marker icons in Leaflet with bundlers\nif (L.Icon && L.Icon.Default && L.Icon.Default.prototype) {\n delete (L.Icon.Default.prototype as any)._getIconUrl\n}\nL.Icon.Default.mergeOptions({\n iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon-2x.png',\n iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-icon.png',\n shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/images/marker-shadow.png',\n})\n\ninterface MapContainerInnerProps extends MapContainerProps {\n setMapReady: (ready: boolean) => void\n}\n\nfunction MapContainerInner({\n center,\n zoom,\n minZoom,\n maxZoom,\n scrollWheelZoom = true,\n dragging = true,\n doubleClickZoom = true,\n zoomControl = true,\n style,\n className,\n children,\n whenReady,\n options,\n setMapReady,\n}: MapContainerInnerProps) {\n const containerRef = useRef<HTMLDivElement>(null)\n const { setMap } = useMapContext()\n const mapRef = useRef<Map | null>(null)\n\n useEffect(() => {\n if (!containerRef.current || mapRef.current) return\n\n // Create the map\n const map = L.map(containerRef.current, {\n center,\n zoom,\n minZoom,\n maxZoom,\n scrollWheelZoom,\n dragging,\n doubleClickZoom,\n zoomControl,\n ...options,\n })\n\n mapRef.current = map\n setMap(map)\n setMapReady(true)\n\n // Call whenReady callback\n if (whenReady) {\n whenReady(map)\n }\n\n // Cleanup\n return () => {\n map.remove()\n mapRef.current = null\n setMap(null)\n setMapReady(false)\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update view when center or zoom changes\n useEffect(() => {\n if (mapRef.current) {\n mapRef.current.setView(center, zoom)\n }\n }, [center, zoom])\n\n return (\n <div\n ref={containerRef}\n style={{\n height: '100%',\n width: '100%',\n ...style,\n }}\n className={className}\n >\n {children}\n </div>\n )\n}\n\n/**\n * Main container component for creating a Leaflet map\n * \n * @example\n * ```tsx\n * <MapContainer\n * center={[51.505, -0.09]}\n * zoom={13}\n * style={{ height: '400px' }}\n * >\n * <TileLayer url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\" />\n * <Marker position={[51.505, -0.09]} />\n * </MapContainer>\n * ```\n */\nexport function MapContainer(props: MapContainerProps) {\n const [mapReady, setMapReady] = useState(false)\n\n return (\n <MapProvider>\n <MapContainerInner {...props} setMapReady={setMapReady}>\n {mapReady ? props.children : null}\n </MapContainerInner>\n </MapProvider>\n )\n}\n","import type { Map } from 'leaflet'\nimport { useMapContext } from '../context/MapContext'\n\n/**\n * Hook to access the Leaflet map instance\n * Must be used within a MapContainer or MapProvider\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const map = useMap()\n * \n * const flyToLocation = () => {\n * map.flyTo([51.505, -0.09], 14)\n * }\n * \n * return <button onClick={flyToLocation}>Go to London</button>\n * }\n * ```\n * \n * @returns The Leaflet map instance\n * @throws Error if map is not yet initialized or used outside MapContainer\n */\nexport function useMap(): Map {\n const { map } = useMapContext()\n \n if (!map) {\n throw new Error(\n 'Map instance is not available. Make sure this hook is used within a MapContainer ' +\n 'and the map has been initialized.'\n )\n }\n \n return map\n}\n","import { useEffect, useRef } from 'react'\nimport L from 'leaflet'\nimport type { TileLayer as LeafletTileLayer } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { TileLayerProps } from '../../types'\n\n/**\n * Component for adding tile layers to the map\n * \n * @example\n * ```tsx\n * <TileLayer\n * url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n * attribution='© OpenStreetMap contributors'\n * maxZoom={19}\n * />\n * ```\n */\nexport function TileLayer({\n url,\n attribution,\n maxZoom,\n minZoom,\n opacity,\n zIndex,\n options,\n}: TileLayerProps) {\n const map = useMap()\n const tileLayerRef = useRef<LeafletTileLayer | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create tile layer\n const tileLayer = L.tileLayer(url, {\n attribution,\n maxZoom,\n minZoom,\n opacity,\n zIndex,\n ...options,\n })\n\n tileLayer.addTo(map)\n tileLayerRef.current = tileLayer\n\n // Cleanup\n return () => {\n tileLayer.remove()\n tileLayerRef.current = null\n }\n }, [map, url, attribution, maxZoom, minZoom, opacity, zIndex, options])\n\n return null\n}\n","import { useEffect, useState, createContext, useContext } from 'react'\nimport L from 'leaflet'\nimport type { Marker as LeafletMarker } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { MarkerProps } from '../../types'\n\n// Context for sharing marker with Popup\ninterface MarkerContextValue {\n marker: LeafletMarker | null\n}\n\nconst MarkerContext = createContext<MarkerContextValue>({ marker: null })\n\nexport function useMarker(): LeafletMarker | null {\n const { marker } = useContext(MarkerContext)\n return marker\n}\n\n/**\n * Component for adding markers to the map\n * \n * @example\n * ```tsx\n * <Marker\n * position={[51.505, -0.09]}\n * draggable={false}\n * eventHandlers={{\n * click: () => console.log('Marker clicked!')\n * }}\n * >\n * <Popup>Hello!</Popup>\n * </Marker>\n * ```\n */\nexport function Marker({\n position,\n draggable = false,\n opacity,\n alt,\n eventHandlers,\n children,\n options,\n}: MarkerProps) {\n const map = useMap()\n const [marker, setMarker] = useState<LeafletMarker | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create marker\n const markerInstance = L.marker(position, {\n draggable,\n opacity,\n alt,\n ...options,\n })\n\n markerInstance.addTo(map)\n setMarker(markerInstance)\n\n // Cleanup\n return () => {\n markerInstance.remove()\n setMarker(null)\n }\n }, [map]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update position\n useEffect(() => {\n if (marker) {\n marker.setLatLng(position)\n }\n }, [position, marker])\n\n // Update draggable option\n useEffect(() => {\n if (marker) {\n if (draggable) {\n marker.dragging?.enable()\n } else {\n marker.dragging?.disable()\n }\n }\n }, [draggable, marker])\n\n // Update opacity\n useEffect(() => {\n if (marker && opacity !== undefined) {\n marker.setOpacity(opacity)\n }\n }, [opacity, marker])\n\n // Attach event handlers\n useEffect(() => {\n if (!marker || !eventHandlers) return\n\n const eventNames = Object.keys(eventHandlers) as Array<keyof typeof eventHandlers>\n \n eventNames.forEach(eventName => {\n const handler = eventHandlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n marker.on(eventName, handler)\n }\n })\n\n return () => {\n eventNames.forEach(eventName => {\n const handler = eventHandlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n marker.off(eventName, handler)\n }\n })\n }\n }, [eventHandlers, marker])\n\n return (\n <MarkerContext.Provider value={{ marker }}>\n {children}\n </MarkerContext.Provider>\n )\n}\n","import { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport L from 'leaflet'\nimport type { Popup as LeafletPopup } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport { useMarker } from '../Marker/Marker'\nimport type { PopupProps } from '../../types'\n\n/**\n * Component for adding popups to markers or the map\n * \n * When used inside a Marker, it automatically binds to that marker.\n * When used standalone, you must provide a position.\n * \n * @example\n * ```tsx\n * // Inside a Marker\n * <Marker position={[51.505, -0.09]}>\n * <Popup>\n * <h3>My Location</h3>\n * <p>This is where I am!</p>\n * </Popup>\n * </Marker>\n * \n * // Standalone\n * <Popup position={[51.505, -0.09]}>\n * Hello from this location!\n * </Popup>\n * ```\n */\nexport function Popup({\n position,\n children,\n maxWidth = 300,\n minWidth = 50,\n autoClose = true,\n closeOnClick = true,\n className,\n options,\n}: PopupProps) {\n const map = useMap()\n const marker = useMarker()\n const popupRef = useRef<LeafletPopup | null>(null)\n const [container, setContainer] = useState<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!map) return\n\n // Create container for React portal\n const containerElement = document.createElement('div')\n setContainer(containerElement)\n\n // Create popup with position if standalone\n const popupOptions = {\n maxWidth,\n minWidth,\n autoClose,\n closeOnClick,\n className,\n ...options,\n }\n\n const popup = !marker && position \n ? L.popup(popupOptions).setLatLng(position)\n : L.popup(popupOptions)\n\n popup.setContent(containerElement)\n popupRef.current = popup\n\n // Bind to marker (don't open yet)\n if (marker) {\n marker.bindPopup(popup)\n }\n\n // Cleanup\n return () => {\n if (marker) {\n marker.unbindPopup()\n }\n popup.remove()\n popupRef.current = null\n setContainer(null)\n }\n }, [map, marker]) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Open popup after container is ready\n useEffect(() => {\n if (popupRef.current && container && map) {\n if (marker) {\n // For marker-bound popups, open them (in real usage, they'd open on click)\n popupRef.current.openOn(map)\n } else if (position) {\n // For standalone popups, open at position\n popupRef.current.openOn(map)\n }\n }\n }, [container, map, marker, position])\n\n // Update position for standalone popups\n useEffect(() => {\n if (popupRef.current && position && !marker && map) {\n popupRef.current.setLatLng(position)\n if (!popupRef.current.isOpen()) {\n popupRef.current.openOn(map)\n }\n }\n }, [position, marker, map])\n\n // Render children into popup container using portal\n if (!container) {\n return null\n }\n\n return createPortal(children, container)\n}\n","import { useEffect, useRef } from 'react'\nimport L from 'leaflet'\nimport type { GeoJSON as LeafletGeoJSON } from 'leaflet'\nimport { useMap } from '../../hooks/useMap'\nimport type { GeoJSONProps } from '../../types'\n\n/**\n * Component for rendering GeoJSON data on the map\n * \n * @example\n * ```tsx\n * const geojsonData = {\n * type: 'FeatureCollection',\n * features: [\n * {\n * type: 'Feature',\n * geometry: { type: 'Point', coordinates: [-0.09, 51.505] },\n * properties: { name: 'London' }\n * }\n * ]\n * }\n * \n * <GeoJSON\n * data={geojsonData}\n * style={{ color: 'blue', weight: 2 }}\n * onEachFeature={(feature, layer) => {\n * layer.bindPopup(feature.properties.name)\n * }}\n * />\n * ```\n */\nexport function GeoJSON({\n data,\n style,\n onEachFeature,\n pointToLayer,\n filter,\n options,\n}: GeoJSONProps) {\n const map = useMap()\n const geoJsonRef = useRef<LeafletGeoJSON | null>(null)\n\n useEffect(() => {\n if (!map || !data) return\n\n // Create GeoJSON layer\n const geoJson = L.geoJSON(data, {\n style,\n onEachFeature,\n pointToLayer,\n filter,\n ...options,\n })\n\n geoJson.addTo(map)\n geoJsonRef.current = geoJson\n\n // Cleanup\n return () => {\n geoJson.remove()\n geoJsonRef.current = null\n }\n }, [map, data, style, onEachFeature, pointToLayer, filter, options])\n\n // Update data when it changes\n useEffect(() => {\n if (geoJsonRef.current && data) {\n geoJsonRef.current.clearLayers()\n geoJsonRef.current.addData(data)\n }\n }, [data])\n\n return null\n}\n","import { useEffect } from 'react'\nimport type { LeafletEventHandlerFnMap } from 'leaflet'\nimport { useMap } from './useMap'\n\n/**\n * Hook to subscribe to Leaflet map events\n * Automatically cleans up event listeners on unmount\n * \n * @example\n * ```tsx\n * function LocationLogger() {\n * useMapEvents({\n * click: (e) => {\n * console.log('Clicked at:', e.latlng)\n * },\n * zoomend: () => {\n * console.log('Zoom changed')\n * },\n * moveend: () => {\n * console.log('Map moved')\n * }\n * })\n * \n * return null\n * }\n * ```\n * \n * @param handlers - Object mapping event names to handler functions\n */\nexport function useMapEvents(handlers: LeafletEventHandlerFnMap): void {\n const map = useMap()\n\n useEffect(() => {\n if (!map || !handlers) return\n\n // Register all event handlers\n const eventNames = Object.keys(handlers) as Array<keyof LeafletEventHandlerFnMap>\n \n eventNames.forEach(eventName => {\n const handler = handlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n map.on(eventName, handler)\n }\n })\n\n // Cleanup: remove all event handlers\n return () => {\n eventNames.forEach(eventName => {\n const handler = handlers[eventName]\n if (handler) {\n // @ts-expect-error - Leaflet's type definitions require specific handler types per event, but we're dynamically iterating\n map.off(eventName, handler)\n }\n })\n }\n }, [map, handlers])\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geekles007/motion-map-components",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Modern React components for interactive maps with Leaflet",
|
|
5
|
+
"author": "Geekles",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Geekles007/react-map-components.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Geekles007/react-map-components#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/Geekles007/react-map-components/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"map",
|
|
18
|
+
"leaflet",
|
|
19
|
+
"mapbox",
|
|
20
|
+
"gis",
|
|
21
|
+
"components",
|
|
22
|
+
"typescript",
|
|
23
|
+
"mapping",
|
|
24
|
+
"geojson",
|
|
25
|
+
"markers"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/index.d.cts",
|
|
39
|
+
"default": "./dist/index.cjs"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./styles.css": "./dist/styles.css"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"dist",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"sideEffects": [
|
|
50
|
+
"*.css"
|
|
51
|
+
],
|
|
52
|
+
"scripts": {
|
|
53
|
+
"dev": "storybook dev -p 6006",
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"build:storybook": "storybook build",
|
|
56
|
+
"test": "vitest",
|
|
57
|
+
"test:ui": "vitest --ui",
|
|
58
|
+
"test:coverage": "vitest --coverage",
|
|
59
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
60
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
61
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,css}\"",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
63
|
+
"prepublishOnly": "npm run build",
|
|
64
|
+
"release": "changeset publish",
|
|
65
|
+
"prepare": "husky install"
|
|
66
|
+
},
|
|
67
|
+
"peerDependencies": {
|
|
68
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
69
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"leaflet": "^1.9.4"
|
|
73
|
+
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@changesets/cli": "^2.27.1",
|
|
76
|
+
"@storybook/addon-essentials": "^8.0.4",
|
|
77
|
+
"@storybook/addon-interactions": "^8.0.4",
|
|
78
|
+
"@storybook/addon-links": "^8.0.4",
|
|
79
|
+
"@storybook/blocks": "^8.0.4",
|
|
80
|
+
"@storybook/react": "^8.0.4",
|
|
81
|
+
"@storybook/react-vite": "^8.0.4",
|
|
82
|
+
"@storybook/test": "^8.0.4",
|
|
83
|
+
"@testing-library/jest-dom": "^6.4.2",
|
|
84
|
+
"@testing-library/react": "^14.2.2",
|
|
85
|
+
"@types/leaflet": "^1.9.8",
|
|
86
|
+
"@types/react": "^18.2.67",
|
|
87
|
+
"@types/react-dom": "^18.2.22",
|
|
88
|
+
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
|
89
|
+
"@typescript-eslint/parser": "^7.3.1",
|
|
90
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
91
|
+
"@vitest/coverage-v8": "^1.4.0",
|
|
92
|
+
"@vitest/ui": "^1.4.0",
|
|
93
|
+
"eslint": "^8.57.0",
|
|
94
|
+
"eslint-config-prettier": "^9.1.0",
|
|
95
|
+
"eslint-plugin-react": "^7.34.1",
|
|
96
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
97
|
+
"eslint-plugin-storybook": "^0.8.0",
|
|
98
|
+
"husky": "^9.0.11",
|
|
99
|
+
"jsdom": "^24.0.0",
|
|
100
|
+
"lint-staged": "^15.2.2",
|
|
101
|
+
"prettier": "^3.2.5",
|
|
102
|
+
"react": "^18.2.0",
|
|
103
|
+
"react-dom": "^18.2.0",
|
|
104
|
+
"storybook": "^8.0.4",
|
|
105
|
+
"tsup": "^8.0.2",
|
|
106
|
+
"typescript": "^5.4.2",
|
|
107
|
+
"vite": "^5.2.6",
|
|
108
|
+
"vitest": "^1.4.0"
|
|
109
|
+
},
|
|
110
|
+
"lint-staged": {
|
|
111
|
+
"*.{ts,tsx}": [
|
|
112
|
+
"eslint --fix",
|
|
113
|
+
"prettier --write"
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
"engines": {
|
|
117
|
+
"node": ">=18.0.0"
|
|
118
|
+
}
|
|
119
|
+
}
|