├── .babelrc ├── .gitignore ├── example ├── index.html ├── Checkboxes.js ├── css │ └── default.css └── index.js ├── Freedraw.jsx ├── LICENSE ├── package.json ├── README.md └── Freedraw.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react", "@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | .parcel-cache/ 13 | dist/ 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React-Leaflet-Freedraw examples 6 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/Checkboxes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Checkbox = ({ type = 'checkbox', name, checked = false, onChange }) => ( 4 | 5 | ); 6 | 7 | const CheckboxContainer = (props) => ( 8 | <> 9 | {props.checkboxes.map((item) => ( 10 |
11 | 19 |
20 | ))} 21 | 22 | ); 23 | 24 | export default CheckboxContainer; 25 | -------------------------------------------------------------------------------- /Freedraw.jsx: -------------------------------------------------------------------------------- 1 | import LeafletFreedraw from 'leaflet-freedraw'; 2 | import { createLayerComponent } from '@react-leaflet/core'; 3 | 4 | function createLeafletElement(props, context) { 5 | const instance = new LeafletFreedraw({ ...props }); 6 | return { instance, context: { ...context, overlayContainer: instance } }; 7 | } 8 | 9 | function updateLeafletElement(instance, props, prevProps) { 10 | if (props.mode !== prevProps.mode) { 11 | instance.mode(props.mode); 12 | } 13 | } 14 | 15 | const Freedraw = createLayerComponent( 16 | createLeafletElement, 17 | updateLeafletElement 18 | ); 19 | 20 | export default Freedraw; 21 | 22 | export * from 'leaflet-freedraw'; 23 | -------------------------------------------------------------------------------- /example/css/default.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | overflow: hidden; 4 | } 5 | 6 | a { 7 | color: dodgerblue; 8 | } 9 | 10 | .leaflet-container { 11 | height: 400px; 12 | width: 90%; 13 | margin: 0 auto; 14 | } 15 | 16 | path.leaflet-line { 17 | stroke: #50622b; 18 | stroke-width: 2; 19 | } 20 | 21 | div.leaflet-edge { 22 | background-color: #95bc59; 23 | box-shadow: 0 0 0 2px white, 0 0 10px rgba(0, 0, 0, 0.35); 24 | border-radius: 50%; 25 | cursor: move; 26 | outline: none; 27 | transition: background-color 0.25s; 28 | } 29 | 30 | div.leaflet-edge.disabled { 31 | pointer-events: none; 32 | background-color: #bbb; 33 | } 34 | 35 | path.leaflet-polygon { 36 | fill: #b4cd8a; 37 | stroke: #50622b; 38 | stroke-width: 2; 39 | fill-opacity: 0.75; 40 | } 41 | 42 | .map.mode-create { 43 | cursor: crosshair; 44 | } 45 | 46 | .checkboxContainer { 47 | display: flex; 48 | flex-direction: column; 49 | margin: 40px 40px; 50 | } 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Elango Bharathi Dhandapani and contributors 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. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-leaflet-freedraw", 3 | "version": "3.0.1", 4 | "description": "React Component for freedraw", 5 | "main": "Freedraw.js", 6 | "scripts": { 7 | "start": "babel Freedraw.jsx > Freedraw.js", 8 | "example": "parcel example/index.html" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/elangobharathi/react-leaflet-freedraw.git" 13 | }, 14 | "author": "Elango Bharathi Dhandapani", 15 | "license": "MIT", 16 | "keywords": [ 17 | "react", 18 | "react-leaflet", 19 | "react-leaflet-v3", 20 | "freedraw", 21 | "leaflet-freedraw", 22 | "react-leaflet-freedraw" 23 | ], 24 | "peerDependencies": { 25 | "leaflet": "^1.3.3", 26 | "react-leaflet": "^3.1.0", 27 | "react": "^17.0.2", 28 | "react-dom": "^17.0.2" 29 | }, 30 | "devDependencies": { 31 | "@babel/cli": "^7.13.16", 32 | "@babel/core": "^7.14.2", 33 | "@babel/preset-env": "^7.14.2", 34 | "@babel/preset-react": "^7.13.13", 35 | "parcel": "^2.0.0-beta.2", 36 | "leaflet": "^1.3.3", 37 | "react": "^17.0.2", 38 | "react-dom": "^17.0.2", 39 | "react-leaflet": "^3.1.0" 40 | }, 41 | "dependencies": { 42 | "@react-leaflet/core": "^1.0.2", 43 | "leaflet-freedraw": "^2.13.3", 44 | "ramda": "^0.27.1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-leaflet-freedraw 2 | 3 | Plugin for [react-leaflet v3](https://github.com/PaulLeCam/react-leaflet) to integrate [Leaflet.FreeDraw](https://github.com/Wildhoney/Leaflet.FreeDraw). 4 | 5 | Please checkout [codesandbox EXAMPLE](https://codesandbox.io/s/react-leaflet-freedraw-example-1fy3l?file=/src/App.js) using this package with some primary use cases. 6 | 7 | ## Install 8 | 9 | `npm install react-leaflet-freedraw --save` 10 | 11 | or 12 | 13 | `yarn add react-leaflet-freedraw` 14 | 15 | Make sure that you have the following peer dependencies installed. 16 | 17 | `npm install leaflet react-leaflet react react-dom --save` 18 | 19 | or 20 | 21 | `yarn add leaflet react-leaflet react react-dom` 22 | 23 | ## Getting started 24 | 25 | Please make sure that you go through [Leaflet.FreeDraw](https://github.com/Wildhoney/Leaflet.FreeDraw) readme before integrating this component. 26 | 27 | You need to wrap this component into MapContainer component and pass the options as shown below. 28 | 29 | ```javascript 30 | import React, { useRef } from 'react'; 31 | import { MapContainer } from 'react-leaflet'; 32 | import Freedraw, { ALL } from 'react-leaflet-freedraw'; 33 | 34 | const Component = () => { 35 | const freedrawRef = useRef(null); 36 | 37 | return ( 38 | 39 | 43 | console.log( 44 | 'markers drawn - latLngs', 45 | event.latLngs, 46 | 'Polygons:', 47 | freedrawRef.current.size() 48 | ), 49 | mode: (event) => console.log('mode changed', event), 50 | }} 51 | ref={freedrawRef} 52 | /> 53 | 54 | ); 55 | }; 56 | ``` 57 | 58 | It supports all the options mentioned in [Leaflet.FreeDraw](https://github.com/Wildhoney/Leaflet.FreeDraw). 59 | 60 | A detailed example of how to use this componenet is in the [example folder of this repo](https://github.com/elangobharathi/react-leaflet-freedraw/tree/master/example). To run the example, 61 | 62 | 1. Clone this repo 63 | 2. Run `npm i` 64 | 3. Run `npm run example` 65 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | useCallback, 3 | useEffect, 4 | useMemo, 5 | useReducer, 6 | useRef, 7 | } from 'react'; 8 | import { render } from 'react-dom'; 9 | import { MapContainer, TileLayer } from 'react-leaflet'; 10 | import Freedraw, { CREATE, EDIT, DELETE, APPEND, ALL } from '../Freedraw'; 11 | import CheckboxContainer from './Checkboxes'; 12 | 13 | const intialState = [ 14 | { 15 | id: 'create', 16 | label: 'Create', 17 | mode: CREATE, 18 | isChecked: true, 19 | }, 20 | { 21 | id: 'edit', 22 | label: 'Edit Polygons', 23 | mode: EDIT, 24 | isChecked: true, 25 | }, 26 | { 27 | id: 'attach-elbows', 28 | label: 'Attach Elbows', 29 | mode: APPEND, 30 | isChecked: true, 31 | }, 32 | { 33 | id: 'delete', 34 | label: 'Delete', 35 | mode: DELETE, 36 | isChecked: true, 37 | }, 38 | ]; 39 | 40 | function reducer(state = intialState, event) { 41 | return state.map((control) => { 42 | if (control.id === event.target.name) { 43 | return { 44 | ...control, 45 | isChecked: event.target.checked, 46 | }; 47 | } 48 | return { 49 | ...control, 50 | }; 51 | }); 52 | } 53 | 54 | function Example() { 55 | const [state, dispatch] = useReducer(reducer, intialState); 56 | const freedrawRef = useRef(null); 57 | 58 | const handleMarkersDraw = useCallback( 59 | (event) => 60 | console.log( 61 | 'markers drawn - latLngs', 62 | event.latLngs, 63 | 'Polygons:', 64 | freedrawRef.current.size() 65 | ), 66 | [] 67 | ); 68 | const handleModeChange = useCallback( 69 | (event) => console.log('mode changed', event), 70 | [] 71 | ); 72 | 73 | const handlers = useMemo( 74 | () => ({ 75 | markers: handleMarkersDraw, 76 | mode: handleModeChange, 77 | }), 78 | [handleMarkersDraw, handleModeChange] 79 | ); 80 | 81 | const handleEscapeKey = useCallback((event) => { 82 | // Cancel the current FreeDraw action when the escape key is pressed. 83 | if (event.key === 'Escape') { 84 | freedrawRef.current.cancel(); 85 | } 86 | }, []); 87 | 88 | useEffect(() => { 89 | window.addEventListener('keydown', handleEscapeKey); 90 | return () => window.removeEventListener('keydown', handleEscapeKey); 91 | }, [handleEscapeKey]); 92 | 93 | const mode = state.reduce((result, current) => { 94 | if (current.isChecked) { 95 | return result | current.mode; 96 | } else { 97 | return result ^ current.mode; 98 | } 99 | }, ALL); 100 | 101 | return ( 102 |
103 | 109 | 113 | 114 | 115 |
116 | 117 |
118 |
119 | ); 120 | } 121 | 122 | render(, document.getElementById('app')); 123 | -------------------------------------------------------------------------------- /Freedraw.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 4 | 5 | Object.defineProperty(exports, "__esModule", { 6 | value: true 7 | }); 8 | var _exportNames = {}; 9 | exports["default"] = void 0; 10 | 11 | var _leafletFreedraw = _interopRequireWildcard(require("leaflet-freedraw")); 12 | 13 | Object.keys(_leafletFreedraw).forEach(function (key) { 14 | if (key === "default" || key === "__esModule") return; 15 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 16 | if (key in exports && exports[key] === _leafletFreedraw[key]) return; 17 | Object.defineProperty(exports, key, { 18 | enumerable: true, 19 | get: function get() { 20 | return _leafletFreedraw[key]; 21 | } 22 | }); 23 | }); 24 | 25 | var _core = require("@react-leaflet/core"); 26 | 27 | function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } 28 | 29 | function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 30 | 31 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 32 | 33 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 34 | 35 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 36 | 37 | function createLeafletElement(props, context) { 38 | var instance = new _leafletFreedraw["default"](_objectSpread({}, props)); 39 | return { 40 | instance: instance, 41 | context: _objectSpread(_objectSpread({}, context), {}, { 42 | overlayContainer: instance 43 | }) 44 | }; 45 | } 46 | 47 | function updateLeafletElement(instance, props, prevProps) { 48 | if (props.mode !== prevProps.mode) { 49 | instance.mode(props.mode); 50 | } 51 | } 52 | 53 | var Freedraw = (0, _core.createLayerComponent)(createLeafletElement, updateLeafletElement); 54 | var _default = Freedraw; 55 | exports["default"] = _default; 56 | 57 | --------------------------------------------------------------------------------