diff --git a/.eslintrc.js b/.eslintrc.js index 96b5f933..ccc691a2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,11 +3,11 @@ module.exports = { 'airbnb-base', 'plugin:import/errors', 'plugin:react/recommended', - 'plugin:jsx-a11y/recommended', + // 'plugin:jsx-a11y/recommended', 'plugin:import/recommended', 'prettier', ], - plugins: ['react', 'import', 'jsx-a11y'], + plugins: ['react', 'import', /* 'jsx-a11y' */ '@emotion'], parserOptions: { ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features sourceType: 'module', // Allows for the use of imports @@ -16,6 +16,9 @@ module.exports = { 'newline-before-return': 'error', eqeqeq: ['error', 'always'], 'prefer-template': 'error', + '@emotion/jsx-import': 'error', + 'react/no-unknown-property': ['error', { ignore: ['css'] }], + }, env: { browser: true, diff --git a/package-lock.json b/package-lock.json index 83863994..c95240e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,20 +11,26 @@ "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@turf/center": "^6.5.0", + "@turf/center-of-mass": "^6.5.0", + "@turf/helpers": "^6.5.0", "classnames": "^2.3.2", "core-js": "^3.31.0", "deepmerge": "^4.3.1", "expect.js": "^0.3.1", + "mapbox-gl": "^2.15.0", "mobx": "^6.9.0", "papaparse": "^5.4.1", "prettier": "^3.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-select": "^5.7.3" + "react-select": "^5.7.3", + "reactjs-popup": "^2.0.5" }, "devDependencies": { "@babel/preset-react": "^7.22.5", + "@emotion/eslint-plugin": "^11.11.0", "@types/d3": "^7.4.0", "eslint": "^8.44.0", "eslint-config-airbnb-base": "^15.0.0", @@ -1797,6 +1803,18 @@ "stylis": "4.2.0" } }, + "node_modules/@emotion/eslint-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/eslint-plugin/-/eslint-plugin-11.11.0.tgz", + "integrity": "sha512-jCOYqU/0Sqm+g+6D7QuIlG99q8YAF0T7BP98zQF/MPZKfbcm46z5mizXn0YlhZ9AYZfNtZ1DeODXdncYxZzR4Q==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": "6 || 7 || 8" + } + }, "node_modules/@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", @@ -2265,6 +2283,81 @@ "node": ">=8" } }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/geojson-rewind/node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", + "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2371,6 +2464,100 @@ } } }, + "node_modules/@turf/bbox": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", + "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.5.0.tgz", + "integrity": "sha512-T8KtMTfSATWcAX088rEDKjyvQCBkUsLnK/Txb6/8WUXIeOZyHu42G7MkdkHRoHtwieLdduDdmPLFyTdG5/e7ZQ==", + "dependencies": { + "@turf/bbox": "^6.5.0", + "@turf/helpers": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-6.5.0.tgz", + "integrity": "sha512-EWrriU6LraOfPN7m1jZi+1NLTKNkuIsGLZc2+Y8zbGruvUW+QV7K0nhf7iZWutlxHXTBqEXHbKue/o79IumAsQ==", + "dependencies": { + "@turf/centroid": "^6.5.0", + "@turf/convex": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz", + "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-6.5.0.tgz", + "integrity": "sha512-x7ZwC5z7PJB0SBwNh7JCeCNx7Iu+QSrH7fYgK0RhhNop13TqUlvHMirMLRgf2db1DqUetrAO2qHJeIuasquUWg==", + "dependencies": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0", + "concaveman": "*" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", + "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", + "dependencies": { + "@turf/helpers": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", + "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", + "dependencies": { + "@turf/helpers": "^6.5.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -5540,6 +5727,17 @@ "typedarray": "^0.0.6" } }, + "node_modules/concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "dependencies": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", @@ -6067,6 +6265,11 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -6801,6 +7004,11 @@ "object.defaults": "^1.1.0" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -9049,6 +9257,11 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -9137,6 +9350,11 @@ "assert-plus": "^1.0.0" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -9334,6 +9552,11 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -10287,7 +10510,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -11960,6 +12182,11 @@ "node": ">=8.0" } }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, "node_modules/kew": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", @@ -12784,6 +13011,35 @@ "node": ">=0.10.0" } }, + "node_modules/mapbox-gl": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz", + "integrity": "sha512-fjv+aYrd5TIHiL7wRa+W7KjtUqKWziJMZUkK5hm8TvJ3OLeNPx4NmW/DgfYhd/jHej8wWL+QJBDbdMMAKvNC0A==", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.1", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^8.0.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + } + }, "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -13765,6 +14021,11 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, "node_modules/mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", @@ -15086,6 +15347,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -15354,6 +15627,11 @@ "node": ">=0.10.0" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, "node_modules/portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -16052,6 +16330,11 @@ "node": ">=0.10.0" } }, + "node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -16143,6 +16426,11 @@ "react-is": "^16.13.1" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -16319,6 +16607,11 @@ } ] }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -16362,6 +16655,14 @@ "node": ">= 0.8" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -16445,6 +16746,18 @@ "react-dom": ">=16.6.0" } }, + "node_modules/reactjs-popup": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz", + "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -16964,6 +17277,14 @@ "node": ">= 0.10" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -17055,6 +17376,11 @@ "inherits": "^2.0.1" } }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -17102,6 +17428,11 @@ "integrity": "sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg==", "dev": true }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -18455,6 +18786,14 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "dependencies": { + "kdbush": "^4.0.2" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -18821,6 +19160,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "node_modules/tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "node_modules/tmatch": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-2.0.1.tgz", @@ -19666,6 +20010,16 @@ "node": ">=0.10.0" } }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", @@ -22361,6 +22715,13 @@ "stylis": "4.2.0" } }, + "@emotion/eslint-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/eslint-plugin/-/eslint-plugin-11.11.0.tgz", + "integrity": "sha512-jCOYqU/0Sqm+g+6D7QuIlG99q8YAF0T7BP98zQF/MPZKfbcm46z5mizXn0YlhZ9AYZfNtZ1DeODXdncYxZzR4Q==", + "dev": true, + "requires": {} + }, "@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", @@ -22718,6 +23079,65 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "requires": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + } + } + }, + "@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" + }, + "@mapbox/mapbox-gl-supported": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", + "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" + }, + "@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==" + }, + "@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "requires": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -22776,6 +23196,76 @@ "schema-utils": "^2.6.5" } }, + "@turf/bbox": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz", + "integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/center": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-6.5.0.tgz", + "integrity": "sha512-T8KtMTfSATWcAX088rEDKjyvQCBkUsLnK/Txb6/8WUXIeOZyHu42G7MkdkHRoHtwieLdduDdmPLFyTdG5/e7ZQ==", + "requires": { + "@turf/bbox": "^6.5.0", + "@turf/helpers": "^6.5.0" + } + }, + "@turf/center-of-mass": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-6.5.0.tgz", + "integrity": "sha512-EWrriU6LraOfPN7m1jZi+1NLTKNkuIsGLZc2+Y8zbGruvUW+QV7K0nhf7iZWutlxHXTBqEXHbKue/o79IumAsQ==", + "requires": { + "@turf/centroid": "^6.5.0", + "@turf/convex": "^6.5.0", + "@turf/helpers": "^6.5.0", + "@turf/invariant": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/centroid": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.5.0.tgz", + "integrity": "sha512-MwE1oq5E3isewPprEClbfU5pXljIK/GUOMbn22UM3IFPDJX0KeoyLNwghszkdmFp/qMGL/M13MMWvU+GNLXP/A==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0" + } + }, + "@turf/convex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-6.5.0.tgz", + "integrity": "sha512-x7ZwC5z7PJB0SBwNh7JCeCNx7Iu+QSrH7fYgK0RhhNop13TqUlvHMirMLRgf2db1DqUetrAO2qHJeIuasquUWg==", + "requires": { + "@turf/helpers": "^6.5.0", + "@turf/meta": "^6.5.0", + "concaveman": "*" + } + }, + "@turf/helpers": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", + "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==" + }, + "@turf/invariant": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", + "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", + "requires": { + "@turf/helpers": "^6.5.0" + } + }, + "@turf/meta": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz", + "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==", + "requires": { + "@turf/helpers": "^6.5.0" + } + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -25469,6 +25959,17 @@ "typedarray": "^0.0.6" } }, + "concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "requires": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, "confusing-browser-globals": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", @@ -25902,6 +26403,11 @@ "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", "dev": true }, + "csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -26511,6 +27017,11 @@ "object.defaults": "^1.1.0" } }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -28302,6 +28813,11 @@ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, + "geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -28371,6 +28887,11 @@ "assert-plus": "^1.0.0" } }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -28532,6 +29053,11 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -29285,8 +29811,7 @@ "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "iferr": { "version": "0.1.5", @@ -30559,6 +31084,11 @@ } } }, + "kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==" + }, "kew": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", @@ -31181,6 +31711,35 @@ "object-visit": "^1.0.0" } }, + "mapbox-gl": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-2.15.0.tgz", + "integrity": "sha512-fjv+aYrd5TIHiL7wRa+W7KjtUqKWziJMZUkK5hm8TvJ3OLeNPx4NmW/DgfYhd/jHej8wWL+QJBDbdMMAKvNC0A==", + "requires": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.1", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^8.0.0", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + } + }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -31974,6 +32533,11 @@ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "dev": true }, + "murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, "mute-stdout": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", @@ -33042,6 +33606,15 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -33251,6 +33824,11 @@ } } }, + "point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -33865,6 +34443,11 @@ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", "dev": true }, + "potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==" + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -33932,6 +34515,11 @@ "react-is": "^16.13.1" } }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -34073,6 +34661,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -34110,6 +34703,14 @@ "unpipe": "1.0.0" } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "requires": { + "quickselect": "^2.0.0" + } + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -34175,6 +34776,12 @@ "prop-types": "^15.6.2" } }, + "reactjs-popup": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz", + "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==", + "requires": {} + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -34594,6 +35201,14 @@ "value-or-function": "^3.0.0" } }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -34665,6 +35280,11 @@ "inherits": "^2.0.1" } }, + "robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -34695,6 +35315,11 @@ "integrity": "sha512-+GztYEPRpIsQoCSraWHDBs9WVy4eVME16zhOtDB4H9J4xN0XRhknnmLOl+4gRgZtu8dpp9N/utSPjKH/xmDzXg==", "dev": true }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -35849,6 +36474,14 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, + "supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "requires": { + "kdbush": "^4.0.2" + } + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -36135,6 +36768,11 @@ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", "dev": true }, + "tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "tmatch": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-2.0.1.tgz", @@ -36824,6 +37462,16 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", "dev": true }, + "vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "requires": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", diff --git a/package.json b/package.json index c1a1f965..f9a6b3ca 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "devDependencies": { "@babel/preset-react": "^7.22.5", + "@emotion/eslint-plugin": "^11.11.0", "@types/d3": "^7.4.0", "eslint": "^8.44.0", "eslint-config-airbnb-base": "^15.0.0", @@ -39,16 +40,21 @@ "dependencies": { "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@turf/center": "^6.5.0", + "@turf/center-of-mass": "^6.5.0", + "@turf/helpers": "^6.5.0", "classnames": "^2.3.2", "core-js": "^3.31.0", "deepmerge": "^4.3.1", "expect.js": "^0.3.1", + "mapbox-gl": "^2.15.0", "mobx": "^6.9.0", "papaparse": "^5.4.1", "prettier": "^3.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-select": "^5.7.3" + "react-select": "^5.7.3", + "reactjs-popup": "^2.0.5" } } diff --git a/src/components/BaseMap/BaseMap.js b/src/components/BaseMap/BaseMap.js new file mode 100644 index 00000000..4fc99046 --- /dev/null +++ b/src/components/BaseMap/BaseMap.js @@ -0,0 +1,117 @@ +/** @jsx jsx */ +import { css, Global, jsx } from '@emotion/react'; +import mapbox from 'mapbox-gl'; +import PropTypes from 'prop-types'; +import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react'; +import { BaseMapLayer } from './BaseMapLayer'; + +const defaultStyles = { + width: '100%', // spotlights default + position: 'absolute', + top: 0, + bottom: 0, + background: '#F3F3F3', // spotlights default +}; + +const BaseMap = (props) => { + mapbox.accessToken = props.accessToken; + const mapNode = useRef(null); + const [baseMap, setBaseMap] = useState(undefined); + + useEffect(() => { + if (mapNode && mapNode.current) { + const map = new mapbox.Map({ + container: mapNode.current, + ...props.options, + }); + + if (props.showNavigationControls) { + map.addControl(new mapbox.NavigationControl()); + } + + map.on('load', (event) => { + setBaseMap(map); + if (props.onLoad) { + props.onLoad(map, event); + } + }); + } + }, []); + + const renderLayers = () => + Children.map(props.children, (child) => + isValidElement(child) && child.type === BaseMapLayer ? cloneElement(child, { map: baseMap }) : null + ); + + return ( +
+ {renderLayers()} + +
+ ); +}; + +BaseMap.defaultProps = { + style: defaultStyles, + options: { + minZoom: 6, + zoom: 6.1, + }, + showNavigationControls: true, + background: 'inherit', +}; + +BaseMap.propTypes = { + accessToken: PropTypes.string, + style: PropTypes.object, + background: PropTypes.string, + showNavigationControls: PropTypes.bool, + onLoad: PropTypes.func, + options: PropTypes.object, + children: PropTypes.node, +}; + +// eslint-disable-next-line import/prefer-default-export +export { BaseMap }; diff --git a/src/components/BaseMap/BaseMapLayer.js b/src/components/BaseMap/BaseMapLayer.js new file mode 100644 index 00000000..cf02519e --- /dev/null +++ b/src/components/BaseMap/BaseMapLayer.js @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types'; + +const BaseMapLayer = ({ map, show, onAdd, ...options }) => { + if (map) { + if (map.getLayer(options.id)) { + map.removeLayer(options.id); + } + if (show) { + map.addLayer(options); + if (onAdd) { + onAdd(map, options.id); + } + } + } + + return null; +}; + +BaseMapLayer.defaultProps = { show: true }; +BaseMapLayer.propTypes = { + map: PropTypes.object, + show: PropTypes.bool, + onAdd: PropTypes.func, +}; + +// eslint-disable-next-line import/prefer-default-export +export { BaseMapLayer }; diff --git a/src/components/BaseMap/index.js b/src/components/BaseMap/index.js new file mode 100644 index 00000000..1eaf11af --- /dev/null +++ b/src/components/BaseMap/index.js @@ -0,0 +1,2 @@ +export * from './BaseMap'; +export * from './BaseMapLayer'; diff --git a/src/components/BaseMap/utils/index.js b/src/components/BaseMap/utils/index.js new file mode 100644 index 00000000..886675c3 --- /dev/null +++ b/src/components/BaseMap/utils/index.js @@ -0,0 +1,186 @@ +import centerOfMass from '@turf/center-of-mass'; +import { polygon } from '@turf/helpers'; +import { LngLat } from 'mapbox-gl'; + +const formatNumber = (value, decimals = 1) => { + if (value >= 1000000000000) { + return `${(value / 1000000000000).toFixed(decimals)}tn`; + } + if (value >= 1000000000) { + return `${(value / 1000000000).toFixed(decimals)}bn`; + } + if (value >= 1000000) { + return `${(value / 1000000).toFixed(decimals)}m`; + } + if (value >= 1000) { + return `${(value / 1000).toFixed(decimals)}k`; + } + if (value >= 0) { + return `${value.toFixed(decimals)}`; + } + if (value <= -1000000000000) { + return `${(value / 1000000000000).toFixed(decimals)}tn`; + } + if (value <= -1000000000) { + return `${(value / 1000000000).toFixed(decimals)}bn`; + } + if (value <= -1000000) { + return `${(value / 1000000).toFixed(decimals)}m`; + } + if (value <= 1000) { + return `${(value / 1000).toFixed(decimals)}k`; + } + + return `${value.toFixed(decimals)}`; +}; + +export const COLOURED_LAYER = 'highlight'; + +export const setZoomByContainerWidth = (map, container, options) => { + if (container.clientWidth < 700) { + map.setZoom(options.zoom ? options.zoom - 1 : 5); + } else if (container.clientWidth < 900) { + map.setZoom(options.minZoom ? options.minZoom + 0.8 : 5.8); + } else { + map.setZoom(options.zoom || 6.1); + } +}; + +export const getProperLocationName = (locationName, formatter) => (formatter ? formatter(locationName) : locationName); + +export const getLocationStyles = (data, range, colours, format) => { + if (data && range && colours) { + return data.map((location) => { + const locationID = getProperLocationName(location.name, format); + const matchingRange = range.find((rng) => location.value !== null && location.value <= parseFloat(rng)); + + if (matchingRange) { + return [locationID, colours[range.indexOf(matchingRange)]]; + } + if (location.value > parseFloat(range[range.length - 1])) { + return [locationID, colours[colours.length - 1]]; + } + + return [locationID, '#b3adad']; + }); + } + + return []; +}; + +const getTooltipValue = (options, location) => + location && typeof location.value === 'number' + ? `${options.dataPrefix || ''}${formatNumber( + location.value + )}${options.dataSuffix || ''}` + : 'No Data'; + +export const getLocationNameFromEvent = (event, nameProperty) => { + if (event.features && event.features[0].properties) { + const { geometry } = event.features[0]; + if (geometry.type === 'Polygon' || geometry.type === 'MultiPolygon') { + const locationName = event.features[0].properties[nameProperty]; + + return locationName || null; + } + } + + return null; +}; + +const getLngLatFromFeature = (feature) => { + const position = feature.geometry && feature.geometry.coordinates; + + return position ? new LngLat(position[0], position[1]) : null; +}; + +export const getPosition = (feature) => { + if (Array.isArray(feature)) { + const coordinates = feature.map((item) => { + const center = centerOfMass(item.geometry); + + return center.geometry.coordinates; + }); + coordinates.push(coordinates[0]); + + return getLngLatFromFeature(centerOfMass(polygon([coordinates]))); + } + + return getLngLatFromFeature(centerOfMass(feature)); +}; + +export const getPositionFromLocationName = (map, locationName, options) => { + const features = map.querySourceFeatures('composite', { + sourceLayer: options.sourceLayer, + filter: ['in', options.nameProperty, getProperLocationName(locationName, options.formatter)], + }); + + return features && features.length ? getPosition(features) : null; +}; + +export const flyToLocation = (map, locationName, options, recenter = true) => + new Promise((resolve, reject) => { + const position = getPositionFromLocationName(map, locationName, options); + if (position) { + map.flyTo({ + center: position, + zoom: 16, + }); + resolve(position); + } else if (recenter) { + // reset view before next flyTo, otherwise flying to locations not currently visible shall fail + map.flyTo({ center: options.center, zoom: options.zoom || 6.1 }); + setTimeout(() => { + flyToLocation(map, locationName, options, false) + .then((_position) => resolve(_position)) + .catch(reject); + }, 500); + } else { + reject(new Error(`Location ${locationName} not found!`)); + } + }); + +export const renderPopup = (map, popup, position, locationName, value) => + popup + .setLngLat(position) + .setHTML( + ` +
+
+ ${locationName.toLowerCase()} +
+ ${value ? `${value}` : ''} +
+ ` + ) + .addTo(map); + +const findLocationData = (locationName, data, formatter) => + data.find((location) => { + const name = formatter ? formatter(location.name) : location.name; + + return locationName.toLowerCase() === `${name}`.toLowerCase(); + }); + +export const renderTooltipFromEvent = (map, event, options) => { + const { popup, nameProperty, formatter, data } = options; + const locationName = getLocationNameFromEvent(event, nameProperty); + if (locationName) { + const boundaryName = formatter ? formatter(locationName, 'tooltip') : locationName; + if (data) { + const location = findLocationData(boundaryName, data); + renderPopup(map, popup, event.lngLat, boundaryName, getTooltipValue(options, location)); + } else { + renderPopup(map, popup, event.lngLat, boundaryName, ''); + } + } +}; + +export const renderTooltipFromLocation = (map, locationName, config, options) => { + const { popup, data } = options; + const position = getPositionFromLocationName(map, locationName, config); + if (position) { + const location = findLocationData(locationName, data); + renderPopup(map, popup, position, locationName, getTooltipValue(options, location)); + } +}; diff --git a/src/components/ErrorBoundary.js b/src/components/ErrorBoundary.js new file mode 100644 index 00000000..e6bcde7f --- /dev/null +++ b/src/components/ErrorBoundary.js @@ -0,0 +1,32 @@ +import PropTypes from 'prop-types'; +import React, { Component } from 'react'; + +class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + // eslint-disable-next-line class-methods-use-this + componentDidCatch(error, errorInfo) { + window.console.log('Error Boundary:', error, errorInfo); + } + + render() { + return this.state.hasError ? ( +
Something went wrong! Please contact your administrator
+ ) : ( + this.props.children + ); + } +} + +ErrorBoundary.propTypes = { + children: PropTypes.any, +}; + +export default ErrorBoundary; diff --git a/src/components/IndicatorStat/IndicatorStat.js b/src/components/IndicatorStat/IndicatorStat.js new file mode 100644 index 00000000..1ae41c70 --- /dev/null +++ b/src/components/IndicatorStat/IndicatorStat.js @@ -0,0 +1,25 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import SpotlightPopup from '../SpotlightPopup'; + +const IndicatorStat = ({ meta = {}, heading, children }) => ( +
+

+ {heading} + {meta.description || meta.source ? : null} +

+ {children} +
+); + +IndicatorStat.defaultProps = { meta: {} }; +IndicatorStat.propTypes = { + heading: PropTypes.string, + meta: PropTypes.shape({ + description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + source: PropTypes.string, + }), + children: PropTypes.any, +}; + +export default IndicatorStat; diff --git a/src/components/IndicatorStat/IndicatorStatDataViewer.js b/src/components/IndicatorStat/IndicatorStatDataViewer.js new file mode 100644 index 00000000..86aed322 --- /dev/null +++ b/src/components/IndicatorStat/IndicatorStatDataViewer.js @@ -0,0 +1,30 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/react'; +import PropTypes from 'prop-types'; +import SpotlightPopup from '../SpotlightPopup'; + +const IndicatorStatDataViewer = ({ value, note }) => ( +

+ {value} + {note && note.content ? ( + + {note.content}{' '} + {note.meta ? : null} + + ) : null} +

+); + +IndicatorStatDataViewer.propTypes = { + note: PropTypes.object, + value: PropTypes.string, +}; + +export default IndicatorStatDataViewer; diff --git a/src/components/IndicatorStat/index.js b/src/components/IndicatorStat/index.js new file mode 100644 index 00000000..f2bfca74 --- /dev/null +++ b/src/components/IndicatorStat/index.js @@ -0,0 +1,3 @@ +import IndicatorStat from './IndicatorStat'; + +export default IndicatorStat; diff --git a/src/components/SpotlightPopup/SpotlightPopup.js b/src/components/SpotlightPopup/SpotlightPopup.js new file mode 100644 index 00000000..065556ba --- /dev/null +++ b/src/components/SpotlightPopup/SpotlightPopup.js @@ -0,0 +1,79 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/react'; +import PropTypes from 'prop-types'; +import Popup from 'reactjs-popup'; +import SpotlightPopupContent from './SpotlightPopupContent'; + +const SpotlightPopup = (props) => { + const hideAllPopups = () => { + const popups = document.querySelectorAll('.popup-content '); + popups.forEach((popup) => { + popup.setAttribute('style', 'display:none;'); + }); + }; + const popUpContentStyles = { + zIndex: 200, + width: '350px', + background: '#fff', + fontWeight: 400, + fontStyle: 'normal', + fontSize: '12px', + color: '#60575d', + border: 0, + borderRadius: '0.28571429rem', + boxShadow: '0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15)', + padding: '5px', + }; + const customArrowStyle = { + height: '10px', + width: '10px', + position: 'absolute', + background: 'rgb(255, 255, 255)', + transform: 'rotate(225deg)', + margin: '-5px', + zIndex: -1, + boxShadow: 'rgb(0 0 0 / 20%) 1px 1px 1px', + bottom: '0%', + left: '82px', + }; + + return ( + + + + } + offsetX={20} + arrowStyle={customArrowStyle} + position="bottom center" + closeOnDocumentClick + contentStyle={popUpContentStyles} + > + {(close) => } + + ); +}; + +SpotlightPopup.propTypes = { + description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + source: PropTypes.string, +}; + +export default SpotlightPopup; diff --git a/src/components/SpotlightPopup/SpotlightPopupContent.js b/src/components/SpotlightPopup/SpotlightPopupContent.js new file mode 100644 index 00000000..83088e00 --- /dev/null +++ b/src/components/SpotlightPopup/SpotlightPopupContent.js @@ -0,0 +1,91 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/react'; +import React from 'react'; +import PropTypes from 'prop-types'; + +const SpotlightPopupContent = ({ close, description, source }) => { + const renderSourceText = () => { + if (source) { + return ( + + + Source:{' '} + + {source} + + ); + } + + return null; + }; + + return ( +
+ + × + +
+

+ {description} +

+ {source ? ( + +
+

+ {renderSourceText()} +

{' '} +
+ ) : null} +
+
+ ); +}; + +SpotlightPopupContent.propTypes = { + description: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + source: PropTypes.string, + close: PropTypes.func.isRequired, +}; + +export default SpotlightPopupContent; diff --git a/src/components/SpotlightPopup/index.js b/src/components/SpotlightPopup/index.js new file mode 100644 index 00000000..b3d17170 --- /dev/null +++ b/src/components/SpotlightPopup/index.js @@ -0,0 +1,3 @@ +import SpotlightPopup from './SpotlightPopup'; + +export default SpotlightPopup; diff --git a/src/components/SpotlightTab/SpotlightTab.js b/src/components/SpotlightTab/SpotlightTab.js new file mode 100644 index 00000000..d82d3d94 --- /dev/null +++ b/src/components/SpotlightTab/SpotlightTab.js @@ -0,0 +1,20 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/react'; +import PropTypes from 'prop-types'; + +const SpotlightTab = ({ children }) => ( +
+ {children} +
+); + +SpotlightTab.propTypes = { + children: PropTypes.any, +}; + +export default SpotlightTab; diff --git a/src/components/SpotlightTab/TabContainer.js b/src/components/SpotlightTab/TabContainer.js new file mode 100644 index 00000000..6f3050e3 --- /dev/null +++ b/src/components/SpotlightTab/TabContainer.js @@ -0,0 +1,28 @@ +import React, { Children, isValidElement } from 'react'; +import PropTypes from 'prop-types'; +import TabContent from './TabContent'; + +const TabContainer = ({ id, active, label, children, onActivate }) => { + const renderContent = () => + Children.map(children, (child) => (isValidElement(child) && child.type === TabContent ? child : null)); + + return ( +
+ + + {renderContent()} +
+ ); +}; + +TabContainer.propTypes = { + id: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + active: PropTypes.bool, + onActivate: PropTypes.func, + children: PropTypes.any, +}; + +export default TabContainer; diff --git a/src/components/SpotlightTab/TabContent.js b/src/components/SpotlightTab/TabContent.js new file mode 100644 index 00000000..7c58bf00 --- /dev/null +++ b/src/components/SpotlightTab/TabContent.js @@ -0,0 +1,20 @@ +/** @jsx jsx */ +import { css, jsx } from '@emotion/react'; +import PropTypes from 'prop-types'; + +const TabContent = ({ children }) => ( +
+ {children} +
+); + +TabContent.propTypes = { + children: PropTypes.any, +}; + +export default TabContent; diff --git a/src/components/SpotlightTab/TabContentHeader.js b/src/components/SpotlightTab/TabContentHeader.js new file mode 100644 index 00000000..8d202754 --- /dev/null +++ b/src/components/SpotlightTab/TabContentHeader.js @@ -0,0 +1,15 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const TabContentHeader = ({ children, onClick }) => ( +
+ {children} +
+); + +TabContentHeader.propTypes = { + children: PropTypes.any, + onClick: PropTypes.func, +}; + +export default TabContentHeader; diff --git a/src/components/SpotlightTab/index.js b/src/components/SpotlightTab/index.js new file mode 100644 index 00000000..d4a98230 --- /dev/null +++ b/src/components/SpotlightTab/index.js @@ -0,0 +1,3 @@ +import SpotlightTab from './SpotlightTab'; + +export default SpotlightTab; diff --git a/src/core/ExampleChart.js b/src/core/ExampleChart.js index a2a83d4b..748d3039 100644 --- a/src/core/ExampleChart.js +++ b/src/core/ExampleChart.js @@ -1,4 +1,5 @@ -import React from 'react'; +/** @jsx jsx */ +import { jsx } from '@emotion/react' import { createRoot } from 'react-dom/client'; import deepMerge from 'deepmerge'; import defaultOptions, { handleResize } from '../charts/echarts/index'; diff --git a/src/utils/data.js b/src/utils/data.js index 636893f2..f0eb4543 100644 --- a/src/utils/data.js +++ b/src/utils/data.js @@ -2,7 +2,7 @@ import { parse } from 'papaparse'; export const ACTIVE_BRANCH = 'main'; -const fetchCSVData = (url) => +export const fetchCSVData = (url) => new Promise((resolve) => { parse(url, { download: true, @@ -39,4 +39,12 @@ export const getYearsFromRange = (range) => { return count.map((key) => range[0] + key); }; -export default fetchCSVData; +const fetchData = (url) => { + if (url.endsWith('csv')) { + return fetchCSVData(url); + } + + return window.fetch(url).then((response) => response.json()); +}; + +export default fetchData; diff --git a/src/utils/index.js b/src/utils/index.js index 7af0afd3..56bfe76e 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,4 @@ +import deepMerge from 'deepmerge'; import { createElement } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import NoData from '../components/NoData'; @@ -13,5 +14,21 @@ export const removeNoData = (rootNode) => { unmountComponentAtNode(rootNode); }; +export const combineMerge = (target, source, options) => { + const destination = target.slice(); + + source.forEach((item, index) => { + if (typeof destination[index] === 'undefined') { + destination[index] = options.cloneUnlessOtherwiseSpecified(item, options); + } else if (options.isMergeableObject(item)) { + destination[index] = deepMerge(target[index], item, options); + } else if (target.indexOf(item) === -1) { + destination.push(item); + } + }); + + return destination; +}; + export * from './constants'; export * from './chart';