diff --git a/.gitignore b/.gitignore index c423abf15..a4fbc0453 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,5 @@ build !app/frontend/.env !local-infrastructure/.env .history +realm-export.json +chefs_build \ No newline at end of file diff --git a/app/frontend/package-lock.json b/app/frontend/package-lock.json index c1730e50d..897cfee5a 100644 --- a/app/frontend/package-lock.json +++ b/app/frontend/package-lock.json @@ -22,6 +22,9 @@ "font-awesome": "^4.7.0", "formiojs": "^4.14.13", "keycloak-js": "^21.1.1", + "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", + "leaflet-geosearch": "^4.0.0", "lodash": "^4.17.21", "mitt": "^3.0.0", "moment": "^2.29.4", @@ -591,6 +594,12 @@ "vue": ">= 3.0.0 < 4" } }, + "node_modules/@googlemaps/js-api-loader": { + "version": "1.16.8", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz", + "integrity": "sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ==", + "optional": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -2862,9 +2871,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/functions-have-names": { "version": "1.2.3", @@ -3785,6 +3797,25 @@ "js-sha256": "^0.9.0" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" + }, + "node_modules/leaflet-draw": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/leaflet-draw/-/leaflet-draw-1.0.4.tgz", + "integrity": "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ==" + }, + "node_modules/leaflet-geosearch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/leaflet-geosearch/-/leaflet-geosearch-4.0.0.tgz", + "integrity": "sha512-a92VNY9gxyv3oyEDqIWoCNoBllajWRYejztzOSNmpLRtzpA6JtGgy/wwl9tsB8+6Eek1fe+L6+W0MDEOaidbXA==", + "optionalDependencies": { + "@googlemaps/js-api-loader": "^1.16.6", + "leaflet": "^1.6.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", diff --git a/app/frontend/package.json b/app/frontend/package.json index e405c8cc5..72078a176 100644 --- a/app/frontend/package.json +++ b/app/frontend/package.json @@ -43,6 +43,9 @@ "font-awesome": "^4.7.0", "formiojs": "^4.14.13", "keycloak-js": "^21.1.1", + "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", + "leaflet-geosearch": "^4.0.0", "lodash": "^4.17.21", "mitt": "^3.0.0", "moment": "^2.29.4", diff --git a/app/frontend/src/utils/constants.js b/app/frontend/src/utils/constants.js index 05bb2441f..9c7ee6511 100755 --- a/app/frontend/src/utils/constants.js +++ b/app/frontend/src/utils/constants.js @@ -323,6 +323,7 @@ export const FormDesignerBuilderOptions = Object.freeze({ orgbook: true, bcaddress: true, simplebcaddress: true, + map: true, }, }, }); diff --git a/components/package-lock.json b/components/package-lock.json index 5cea1c98e..1cf47ccc6 100644 --- a/components/package-lock.json +++ b/components/package-lock.json @@ -9,8 +9,14 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { + "@types/leaflet": "^1.9.12", + "@types/leaflet-draw": "^1.0.11", "autocompleter": "^7.0.1", + "css-loader": "^7.1.2", "formiojs": "^4.14.6", + "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", + "leaflet-geosearch": "^4.0.0", "lodash": "^4.17.21", "native-promise-only": "^0.8.1", "path-browserify": "^1.0.1", @@ -19,6 +25,7 @@ "devDependencies": { "@types/chai": "^4.3.1", "@types/ejs": "^3.1.1", + "@types/google.maps": "^3.58.1", "@types/mocha": "^9.1.1", "@types/node": "^16.11.8", "@types/sinon": "^10.0.12", @@ -736,6 +743,12 @@ "resolved": "https://registry.npmjs.org/@formio/vanilla-text-mask/-/vanilla-text-mask-5.1.1.tgz", "integrity": "sha512-7MhrbMypySPi7RLchg0ys7HnS3Wqddbq/btAijKB1nA94TE7AOOLhpZJWcNm3kOlX0Y3nHfoavj/HP7vsvF34Q==" }, + "node_modules/@googlemaps/js-api-loader": { + "version": "1.16.8", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.8.tgz", + "integrity": "sha512-CROqqwfKotdO6EBjZO/gQGVTbeDps5V7Mt9+8+5Q+jTg5CRMi3Ii/L9PmV3USROrt2uWxtGzJHORmByxyo9pSQ==", + "optional": true + }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -995,11 +1008,38 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/google.maps": { + "version": "3.58.1", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.58.1.tgz", + "integrity": "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/leaflet": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/leaflet-draw": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@types/leaflet-draw/-/leaflet-draw-1.0.11.tgz", + "integrity": "sha512-dyedtNm3aSmnpi6FM6VSl28cQuvP+MD7pgpXyO3Q1ZOCvrJKmzaDq0P3YZTnnBs61fQCKSnNYmbvCkDgFT9FHQ==", + "dependencies": { + "@types/leaflet": "*" + } + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -1954,6 +1994,62 @@ "custom-event": "^1.0.0" } }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -3148,6 +3244,17 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/idb": { "version": "6.1.5", "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", @@ -3915,6 +4022,25 @@ "node": ">=10.13.0" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" + }, + "node_modules/leaflet-draw": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/leaflet-draw/-/leaflet-draw-1.0.4.tgz", + "integrity": "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ==" + }, + "node_modules/leaflet-geosearch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/leaflet-geosearch/-/leaflet-geosearch-4.0.0.tgz", + "integrity": "sha512-a92VNY9gxyv3oyEDqIWoCNoBllajWRYejztzOSNmpLRtzpA6JtGgy/wwl9tsB8+6Eek1fe+L6+W0MDEOaidbXA==", + "optionalDependencies": { + "@googlemaps/js-api-loader": "^1.16.6", + "leaflet": "^1.6.0" + } + }, "node_modules/liftoff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.0.tgz", @@ -5118,10 +5244,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", - "dev": true, + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "funding": [ { "type": "opencollective", @@ -5145,11 +5270,82 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -5737,7 +5933,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6478,8 +6673,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "node_modules/uuid": { "version": "3.4.0", diff --git a/components/package.json b/components/package.json index 5098b5065..13ce65d7d 100644 --- a/components/package.json +++ b/components/package.json @@ -47,8 +47,14 @@ "components" ], "dependencies": { + "@types/leaflet": "^1.9.12", + "@types/leaflet-draw": "^1.0.11", "autocompleter": "^7.0.1", + "css-loader": "^7.1.2", "formiojs": "^4.14.6", + "leaflet": "^1.9.4", + "leaflet-draw": "^1.0.4", + "leaflet-geosearch": "^4.0.0", "lodash": "^4.17.21", "native-promise-only": "^0.8.1", "path-browserify": "^1.0.1", @@ -57,6 +63,7 @@ "devDependencies": { "@types/chai": "^4.3.1", "@types/ejs": "^3.1.1", + "@types/google.maps": "^3.58.1", "@types/mocha": "^9.1.1", "@types/node": "^16.11.8", "@types/sinon": "^10.0.12", diff --git a/components/src/components/Map/Common/Constants.d.ts b/components/src/components/Map/Common/Constants.d.ts new file mode 100644 index 000000000..0b0a10000 --- /dev/null +++ b/components/src/components/Map/Common/Constants.d.ts @@ -0,0 +1,4 @@ +export declare abstract class Constants { + static readonly DEFAULT_HELP_LINK: string; + static readonly ADV: string; +} diff --git a/components/src/components/Map/Common/Constants.js b/components/src/components/Map/Common/Constants.js new file mode 100644 index 000000000..0ca555292 --- /dev/null +++ b/components/src/components/Map/Common/Constants.js @@ -0,0 +1,4 @@ +export class Constants { + static DEFAULT_HELP_LINK = 'https://github.com/bcgov/common-hosted-form-service/wiki'; + static ADV = ''; +} diff --git a/components/src/components/Map/Common/marker-icon.png b/components/src/components/Map/Common/marker-icon.png new file mode 100644 index 000000000..950edf246 Binary files /dev/null and b/components/src/components/Map/Common/marker-icon.png differ diff --git a/components/src/components/Map/Component.form.ts b/components/src/components/Map/Component.form.ts new file mode 100644 index 000000000..6376c87af --- /dev/null +++ b/components/src/components/Map/Component.form.ts @@ -0,0 +1,46 @@ +import baseEditForm from 'formiojs/components/_classes/component/Component.form'; +import EditData from './editForm/Component.edit.data'; +import EditDisplay from './editForm/Component.edit.display'; +import AdvancedEditLogic from '../Common/Advanced.edit.logic'; +export default function (...extend) { + return baseEditForm( + [ + EditDisplay, + EditData, + { + key: 'display', + ignore: true, + }, + { + key: 'data', + ignore: true, + }, + { + key: 'validation', + ignore: true, + }, + { + key: 'api', + components: [ + { + key: 'tags', + ignore: true, + }, + { + key: 'properties', + ignore: true, + }, + ], + }, + { + key: 'logic', + components: AdvancedEditLogic, + }, + { + key: 'layout', + ignore: true, + }, + ], + ...extend + ); +} diff --git a/components/src/components/Map/Component.ts b/components/src/components/Map/Component.ts new file mode 100644 index 000000000..e66474baf --- /dev/null +++ b/components/src/components/Map/Component.ts @@ -0,0 +1,182 @@ +import { Components } from 'formiojs'; +const FieldComponent = (Components as any).components.field; +import MapService from './services/MapService'; +import baseEditForm from './Component.form'; +import * as L from 'leaflet'; + +const DEFAULT_CENTER: [number, number] = [ + 53.96717190097409, -123.98320425388914, +]; // Ensure CENTER is a tuple with exactly two elements +const DEFAULT_CONTAINER_HEIGHT = '400px'; + +export default class Component extends (FieldComponent as any) { + static schema(...extend) { + return FieldComponent.schema({ + type: 'map', + label: 'Map', + key: 'map', + input: true, + defaultvalue: { features: [] }, + ...extend, + }); + } + + static get builderInfo() { + return { + title: 'Map', + group: 'basic', + icon: 'map', + weight: 70, + schema: Component.schema(), + }; + } + + static editForm = baseEditForm; + + componentID: string; + mapService: MapService; + + constructor(component, options, data) { + super(component, options, data); + this.componentID = super.elementInfo().component.id; + } + + render() { + return super.render( + `
Only ${options.numPoints} features per submission
` + ) + .addTo(map); + } else { + drawnItems.addLayer(layer); + } + this.bindPopupToLayer(layer); + options.onDrawnItemsChange(drawnItems.getLayers()); + }); + map.on(L.Draw.Event.DELETED, (e) => { + options.onDrawnItemsChange(drawnItems.getLayers()); + }); + map.on(L.Draw.Event.EDITSTOP, (e) => { + options.onDrawnItemsChange(drawnItems.getLayers()); + }); + map.on('resize', () => { + map.invalidateSize(); + }); + } + } + + initializeMap(options: MapServiceOptions) { + const { + mapContainer, + center, + drawOptions, + form, + defaultZoom, + readOnlyMap, + viewMode, + myLocation, + bcGeocoder, + } = options; + + if (drawOptions.rectangle) { + drawOptions.rectangle.showArea = false; + } + // Check to see if there is the formio read only class in the current page, and set notEditable to true if the map is inside a read-only page + + // if the user chooses it to be read-only, and the + const map = L.map(mapContainer, { + zoomAnimation: viewMode, + }).setView(center, defaultZoom || DEFAULT_MAP_ZOOM); + L.tileLayer(DEFAULT_MAP_LAYER_URL, { + attribution: DEFAULT_LAYER_ATTRIBUTION, + }).addTo(map); + + // Initialize Draw Layer + const drawnItems = new L.FeatureGroup(); + + map.addLayer(drawnItems); + + if (myLocation) { + const myLocationButton = L.Control.extend({ + options: { + position: 'bottomright', + }, + onAdd(map) { + const container = L.DomUtil.create( + 'div', + 'leaflet-bar leaflet-control' + ); + const button = L.DomUtil.create( + 'a', + 'leaflet-control-button', + container + ); + button.innerHTML = ''; + L.DomEvent.disableClickPropagation(button); + L.DomEvent.on(button, 'click', () => { + if ('geolocation' in navigator) { + navigator.geolocation.getCurrentPosition((position) => { + map.setView( + [position.coords.latitude, position.coords.longitude], + 14 + ); + L.popup() + .setLatLng([ + position.coords.latitude, + position.coords.longitude, + ]) + .setContent( + `(${position.coords.latitude}, ${position.coords.longitude})` + ) + .openOn(map); + }); + } + }); + container.title = 'Click to center the map on your location'; + return container; + }, + }); + const myLocationControl = new myLocationButton(); + myLocationControl.addTo(map); + } + + if (bcGeocoder) { + const geocoderControl = new (GeoSearch.GeoSearchControl as any)({ + provider: new BCGeocoderProvider(), + style: 'bar', + position: 'bottomleft', + showMarker: false, + }); + map.addControl(geocoderControl); + map.on('geosearch/showlocation', (e) => { + L.popup() + .setLatLng([(e as any).location.y, (e as any).location.x]) + .setContent(`${(e as any).location.label}`) + .openOn(map); + }); + } + + // Add Drawing Controllers + if (!readOnlyMap) { + if (!viewMode) { + const drawControl = new L.Control.Draw({ + draw: drawOptions, + edit: { + featureGroup: drawnItems, + }, + }); + map.addControl(drawControl); + } + } + + // Checking to see if the map should be interactable + const componentEditNode = + document.getElementsByClassName(COMPONENT_EDIT_CLASS); + if (form) { + if (form[0]?.classList.contains('formbuilder')) { + map.invalidateSize(); + map.dragging.disable(); + map.scrollWheelZoom.disable(); + if (this.hasChildNode(componentEditNode[0], mapContainer)) { + map.dragging.enable(); + map.scrollWheelZoom.enable(); + } + } + } + return { map, drawnItems }; + } + bindPopupToLayer(layer) { + if (layer instanceof L.Marker) { + layer + .bindPopup( + `(${layer.getLatLng().lat.toFixed(DECIMALS_LATLNG)},${layer + .getLatLng() + .lng.toFixed(DECIMALS_LATLNG)})
` + ) + .openPopup(); + } else if (layer instanceof L.Circle) { + layer + .bindPopup( + `(${layer.getLatLng().lat.toFixed(DECIMALS_LATLNG)},${layer + .getLatLng() + .lng.toFixed(DECIMALS_LATLNG)})
` + ) + .openPopup(); + } else if (layer instanceof L.Rectangle || layer instanceof L.Polygon) { + const bounds = layer.getBounds(); + const center = bounds.getCenter(); + layer + .bindPopup( + `(${center.lat.toFixed(DECIMALS_LATLNG)},${center.lng.toFixed( + DECIMALS_LATLNG + )})
` + ) + .openPopup(); + } + } + + loadDrawnItems(items) { + const { drawnItems } = this; + if (!drawnItems) { + console.error('drawnItems is undefined'); + return; + } + drawnItems.clearLayers(); + if (!Array.isArray(items)) { + items = [items]; + } + items.forEach((item) => { + let layer; + if (item.type === 'marker') { + layer = L.marker(item.coordinates); + } else if (item.type === 'rectangle') { + layer = L.rectangle(item.bounds); + } else if (item.type === 'circle') { + layer = L.circle(item.coordinates, { radius: item.radius }); + } else if (item.type === 'polygon') { + layer = L.polygon(item.coordinates); + } else if (item.type === 'polyline') { + layer = L.polyline(item.coordinates); + } + if (layer) { + drawnItems.addLayer(layer); + this.bindPopupToLayer(layer); + } + }); + } + + hasChildNode(parent, targetNode) { + if (parent === targetNode) { + return true; + } + for (let i = 0; i < parent?.childNodes?.length; i++) { + if (this.hasChildNode(parent.childNodes[i], targetNode)) { + return true; + } + } + return false; + } +} +export default MapService; diff --git a/components/src/components/index.ts b/components/src/components/index.ts index 8361f59d1..ae1f2cf05 100755 --- a/components/src/components/index.ts +++ b/components/src/components/index.ts @@ -45,6 +45,7 @@ import simplesignatureadvanced from './SimpleSignatureAdvanced/Component'; import simplebuttonadvanced from './SimpleButtonAdvanced/Component'; import bcaddress from './BCAddress/Component'; import simplebcaddress from './SimpleBCAddress/Component'; +import map from './Map/Component'; export default { orgbook, @@ -93,5 +94,6 @@ export default { simplesignatureadvanced, simplebuttonadvanced, bcaddress, - simplebcaddress + simplebcaddress, + map, }; diff --git a/components/webpack.config.js b/components/webpack.config.js index c82fc573d..446560860 100755 --- a/components/webpack.config.js +++ b/components/webpack.config.js @@ -1,6 +1,14 @@ const path = require('path'); module.exports = { + module: { + rules: [ + { + test: /\.css$/i, + use: ["css-loader"], + }, + ], + }, entry: path.join(path.resolve(__dirname, 'lib'), 'index.js'), output: { library: 'BcGovFormioComponents',