diff --git a/examples/class/edit-control.js b/examples/class/edit-control.js deleted file mode 100644 index c89a84e..0000000 --- a/examples/class/edit-control.js +++ /dev/null @@ -1,252 +0,0 @@ -import React, { Component, createRef } from 'react'; -import { MapContainer, TileLayer, Circle, FeatureGroup } from 'react-leaflet'; -import L from 'leaflet'; -import { EditControl } from '../../src'; - -// work around broken icons when using webpack, see https://github.com/PaulLeCam/react-leaflet/issues/255 - -delete L.Icon.Default.prototype._getIconUrl; -L.Icon.Default.mergeOptions({ - iconRetinaUrl: - 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', - iconUrl: - 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', - shadowUrl: - 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-shadow.png', -}); - -// - -let polyline; - -export default class EditControlExample extends Component { - featureGroupRef = createRef(); - // see http://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html#l-draw-event for leaflet-draw events doc - - _onEdited = (e) => { - let numEdited = 0; - e.layers.eachLayer((layer) => { - numEdited += 1; - }); - console.log(`_onEdited: edited ${numEdited} layers`, e); - - this._onChange(); - }; - - _onCreated = (e) => { - let type = e.layerType; - let layer = e.layer; - if (type === 'marker') { - // Do marker specific actions - console.log('_onCreated: marker created', e); - } else { - console.log('_onCreated: something else created:', type, e); - } - // Do whatever else you need to. (save to db; etc) - - this._onChange(); - }; - - _onDeleted = (e) => { - let numDeleted = 0; - e.layers.eachLayer((layer) => { - numDeleted += 1; - }); - console.log(`onDeleted: removed ${numDeleted} layers`, e); - - this._onChange(); - }; - - _onMounted = (drawControl) => { - console.log('_onMounted', drawControl); - }; - - _onEditStart = (e) => { - console.log('_onEditStart', e); - }; - - _onEditStop = (e) => { - console.log('_onEditStop', e); - }; - - _onDeleteStart = (e) => { - console.log('_onDeleteStart', e); - }; - - _onDeleteStop = (e) => { - console.log('_onDeleteStop', e); - }; - - render() { - const fgInstance = this.featureGroupRef.current; - return ( - - - - {fgInstance && fgInstance._leaflet_id && ( - - )} - - - ); - } - - _editableFG = null; - - _onFeatureGroupReady = (reactFGref) => { - // populate the leaflet FeatureGroup with the geoJson layers - - let leafletGeoJSON = new L.GeoJSON(getGeoJson()); - let leafletFG = reactFGref; - - leafletGeoJSON.eachLayer((layer) => { - leafletFG.addLayer(layer); - }); - - // store the ref for future access to content - - this._editableFG = reactFGref; - }; - - _onChange = () => { - // this._editableFG contains the edited geometry, which can be manipulated through the leaflet API - - const { onChange } = this.props; - - if (!this._editableFG || !onChange) { - return; - } - console.log(this._editableFG); - const geojsonData = this._editableFG.toGeoJSON(); - onChange(geojsonData); - }; -} - -// data taken from the example in https://github.com/PaulLeCam/react-leaflet/issues/176 - -function getGeoJson() { - return { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - properties: {}, - geometry: { - type: 'LineString', - coordinates: [ - [-122.47979164123535, 37.830124319877235], - [-122.47721672058105, 37.809377088502615], - ], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [-122.46923446655273, 37.80293476836673], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [-122.48399734497069, 37.83466623607849], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Point', - coordinates: [-122.47867584228514, 37.81893781173967], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-122.48069286346434, 37.800637436707525], - [-122.48069286346434, 37.803104310307276], - [-122.47950196266174, 37.803104310307276], - [-122.47950196266174, 37.800637436707525], - [-122.48069286346434, 37.800637436707525], - ], - ], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-122.48103886842728, 37.833075326166274], - [-122.48065531253813, 37.832558431940114], - [-122.4799284338951, 37.8322660885204], - [-122.47963070869446, 37.83231693093747], - [-122.47948586940764, 37.832467339549524], - [-122.47945636510849, 37.83273426112019], - [-122.47959315776825, 37.83289737938241], - [-122.48004108667372, 37.833109220743104], - [-122.48058557510376, 37.83328293020496], - [-122.48080283403395, 37.83332529830436], - [-122.48091548681259, 37.83322785163939], - [-122.48103886842728, 37.833075326166274], - ], - ], - }, - }, - { - type: 'Feature', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [ - [ - [-122.48043537139893, 37.82564992009924], - [-122.48129367828368, 37.82629397920697], - [-122.48240947723389, 37.82544653184479], - [-122.48373985290527, 37.82632787689904], - [-122.48425483703613, 37.82680244295304], - [-122.48605728149415, 37.82639567223645], - [-122.4898338317871, 37.82663295542695], - [-122.4930953979492, 37.82415839321614], - [-122.49700069427489, 37.821887146654376], - [-122.4991464614868, 37.82171764783966], - [-122.49850273132326, 37.81798857543524], - [-122.50923156738281, 37.82090404811055], - [-122.51232147216798, 37.823344820392535], - [-122.50150680541992, 37.8271414168374], - [-122.48743057250977, 37.83093781796035], - [-122.48313903808594, 37.82822612280363], - [-122.48043537139893, 37.82564992009924], - ], - ], - }, - }, - ], - }; -} diff --git a/examples/class/edit-control.tsx b/examples/class/edit-control.tsx new file mode 100644 index 0000000..b33af1e --- /dev/null +++ b/examples/class/edit-control.tsx @@ -0,0 +1,112 @@ +import React, { Component, createRef } from 'react'; +import { MapContainer, TileLayer, FeatureGroup } from 'react-leaflet'; +import * as L from 'leaflet'; +import { EditControl } from '../../src'; + +delete (L.Icon.Default.prototype as any)._getIconUrl; +L.Icon.Default.mergeOptions({ + iconRetinaUrl: + 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', + iconUrl: + 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', + shadowUrl: + 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-shadow.png', +}); + +export default class EditControlExample extends Component<{ onChange?: (g: any) => void }> { + featureGroupRef = createRef(); + + _onEdited = (e: L.DrawEvents.Edited) => { + let numEdited = 0; + e.layers.eachLayer(() => { + numEdited += 1; + }); + // eslint-disable-next-line no-console + console.log(`_onEdited: edited ${numEdited} layers`, e); + this._onChange(); + }; + + _onCreated = (e: L.DrawEvents.Created) => { + const type = e.layerType; + // eslint-disable-next-line no-console + console.log('_onCreated:', type, e); + this._onChange(); + }; + + _onDeleted = (e: L.DrawEvents.Deleted) => { + let numDeleted = 0; + e.layers.eachLayer(() => { + numDeleted += 1; + }); + // eslint-disable-next-line no-console + console.log(`onDeleted: removed ${numDeleted} layers`, e); + this._onChange(); + }; + + _onMounted = (drawControl: L.Control.Draw) => { + // eslint-disable-next-line no-console + console.log('_onMounted', drawControl); + }; + + _onEditStart = (e: L.DrawEvents.EditStart) => { + // eslint-disable-next-line no-console + console.log('_onEditStart', e); + }; + + _onEditStop = (e: L.DrawEvents.EditStop) => { + // eslint-disable-next-line no-console + console.log('_onEditStop', e); + }; + + _onDeleteStart = (e: L.DrawEvents.DeleteStart) => { + // eslint-disable-next-line no-console + console.log('_onDeleteStart', e); + }; + + _onDeleteStop = (e: L.DrawEvents.DeleteStop) => { + // eslint-disable-next-line no-console + console.log('_onDeleteStop', e); + }; + + render() { + const fgInstance = this.featureGroupRef.current as unknown as L.FeatureGroup | undefined; + return ( + + + + {fgInstance && (fgInstance as any)._leaflet_id && ( + + )} + + + ); + } + + _editableFG: L.FeatureGroup | null = null; + + _onChange = () => { + const { onChange } = this.props; + if (!this._editableFG || !onChange) return; + const geojsonData = (this._editableFG as any).toGeoJSON(); + onChange(geojsonData); + }; +} + + diff --git a/examples/class/index.js b/examples/class/index.tsx similarity index 65% rename from examples/class/index.js rename to examples/class/index.tsx index 1a927e4..357cf7a 100644 --- a/examples/class/index.js +++ b/examples/class/index.tsx @@ -9,10 +9,11 @@ const example = ( ); -function onChange(geojson) { +function onChange(geojson: unknown) { + // eslint-disable-next-line no-console console.log('geojson changed', geojson); } -createRoot(document.getElementById('app')).render( - example -); +createRoot(document.getElementById('app') as HTMLElement).render(example); + + diff --git a/examples/class/tsconfig.json b/examples/class/tsconfig.json new file mode 100644 index 0000000..fa677a4 --- /dev/null +++ b/examples/class/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "node", + "target": "ES2020", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true + }, + "include": ["./**/*"], + "exclude": ["dist", "node_modules"] +} + + diff --git a/examples/class/webpack.config.js b/examples/class/webpack.config.js index ba285ac..1691fe1 100644 --- a/examples/class/webpack.config.js +++ b/examples/class/webpack.config.js @@ -1,19 +1,32 @@ /* eslint-disable */ const webpack = require("webpack"); const LodashModuleReplacementPlugin = require("lodash-webpack-plugin"); +const path = require("path"); module.exports = { mode: "development", devtool: "source-map", entry: { - app: __dirname + "/index.js", + app: __dirname + "/index.tsx", + }, + resolve: { + extensions: [".ts", ".tsx", ".js"], + alias: { + // Use a local tsconfig that doesn't constrain rootDir to src + // This avoids TS picking root tsconfig for the example app + } }, module: { rules: [ { - test: /\.js$/, + test: /\.(ts|tsx)$/, exclude: /node_modules/, - loader: "babel-loader", + use: { + loader: "ts-loader", + options: { + configFile: path.resolve(__dirname, "tsconfig.json"), + }, + }, }, ], }, diff --git a/package-lock.json b/package-lock.json index cce343a..bf92be0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-leaflet-draw-next", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-leaflet-draw-next", - "version": "1.0.2", + "version": "1.0.3", "license": "ISC", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -25,6 +25,8 @@ "@types/leaflet-draw": "^1.0.11", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "babel-loader": "^8.1.0", "eslint": "^8.17.0", "eslint-plugin-react": "^7.21.5", @@ -35,6 +37,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-leaflet": "^5.0.0", + "ts-loader": "^9.0.0", "typescript": "^5.9.2", "vite": "^6.3.5", "webpack": "^5.99.9", @@ -2586,6 +2589,48 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -3311,6 +3356,13 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/serve-index": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", @@ -3348,6 +3400,282 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -5738,6 +6066,13 @@ "dev": true, "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -5953,10 +6288,11 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -8632,6 +8968,139 @@ "node": ">=0.6" } }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 25a7bfd..6444b87 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 0", "build:dist": "npm run build:umd && npm run build:umd:min && npm run build:esm", - "build:umd": "webpack ./src/index.js --output-filename react-leaflet-draw-next.js", - "build:umd:min": "webpack ./src/index.js --output-filename react-leaflet-draw-next.min.js", - "build:esm": "babel src --out-dir dist/esm", + "typecheck": "tsc --noEmit", + "build:umd": "webpack ./src/index.ts --output-filename react-leaflet-draw-next.js", + "build:umd:min": "webpack ./src/index.ts --output-filename react-leaflet-draw-next.min.js", + "build:esm": "tsc -p tsconfig.json", "build": " npm run build:dist", "prepublish": "npm run build", "version": "npm run build", @@ -52,7 +53,7 @@ "url": "https://github.com/itseasy21/react-leaflet-draw-next/issues" }, "homepage": "https://github.com/itseasy21/react-leaflet-draw-next#readme", - "types": "src/index.d.ts", + "types": "dist/esm/index.d.ts", "peerDependencies": { "leaflet": "^1.9.4", "leaflet-draw": "^1.0.4", @@ -73,6 +74,8 @@ "@types/leaflet-draw": "^1.0.11", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "babel-loader": "^8.1.0", "eslint": "^8.17.0", "eslint-plugin-react": "^7.21.5", @@ -83,6 +86,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-leaflet": "^5.0.0", + "ts-loader": "^9.0.0", "typescript": "^5.9.2", "vite": "^6.3.5", "webpack": "^5.99.9", diff --git a/src/EditControl.js b/src/EditControl.js deleted file mode 100644 index 8f601fc..0000000 --- a/src/EditControl.js +++ /dev/null @@ -1,157 +0,0 @@ -import { PropTypes } from 'prop-types'; -import 'leaflet-draw'; // eslint-disable-line -import isEqual from 'fast-deep-equal'; -import React, { useRef } from 'react'; -import { useMap } from 'react-leaflet'; - -import leaflet, { Map, Control } from 'leaflet'; - -const eventHandlers = { - onEdited: 'draw:edited', - onDrawStart: 'draw:drawstart', - onDrawStop: 'draw:drawstop', - onDrawVertex: 'draw:drawvertex', - onEditStart: 'draw:editstart', - onEditMove: 'draw:editmove', - onEditResize: 'draw:editresize', - onEditVertex: 'draw:editvertex', - onEditStop: 'draw:editstop', - onDeleted: 'draw:deleted', - onDeleteStart: 'draw:deletestart', - onDeleteStop: 'draw:deletestop', -}; - -function EditControl(props) { - const map = useMap(); - const drawRef = useRef(); - const propsRef = useRef(props); - - function onDrawCreate(e) { - const { onCreated, featureGroup } = props; - if (featureGroup) { - featureGroup.addLayer(e.layer); - } - onCreated && onCreated(e); - } - - React.useEffect(() => { - const { onMounted, featureGroup } = props; - - for (const key in eventHandlers) { - if (Object.prototype.hasOwnProperty.call(eventHandlers, key)) { - map.on(eventHandlers[key], (evt) => { - let handlers = Object.keys(eventHandlers).filter( - (handler) => eventHandlers[handler] === evt.type - ); - if (handlers.length === 1) { - let handler = handlers[0]; - props[handler] && props[handler](evt); - } - }); - } - } - map.on(leaflet.Draw.Event.CREATED, onDrawCreate); - drawRef.current = createDrawElement(props, featureGroup); - map.addControl(drawRef.current); - onMounted && onMounted(drawRef.current); - - return () => { - map.off(leaflet.Draw.Event.CREATED, onDrawCreate); - - for (const key in eventHandlers) { - if (Object.prototype.hasOwnProperty.call(eventHandlers, key) && props[key]) { - map.off(eventHandlers[key], props[key]); - } - } - - if (drawRef.current) { - drawRef.current.remove(map); - } - }; - }, [props.onCreated, props.onDeleted, props.onEdited, props.featureGroup]); - - React.useEffect(() => { - if ( - isEqual(props.draw, propsRef.current.draw) && - isEqual(props.edit, propsRef.current.edit) && - props.position === propsRef.current.position && - props.featureGroup === propsRef.current.featureGroup - ) { - return undefined; - } - - drawRef.current.remove(map); - drawRef.current = createDrawElement(props, props.featureGroup); - drawRef.current.addTo(map); - - const { onMounted } = props; - onMounted && onMounted(drawRef.current); - - return () => { - if (drawRef.current) { - drawRef.current.remove(map); - } - }; - }, [ - props.draw, - props.edit, - props.position, - props.featureGroup, - props.onCreated, - props.onDeleted, - props.onEdited - ]); - - return null; -} - -function createDrawElement(props, featureGroup) { - const { draw, edit, position } = props; - const options = { - edit: { - ...edit, - featureGroup: featureGroup, - }, - }; - - if (draw) { - options.draw = { ...draw }; - } - - if (position) { - options.position = position; - } - - return new Control.Draw(options); -} - -EditControl.propTypes = { - ...Object.keys(eventHandlers).reduce((acc, val) => { - acc[val] = PropTypes.func; - return acc; - }, {}), - onCreated: PropTypes.func, - onMounted: PropTypes.func, - draw: PropTypes.shape({ - polyline: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - polygon: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - rectangle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - circle: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - marker: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - }), - edit: PropTypes.shape({ - edit: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - remove: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - poly: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]), - allowIntersection: PropTypes.bool, - }), - position: PropTypes.oneOf([ - 'topright', - 'topleft', - 'bottomright', - 'bottomleft', - ]), - featureGroup: PropTypes.object, // L.FeatureGroup instance -}; - -export default EditControl; diff --git a/src/EditControl.tsx b/src/EditControl.tsx new file mode 100644 index 0000000..77959dd --- /dev/null +++ b/src/EditControl.tsx @@ -0,0 +1,138 @@ +import 'leaflet-draw'; +import isEqual from 'fast-deep-equal'; +import React from 'react'; +import { useMap } from 'react-leaflet'; +import * as L from 'leaflet'; +import type { EditControlProps } from './types'; + +const eventHandlers = { + onEdited: L.Draw.Event.EDITED, + onDrawStart: L.Draw.Event.DRAWSTART, + onDrawStop: L.Draw.Event.DRAWSTOP, + onDrawVertex: L.Draw.Event.DRAWVERTEX, + onEditStart: L.Draw.Event.EDITSTART, + onEditMove: L.Draw.Event.EDITMOVE, + onEditResize: L.Draw.Event.EDITRESIZE, + onEditVertex: L.Draw.Event.EDITVERTEX, + onEditStop: L.Draw.Event.EDITSTOP, + onDeleted: L.Draw.Event.DELETED, + onDeleteStart: L.Draw.Event.DELETESTART, + onDeleteStop: L.Draw.Event.DELETESTOP, +} as const; + +function createDrawElement( + props: EditControlProps, + featureGroup?: L.FeatureGroup +): L.Control.Draw { + const { draw, edit, position } = props; + const options: L.Control.DrawConstructorOptions = { + edit: { + ...(edit ?? {}), + featureGroup: featureGroup as unknown as L.FeatureGroup, + }, + }; + + if (draw) { + // Cast is safe: keys align with L.Control.DrawOptions + options.draw = { ...(draw as L.Control.DrawOptions) }; + } + + if (position) { + options.position = position; + } + + return new L.Control.Draw(options); +} + +function EditControl( + props: EditControlProps +) { + const map = useMap() as L.Map; + const drawRef = React.useRef(null); + const propsRef = React.useRef(props); + + const onDrawCreate = React.useCallback( + (evt: L.LeafletEvent) => { + const e = evt as L.DrawEvents.Created; + const { onCreated, featureGroup } = props; + if (featureGroup) { + featureGroup.addLayer(e.layer); + } + onCreated && (onCreated as unknown as (e: L.LeafletEvent) => void)(e); + }, + [props] + ); + + React.useEffect(() => { + const { onMounted, featureGroup } = props; + + (Object.keys(eventHandlers) as Array).forEach((key) => { + const eventType = eventHandlers[key]; + map.on(eventType, (evt: L.LeafletEvent) => { + const maybeHandler = props[key] as ((e: L.LeafletEvent) => void) | undefined; + if (maybeHandler) { + maybeHandler(evt); + } + }); + }); + + map.on(L.Draw.Event.CREATED, onDrawCreate); + drawRef.current = createDrawElement(props, featureGroup); + map.addControl(drawRef.current); + onMounted && onMounted(drawRef.current); + + return () => { + map.off(L.Draw.Event.CREATED, onDrawCreate); + + (Object.keys(eventHandlers) as Array).forEach((key) => { + const eventType = eventHandlers[key]; + map.off(eventType); + }); + + if (drawRef.current) { + drawRef.current.remove(); + } + }; + }, [map, onDrawCreate, props]); + + React.useEffect(() => { + if ( + isEqual(props.draw, propsRef.current.draw) && + isEqual(props.edit, propsRef.current.edit) && + props.position === propsRef.current.position && + props.featureGroup === propsRef.current.featureGroup + ) { + return undefined; + } + + if (drawRef.current) { + drawRef.current.remove(); + } + drawRef.current = createDrawElement(props, props.featureGroup); + drawRef.current.addTo(map); + + const { onMounted } = props; + onMounted && onMounted(drawRef.current); + + return () => { + if (drawRef.current) { + drawRef.current.remove(); + } + }; + }, [ + map, + props.draw, + props.edit, + props.position, + props.featureGroup, + props.onCreated, + props.onDeleted, + props.onEdited, + ]); + + return null; +} + +export default EditControl; + + diff --git a/src/index.d.ts b/src/index.d.ts deleted file mode 100644 index d3d2dff..0000000 --- a/src/index.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react'; -import 'leaflet-draw'; -import type { - DrawOptions, - Control, - ControlPosition, - DrawEvents, -} from 'leaflet'; -import * as L from 'leaflet'; - -/** - * Props interface for the EditControl component - */ -interface EditControlProps { - /** Callback fired when features are edited */ - onEdited?: (v: DrawEvents.Edited) => void; - /** Callback fired when drawing starts */ - onDrawStart?: (v: DrawEvents.DrawStart) => void; - /** Callback fired when drawing stops */ - onDrawStop?: (v: DrawEvents.DrawStop) => void; - /** Callback fired when a vertex is drawn */ - onDrawVertex?: (v: DrawEvents.DrawVertex) => void; - /** Callback fired when editing starts */ - onEditStart?: (v: DrawEvents.EditStart) => void; - /** Callback fired when features are moved during editing */ - onEditMove?: (v: DrawEvents.EditMove) => void; - /** Callback fired when features are resized during editing */ - onEditResize?: (v: DrawEvents.EditResize) => void; - /** Callback fired when a vertex is edited */ - onEditVertex?: (v: DrawEvents.EditVertex) => void; - /** Callback fired when editing stops */ - onEditStop?: (v: DrawEvents.EditStop) => void; - /** Callback fired when features are deleted */ - onDeleted?: (v: DrawEvents.Deleted) => void; - /** Callback fired when deletion starts */ - onDeleteStart?: (v: DrawEvents.DeleteStart) => void; - /** Callback fired when deletion stops */ - onDeleteStop?: (v: DrawEvents.DeleteStop) => void; - /** Callback fired when new features are created */ - onCreated?: (v: DrawEvents.Created) => void; - /** Callback fired when the control is mounted */ - onMounted?: Function; - /** Edit options for the control */ - edit?: Control.EditOptions; - /** Draw options for different shape types */ - draw: { - /** Polyline drawing options */ - polyline?: DrawOptions.PolylineOptions | boolean; - /** Polygon drawing options */ - polygon?: DrawOptions.PolygonOptions | boolean; - /** Rectangle drawing options */ - rectangle?: DrawOptions.RectangleOptions | boolean; - /** Circle drawing options */ - circle?: DrawOptions.CircleOptions | boolean; - /** Marker drawing options */ - marker?: DrawOptions.MarkerOptions | boolean; - /** Circle marker drawing options */ - circlemarker?: DrawOptions.CircleOptions | boolean; - }; - /** Position of the control on the map */ - position: ControlPosition; - /** Feature group to manage drawn features */ - featureGroup?: L.FeatureGroup; -} - -export class EditControl extends React.Component {} diff --git a/src/index.js b/src/index.ts similarity index 52% rename from src/index.js rename to src/index.ts index 3b476c5..be1978a 100644 --- a/src/index.js +++ b/src/index.ts @@ -1 +1,4 @@ export { default as EditControl } from './EditControl'; +export type { EditControlProps } from './types'; + + diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..026b188 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,46 @@ +import type * as L from 'leaflet'; + +export type DrawEventMap = { + onEdited?: (e: L.DrawEvents.Edited) => void; + onDrawStart?: (e: L.DrawEvents.DrawStart) => void; + onDrawStop?: (e: L.DrawEvents.DrawStop) => void; + onDrawVertex?: (e: L.DrawEvents.DrawVertex) => void; + onEditStart?: (e: L.DrawEvents.EditStart) => void; + onEditMove?: (e: L.DrawEvents.EditMove) => void; + onEditResize?: (e: L.DrawEvents.EditResize) => void; + onEditVertex?: (e: L.DrawEvents.EditVertex) => void; + onEditStop?: (e: L.DrawEvents.EditStop) => void; + onDeleted?: (e: L.DrawEvents.Deleted) => void; + onDeleteStart?: (e: L.DrawEvents.DeleteStart) => void; + onDeleteStop?: (e: L.DrawEvents.DeleteStop) => void; + onCreated?: (e: L.DrawEvents.Created & { layer: TLayer }) => void; +}; + +export type DrawOptions = { + polyline?: L.DrawOptions.PolylineOptions | boolean; + polygon?: L.DrawOptions.PolygonOptions | boolean; + rectangle?: L.DrawOptions.RectangleOptions | boolean; + circle?: L.DrawOptions.CircleOptions | boolean; + marker?: L.DrawOptions.MarkerOptions | boolean; + circlemarker?: L.DrawOptions.CircleMarkerOptions | boolean; +}; + +export interface EditControlProps< + TLayer extends L.Layer = L.Layer, + TProperties = unknown +> extends DrawEventMap { + onMounted?: (control: L.Control.Draw) => void; + edit?: L.Control.EditOptions; + draw?: DrawOptions; + position?: L.ControlPosition; + featureGroup?: L.FeatureGroup; +} + +export type EditControlEventHandlers = DrawEventMap< + TLayer, + TProperties +>; + +export type { L }; + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d042d9f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "jsx": "react-jsx", + "strict": true, + "noImplicitAny": true, + "noImplicitThis": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "dist/esm", + "types": [ + "leaflet", + "leaflet-draw", + "react" + ] + }, + "include": ["src"], + "exclude": ["dist", "node_modules", "examples"] +} + + diff --git a/webpack.config.js b/webpack.config.js index 18c54cc..d5eb66e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,9 @@ const LodashModuleReplacementPlugin = require('lodash-webpack-plugin'); module.exports = { mode: 'production', + resolve: { + extensions: ['.ts', '.tsx', '.js'] + }, output: { library: 'ReactLeaflet', libraryTarget: 'umd' @@ -43,9 +46,14 @@ module.exports = { ], module: { rules: [ + { + test: /\.(ts|tsx)$/, + use: 'ts-loader', + exclude: /node_modules/ + }, { test: /\.js$/, - use: "babel-loader" + use: 'babel-loader' } ] },