mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-29 08:10:27 +01:00
Merge remote-tracking branch 'upstream/master' into feature/color-accessibility-ui
Conflicts: src/components/App.jsx src/styles/_components.scss
This commit is contained in:
commit
8f07a79a49
63 changed files with 666 additions and 663 deletions
2
.babelrc
2
.babelrc
|
@ -5,7 +5,7 @@
|
||||||
"test": {
|
"test": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
["istanbul", {
|
["istanbul", {
|
||||||
exclude: ["node_modules/**", "test/**"]
|
"exclude": ["node_modules/**", "test/**"]
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
22
.travis.yml
22
.travis.yml
|
@ -1,22 +0,0 @@
|
||||||
language: node_js
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: osx
|
|
||||||
node_js: "6"
|
|
||||||
- os: osx
|
|
||||||
node_js: "8"
|
|
||||||
- os: osx
|
|
||||||
node_js: "9"
|
|
||||||
install:
|
|
||||||
- npm install
|
|
||||||
script:
|
|
||||||
- mkdir public
|
|
||||||
- node --stack_size=100000 $(which npm) run build
|
|
||||||
- npm run lint
|
|
||||||
- npm run lint-styles
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-4.8
|
|
|
@ -22,6 +22,11 @@ targeted at developers and map designers.
|
||||||
|
|
||||||
Mapbox has built one of the best and most amazing OSS ecosystems. A key component to ensure its longevity and independance is an OSS map designer.
|
Mapbox has built one of the best and most amazing OSS ecosystems. A key component to ensure its longevity and independance is an OSS map designer.
|
||||||
|
|
||||||
|
|
||||||
|
## Donations
|
||||||
|
If you or your organisation has seen value from Maputnik, please consider donating at <https://maputnik.github.io/donate>
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The documentation can be found in the [Wiki](https://github.com/maputnik/editor/wiki). You are welcome to collaborate!
|
The documentation can be found in the [Wiki](https://github.com/maputnik/editor/wiki). You are welcome to collaborate!
|
||||||
|
@ -157,6 +162,6 @@ Sina Martinelli, Nicholas Doiron, Neil Cawse, Urs42, Benedikt Groß, Manuel Roth
|
||||||
|
|
||||||
Maputnik is [licensed under MIT](LICENSE) and is Copyright (c) Lukas Martinelli and contributors.
|
Maputnik is [licensed under MIT](LICENSE) and is Copyright (c) Lukas Martinelli and contributors.
|
||||||
|
|
||||||
**Disclaimer** This project is not affiliated with Mapbox or Mapbox Studio. It is a independent style editor for the
|
**Disclaimer** This project is not affiliated with Mapbox or Mapbox Studio. It is an independent style editor for the
|
||||||
open source technology in the Mapbox GL ecosystem.
|
open source technology in the Mapbox GL ecosystem.
|
||||||
As contributor please take extra care of not violating any Mapbox trademarks. Do not get inspired by Mapbox Studio and make your own decisions for a good style editor.
|
As contributor please take extra care of not violating any Mapbox trademarks. Do not get inspired by Mapbox Studio and make your own decisions for a good style editor.
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
image: Visual Studio 2017
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- nodejs_version: "6"
|
|
||||||
- nodejs_version: "8"
|
- nodejs_version: "8"
|
||||||
- nodejs_version: "9"
|
- nodejs_version: "9"
|
||||||
|
- nodejs_version: "10"
|
||||||
platform:
|
platform:
|
||||||
- x86
|
- x86
|
||||||
- x64
|
- x64
|
||||||
|
|
|
@ -14,25 +14,6 @@ var OUTPATH = artifacts.pathSync("/build");
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
app: './src/index.jsx',
|
app: './src/index.jsx',
|
||||||
vendor: [
|
|
||||||
'file-saver',
|
|
||||||
'mapbox-gl/dist/mapbox-gl.js',
|
|
||||||
"lodash.clonedeep",
|
|
||||||
"lodash.throttle",
|
|
||||||
'color',
|
|
||||||
'react',
|
|
||||||
"react-dom",
|
|
||||||
"react-color",
|
|
||||||
"react-file-reader-input",
|
|
||||||
"react-collapse",
|
|
||||||
"react-height",
|
|
||||||
"react-icon-base",
|
|
||||||
"react-motion",
|
|
||||||
"react-sortable-hoc",
|
|
||||||
"request",
|
|
||||||
//TODO: Icons raise multi vendor errors?
|
|
||||||
//"react-icons",
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: OUTPATH,
|
path: OUTPATH,
|
||||||
|
@ -55,7 +36,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.NoEmitOnErrorsPlugin(),
|
new webpack.NoEmitOnErrorsPlugin(),
|
||||||
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[chunkhash].vendor.js' }),
|
|
||||||
new WebpackCleanupPlugin(),
|
new WebpackCleanupPlugin(),
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env': {
|
||||||
|
|
86
package-lock.json
generated
86
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "1.2.0",
|
"version": "1.5.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -281,25 +281,20 @@
|
||||||
"wgs84": "0.0.0"
|
"wgs84": "0.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mapbox/gl-matrix": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/gl-matrix/-/gl-matrix-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-5RJqq01kw2uBx6l9CuDd3eV3PSs="
|
|
||||||
},
|
|
||||||
"@mapbox/jsonlint-lines-primitives": {
|
"@mapbox/jsonlint-lines-primitives": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.1.tgz",
|
||||||
"integrity": "sha512-LGegvJq+ks4UFnEAvtAhygFRZxaNg2vw7PsvLOAXY8ziJLv5aFDSdHDEMJA/Q8PG5dNhMioUvo0kNqb7U5dPoQ=="
|
"integrity": "sha512-LGegvJq+ks4UFnEAvtAhygFRZxaNg2vw7PsvLOAXY8ziJLv5aFDSdHDEMJA/Q8PG5dNhMioUvo0kNqb7U5dPoQ=="
|
||||||
},
|
},
|
||||||
"@mapbox/mapbox-gl-rtl-text": {
|
"@mapbox/mapbox-gl-rtl-text": {
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-rtl-text/-/mapbox-gl-rtl-text-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-rtl-text/-/mapbox-gl-rtl-text-0.2.0.tgz",
|
||||||
"integrity": "sha512-x5xSHNAD5MeuasbEpGyDBGy4zPQysDdvDUlnjDKMxic8KqmcxcCP3ojlIaqEWYFvMmq7FKyWIF7K8jO4bHavKQ=="
|
"integrity": "sha512-oijFgP0DTMRtLzEpZM5lCrlagoQaOXUhZOMKOXaCF56QohKdRO4uu8RUKcuvfpWz6ruQcyH2yikBl09aWh3Duw=="
|
||||||
},
|
},
|
||||||
"@mapbox/mapbox-gl-style-spec": {
|
"@mapbox/mapbox-gl-style-spec": {
|
||||||
"version": "12.0.0",
|
"version": "13.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-12.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-style-spec/-/mapbox-gl-style-spec-13.1.0.tgz",
|
||||||
"integrity": "sha1-dRW/rbs6hZ66HFbZn8E4P6YpJ0c=",
|
"integrity": "sha512-WoviOAN39bZ3Fm3jBycGZg64rAihZ1ykVOR/XAiJLPviyPGTSWo0kIzcW2FRg23LJ0FSysEXhbDWXtHADUFj+w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@mapbox/jsonlint-lines-primitives": "2.0.1",
|
"@mapbox/jsonlint-lines-primitives": "2.0.1",
|
||||||
"@mapbox/unitbezier": "0.0.0",
|
"@mapbox/unitbezier": "0.0.0",
|
||||||
|
@ -309,11 +304,6 @@
|
||||||
"sort-object": "0.3.2"
|
"sort-object": "0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mapbox/mapbox-gl-supported": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.3.1.tgz",
|
|
||||||
"integrity": "sha512-tMdtbKjxVaA3WZubFTKIPjAHKNz8PCqUGdRgT7RIpqLHLxZi3eQWzixrnyYA5dglEnbfcIEPvtRQu/4yXPt2YQ=="
|
|
||||||
},
|
|
||||||
"@mapbox/point-geometry": {
|
"@mapbox/point-geometry": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
|
||||||
|
@ -6536,11 +6526,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"geojson-vt": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-Idr9VfV+EtycnleWLKgYbTFAthfE1V700eCqTlC6P2ODh715fTp4cCQYleYh3TPLzlzn2K4Gk514TstsoDue9w=="
|
|
||||||
},
|
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
|
||||||
|
@ -6584,6 +6569,11 @@
|
||||||
"utf8": "2.1.2"
|
"utf8": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gl-matrix": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-22I6q7aO2oKNahNV0+9JavVNUhQXRTvR5jP2s8U1l93TkjcQe8RK6MeMYpM7+66R0sCVUgSdO97BL439vePyzQ=="
|
||||||
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
||||||
|
@ -8216,11 +8206,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"kdbush": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-PL0D6d6tnA9vZszblkUOXOzGQOA="
|
|
||||||
},
|
|
||||||
"kew": {
|
"kew": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
|
||||||
|
@ -8582,13 +8567,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mapbox-gl": {
|
"mapbox-gl": {
|
||||||
"version": "0.45.0",
|
"version": "0.47.0",
|
||||||
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.47.0.tgz",
|
||||||
"integrity": "sha1-r3HMgk8NflHM1cUF6q5BG8CRDM0=",
|
"integrity": "sha512-y1AlNYMAKaqEtaqni0zOMYj9gTc1gZ0lqLkxXK9iFg5+ZBITc5DL9AcrXhpEXNxUzXKFa7dZkSULyNaqXFQ8yQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@mapbox/gl-matrix": "0.0.1",
|
|
||||||
"@mapbox/jsonlint-lines-primitives": "2.0.1",
|
"@mapbox/jsonlint-lines-primitives": "2.0.1",
|
||||||
"@mapbox/mapbox-gl-supported": "1.3.1",
|
"@mapbox/mapbox-gl-supported": "1.4.0",
|
||||||
"@mapbox/point-geometry": "0.1.0",
|
"@mapbox/point-geometry": "0.1.0",
|
||||||
"@mapbox/shelf-pack": "3.1.0",
|
"@mapbox/shelf-pack": "3.1.0",
|
||||||
"@mapbox/tiny-sdf": "1.1.0",
|
"@mapbox/tiny-sdf": "1.1.0",
|
||||||
|
@ -8599,7 +8583,8 @@
|
||||||
"csscolorparser": "1.0.3",
|
"csscolorparser": "1.0.3",
|
||||||
"earcut": "2.1.3",
|
"earcut": "2.1.3",
|
||||||
"geojson-rewind": "0.3.1",
|
"geojson-rewind": "0.3.1",
|
||||||
"geojson-vt": "3.1.1",
|
"geojson-vt": "3.2.0",
|
||||||
|
"gl-matrix": "2.7.1",
|
||||||
"gray-matter": "3.1.1",
|
"gray-matter": "3.1.1",
|
||||||
"grid-index": "1.0.0",
|
"grid-index": "1.0.0",
|
||||||
"minimist": "0.0.8",
|
"minimist": "0.0.8",
|
||||||
|
@ -8608,10 +8593,35 @@
|
||||||
"rw": "1.3.3",
|
"rw": "1.3.3",
|
||||||
"shuffle-seed": "1.1.6",
|
"shuffle-seed": "1.1.6",
|
||||||
"sort-object": "0.3.2",
|
"sort-object": "0.3.2",
|
||||||
"supercluster": "2.3.0",
|
"supercluster": "4.1.1",
|
||||||
"through2": "2.0.3",
|
"through2": "2.0.3",
|
||||||
"tinyqueue": "1.2.3",
|
"tinyqueue": "1.2.3",
|
||||||
"vt-pbf": "3.1.0"
|
"vt-pbf": "3.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/mapbox-gl-supported": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-ZD0Io4XK+/vU/4zpANjOtdWfVszAgnaMPsGR6LKsWh4kLIEv9qoobTVmJPPuwuM+ZI2b3BlZ6DYw1XHVmv6YTA=="
|
||||||
|
},
|
||||||
|
"geojson-vt": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-qk7sEv7dMfuGzflwClsgtO1fWPut/TqCInWEEUJc/Ofn4tmqBGznnPv3eUdxtwMkulMaAwSL3osHiyN03XJd/w=="
|
||||||
|
},
|
||||||
|
"kdbush": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-9KqSdmWCkBIisFIGclT0FRagKhI7IVbMyUjsxCFG0Ly1Dg6whlxJ7b9lrq8ifk3X/fGeJzok1R75LQfZTfA5zQ=="
|
||||||
|
},
|
||||||
|
"supercluster": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-sF0FfUOPFp96DKzwWFLeQOEqqKu2PpcesxAFeFsknA/q7g7igVVn/p3NI2XHEghNSyDAqunKNKqAbqNO8+7NDQ==",
|
||||||
|
"requires": {
|
||||||
|
"kdbush": "2.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mapbox-gl-inspect": {
|
"mapbox-gl-inspect": {
|
||||||
|
@ -14176,14 +14186,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"supercluster": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-2.3.0.tgz",
|
|
||||||
"integrity": "sha1-h6tWCBu+qaHXJN9TUe6ejDry9Is=",
|
|
||||||
"requires": {
|
|
||||||
"kdbush": "1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||||
|
|
22
package.json
22
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "1.2.0",
|
"version": "1.5.0",
|
||||||
"description": "A MapboxGL visual style editor",
|
"description": "A MapboxGL visual style editor",
|
||||||
"main": "''",
|
"main": "''",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -10,8 +10,7 @@
|
||||||
"test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch",
|
"test-watch": "cross-env NODE_ENV=test wdio config/wdio.conf.js --watch",
|
||||||
"start": "webpack-dev-server --progress --profile --colors --config config/webpack.config.js",
|
"start": "webpack-dev-server --progress --profile --colors --config config/webpack.config.js",
|
||||||
"lint": "eslint --ext js --ext jsx {src,test}",
|
"lint": "eslint --ext js --ext jsx {src,test}",
|
||||||
"lint-styles": "stylelint 'src/styles/*.scss'",
|
"lint-styles": "stylelint 'src/styles/*.scss'"
|
||||||
"nsp": "nsp check --reporter summary"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -21,44 +20,39 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/maputnik/editor#readme",
|
"homepage": "https://github.com/maputnik/editor#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.1.2",
|
"@mapbox/mapbox-gl-rtl-text": "^0.2.0",
|
||||||
"@mapbox/mapbox-gl-style-spec": "^12.0.0",
|
"@mapbox/mapbox-gl-style-spec": "^13.1.0",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"codemirror": "^5.37.0",
|
"codemirror": "^5.37.0",
|
||||||
"color": "^3.0.0",
|
"color": "^3.0.0",
|
||||||
"file-saver": "^1.3.8",
|
"file-saver": "^1.3.8",
|
||||||
"github-api": "^3.0.0",
|
|
||||||
"jsonlint": "github:josdejong/jsonlint#85a19d7",
|
"jsonlint": "github:josdejong/jsonlint#85a19d7",
|
||||||
"lodash.capitalize": "^4.2.1",
|
"lodash.capitalize": "^4.2.1",
|
||||||
"lodash.clamp": "^4.0.3",
|
"lodash.clamp": "^4.0.3",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"mapbox-gl": "^0.45.0",
|
"mapbox-gl": "^0.47.0",
|
||||||
"mapbox-gl-inspect": "^1.3.1",
|
"mapbox-gl-inspect": "^1.3.1",
|
||||||
"maputnik-design": "github:maputnik/design",
|
"maputnik-design": "github:maputnik/design",
|
||||||
"mousetrap": "^1.6.1",
|
|
||||||
"ol-mapbox-style": "^2.10.1",
|
"ol-mapbox-style": "^2.10.1",
|
||||||
"ol": "^4.6.5",
|
"ol": "^4.6.5",
|
||||||
"prop-types": "^15.6.0",
|
"prop-types": "^15.6.0",
|
||||||
"react": "^16.3.2",
|
"react": "^16.3.2",
|
||||||
"react-addons-pure-render-mixin": "^15.6.2",
|
|
||||||
"react-aria-menubutton": "^5.1.1",
|
"react-aria-menubutton": "^5.1.1",
|
||||||
"react-aria-modal": "^2.12.1",
|
"react-aria-modal": "^2.12.1",
|
||||||
|
"react-autobind": "^1.0.6",
|
||||||
"react-autocomplete": "^1.7.2",
|
"react-autocomplete": "^1.7.2",
|
||||||
"react-codemirror2": "^4.2.1",
|
"react-codemirror2": "^4.2.1",
|
||||||
"react-collapse": "^4.0.3",
|
"react-collapse": "^4.0.3",
|
||||||
"react-color": "^2.14.1",
|
"react-color": "^2.14.1",
|
||||||
"react-copy-to-clipboard": "^5.0.1",
|
|
||||||
"react-dom": "^16.3.2",
|
"react-dom": "^16.3.2",
|
||||||
"react-file-reader-input": "^1.1.4",
|
"react-file-reader-input": "^1.1.4",
|
||||||
"react-height": "^3.0.0",
|
|
||||||
"react-icon-base": "^2.1.1",
|
"react-icon-base": "^2.1.1",
|
||||||
"react-icons": "^2.2.7",
|
"react-icons": "^2.2.7",
|
||||||
"react-motion": "^0.5.2",
|
"react-motion": "^0.5.2",
|
||||||
"react-sortable-hoc": "^0.6.8",
|
"react-sortable-hoc": "^0.6.8",
|
||||||
"reconnecting-websocket": "^3.2.2",
|
"reconnecting-websocket": "^3.2.2",
|
||||||
"request": "^2.85.0",
|
|
||||||
"url": "^0.11.0"
|
"url": "^0.11.0"
|
||||||
},
|
},
|
||||||
"jshintConfig": {
|
"jshintConfig": {
|
||||||
|
@ -117,7 +111,6 @@
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-register": "^6.26.0",
|
"babel-register": "^6.26.0",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"base64-loader": "^1.0.0",
|
|
||||||
"copy-webpack-plugin": "^4.5.1",
|
"copy-webpack-plugin": "^4.5.1",
|
||||||
"cors": "^2.8.4",
|
"cors": "^2.8.4",
|
||||||
"cross-env": "^5.1.4",
|
"cross-env": "^5.1.4",
|
||||||
|
@ -135,7 +128,7 @@
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mocha": "^5.1.1",
|
"mocha": "^5.1.1",
|
||||||
"node-sass": "^4.9.0",
|
"node-sass": "^4.9.0",
|
||||||
"nsp": "^3.1.0",
|
"raw-loader": "^0.5.1",
|
||||||
"react-hot-loader": "^3.1.1",
|
"react-hot-loader": "^3.1.1",
|
||||||
"sass-loader": "^7.0.1",
|
"sass-loader": "^7.0.1",
|
||||||
"selenium-standalone": "^6.14.0",
|
"selenium-standalone": "^6.14.0",
|
||||||
|
@ -147,7 +140,6 @@
|
||||||
"uglifyjs-webpack-plugin": "^1.2.4",
|
"uglifyjs-webpack-plugin": "^1.2.4",
|
||||||
"uuid": "^3.1.0",
|
"uuid": "^3.1.0",
|
||||||
"wdio-mocha-framework": "^0.5.13",
|
"wdio-mocha-framework": "^0.5.13",
|
||||||
"wdio-phantomjs-service": "^0.2.2",
|
|
||||||
"wdio-selenium-standalone-service": "0.0.10",
|
"wdio-selenium-standalone-service": "0.0.10",
|
||||||
"wdio-spec-reporter": "^0.1.2",
|
"wdio-spec-reporter": "^0.1.2",
|
||||||
"webdriverio": "^4.12.0",
|
"webdriverio": "^4.12.0",
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
import autoBind from 'react-autobind';
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import Mousetrap from 'mousetrap'
|
|
||||||
import cloneDeep from 'lodash.clonedeep'
|
import cloneDeep from 'lodash.clonedeep'
|
||||||
import clamp from 'lodash.clamp'
|
import clamp from 'lodash.clamp'
|
||||||
import {arrayMove} from 'react-sortable-hoc';
|
import {arrayMove} from 'react-sortable-hoc'
|
||||||
import url from 'url'
|
import url from 'url'
|
||||||
|
|
||||||
import MapboxGlMap from './map/MapboxGlMap'
|
import MapboxGlMap from './map/MapboxGlMap'
|
||||||
import OpenLayers3Map from './map/OpenLayers3Map'
|
|
||||||
import LayerList from './layers/LayerList'
|
import LayerList from './layers/LayerList'
|
||||||
import LayerEditor from './layers/LayerEditor'
|
import LayerEditor from './layers/LayerEditor'
|
||||||
import Toolbar from './Toolbar'
|
import Toolbar from './Toolbar'
|
||||||
|
@ -18,13 +17,14 @@ import ExportModal from './modals/ExportModal'
|
||||||
import SourcesModal from './modals/SourcesModal'
|
import SourcesModal from './modals/SourcesModal'
|
||||||
import OpenModal from './modals/OpenModal'
|
import OpenModal from './modals/OpenModal'
|
||||||
import ShortcutsModal from './modals/ShortcutsModal'
|
import ShortcutsModal from './modals/ShortcutsModal'
|
||||||
|
import SurveyModal from './modals/SurveyModal'
|
||||||
|
|
||||||
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
||||||
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import style from '../libs/style.js'
|
import style from '../libs/style'
|
||||||
import { initialStyleUrl, loadStyleUrl } from '../libs/urlopen'
|
import { initialStyleUrl, loadStyleUrl } from '../libs/urlopen'
|
||||||
import { undoMessages, redoMessages } from '../libs/diffmessage'
|
import { undoMessages, redoMessages } from '../libs/diffmessage'
|
||||||
import { loadDefaultStyle, StyleStore } from '../libs/stylestore'
|
import { StyleStore } from '../libs/stylestore'
|
||||||
import { ApiStyleStore } from '../libs/apistore'
|
import { ApiStyleStore } from '../libs/apistore'
|
||||||
import { RevisionStore } from '../libs/revisions'
|
import { RevisionStore } from '../libs/revisions'
|
||||||
import LayerWatcher from '../libs/layerwatcher'
|
import LayerWatcher from '../libs/layerwatcher'
|
||||||
|
@ -34,7 +34,7 @@ import Debug from '../libs/debug'
|
||||||
import queryUtil from '../libs/query-util'
|
import queryUtil from '../libs/query-util'
|
||||||
|
|
||||||
import MapboxGl from 'mapbox-gl'
|
import MapboxGl from 'mapbox-gl'
|
||||||
import mapboxUtil from 'mapbox-gl/src/util/mapbox'
|
import { normalizeSourceURL } from 'mapbox-gl/src/util/mapbox'
|
||||||
|
|
||||||
|
|
||||||
function updateRootSpec(spec, fieldName, newValues) {
|
function updateRootSpec(spec, fieldName, newValues) {
|
||||||
|
@ -53,6 +53,8 @@ function updateRootSpec(spec, fieldName, newValues) {
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
autoBind(this);
|
||||||
|
|
||||||
this.revisionStore = new RevisionStore()
|
this.revisionStore = new RevisionStore()
|
||||||
this.styleStore = new ApiStyleStore({
|
this.styleStore = new ApiStyleStore({
|
||||||
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false)
|
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false)
|
||||||
|
@ -172,9 +174,11 @@ export default class App extends React.Component {
|
||||||
open: false,
|
open: false,
|
||||||
shortcuts: false,
|
shortcuts: false,
|
||||||
export: false,
|
export: false,
|
||||||
|
survey: localStorage.hasOwnProperty('survey') ? false : true
|
||||||
},
|
},
|
||||||
mapOptions: {
|
mapOptions: {
|
||||||
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries")
|
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
|
||||||
|
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,14 +187,31 @@ export default class App extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleKeyPress(e) {
|
||||||
|
if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
|
||||||
|
if(e.metaKey && e.shiftKey && e.keyCode === 90) {
|
||||||
|
this.onRedo(e);
|
||||||
|
}
|
||||||
|
else if(e.metaKey && e.keyCode === 90) {
|
||||||
|
this.onUndo(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(e.ctrlKey && e.keyCode === 90) {
|
||||||
|
this.onUndo(e);
|
||||||
|
}
|
||||||
|
else if(e.ctrlKey && e.keyCode === 89) {
|
||||||
|
this.onRedo(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
Mousetrap.bind(['mod+z'], this.onUndo.bind(this));
|
window.addEventListener("keydown", this.handleKeyPress);
|
||||||
Mousetrap.bind(['mod+y', 'mod+shift+z'], this.onRedo.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
Mousetrap.unbind(['mod+z'], this.onUndo.bind(this));
|
window.removeEventListener("keydown", this.handleKeyPress);
|
||||||
Mousetrap.unbind(['mod+y', 'mod+shift+z'], this.onRedo.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStyle(snapshotStyle) {
|
saveStyle(snapshotStyle) {
|
||||||
|
@ -213,7 +234,7 @@ export default class App extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onStyleChanged(newStyle, save=true) {
|
onStyleChanged = (newStyle, save=true) => {
|
||||||
|
|
||||||
const errors = styleSpec.validate(newStyle, styleSpec.latest)
|
const errors = styleSpec.validate(newStyle, styleSpec.latest)
|
||||||
if(errors.length === 0) {
|
if(errors.length === 0) {
|
||||||
|
@ -240,7 +261,7 @@ export default class App extends React.Component {
|
||||||
this.fetchSources();
|
this.fetchSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
onUndo() {
|
onUndo = () => {
|
||||||
const activeStyle = this.revisionStore.undo()
|
const activeStyle = this.revisionStore.undo()
|
||||||
const messages = undoMessages(this.state.mapStyle, activeStyle)
|
const messages = undoMessages(this.state.mapStyle, activeStyle)
|
||||||
this.saveStyle(activeStyle)
|
this.saveStyle(activeStyle)
|
||||||
|
@ -250,7 +271,7 @@ export default class App extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onRedo() {
|
onRedo = () => {
|
||||||
const activeStyle = this.revisionStore.redo()
|
const activeStyle = this.revisionStore.redo()
|
||||||
const messages = redoMessages(this.state.mapStyle, activeStyle)
|
const messages = redoMessages(this.state.mapStyle, activeStyle)
|
||||||
this.saveStyle(activeStyle)
|
this.saveStyle(activeStyle)
|
||||||
|
@ -260,7 +281,7 @@ export default class App extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMoveLayer(move) {
|
onMoveLayer = (move) => {
|
||||||
let { oldIndex, newIndex } = move;
|
let { oldIndex, newIndex } = move;
|
||||||
let layers = this.state.mapStyle.layers;
|
let layers = this.state.mapStyle.layers;
|
||||||
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
||||||
|
@ -278,7 +299,7 @@ export default class App extends React.Component {
|
||||||
this.onLayersChange(layers);
|
this.onLayersChange(layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayersChange(changedLayers) {
|
onLayersChange = (changedLayers) => {
|
||||||
const changedStyle = {
|
const changedStyle = {
|
||||||
...this.state.mapStyle,
|
...this.state.mapStyle,
|
||||||
layers: changedLayers
|
layers: changedLayers
|
||||||
|
@ -286,7 +307,7 @@ export default class App extends React.Component {
|
||||||
this.onStyleChanged(changedStyle)
|
this.onStyleChanged(changedStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayerDestroy(layerId) {
|
onLayerDestroy = (layerId) => {
|
||||||
let layers = this.state.mapStyle.layers;
|
let layers = this.state.mapStyle.layers;
|
||||||
const remainingLayers = layers.slice(0);
|
const remainingLayers = layers.slice(0);
|
||||||
const idx = style.indexOfLayer(remainingLayers, layerId)
|
const idx = style.indexOfLayer(remainingLayers, layerId)
|
||||||
|
@ -294,7 +315,7 @@ export default class App extends React.Component {
|
||||||
this.onLayersChange(remainingLayers);
|
this.onLayersChange(remainingLayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayerCopy(layerId) {
|
onLayerCopy = (layerId) => {
|
||||||
let layers = this.state.mapStyle.layers;
|
let layers = this.state.mapStyle.layers;
|
||||||
const changedLayers = layers.slice(0)
|
const changedLayers = layers.slice(0)
|
||||||
const idx = style.indexOfLayer(changedLayers, layerId)
|
const idx = style.indexOfLayer(changedLayers, layerId)
|
||||||
|
@ -305,7 +326,7 @@ export default class App extends React.Component {
|
||||||
this.onLayersChange(changedLayers)
|
this.onLayersChange(changedLayers)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayerVisibilityToggle(layerId) {
|
onLayerVisibilityToggle = (layerId) => {
|
||||||
let layers = this.state.mapStyle.layers;
|
let layers = this.state.mapStyle.layers;
|
||||||
const changedLayers = layers.slice(0)
|
const changedLayers = layers.slice(0)
|
||||||
const idx = style.indexOfLayer(changedLayers, layerId)
|
const idx = style.indexOfLayer(changedLayers, layerId)
|
||||||
|
@ -320,7 +341,7 @@ export default class App extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
onLayerIdChange(oldId, newId) {
|
onLayerIdChange = (oldId, newId) => {
|
||||||
const changedLayers = this.state.mapStyle.layers.slice(0)
|
const changedLayers = this.state.mapStyle.layers.slice(0)
|
||||||
const idx = style.indexOfLayer(changedLayers, oldId)
|
const idx = style.indexOfLayer(changedLayers, oldId)
|
||||||
|
|
||||||
|
@ -332,7 +353,7 @@ export default class App extends React.Component {
|
||||||
this.onLayersChange(changedLayers)
|
this.onLayersChange(changedLayers)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayerChanged(layer) {
|
onLayerChanged = (layer) => {
|
||||||
const changedLayers = this.state.mapStyle.layers.slice(0)
|
const changedLayers = this.state.mapStyle.layers.slice(0)
|
||||||
const idx = style.indexOfLayer(changedLayers, layer.id)
|
const idx = style.indexOfLayer(changedLayers, layer.id)
|
||||||
changedLayers[idx] = layer
|
changedLayers[idx] = layer
|
||||||
|
@ -340,7 +361,7 @@ export default class App extends React.Component {
|
||||||
this.onLayersChange(changedLayers)
|
this.onLayersChange(changedLayers)
|
||||||
}
|
}
|
||||||
|
|
||||||
setMapState(newState) {
|
setMapState = (newState) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
mapState: newState
|
mapState: newState
|
||||||
})
|
})
|
||||||
|
@ -362,12 +383,14 @@ export default class App extends React.Component {
|
||||||
if(!this.state.sources.hasOwnProperty(key) && val.type === "vector" && val.hasOwnProperty("url")) {
|
if(!this.state.sources.hasOwnProperty(key) && val.type === "vector" && val.hasOwnProperty("url")) {
|
||||||
let url = val.url;
|
let url = val.url;
|
||||||
try {
|
try {
|
||||||
url = mapboxUtil.normalizeSourceURL(url, MapboxGl.accessToken);
|
url = normalizeSourceURL(url, MapboxGl.accessToken);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
console.warn("Failed to normalizeSourceURL: ", err);
|
console.warn("Failed to normalizeSourceURL: ", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(url)
|
fetch(url, {
|
||||||
|
mode: 'cors',
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
|
@ -404,7 +427,7 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
mapRenderer() {
|
mapRenderer() {
|
||||||
const mapProps = {
|
const mapProps = {
|
||||||
mapStyle: style.replaceAccessToken(this.state.mapStyle, {allowFallback: true}),
|
mapStyle: style.replaceAccessTokens(this.state.mapStyle, {allowFallback: true}),
|
||||||
options: this.state.mapOptions,
|
options: this.state.mapOptions,
|
||||||
onDataChange: (e) => {
|
onDataChange: (e) => {
|
||||||
this.layerWatcher.analyzeMap(e.map)
|
this.layerWatcher.analyzeMap(e.map)
|
||||||
|
@ -419,28 +442,29 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
// Check if OL3 code has been loaded?
|
// Check if OL3 code has been loaded?
|
||||||
if(renderer === 'ol3') {
|
if(renderer === 'ol3') {
|
||||||
mapElement = <OpenLayers3Map {...mapProps} />
|
mapElement = <div>TODO</div>
|
||||||
} else {
|
} else {
|
||||||
mapElement = <MapboxGlMap {...mapProps}
|
mapElement = <MapboxGlMap {...mapProps}
|
||||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||||
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
||||||
onLayerSelect={this.onLayerSelect.bind(this)} />
|
onLayerSelect={this.onLayerSelect} />
|
||||||
}
|
}
|
||||||
|
|
||||||
let filterName = "";
|
let filterName;
|
||||||
if(this.state.mapState.match(/^filter-/)) {
|
if(this.state.mapState.match(/^filter-/)) {
|
||||||
filterName = this.state.mapState.replace(/^filter-/, "");
|
filterName = this.state.mapState.replace(/^filter-/, "");
|
||||||
}
|
}
|
||||||
const elementStyle = {
|
const elementStyle = {};
|
||||||
"filter": `url('#${filterName}')`
|
if (filterName) {
|
||||||
|
elementStyle.filter = `url('color-accessibility.svg#${filterName}')`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div style={elementStyle}>
|
return <div style={elementStyle} className="maputnik-map__container">
|
||||||
{mapElement}
|
{mapElement}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayerSelect(layerId) {
|
onLayerSelect = (layerId) => {
|
||||||
const idx = style.indexOfLayer(this.state.mapStyle.layers, layerId)
|
const idx = style.indexOfLayer(this.state.mapStyle.layers, layerId)
|
||||||
this.setState({ selectedLayerIndex: idx })
|
this.setState({ selectedLayerIndex: idx })
|
||||||
}
|
}
|
||||||
|
@ -452,6 +476,10 @@ export default class App extends React.Component {
|
||||||
[modalName]: !this.state.isOpen[modalName]
|
[modalName]: !this.state.isOpen[modalName]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if(modalName === 'survey') {
|
||||||
|
localStorage.setItem('survey', '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -464,19 +492,19 @@ export default class App extends React.Component {
|
||||||
mapStyle={this.state.mapStyle}
|
mapStyle={this.state.mapStyle}
|
||||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||||
sources={this.state.sources}
|
sources={this.state.sources}
|
||||||
onStyleChanged={this.onStyleChanged.bind(this)}
|
onStyleChanged={this.onStyleChanged}
|
||||||
onStyleOpen={this.onStyleChanged.bind(this)}
|
onStyleOpen={this.onStyleChanged}
|
||||||
onSetMapState={this.setMapState.bind(this)}
|
onSetMapState={this.setMapState}
|
||||||
onToggleModal={this.toggleModal.bind(this)}
|
onToggleModal={this.toggleModal.bind(this)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
const layerList = <LayerList
|
const layerList = <LayerList
|
||||||
onMoveLayer={this.onMoveLayer.bind(this)}
|
onMoveLayer={this.onMoveLayer}
|
||||||
onLayerDestroy={this.onLayerDestroy.bind(this)}
|
onLayerDestroy={this.onLayerDestroy}
|
||||||
onLayerCopy={this.onLayerCopy.bind(this)}
|
onLayerCopy={this.onLayerCopy}
|
||||||
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
|
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
|
||||||
onLayersChange={this.onLayersChange.bind(this)}
|
onLayersChange={this.onLayersChange}
|
||||||
onLayerSelect={this.onLayerSelect.bind(this)}
|
onLayerSelect={this.onLayerSelect}
|
||||||
selectedLayerIndex={this.state.selectedLayerIndex}
|
selectedLayerIndex={this.state.selectedLayerIndex}
|
||||||
layers={layers}
|
layers={layers}
|
||||||
sources={this.state.sources}
|
sources={this.state.sources}
|
||||||
|
@ -490,12 +518,12 @@ export default class App extends React.Component {
|
||||||
sources={this.state.sources}
|
sources={this.state.sources}
|
||||||
vectorLayers={this.state.vectorLayers}
|
vectorLayers={this.state.vectorLayers}
|
||||||
spec={this.state.spec}
|
spec={this.state.spec}
|
||||||
onMoveLayer={this.onMoveLayer.bind(this)}
|
onMoveLayer={this.onMoveLayer}
|
||||||
onLayerChanged={this.onLayerChanged.bind(this)}
|
onLayerChanged={this.onLayerChanged}
|
||||||
onLayerDestroy={this.onLayerDestroy.bind(this)}
|
onLayerDestroy={this.onLayerDestroy}
|
||||||
onLayerCopy={this.onLayerCopy.bind(this)}
|
onLayerCopy={this.onLayerCopy}
|
||||||
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
|
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
|
||||||
onLayerIdChange={this.onLayerIdChange.bind(this)}
|
onLayerIdChange={this.onLayerIdChange}
|
||||||
/> : null
|
/> : null
|
||||||
|
|
||||||
const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel
|
const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel
|
||||||
|
@ -511,27 +539,31 @@ export default class App extends React.Component {
|
||||||
/>
|
/>
|
||||||
<SettingsModal
|
<SettingsModal
|
||||||
mapStyle={this.state.mapStyle}
|
mapStyle={this.state.mapStyle}
|
||||||
onStyleChanged={this.onStyleChanged.bind(this)}
|
onStyleChanged={this.onStyleChanged}
|
||||||
isOpen={this.state.isOpen.settings}
|
isOpen={this.state.isOpen.settings}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'settings')}
|
onOpenToggle={this.toggleModal.bind(this, 'settings')}
|
||||||
/>
|
/>
|
||||||
<ExportModal
|
<ExportModal
|
||||||
mapStyle={this.state.mapStyle}
|
mapStyle={this.state.mapStyle}
|
||||||
onStyleChanged={this.onStyleChanged.bind(this)}
|
onStyleChanged={this.onStyleChanged}
|
||||||
isOpen={this.state.isOpen.export}
|
isOpen={this.state.isOpen.export}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'export')}
|
onOpenToggle={this.toggleModal.bind(this, 'export')}
|
||||||
/>
|
/>
|
||||||
<OpenModal
|
<OpenModal
|
||||||
isOpen={this.state.isOpen.open}
|
isOpen={this.state.isOpen.open}
|
||||||
onStyleOpen={this.onStyleChanged.bind(this)}
|
onStyleOpen={this.onStyleChanged}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'open')}
|
onOpenToggle={this.toggleModal.bind(this, 'open')}
|
||||||
/>
|
/>
|
||||||
<SourcesModal
|
<SourcesModal
|
||||||
mapStyle={this.state.mapStyle}
|
mapStyle={this.state.mapStyle}
|
||||||
onStyleChanged={this.onStyleChanged.bind(this)}
|
onStyleChanged={this.onStyleChanged}
|
||||||
isOpen={this.state.isOpen.sources}
|
isOpen={this.state.isOpen.sources}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'sources')}
|
onOpenToggle={this.toggleModal.bind(this, 'sources')}
|
||||||
/>
|
/>
|
||||||
|
<SurveyModal
|
||||||
|
isOpen={this.state.isOpen.survey}
|
||||||
|
onOpenToggle={this.toggleModal.bind(this, 'survey')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
return <AppLayout
|
return <AppLayout
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import FileReaderInput from 'react-file-reader-input'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
|
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
|
||||||
|
|
||||||
import MdFileDownload from 'react-icons/lib/md/file-download'
|
import MdFileDownload from 'react-icons/lib/md/file-download'
|
||||||
import MdFileUpload from 'react-icons/lib/md/file-upload'
|
|
||||||
import OpenIcon from 'react-icons/lib/md/open-in-browser'
|
import OpenIcon from 'react-icons/lib/md/open-in-browser'
|
||||||
import SettingsIcon from 'react-icons/lib/md/settings'
|
import SettingsIcon from 'react-icons/lib/md/settings'
|
||||||
import MdInfo from 'react-icons/lib/md/info'
|
|
||||||
import SourcesIcon from 'react-icons/lib/md/layers'
|
import SourcesIcon from 'react-icons/lib/md/layers'
|
||||||
import MdSave from 'react-icons/lib/md/save'
|
|
||||||
import MdStyle from 'react-icons/lib/md/style'
|
|
||||||
import MdMap from 'react-icons/lib/md/map'
|
|
||||||
import MdInsertEmoticon from 'react-icons/lib/md/insert-emoticon'
|
|
||||||
import MdFontDownload from 'react-icons/lib/md/font-download'
|
|
||||||
import HelpIcon from 'react-icons/lib/md/help-outline'
|
import HelpIcon from 'react-icons/lib/md/help-outline'
|
||||||
import InspectionIcon from 'react-icons/lib/md/find-in-page'
|
import InspectionIcon from 'react-icons/lib/md/find-in-page'
|
||||||
|
import SurveyIcon from 'react-icons/lib/md/assignment-turned-in'
|
||||||
|
|
||||||
import ColorIcon from 'react-icons/lib/md/color-lens'
|
import ColorIcon from 'react-icons/lib/md/color-lens'
|
||||||
import MapIcon from 'react-icons/lib/md/map'
|
import MapIcon from 'react-icons/lib/md/map'
|
||||||
|
@ -26,7 +19,6 @@ import ViewIcon from 'react-icons/lib/md/remove-red-eye'
|
||||||
import logoImage from 'maputnik-design/logos/logo-color.svg'
|
import logoImage from 'maputnik-design/logos/logo-color.svg'
|
||||||
import pkgJson from '../../package.json'
|
import pkgJson from '../../package.json'
|
||||||
|
|
||||||
import style from '../libs/style'
|
|
||||||
|
|
||||||
class IconText extends React.Component {
|
class IconText extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -58,6 +50,28 @@ class ToolbarLink extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ToolbarLinkHighlighted extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
|
href: PropTypes.string,
|
||||||
|
onToggleModal: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <a
|
||||||
|
className={classnames('maputnik-toolbar-link', "maputnik-toolbar-link--highlighted", this.props.className)}
|
||||||
|
href={this.props.href}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span className="maputnik-toolbar-link-wrapper">
|
||||||
|
{this.props.children}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ToolbarAction extends React.Component {
|
class ToolbarAction extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
@ -89,16 +103,13 @@ export default class Toolbar extends React.Component {
|
||||||
onToggleModal: PropTypes.func,
|
onToggleModal: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
state = {
|
||||||
super(props)
|
isOpen: {
|
||||||
this.state = {
|
settings: false,
|
||||||
isOpen: {
|
sources: false,
|
||||||
settings: false,
|
open: false,
|
||||||
sources: false,
|
add: false,
|
||||||
open: false,
|
export: false,
|
||||||
add: false,
|
|
||||||
export: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +222,10 @@ export default class Toolbar extends React.Component {
|
||||||
<HelpIcon />
|
<HelpIcon />
|
||||||
<IconText>Help</IconText>
|
<IconText>Help</IconText>
|
||||||
</ToolbarLink>
|
</ToolbarLink>
|
||||||
|
<ToolbarLinkHighlighted href={"https://gregorywolanski.typeform.com/to/cPgaSY"}>
|
||||||
|
<SurveyIcon />
|
||||||
|
<IconText>Take the Maputnik Survey</IconText>
|
||||||
|
</ToolbarLinkHighlighted>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,17 +19,14 @@ class ColorField extends React.Component {
|
||||||
default: PropTypes.string,
|
default: PropTypes.string,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
state = {
|
||||||
super(props)
|
pickerOpened: false
|
||||||
this.state = {
|
|
||||||
pickerOpened: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: I much rather would do this with absolute positioning
|
//TODO: I much rather would do this with absolute positioning
|
||||||
//but I am too stupid to get it to work together with fixed position
|
//but I am too stupid to get it to work together with fixed position
|
||||||
//and scrollbars so I have to fallback to JavaScript
|
//and scrollbars so I have to fallback to JavaScript
|
||||||
calcPickerOffset() {
|
calcPickerOffset = () => {
|
||||||
const elem = this.colorInput
|
const elem = this.colorInput
|
||||||
if(elem) {
|
if(elem) {
|
||||||
const pos = elem.getBoundingClientRect()
|
const pos = elem.getBoundingClientRect()
|
||||||
|
@ -45,7 +42,7 @@ class ColorField extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePicker() {
|
togglePicker = () => {
|
||||||
this.setState({ pickerOpened: !this.state.pickerOpened })
|
this.setState({ pickerOpened: !this.state.pickerOpened })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +82,7 @@ class ColorField extends React.Component {
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="maputnik-color-picker-offset"
|
className="maputnik-color-picker-offset"
|
||||||
onClick={this.togglePicker.bind(this)}
|
onClick={this.togglePicker}
|
||||||
style={{
|
style={{
|
||||||
zIndex: -1,
|
zIndex: -1,
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
|
@ -108,7 +105,7 @@ class ColorField extends React.Component {
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
className="maputnik-color"
|
className="maputnik-color"
|
||||||
ref={(input) => this.colorInput = input}
|
ref={(input) => this.colorInput = input}
|
||||||
onClick={this.togglePicker.bind(this)}
|
onClick={this.togglePicker}
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
placeholder={this.props.default}
|
placeholder={this.props.default}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export default class DocLabel extends React.Component {
|
||||||
<div className="maputnik-doc-popup">
|
<div className="maputnik-doc-popup">
|
||||||
{this.props.doc}
|
{this.props.doc}
|
||||||
</div>
|
</div>
|
||||||
</div >
|
</div>
|
||||||
</label>
|
</label>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
addStop() {
|
addStop = () => {
|
||||||
const stops = this.props.value.stops.slice(0)
|
const stops = this.props.value.stops.slice(0)
|
||||||
const lastStop = stops[stops.length - 1]
|
const lastStop = stops[stops.length - 1]
|
||||||
if (typeof lastStop[0] === "object") {
|
if (typeof lastStop[0] === "object") {
|
||||||
|
@ -53,7 +53,7 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
this.props.onChange(this.props.fieldName, changedValue)
|
this.props.onChange(this.props.fieldName, changedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteStop(stopIdx) {
|
deleteStop = (stopIdx) => {
|
||||||
const stops = this.props.value.stops.slice(0)
|
const stops = this.props.value.stops.slice(0)
|
||||||
stops.splice(stopIdx, 1)
|
stops.splice(stopIdx, 1)
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
this.props.onChange(this.props.fieldName, changedValue)
|
this.props.onChange(this.props.fieldName, changedValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeZoomFunction() {
|
makeZoomFunction = () => {
|
||||||
const zoomFunc = {
|
const zoomFunc = {
|
||||||
stops: [
|
stops: [
|
||||||
[6, this.props.value],
|
[6, this.props.value],
|
||||||
|
@ -79,7 +79,7 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
this.props.onChange(this.props.fieldName, zoomFunc)
|
this.props.onChange(this.props.fieldName, zoomFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
makeDataFunction() {
|
makeDataFunction = () => {
|
||||||
const dataFunc = {
|
const dataFunc = {
|
||||||
property: "",
|
property: "",
|
||||||
type: "categorical",
|
type: "categorical",
|
||||||
|
@ -102,8 +102,8 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
fieldName={this.props.fieldName}
|
fieldName={this.props.fieldName}
|
||||||
fieldSpec={this.props.fieldSpec}
|
fieldSpec={this.props.fieldSpec}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onDeleteStop={this.deleteStop.bind(this)}
|
onDeleteStop={this.deleteStop}
|
||||||
onAddStop={this.addStop.bind(this)}
|
onAddStop={this.addStop}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -114,8 +114,8 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
fieldName={this.props.fieldName}
|
fieldName={this.props.fieldName}
|
||||||
fieldSpec={this.props.fieldSpec}
|
fieldSpec={this.props.fieldSpec}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onDeleteStop={this.deleteStop.bind(this)}
|
onDeleteStop={this.deleteStop}
|
||||||
onAddStop={this.addStop.bind(this)}
|
onAddStop={this.addStop}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -126,8 +126,8 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
fieldName={this.props.fieldName}
|
fieldName={this.props.fieldName}
|
||||||
fieldSpec={this.props.fieldSpec}
|
fieldSpec={this.props.fieldSpec}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onZoomClick={this.makeZoomFunction.bind(this)}
|
onZoomClick={this.makeZoomFunction}
|
||||||
onDataClick={this.makeDataFunction.bind(this)}
|
onDataClick={this.makeDataFunction}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default class PropertyGroup extends React.Component {
|
||||||
spec: PropTypes.object.isRequired,
|
spec: PropTypes.object.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
onPropertyChange(property, newValue) {
|
onPropertyChange = (property, newValue) => {
|
||||||
const group = getGroupName(this.props.spec, this.props.layer.type, property)
|
const group = getGroupName(this.props.spec, this.props.layer.type, property)
|
||||||
this.props.onChange(group , property, newValue)
|
this.props.onChange(group , property, newValue)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ export default class PropertyGroup extends React.Component {
|
||||||
const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName]
|
const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName]
|
||||||
|
|
||||||
return <FunctionSpecField
|
return <FunctionSpecField
|
||||||
onChange={this.onPropertyChange.bind(this)}
|
onChange={this.onPropertyChange}
|
||||||
key={fieldName}
|
key={fieldName}
|
||||||
fieldName={fieldName}
|
fieldName={fieldName}
|
||||||
value={fieldValue === undefined ? fieldSpec.default : fieldValue}
|
value={fieldValue === undefined ? fieldSpec.default : fieldValue}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import color from 'color'
|
|
||||||
|
|
||||||
import ColorField from './ColorField'
|
import ColorField from './ColorField'
|
||||||
import NumberInput from '../inputs/NumberInput'
|
import NumberInput from '../inputs/NumberInput'
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default class FunctionButtons extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let makeZoomButton, makeDataButton
|
let makeZoomButton, makeDataButton
|
||||||
if (this.props.fieldSpec['zoom-function']) {
|
if (this.props.fieldSpec.expression.parameters.includes('zoom')) {
|
||||||
makeZoomButton = <Button
|
makeZoomButton = <Button
|
||||||
className="maputnik-make-zoom-function"
|
className="maputnik-make-zoom-function"
|
||||||
onClick={this.props.onZoomClick}
|
onClick={this.props.onZoomClick}
|
||||||
|
|
|
@ -13,6 +13,30 @@ import docUid from '../../libs/document-uid'
|
||||||
import sortNumerically from '../../libs/sort-numerically'
|
import sortNumerically from '../../libs/sort-numerically'
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We cache a reference for each stop by its index.
|
||||||
|
*
|
||||||
|
* When the stops are reordered the references are also updated (see this.orderStops) this allows React to use the same key for the element and keep keyboard focus.
|
||||||
|
*/
|
||||||
|
function setStopRefs(props, state) {
|
||||||
|
// This is initialsed below only if required to improved performance.
|
||||||
|
let newRefs;
|
||||||
|
|
||||||
|
if(props.value && props.value.stops) {
|
||||||
|
props.value.stops.forEach((val, idx) => {
|
||||||
|
if(!state.refs.hasOwnProperty(idx)) {
|
||||||
|
if(!newRefs) {
|
||||||
|
newRefs = {...state};
|
||||||
|
}
|
||||||
|
newRefs[idx] = docUid("stop-");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return newRefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class ZoomProperty extends React.Component {
|
export default class ZoomProperty extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
@ -29,45 +53,13 @@ export default class ZoomProperty extends React.Component {
|
||||||
]),
|
]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
constructor() {
|
refs: {}
|
||||||
super()
|
|
||||||
this.state = {
|
|
||||||
refs: {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setState({
|
const newRefs = setStopRefs(this.props, this.state);
|
||||||
refs: this.setStopRefs(this.props)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We cache a reference for each stop by its index.
|
|
||||||
*
|
|
||||||
* When the stops are reordered the references are also updated (see this.orderStops) this allows React to use the same key for the element and keep keyboard focus.
|
|
||||||
*/
|
|
||||||
setStopRefs(props) {
|
|
||||||
// This is initialsed below only if required to improved performance.
|
|
||||||
let newRefs;
|
|
||||||
|
|
||||||
if(props.value && props.value.stops) {
|
|
||||||
props.value.stops.forEach((val, idx) => {
|
|
||||||
if(!this.state.refs.hasOwnProperty(idx)) {
|
|
||||||
if(!newRefs) {
|
|
||||||
newRefs = {...this.state.refs};
|
|
||||||
}
|
|
||||||
newRefs[idx] = docUid("stop-");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
||||||
const newRefs = this.setStopRefs(nextProps);
|
|
||||||
if(newRefs) {
|
if(newRefs) {
|
||||||
this.setState({
|
this.setState({
|
||||||
refs: newRefs
|
refs: newRefs
|
||||||
|
@ -75,6 +67,16 @@ export default class ZoomProperty extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props, state) {
|
||||||
|
const newRefs = setStopRefs(props, state);
|
||||||
|
if(newRefs) {
|
||||||
|
return {
|
||||||
|
refs: newRefs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Order the stops altering the refs to reflect their new position.
|
// Order the stops altering the refs to reflect their new position.
|
||||||
orderStopsByZoom(stops) {
|
orderStopsByZoom(stops) {
|
||||||
const mappedWithRef = stops
|
const mappedWithRef = stops
|
||||||
|
|
|
@ -9,9 +9,6 @@ import SingleFilterEditor from './SingleFilterEditor'
|
||||||
import FilterEditorBlock from './FilterEditorBlock'
|
import FilterEditorBlock from './FilterEditorBlock'
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
|
|
||||||
import DeleteIcon from 'react-icons/lib/md/delete'
|
|
||||||
import AddIcon from 'react-icons/lib/fa/plus'
|
|
||||||
|
|
||||||
function hasCombiningFilter(filter) {
|
function hasCombiningFilter(filter) {
|
||||||
return combiningFilterOps.indexOf(filter[0]) >= 0
|
return combiningFilterOps.indexOf(filter[0]) >= 0
|
||||||
}
|
}
|
||||||
|
@ -60,7 +57,7 @@ export default class CombiningFilterEditor extends React.Component {
|
||||||
this.props.onChange(newFilter)
|
this.props.onChange(newFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilterItem() {
|
addFilterItem = () => {
|
||||||
const newFilterItem = this.combiningFilter().slice(0)
|
const newFilterItem = this.combiningFilter().slice(0)
|
||||||
newFilterItem.push(['==', 'name', ''])
|
newFilterItem.push(['==', 'name', ''])
|
||||||
this.props.onChange(newFilterItem)
|
this.props.onChange(newFilterItem)
|
||||||
|
@ -105,7 +102,7 @@ export default class CombiningFilterEditor extends React.Component {
|
||||||
<Button
|
<Button
|
||||||
data-wd-key="layer-filter-button"
|
data-wd-key="layer-filter-button"
|
||||||
className="maputnik-add-filter"
|
className="maputnik-add-filter"
|
||||||
onClick={this.addFilterItem.bind(this)}>
|
onClick={this.addFilterItem}>
|
||||||
Add filter
|
Add filter
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,18 +14,15 @@ class AutocompleteInput extends React.Component {
|
||||||
keepMenuWithinWindowBounds: PropTypes.bool
|
keepMenuWithinWindowBounds: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
maxHeight: MAX_HEIGHT
|
||||||
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
options: [],
|
options: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
maxHeight: MAX_HEIGHT
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
calcMaxHeight() {
|
calcMaxHeight() {
|
||||||
if(this.props.keepMenuWithinWindowBounds) {
|
if(this.props.keepMenuWithinWindowBounds) {
|
||||||
const maxHeight = window.innerHeight - this.autocompleteMenuEl.getBoundingClientRect().top;
|
const maxHeight = window.innerHeight - this.autocompleteMenuEl.getBoundingClientRect().top;
|
||||||
|
@ -38,6 +35,7 @@ class AutocompleteInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.calcMaxHeight();
|
this.calcMaxHeight();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class DynamicArrayInput extends React.Component {
|
||||||
return this.props.value || this.props.default || []
|
return this.props.value || this.props.default || []
|
||||||
}
|
}
|
||||||
|
|
||||||
addValue() {
|
addValue = () => {
|
||||||
const values = this.values.slice(0)
|
const values = this.values.slice(0)
|
||||||
if (this.props.type === 'number') {
|
if (this.props.type === 'number') {
|
||||||
values.push(0)
|
values.push(0)
|
||||||
|
@ -35,7 +35,6 @@ class DynamicArrayInput extends React.Component {
|
||||||
values.push("")
|
values.push("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.props.onChange(values)
|
this.props.onChange(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ class DynamicArrayInput extends React.Component {
|
||||||
{inputs}
|
{inputs}
|
||||||
<Button
|
<Button
|
||||||
className="maputnik-array-add-value"
|
className="maputnik-array-add-value"
|
||||||
onClick={this.addValue.bind(this)}
|
onClick={this.addValue}
|
||||||
>
|
>
|
||||||
Add value
|
Add value
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -17,8 +17,10 @@ class NumberInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
this.setState({ value: nextProps.value })
|
return {
|
||||||
|
value: props.value
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
changeValue(newValue) {
|
changeValue(newValue) {
|
||||||
|
@ -49,7 +51,7 @@ class NumberInput extends React.Component {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
resetValue() {
|
resetValue = () => {
|
||||||
// Reset explicitly to default value if value has been cleared
|
// Reset explicitly to default value if value has been cleared
|
||||||
if(this.state.value === "") {
|
if(this.state.value === "") {
|
||||||
return this.changeValue(this.props.default)
|
return this.changeValue(this.props.default)
|
||||||
|
@ -72,7 +74,7 @@ class NumberInput extends React.Component {
|
||||||
placeholder={this.props.default}
|
placeholder={this.props.default}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
onChange={e => this.changeValue(e.target.value)}
|
onChange={e => this.changeValue(e.target.value)}
|
||||||
onBlur={this.resetValue.bind(this)}
|
onBlur={this.resetValue}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,10 @@ class StringInput extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
this.setState({ value: nextProps.value || '' })
|
return {
|
||||||
|
value: props.value || ''
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -29,10 +29,10 @@ class JSONEditor extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
this.setState({
|
return {
|
||||||
code: JSON.stringify(nextProps.layer, null, 2)
|
code: JSON.stringify(props.layer, null, 2)
|
||||||
})
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
|
|
@ -16,9 +16,6 @@ import LayerSourceLayerBlock from './LayerSourceLayerBlock'
|
||||||
|
|
||||||
import MoreVertIcon from 'react-icons/lib/md/more-vert'
|
import MoreVertIcon from 'react-icons/lib/md/more-vert'
|
||||||
|
|
||||||
import InputBlock from '../inputs/InputBlock'
|
|
||||||
import MultiButtonInput from '../inputs/MultiButtonInput'
|
|
||||||
|
|
||||||
import { changeType, changeProperty } from '../../libs/layer'
|
import { changeType, changeProperty } from '../../libs/layer'
|
||||||
import layout from '../../config/layout.json'
|
import layout from '../../config/layout.json'
|
||||||
|
|
||||||
|
@ -36,7 +33,7 @@ function layoutGroups(layerType) {
|
||||||
title: 'JSON Editor',
|
title: 'JSON Editor',
|
||||||
type: 'jsoneditor'
|
type: 'jsoneditor'
|
||||||
}
|
}
|
||||||
return [layerGroup, filterGroup].concat(layout[layerType].groups).concat([editorGroup])
|
return [layerGroup, filterGroup].concat(layout[layerType].groups).concat([editorGroup])
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Layer editor supporting multiple types of layers. */
|
/** Layer editor supporting multiple types of layers. */
|
||||||
|
@ -79,18 +76,18 @@ export default class LayerEditor extends React.Component {
|
||||||
this.state = { editorGroups }
|
this.state = { editorGroups }
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(props, state) {
|
||||||
const additionalGroups = { ...this.state.editorGroups }
|
const additionalGroups = { ...state.editorGroups }
|
||||||
|
|
||||||
layout[nextProps.layer.type].groups.forEach(group => {
|
layout[props.layer.type].groups.forEach(group => {
|
||||||
if(!(group.title in additionalGroups)) {
|
if(!(group.title in additionalGroups)) {
|
||||||
additionalGroups[group.title] = true
|
additionalGroups[group.title] = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
return {
|
||||||
editorGroups: additionalGroups
|
editorGroups: additionalGroups
|
||||||
})
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildContext () {
|
getChildContext () {
|
||||||
|
|
|
@ -2,13 +2,10 @@ import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import Button from '../Button'
|
|
||||||
import LayerListGroup from './LayerListGroup'
|
import LayerListGroup from './LayerListGroup'
|
||||||
import LayerListItem from './LayerListItem'
|
import LayerListItem from './LayerListItem'
|
||||||
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
|
||||||
import AddModal from '../modals/AddModal'
|
import AddModal from '../modals/AddModal'
|
||||||
|
|
||||||
import style from '../../libs/style.js'
|
|
||||||
import {SortableContainer, SortableHandle} from 'react-sortable-hoc';
|
import {SortableContainer, SortableHandle} from 'react-sortable-hoc';
|
||||||
|
|
||||||
const layerListPropTypes = {
|
const layerListPropTypes = {
|
||||||
|
@ -45,14 +42,11 @@ class LayerListContainer extends React.Component {
|
||||||
onLayerSelect: () => {},
|
onLayerSelect: () => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
state = {
|
||||||
super(props)
|
collapsedGroups: {},
|
||||||
this.state = {
|
areAllGroupsExpanded: false,
|
||||||
collapsedGroups: {},
|
isOpen: {
|
||||||
areAllGroupsExpanded: false,
|
add: false,
|
||||||
isOpen: {
|
|
||||||
add: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +59,7 @@ class LayerListContainer extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLayers() {
|
toggleLayers = () => {
|
||||||
let idx=0
|
let idx=0
|
||||||
|
|
||||||
let newGroups=[]
|
let newGroups=[]
|
||||||
|
@ -179,7 +173,7 @@ class LayerListContainer extends React.Component {
|
||||||
<div className="maputnik-multibutton">
|
<div className="maputnik-multibutton">
|
||||||
<button
|
<button
|
||||||
id="skip-menu"
|
id="skip-menu"
|
||||||
onClick={this.toggleLayers.bind(this)}
|
onClick={this.toggleLayers}
|
||||||
className="maputnik-button">
|
className="maputnik-button">
|
||||||
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
|
{this.state.areAllGroupsExpanded === true ? "Collapse" : "Expand"}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Color from 'color'
|
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
|
|
||||||
import CopyIcon from 'react-icons/lib/md/content-copy'
|
import CopyIcon from 'react-icons/lib/md/content-copy'
|
||||||
|
@ -9,7 +8,6 @@ import VisibilityOffIcon from 'react-icons/lib/md/visibility-off'
|
||||||
import DeleteIcon from 'react-icons/lib/md/delete'
|
import DeleteIcon from 'react-icons/lib/md/delete'
|
||||||
|
|
||||||
import LayerIcon from '../icons/LayerIcon'
|
import LayerIcon from '../icons/LayerIcon'
|
||||||
import LayerEditor from './LayerEditor'
|
|
||||||
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
||||||
|
|
||||||
@SortableHandle
|
@SortableHandle
|
||||||
|
@ -116,7 +114,7 @@ class LayerListItem extends React.Component {
|
||||||
/>
|
/>
|
||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
||||||
action={this.props.visibility === 'visible' ? 'hide' : 'show'}
|
action={this.props.visibility === 'visible' ? 'show' : 'hide'}
|
||||||
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
|
||||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||||
|
|
||||||
class LayerSourceBlock extends React.Component {
|
class LayerSourceBlock extends React.Component {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import * as styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
|
||||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||||
|
|
||||||
class LayerSourceLayer extends React.Component {
|
class LayerSourceLayer extends React.Component {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
|
||||||
import StringInput from '../inputs/StringInput'
|
|
||||||
import LayerIcon from '../icons/LayerIcon'
|
import LayerIcon from '../icons/LayerIcon'
|
||||||
|
|
||||||
function groupFeaturesBySourceLayer(features) {
|
function groupFeaturesBySourceLayer(features) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import MapboxGl from 'mapbox-gl'
|
||||||
import MapboxInspect from 'mapbox-gl-inspect'
|
import MapboxInspect from 'mapbox-gl-inspect'
|
||||||
import FeatureLayerPopup from './FeatureLayerPopup'
|
import FeatureLayerPopup from './FeatureLayerPopup'
|
||||||
import FeaturePropertyPopup from './FeaturePropertyPopup'
|
import FeaturePropertyPopup from './FeaturePropertyPopup'
|
||||||
import style from '../../libs/style.js'
|
|
||||||
import tokens from '../../config/tokens.json'
|
import tokens from '../../config/tokens.json'
|
||||||
import colors from 'mapbox-gl-inspect/lib/colors'
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
import Color from 'color'
|
import Color from 'color'
|
||||||
|
@ -15,6 +14,9 @@ import 'mapbox-gl/dist/mapbox-gl.css'
|
||||||
import '../../mapboxgl.css'
|
import '../../mapboxgl.css'
|
||||||
import '../../libs/mapbox-rtl'
|
import '../../libs/mapbox-rtl'
|
||||||
|
|
||||||
|
|
||||||
|
const IS_SUPPORTED = MapboxGl.supported();
|
||||||
|
|
||||||
function renderPropertyPopup(features) {
|
function renderPropertyPopup(features) {
|
||||||
var mountNode = document.createElement('div');
|
var mountNode = document.createElement('div');
|
||||||
ReactDOM.render(<FeaturePropertyPopup features={features} />, mountNode)
|
ReactDOM.render(<FeaturePropertyPopup features={features} />, mountNode)
|
||||||
|
@ -66,6 +68,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
onDataChange: () => {},
|
onDataChange: () => {},
|
||||||
onLayerSelect: () => {},
|
onLayerSelect: () => {},
|
||||||
mapboxAccessToken: tokens.mapbox,
|
mapboxAccessToken: tokens.mapbox,
|
||||||
|
options: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -80,21 +83,27 @@ export default class MapboxGlMap extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
updateMapFromProps(props) {
|
||||||
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
if(!this.state.map) return
|
if(!this.state.map) return
|
||||||
const metadata = nextProps.mapStyle.metadata || {}
|
const metadata = props.mapStyle.metadata || {}
|
||||||
MapboxGl.accessToken = metadata['maputnik:mapbox_access_token'] || tokens.mapbox
|
MapboxGl.accessToken = metadata['maputnik:mapbox_access_token'] || tokens.mapbox
|
||||||
|
|
||||||
if(!nextProps.inspectModeEnabled) {
|
if(!props.inspectModeEnabled) {
|
||||||
//Mapbox GL now does diffing natively so we don't need to calculate
|
//Mapbox GL now does diffing natively so we don't need to calculate
|
||||||
//the necessary operations ourselves!
|
//the necessary operations ourselves!
|
||||||
this.state.map.setStyle(nextProps.mapStyle, { diff: true})
|
this.state.map.setStyle(props.mapStyle, { diff: true})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
const map = this.state.map;
|
const map = this.state.map;
|
||||||
|
|
||||||
|
this.updateMapFromProps(this.props);
|
||||||
|
|
||||||
if(this.props.inspectModeEnabled !== prevProps.inspectModeEnabled) {
|
if(this.props.inspectModeEnabled !== prevProps.inspectModeEnabled) {
|
||||||
this.state.inspect.toggleInspector()
|
this.state.inspect.toggleInspector()
|
||||||
}
|
}
|
||||||
|
@ -102,10 +111,15 @@ export default class MapboxGlMap extends React.Component {
|
||||||
this.state.inspect.render()
|
this.state.inspect.render()
|
||||||
}
|
}
|
||||||
|
|
||||||
map.showTileBoundaries = this.props.options.showTileBoundaries;
|
if (map) {
|
||||||
|
map.showTileBoundaries = this.props.options.showTileBoundaries;
|
||||||
|
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
const mapOpts = {
|
const mapOpts = {
|
||||||
...this.props.options,
|
...this.props.options,
|
||||||
container: this.container,
|
container: this.container,
|
||||||
|
@ -116,6 +130,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
const map = new MapboxGl.Map(mapOpts);
|
const map = new MapboxGl.Map(mapOpts);
|
||||||
|
|
||||||
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
||||||
|
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
||||||
|
|
||||||
const zoom = new ZoomControl;
|
const zoom = new ZoomControl;
|
||||||
map.addControl(zoom, 'top-right');
|
map.addControl(zoom, 'top-right');
|
||||||
|
@ -161,9 +176,20 @@ export default class MapboxGlMap extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div
|
if(IS_SUPPORTED) {
|
||||||
className="maputnik-map"
|
return <div
|
||||||
ref={x => this.container = x}
|
className="maputnik-map__map"
|
||||||
></div>
|
ref={x => this.container = x}
|
||||||
|
></div>
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return <div
|
||||||
|
className="maputnik-map maputnik-map--error"
|
||||||
|
>
|
||||||
|
<div className="maputnik-map__error-message">
|
||||||
|
Error: Cannot load MapboxGL, WebGL is either unsupported or disabled
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import style from '../../libs/style.js'
|
|
||||||
import isEqual from 'lodash.isequal'
|
|
||||||
import { loadJSON } from '../../libs/urlopen'
|
import { loadJSON } from '../../libs/urlopen'
|
||||||
import 'ol/ol.css'
|
import 'ol/ol.css'
|
||||||
|
|
||||||
|
@ -29,10 +27,10 @@ class OpenLayers3Map extends React.Component {
|
||||||
const styleFunc = olms.apply(this.map, newMapStyle)
|
const styleFunc = olms.apply(this.map, newMapStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
componentDidUpdate() {
|
||||||
require.ensure(["ol", "ol-mapbox-style"], () => {
|
require.ensure(["ol", "ol-mapbox-style"], () => {
|
||||||
if(!this.map) return
|
if(!this.map) return
|
||||||
this.updateStyle(nextProps.mapStyle)
|
this.updateStyle(this.props.mapStyle)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@ import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
|
||||||
import StringInput from '../inputs/StringInput'
|
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
|
|
||||||
import LayerTypeBlock from '../layers/LayerTypeBlock'
|
import LayerTypeBlock from '../layers/LayerTypeBlock'
|
||||||
|
@ -22,7 +20,7 @@ class AddModal extends React.Component {
|
||||||
sources: PropTypes.object.isRequired,
|
sources: PropTypes.object.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
addLayer() {
|
addLayer = () => {
|
||||||
const changedLayers = this.props.layers.slice(0)
|
const changedLayers = this.props.layers.slice(0)
|
||||||
const layer = {
|
const layer = {
|
||||||
id: this.state.id,
|
id: this.state.id,
|
||||||
|
@ -151,7 +149,7 @@ class AddModal extends React.Component {
|
||||||
}
|
}
|
||||||
<Button
|
<Button
|
||||||
className="maputnik-add-layer-button"
|
className="maputnik-add-layer-button"
|
||||||
onClick={this.addLayer.bind(this)}
|
onClick={this.addLayer}
|
||||||
data-wd-key="add-layer"
|
data-wd-key="add-layer"
|
||||||
>
|
>
|
||||||
Add Layer
|
Add Layer
|
||||||
|
|
|
@ -10,207 +10,9 @@ import Button from '../Button'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import MdFileDownload from 'react-icons/lib/md/file-download'
|
import MdFileDownload from 'react-icons/lib/md/file-download'
|
||||||
import TiClipboard from 'react-icons/lib/ti/clipboard'
|
import TiClipboard from 'react-icons/lib/ti/clipboard'
|
||||||
import style from '../../libs/style.js'
|
import style from '../../libs/style'
|
||||||
import GitHub from 'github-api'
|
|
||||||
import { CopyToClipboard } from 'react-copy-to-clipboard'
|
|
||||||
|
|
||||||
|
|
||||||
class Gist extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
mapStyle: PropTypes.object.isRequired,
|
|
||||||
onStyleChanged: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
preview: false,
|
|
||||||
public: false,
|
|
||||||
saving: false,
|
|
||||||
latestGist: null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
preview: !!(nextProps.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onSave() {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
saving: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const preview = this.state.preview;
|
|
||||||
|
|
||||||
const mapboxToken = (this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token'];
|
|
||||||
|
|
||||||
const mapStyleStr = preview ?
|
|
||||||
styleSpec.format(stripAccessTokens(style.replaceAccessToken(this.props.mapStyle))) :
|
|
||||||
styleSpec.format(stripAccessTokens(this.props.mapStyle));
|
|
||||||
const styleTitle = this.props.mapStyle.name || 'Style';
|
|
||||||
const htmlStr = `
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>`+styleTitle+` Preview</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.css" />
|
|
||||||
<script src="https://api.mapbox.com/mapbox-gl-js/v0.44.0/mapbox-gl.js"></script>
|
|
||||||
<style>
|
|
||||||
body { margin:0; padding:0; }
|
|
||||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id='map'></div>
|
|
||||||
<script>
|
|
||||||
mapboxgl.accessToken = '${mapboxToken}';
|
|
||||||
var map = new mapboxgl.Map({
|
|
||||||
container: 'map',
|
|
||||||
style: 'style.json',
|
|
||||||
attributionControl: true,
|
|
||||||
hash: true
|
|
||||||
});
|
|
||||||
map.addControl(new mapboxgl.NavigationControl());
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`
|
|
||||||
const files = {
|
|
||||||
"style.json": {
|
|
||||||
content: mapStyleStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(preview) {
|
|
||||||
files["index.html"] = {
|
|
||||||
content: htmlStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const gh = new GitHub();
|
|
||||||
let gist = gh.getGist(); // not a gist yet
|
|
||||||
gist.create({
|
|
||||||
public: this.state.public,
|
|
||||||
description: styleTitle,
|
|
||||||
files: files
|
|
||||||
}).then(function({data}) {
|
|
||||||
return gist.read();
|
|
||||||
}).then(function({data}) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
latestGist: data,
|
|
||||||
saving: false,
|
|
||||||
});
|
|
||||||
}.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
onPreviewChange(value) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
preview: value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onPublicChange(value) {
|
|
||||||
this.setState({
|
|
||||||
...this.state,
|
|
||||||
public: value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
changeMetadataProperty(property, value) {
|
|
||||||
const changedStyle = {
|
|
||||||
...this.props.mapStyle,
|
|
||||||
metadata: {
|
|
||||||
...this.props.mapStyle.metadata,
|
|
||||||
[property]: value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.props.onStyleChanged(changedStyle)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPreviewLink() {
|
|
||||||
const gist = this.state.latestGist;
|
|
||||||
const user = gist.user || 'anonymous';
|
|
||||||
const preview = !!gist.files['index.html'];
|
|
||||||
if(preview) {
|
|
||||||
return <span><a target="_blank" rel="noopener noreferrer" href={"https://bl.ocks.org/"+user+"/"+gist.id}>Preview</a>,{' '}</span>
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLatestGist() {
|
|
||||||
const gist = this.state.latestGist;
|
|
||||||
const saving = this.state.saving;
|
|
||||||
if(saving) {
|
|
||||||
return <p>Saving...</p>
|
|
||||||
} else if(gist) {
|
|
||||||
const user = gist.user || 'anonymous';
|
|
||||||
const rawGistLink = "https://gist.githubusercontent.com/" + user + "/" + gist.id + "/raw/" + gist.history[0].version + "/style.json"
|
|
||||||
const maputnikStyleLink = "https://maputnik.github.io/editor/?style=" + rawGistLink
|
|
||||||
return <div className="maputnik-render-gist">
|
|
||||||
<p>
|
|
||||||
Latest saved gist:{' '}
|
|
||||||
{this.renderPreviewLink(this)}
|
|
||||||
<a target="_blank" rel="noopener noreferrer" href={"https://gist.github.com/" + user + "/" + gist.id}>Source</a>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<CopyToClipboard text={maputnikStyleLink}>
|
|
||||||
<span>Share this style: <Button><TiClipboard size={18} /></Button></span>
|
|
||||||
</CopyToClipboard>
|
|
||||||
<StringInput value={maputnikStyleLink} />
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <div className="maputnik-export-gist">
|
|
||||||
<Button onClick={this.onSave.bind(this)}>
|
|
||||||
<MdFileDownload />
|
|
||||||
Save to Gist (anonymous)
|
|
||||||
</Button>
|
|
||||||
<div className="maputnik-modal-sub-section">
|
|
||||||
<CheckboxInput
|
|
||||||
value={this.state.public}
|
|
||||||
name='gist-style-public'
|
|
||||||
onChange={this.onPublicChange.bind(this)}
|
|
||||||
/>
|
|
||||||
<span> Public gist</span>
|
|
||||||
</div>
|
|
||||||
<div className="maputnik-modal-sub-section">
|
|
||||||
<CheckboxInput
|
|
||||||
value={this.state.preview}
|
|
||||||
name='gist-style-preview'
|
|
||||||
onChange={this.onPreviewChange.bind(this)}
|
|
||||||
/>
|
|
||||||
<span> Include preview</span>
|
|
||||||
</div>
|
|
||||||
{this.state.preview ?
|
|
||||||
<div>
|
|
||||||
<InputBlock
|
|
||||||
label={"OpenMapTiles Access Token: "}>
|
|
||||||
<StringInput
|
|
||||||
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
|
|
||||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}/>
|
|
||||||
</InputBlock>
|
|
||||||
<InputBlock
|
|
||||||
label={"Mapbox Access Token: "}>
|
|
||||||
<StringInput
|
|
||||||
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
|
|
||||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}/>
|
|
||||||
</InputBlock>
|
|
||||||
<a target="_blank" rel="noopener noreferrer" href="https://openmaptiles.com/hosting/">Get your free access token</a>
|
|
||||||
</div>
|
|
||||||
: null}
|
|
||||||
{this.renderLatestGist()}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stripAccessTokens(mapStyle) {
|
function stripAccessTokens(mapStyle) {
|
||||||
const changedMetadata = { ...mapStyle.metadata }
|
const changedMetadata = { ...mapStyle.metadata }
|
||||||
|
@ -235,10 +37,24 @@ class ExportModal extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadStyle() {
|
downloadStyle() {
|
||||||
const blob = new Blob([styleSpec.format(stripAccessTokens(this.props.mapStyle))], {type: "application/json;charset=utf-8"});
|
const tokenStyle = styleSpec.format(stripAccessTokens(style.replaceAccessTokens(this.props.mapStyle)));
|
||||||
|
|
||||||
|
const blob = new Blob([tokenStyle], {type: "application/json;charset=utf-8"});
|
||||||
saveAs(blob, this.props.mapStyle.id + ".json");
|
saveAs(blob, this.props.mapStyle.id + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeMetadataProperty(property, value) {
|
||||||
|
const changedStyle = {
|
||||||
|
...this.props.mapStyle,
|
||||||
|
metadata: {
|
||||||
|
...this.props.mapStyle.metadata,
|
||||||
|
[property]: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.onStyleChanged(changedStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Modal
|
return <Modal
|
||||||
data-wd-key="export-modal"
|
data-wd-key="export-modal"
|
||||||
|
@ -252,16 +68,34 @@ class ExportModal extends React.Component {
|
||||||
<p>
|
<p>
|
||||||
Download a JSON style to your computer.
|
Download a JSON style to your computer.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<InputBlock label={"MapTiler Access Token: "}>
|
||||||
|
<StringInput
|
||||||
|
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
|
||||||
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
<InputBlock label={"Mapbox Access Token: "}>
|
||||||
|
<StringInput
|
||||||
|
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
|
||||||
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
<InputBlock label={"Thunderforest Access Token: "}>
|
||||||
|
<StringInput
|
||||||
|
value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']}
|
||||||
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
</p>
|
||||||
|
|
||||||
<Button onClick={this.downloadStyle.bind(this)}>
|
<Button onClick={this.downloadStyle.bind(this)}>
|
||||||
<MdFileDownload />
|
<MdFileDownload />
|
||||||
Download
|
Download
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="maputnik-modal-section hide">
|
|
||||||
<h4>Save style</h4>
|
|
||||||
<Gist mapStyle={this.props.mapStyle} onStyleChanged={this.props.onStyleChanged}/>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,6 @@ class LoadingModal extends React.Component {
|
||||||
message: PropTypes.node.isRequired,
|
message: PropTypes.node.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
underlayOnClick(e) {
|
underlayOnClick(e) {
|
||||||
// This stops click events falling through to underlying modals.
|
// This stops click events falling through to underlying modals.
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
|
@ -4,7 +4,6 @@ import LoadingModal from './LoadingModal'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
import FileReaderInput from 'react-file-reader-input'
|
import FileReaderInput from 'react-file-reader-input'
|
||||||
import request from 'request'
|
|
||||||
|
|
||||||
import FileUploadIcon from 'react-icons/lib/md/file-upload'
|
import FileUploadIcon from 'react-icons/lib/md/file-upload'
|
||||||
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
||||||
|
@ -74,42 +73,48 @@ class OpenModal extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onStyleSelect(styleUrl) {
|
onStyleSelect = (styleUrl) => {
|
||||||
this.clearError();
|
this.clearError();
|
||||||
|
|
||||||
const reqOpts = {
|
const activeRequest = fetch(styleUrl, {
|
||||||
url: styleUrl,
|
mode: 'cors',
|
||||||
withCredentials: false,
|
credentials: "same-origin"
|
||||||
}
|
})
|
||||||
|
.then(function(response) {
|
||||||
const activeRequest = request(reqOpts, (error, response, body) => {
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((body) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
activeRequest: null,
|
activeRequest: null,
|
||||||
activeRequestUrl: null
|
activeRequestUrl: null
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!error && response.statusCode == 200) {
|
const mapStyle = style.ensureStyleValidity(body)
|
||||||
const mapStyle = style.ensureStyleValidity(JSON.parse(body))
|
console.log('Loaded style ', mapStyle.id)
|
||||||
console.log('Loaded style ', mapStyle.id)
|
this.props.onStyleOpen(mapStyle)
|
||||||
this.props.onStyleOpen(mapStyle)
|
this.onOpenToggle()
|
||||||
this.onOpenToggle()
|
})
|
||||||
} else {
|
.catch((err) => {
|
||||||
console.warn('Could not open the style URL', styleUrl)
|
this.setState({
|
||||||
}
|
activeRequest: null,
|
||||||
|
activeRequestUrl: null
|
||||||
|
});
|
||||||
|
console.error(err);
|
||||||
|
console.warn('Could not open the style URL', styleUrl)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
activeRequest: activeRequest,
|
activeRequest: activeRequest,
|
||||||
activeRequestUrl: reqOpts.url
|
activeRequestUrl: styleUrl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onOpenUrl() {
|
onOpenUrl = () => {
|
||||||
const url = this.styleUrlElement.value;
|
const url = this.styleUrlElement.value;
|
||||||
this.onStyleSelect(url);
|
this.onStyleSelect(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpload(_, files) {
|
onUpload = (_, files) => {
|
||||||
const [e, file] = files[0];
|
const [e, file] = files[0];
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
@ -146,7 +151,7 @@ class OpenModal extends React.Component {
|
||||||
url={style.url}
|
url={style.url}
|
||||||
title={style.title}
|
title={style.title}
|
||||||
thumbnailUrl={style.thumbnail}
|
thumbnailUrl={style.thumbnail}
|
||||||
onSelect={this.onStyleSelect.bind(this)}
|
onSelect={this.onStyleSelect}
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -170,7 +175,7 @@ class OpenModal extends React.Component {
|
||||||
<section className="maputnik-modal-section">
|
<section className="maputnik-modal-section">
|
||||||
<h2>Upload Style</h2>
|
<h2>Upload Style</h2>
|
||||||
<p>Upload a JSON style from your computer.</p>
|
<p>Upload a JSON style from your computer.</p>
|
||||||
<FileReaderInput onChange={this.onUpload.bind(this)} tabIndex="-1">
|
<FileReaderInput onChange={this.onUpload} tabIndex="-1">
|
||||||
<Button className="maputnik-upload-button"><FileUploadIcon /> Upload</Button>
|
<Button className="maputnik-upload-button"><FileUploadIcon /> Upload</Button>
|
||||||
</FileReaderInput>
|
</FileReaderInput>
|
||||||
</section>
|
</section>
|
||||||
|
@ -182,7 +187,7 @@ class OpenModal extends React.Component {
|
||||||
</p>
|
</p>
|
||||||
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
||||||
<div>
|
<div>
|
||||||
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
|
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl}>Open URL</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,6 @@ class SettingsModal extends React.Component {
|
||||||
onOpenToggle: PropTypes.func.isRequired,
|
onOpenToggle: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
changeStyleProperty(property, value) {
|
changeStyleProperty(property, value) {
|
||||||
const changedStyle = {
|
const changedStyle = {
|
||||||
...this.props.mapStyle,
|
...this.props.mapStyle,
|
||||||
|
@ -86,7 +82,7 @@ class SettingsModal extends React.Component {
|
||||||
/>
|
/>
|
||||||
</InputBlock>
|
</InputBlock>
|
||||||
|
|
||||||
<InputBlock label={"OpenMapTiles Access Token"} doc={"Public access token for the OpenMapTiles CDN."}>
|
<InputBlock label={"MapTiler Access Token"} doc={"Public access token for MapTiler Cloud."}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||||
value={metadata['maputnik:openmaptiles_access_token']}
|
value={metadata['maputnik:openmaptiles_access_token']}
|
||||||
|
@ -94,12 +90,20 @@ class SettingsModal extends React.Component {
|
||||||
/>
|
/>
|
||||||
</InputBlock>
|
</InputBlock>
|
||||||
|
|
||||||
|
<InputBlock label={"Thunderforest Access Token"} doc={"Public access token for Thunderforest services."}>
|
||||||
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.maputnik:thunderforest_access_token"
|
||||||
|
value={metadata['maputnik:thunderforest_access_token']}
|
||||||
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
|
||||||
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
||||||
<SelectInput {...inputProps}
|
<SelectInput {...inputProps}
|
||||||
data-wd-key="modal-settings.maputnik:renderer"
|
data-wd-key="modal-settings.maputnik:renderer"
|
||||||
options={[
|
options={[
|
||||||
['mbgljs', 'MapboxGL JS'],
|
['mbgljs', 'MapboxGL JS'],
|
||||||
['ol3', 'Open Layers 3'],
|
// ['ol3', 'Open Layers 3'],
|
||||||
]}
|
]}
|
||||||
value={metadata['maputnik:renderer'] || 'mbgljs'}
|
value={metadata['maputnik:renderer'] || 'mbgljs'}
|
||||||
onChange={this.changeMetadataProperty.bind(this, 'maputnik:renderer')}
|
onChange={this.changeMetadataProperty.bind(this, 'maputnik:renderer')}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import Button from '../Button'
|
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,10 +10,6 @@ class ShortcutsModal extends React.Component {
|
||||||
onOpenToggle: PropTypes.func.isRequired,
|
onOpenToggle: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const help = [
|
const help = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -236,9 +236,12 @@ class SourcesModal extends React.Component {
|
||||||
<p>
|
<p>
|
||||||
Add one of the publicly available sources to your style.
|
Add one of the publicly available sources to your style.
|
||||||
</p>
|
</p>
|
||||||
<div style={{maxwidth: 500}}>
|
<div className="maputnik-public-sources" style={{maxwidth: 500}}>
|
||||||
{tilesetOptions}
|
{tilesetOptions}
|
||||||
</div>
|
</div>
|
||||||
|
<p>
|
||||||
|
<strong>Note:</strong> Some of the tilesets are not optimised for online use, and as a result the file sizes of the tiles can be quite large (heavy) for online vector rendering. Please review any tilesets before use.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="maputnik-modal-section">
|
<div className="maputnik-modal-section">
|
||||||
|
|
39
src/components/modals/SurveyModal.jsx
Normal file
39
src/components/modals/SurveyModal.jsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import Button from '../Button'
|
||||||
|
import Modal from './Modal'
|
||||||
|
|
||||||
|
import logoImage from 'maputnik-design/logos/logo-color.svg'
|
||||||
|
|
||||||
|
class SurveyModal extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onOpenToggle: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick = () => {
|
||||||
|
window.open('https://gregorywolanski.typeform.com/to/cPgaSY', '_blank');
|
||||||
|
|
||||||
|
this.props.onOpenToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <Modal
|
||||||
|
data-wd-key="modal-survey"
|
||||||
|
isOpen={this.props.isOpen}
|
||||||
|
onOpenToggle={this.props.onOpenToggle}
|
||||||
|
title="Maputnik Survey"
|
||||||
|
>
|
||||||
|
<div className="maputnik-modal-survey">
|
||||||
|
<img className="maputnik-modal-survey__logo" src={logoImage} alt="" width="128" />
|
||||||
|
<h1>You + Maputnik = Maputnik better for you</h1>
|
||||||
|
<p className="maputnik-modal-survey__description">We don’t track you, so we don’t know how you use Maputnik. Help us make Maputnik better for you by completing a 7–minute survey carried out by our contributing designer.</p>
|
||||||
|
<Button onClick={this.onClick} className="maputnik-big-button maputnik-white-button maputnik-wide-button">Take the Maputnik Survey</Button>
|
||||||
|
<p className="maputnik-modal-survey__footnote">It takes 7 minutes, tops! Every question is optional.</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SurveyModal
|
|
@ -193,7 +193,8 @@
|
||||||
"raster-brightness-max",
|
"raster-brightness-max",
|
||||||
"raster-saturation",
|
"raster-saturation",
|
||||||
"raster-contrast",
|
"raster-contrast",
|
||||||
"raster-fade-duration"
|
"raster-fade-duration",
|
||||||
|
"raster-resampling"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -26,31 +26,13 @@
|
||||||
{
|
{
|
||||||
"id": "osm-liberty",
|
"id": "osm-liberty",
|
||||||
"title": "OSM Liberty",
|
"title": "OSM Liberty",
|
||||||
"url": "https://rawgit.com/lukasmartinelli/osm-liberty/gh-pages/style.json",
|
"url": "https://rawgit.com/maputnik/osm-liberty/gh-pages/style.json",
|
||||||
"thumbnail": "https://cdn.rawgit.com/lukasmartinelli/osm-liberty/gh-pages/thumbnail.png"
|
"thumbnail": "https://cdn.rawgit.com/maputnik/osm-liberty/gh-pages/thumbnail.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "empty-style",
|
"id": "empty-style",
|
||||||
"title": "Empty Style",
|
"title": "Empty Style",
|
||||||
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
||||||
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mapbox-satellite",
|
|
||||||
"title": "Mapbox Satellite",
|
|
||||||
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/satellite-v9.json",
|
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/mapbox-satellite.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mapbox-bright",
|
|
||||||
"title": "Mapbox Bright",
|
|
||||||
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/bright-v9.json",
|
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/mapbox-bright.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "mapbox-basic",
|
|
||||||
"title": "Mapbox Basic",
|
|
||||||
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/basic-v9.json",
|
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/mapbox-basic.png"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
{
|
{
|
||||||
"mapbox-streets": {
|
|
||||||
"type": "vector",
|
|
||||||
"url": "mapbox://mapbox.mapbox-streets-v7",
|
|
||||||
"title": "Mapbox Streets"
|
|
||||||
},
|
|
||||||
"openmaptiles": {
|
"openmaptiles": {
|
||||||
"type": "vector",
|
"type": "vector",
|
||||||
"url": "https://free.tilehosting.com/data/v3.json?key={key}",
|
"url": "https://free.tilehosting.com/data/v3.json?key={key}",
|
||||||
"title": "OpenMapTiles"
|
"title": "OpenMapTiles"
|
||||||
|
},
|
||||||
|
"thunderforest_transport": {
|
||||||
|
"type": "vector",
|
||||||
|
"url": "https://tile.thunderforest.com/thunderforest.transport-v1.json?apikey={key}",
|
||||||
|
"title": "Thunderforest Transport (heavy)"
|
||||||
|
},
|
||||||
|
"thunderforest_outdoors": {
|
||||||
|
"type": "vector",
|
||||||
|
"url": "https://tile.thunderforest.com/thunderforest.outdoors-v1.json?apikey={key}",
|
||||||
|
"title": "Thunderforest Outdoors (heavy)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"mapbox": "pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6ImNpeHJmNXNmZTAwNHIycXBid2NqdTJibjMifQ.Dv1-GDpTWi0NP6xW9Fct1w",
|
"mapbox": "pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6ImNpeHJmNXNmZTAwNHIycXBid2NqdTJibjMifQ.Dv1-GDpTWi0NP6xW9Fct1w",
|
||||||
"openmaptiles": "Og58UhhtiiTaLVlPtPgs"
|
"openmaptiles": "Og58UhhtiiTaLVlPtPgs",
|
||||||
|
"thunderforest": "b71f7f0ba4064f5eb9e903859a9cf5c6"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import lodash from 'lodash'
|
import throttle from 'lodash.throttle'
|
||||||
|
|
||||||
|
|
||||||
// Throttle for 3 seconds so when a user enables it they don't have to refresh the page.
|
// Throttle for 3 seconds so when a user enables it they don't have to refresh the page.
|
||||||
const reducedMotionEnabled = lodash.throttle(() => {
|
const reducedMotionEnabled = throttle(() => {
|
||||||
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import request from 'request'
|
|
||||||
import style from './style.js'
|
import style from './style.js'
|
||||||
import ReconnectingWebSocket from 'reconnecting-websocket'
|
import ReconnectingWebSocket from 'reconnecting-websocket'
|
||||||
|
|
||||||
|
@ -14,15 +13,20 @@ export class ApiStyleStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cb) {
|
init(cb) {
|
||||||
request(localUrl + '/styles', (error, response, body) => {
|
fetch(localUrl + '/styles', {
|
||||||
if (!error && body && response.statusCode == 200) {
|
mode: 'cors',
|
||||||
const styleIds = JSON.parse(body)
|
})
|
||||||
this.latestStyleId = styleIds[0]
|
.then(function(response) {
|
||||||
this.notifyLocalChanges()
|
return response.json();
|
||||||
cb(null)
|
})
|
||||||
} else {
|
.then(function(body) {
|
||||||
cb(new Error('Can not connect to style API'))
|
const styleIds = body;
|
||||||
}
|
this.latestStyleId = styleIds[0]
|
||||||
|
this.notifyLocalChanges()
|
||||||
|
cb(null)
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
cb(new Error('Can not connect to style API'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,8 +48,14 @@ export class ApiStyleStore {
|
||||||
|
|
||||||
latestStyle(cb) {
|
latestStyle(cb) {
|
||||||
if(this.latestStyleId) {
|
if(this.latestStyleId) {
|
||||||
request(localUrl + '/styles/' + this.latestStyleId, (error, response, body) => {
|
fetch(localUrl + '/styles/' + this.latestStyleId, {
|
||||||
cb(style.ensureStyleValidity(JSON.parse(body)))
|
mode: 'cors',
|
||||||
|
})
|
||||||
|
.then(function(response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function(body) {
|
||||||
|
cb(style.ensureStyleValidity(body))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No latest style available. You need to init the api backend first.')
|
throw new Error('No latest style available. You need to init the api backend first.')
|
||||||
|
@ -55,11 +65,15 @@ export class ApiStyleStore {
|
||||||
// Save current style replacing previous version
|
// Save current style replacing previous version
|
||||||
save(mapStyle) {
|
save(mapStyle) {
|
||||||
const id = mapStyle.id
|
const id = mapStyle.id
|
||||||
request.put({
|
fetch(localUrl + '/styles/' + id, {
|
||||||
url: localUrl + '/styles/' + id,
|
method: "PUT",
|
||||||
json: true,
|
mode: 'cors',
|
||||||
body: mapStyle
|
headers: {
|
||||||
}, (error, response, body) => {
|
"Content-Type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(mapStyle)
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
if(error) console.error(error)
|
if(error) console.error(error)
|
||||||
})
|
})
|
||||||
return mapStyle
|
return mapStyle
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import MapboxGl from 'mapbox-gl/dist/mapbox-gl.js'
|
import MapboxGl from 'mapbox-gl'
|
||||||
|
|
||||||
// Load mapbox-gl-rtl-text using object urls without needing http://localhost for AJAX.
|
// Load mapbox-gl-rtl-text using object urls without needing http://localhost for AJAX.
|
||||||
const data = require("base64-loader?mimetype=text/javascript!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.js");
|
const data = require("raw-loader?mimetype=text/javascript!@mapbox/mapbox-gl-rtl-text/mapbox-gl-rtl-text.js");
|
||||||
|
|
||||||
const blob = new window.Blob([window.atob(data)]);
|
const blob = new window.Blob([data]);
|
||||||
const objectUrl = window.URL.createObjectURL(blob, {
|
const objectUrl = window.URL.createObjectURL(blob, {
|
||||||
type: "text/javascript"
|
type: "text/javascript"
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
import request from 'request'
|
|
||||||
import npmurl from 'url'
|
import npmurl from 'url'
|
||||||
|
|
||||||
function loadJSON(url, defaultValue, cb) {
|
function loadJSON(url, defaultValue, cb) {
|
||||||
request({
|
fetch(url, {
|
||||||
url: url,
|
mode: 'cors',
|
||||||
withCredentials: false,
|
credentials: "same-origin"
|
||||||
}, (error, response, body) => {
|
})
|
||||||
if (!error && body && response.statusCode == 200) {
|
.then(function(response) {
|
||||||
try {
|
return response.json();
|
||||||
cb(JSON.parse(body))
|
})
|
||||||
} catch(err) {
|
.then(function(body) {
|
||||||
console.error(err)
|
cb(body)
|
||||||
cb(defaultValue)
|
})
|
||||||
}
|
.catch(function() {
|
||||||
} else {
|
console.warn('Can not metadata for ' + url)
|
||||||
console.warn('Can not metadata for ' + url)
|
cb(defaultValue)
|
||||||
cb(defaultValue)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import React from 'react';
|
|
||||||
import deref from '@mapbox/mapbox-gl-style-spec/deref'
|
import deref from '@mapbox/mapbox-gl-style-spec/deref'
|
||||||
import tokens from '../config/tokens.json'
|
import tokens from '../config/tokens.json'
|
||||||
|
|
||||||
|
@ -54,18 +53,28 @@ function indexOfLayer(layers, layerId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAccessToken(mapStyle, opts={}) {
|
function getAccessToken(sourceName, mapStyle, opts) {
|
||||||
const omtSource = mapStyle.sources.openmaptiles
|
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
||||||
if(!omtSource) return mapStyle
|
sourceName = "thunderforest"
|
||||||
if(!omtSource.hasOwnProperty("url")) return mapStyle
|
}
|
||||||
|
|
||||||
const metadata = mapStyle.metadata || {}
|
const metadata = mapStyle.metadata || {}
|
||||||
let accessToken = metadata['maputnik:openmaptiles_access_token'];
|
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
||||||
|
|
||||||
if(opts.allowFallback && !accessToken) {
|
if(opts.allowFallback && !accessToken) {
|
||||||
accessToken = tokens.openmaptiles;
|
accessToken = tokens[sourceName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceSourceAccessToken(mapStyle, sourceName, opts={}) {
|
||||||
|
const source = mapStyle.sources[sourceName]
|
||||||
|
if(!source) return mapStyle
|
||||||
|
if(!source.hasOwnProperty("url")) return mapStyle
|
||||||
|
|
||||||
|
const accessToken = getAccessToken(sourceName, mapStyle, opts)
|
||||||
|
|
||||||
if(!accessToken) {
|
if(!accessToken) {
|
||||||
// Early exit.
|
// Early exit.
|
||||||
return mapStyle;
|
return mapStyle;
|
||||||
|
@ -73,16 +82,34 @@ function replaceAccessToken(mapStyle, opts={}) {
|
||||||
|
|
||||||
const changedSources = {
|
const changedSources = {
|
||||||
...mapStyle.sources,
|
...mapStyle.sources,
|
||||||
openmaptiles: {
|
[sourceName]: {
|
||||||
...omtSource,
|
...source,
|
||||||
url: omtSource.url.replace('{key}', accessToken)
|
url: source.url.replace('{key}', accessToken)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const changedStyle = {
|
const changedStyle = {
|
||||||
...mapStyle,
|
...mapStyle,
|
||||||
glyphs: mapStyle.glyphs ? mapStyle.glyphs.replace('{key}', accessToken) : mapStyle.glyphs,
|
|
||||||
sources: changedSources
|
sources: changedSources
|
||||||
}
|
}
|
||||||
|
return changedStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceAccessTokens(mapStyle, opts={}) {
|
||||||
|
let changedStyle = mapStyle
|
||||||
|
|
||||||
|
Object.keys(mapStyle.sources).forEach((sourceName) => {
|
||||||
|
changedStyle = replaceSourceAccessToken(changedStyle, sourceName, opts);
|
||||||
|
})
|
||||||
|
|
||||||
|
if (mapStyle.glyphs && mapStyle.glyphs.match(/\.tilehosting\.com/)) {
|
||||||
|
const newAccessToken = getAccessToken("openmaptiles", mapStyle, opts);
|
||||||
|
if (newAccessToken) {
|
||||||
|
changedStyle = {
|
||||||
|
...changedStyle,
|
||||||
|
glyphs: mapStyle.glyphs.replace('{key}', newAccessToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return changedStyle
|
return changedStyle
|
||||||
}
|
}
|
||||||
|
@ -92,5 +119,5 @@ export default {
|
||||||
emptyStyle,
|
emptyStyle,
|
||||||
indexOfLayer,
|
indexOfLayer,
|
||||||
generateId,
|
generateId,
|
||||||
replaceAccessToken,
|
replaceAccessTokens,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { colorizeLayers } from './style.js'
|
|
||||||
import style from './style.js'
|
import style from './style.js'
|
||||||
import { loadStyleUrl } from './urlopen'
|
import { loadStyleUrl } from './urlopen'
|
||||||
import publicSources from '../config/styles.json'
|
import publicSources from '../config/styles.json'
|
||||||
import request from 'request'
|
|
||||||
|
|
||||||
const storagePrefix = "maputnik"
|
const storagePrefix = "maputnik"
|
||||||
const stylePrefix = 'style'
|
const stylePrefix = 'style'
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import request from 'request'
|
|
||||||
import url from 'url'
|
import url from 'url'
|
||||||
import style from './style.js'
|
import style from './style.js'
|
||||||
|
|
||||||
|
@ -9,34 +8,40 @@ export function initialStyleUrl() {
|
||||||
|
|
||||||
export function loadStyleUrl(styleUrl, cb) {
|
export function loadStyleUrl(styleUrl, cb) {
|
||||||
console.log('Loading style', styleUrl)
|
console.log('Loading style', styleUrl)
|
||||||
request({
|
fetch(styleUrl, {
|
||||||
url: styleUrl,
|
mode: 'cors',
|
||||||
withCredentials: false,
|
credentials: "same-origin"
|
||||||
}, (error, response, body) => {
|
})
|
||||||
if (!error && response.statusCode == 200) {
|
.then(function(response) {
|
||||||
cb(style.ensureStyleValidity(JSON.parse(body)))
|
return response.json();
|
||||||
} else {
|
})
|
||||||
console.warn('Could not fetch default style', styleUrl)
|
.then(function(body) {
|
||||||
cb(style.emptyStyle)
|
cb(style.ensureStyleValidity(body))
|
||||||
}
|
})
|
||||||
|
.catch(function() {
|
||||||
|
console.warn('Could not fetch default style', styleUrl)
|
||||||
|
cb(style.emptyStyle)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadJSON(url, defaultValue, cb) {
|
export function loadJSON(url, defaultValue, cb) {
|
||||||
request({
|
fetch(url, {
|
||||||
url: url,
|
mode: 'cors',
|
||||||
withCredentials: false,
|
credentials: "same-origin"
|
||||||
}, (error, response, body) => {
|
})
|
||||||
if (!error && body && response.statusCode == 200) {
|
.then(function(response) {
|
||||||
try {
|
return response.json();
|
||||||
cb(JSON.parse(body))
|
})
|
||||||
} catch(err) {
|
.then(function(body) {
|
||||||
console.error(err)
|
try {
|
||||||
cb(defaultValue)
|
cb(body)
|
||||||
}
|
} catch(err) {
|
||||||
} else {
|
console.error(err)
|
||||||
console.error('Can not load JSON from ' + url)
|
|
||||||
cb(defaultValue)
|
cb(defaultValue)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch(function() {
|
||||||
|
console.error('Can not load JSON from ' + url)
|
||||||
|
cb(defaultValue)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
// The UI is 100% height so prevent bounce scroll on OSX
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
|
|
|
@ -1,11 +1,31 @@
|
||||||
// MAP
|
// MAP
|
||||||
.maputnik-map {
|
.maputnik-map__container {
|
||||||
|
display: flex;
|
||||||
position: fixed !important;
|
position: fixed !important;
|
||||||
top: $toolbar-height + $toolbar-offset;
|
top: $toolbar-height + $toolbar-offset;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
height: calc(100% - #{$toolbar-height + $toolbar-offset});
|
height: calc(100% - #{$toolbar-height + $toolbar-offset});
|
||||||
width: 75%;
|
width: calc(
|
||||||
|
100%
|
||||||
|
- 200px /* layer list */
|
||||||
|
- 350px /* layer editor */
|
||||||
|
);
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__error-message {
|
||||||
|
margin: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-map__map {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOC LABEL
|
// DOC LABEL
|
||||||
|
@ -56,6 +76,7 @@
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: lighten($color-midgray, 12);
|
background-color: lighten($color-midgray, 12);
|
||||||
|
@ -70,6 +91,20 @@
|
||||||
font-size: $font-size-5;
|
font-size: $font-size-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-wide-button {
|
||||||
|
padding: $margin-2 $margin-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-green-button {
|
||||||
|
background-color: $color-green;
|
||||||
|
color: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-white-button {
|
||||||
|
background-color: $color-white;
|
||||||
|
color: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-icon-button {
|
.maputnik-icon-button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//SCROLLING
|
//SCROLLING
|
||||||
.maputnik-scroll-container {
|
.maputnik-scroll-container {
|
||||||
overflow-x: visible;
|
overflow-x: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|
|
@ -125,6 +125,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
//SOURCE MODAL
|
//SOURCE MODAL
|
||||||
|
.maputnik-public-sources {
|
||||||
|
margin-bottom: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-public-source {
|
.maputnik-public-source {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-top: 1.5%;
|
margin-top: 1.5%;
|
||||||
|
@ -150,6 +154,7 @@
|
||||||
|
|
||||||
.maputnik-public-source-id {
|
.maputnik-public-source-id {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-active-source-type-editor {
|
.maputnik-active-source-type-editor {
|
||||||
|
@ -240,3 +245,21 @@
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-modal-survey {
|
||||||
|
width: 372px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-modal-survey__logo {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-modal-survey__description {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-modal-survey__footnote {
|
||||||
|
color: $color-green;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
3
src/styles/_react-codemirror.scss
Normal file
3
src/styles/_react-codemirror.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.react-codemirror2 {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
// See <https://github.com/nkbt/react-collapse/commit/4f4fbce7c6c07b082dc62062338c9294c656f9df>
|
// See <https://github.com/nkbt/react-collapse/commit/4f4fbce7c6c07b082dc62062338c9294c656f9df>
|
||||||
.react-collapse-container {
|
.react-collapse-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -57,6 +57,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-toolbar-link--highlighted {
|
||||||
|
line-height: 1;
|
||||||
|
padding: $margin-2 $margin-3;
|
||||||
|
|
||||||
|
.maputnik-toolbar-link-wrapper {
|
||||||
|
background-color: $color-white;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: $margin-2;
|
||||||
|
margin-top: $margin-1;
|
||||||
|
color: $color-black;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .maputnik-toolbar-link-wrapper {
|
||||||
|
background-color: lighten($color-midgray, 12);
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-toolbar-version {
|
.maputnik-toolbar-version {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
|
|
@ -4,6 +4,7 @@ $color-midgray: #36383e;
|
||||||
$color-lowgray: #8e8e8e;
|
$color-lowgray: #8e8e8e;
|
||||||
$color-white: #f0f0f0;
|
$color-white: #f0f0f0;
|
||||||
$color-red: #cf4a4a;
|
$color-red: #cf4a4a;
|
||||||
|
$color-green: #53b972;
|
||||||
$margin-1: 3px;
|
$margin-1: 3px;
|
||||||
$margin-2: 5px;
|
$margin-2: 5px;
|
||||||
$margin-3: 10px;
|
$margin-3: 10px;
|
||||||
|
@ -37,6 +38,7 @@ $toolbar-offset: 0;
|
||||||
@import 'popup';
|
@import 'popup';
|
||||||
@import 'map';
|
@import 'map';
|
||||||
@import 'react-collapse';
|
@import 'react-collapse';
|
||||||
|
@import 'react-codemirror';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hacks for webdriverio isVisibleWithinViewport
|
* Hacks for webdriverio isVisibleWithinViewport
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var config = require("../../config/specs");
|
var config = require("../../config/specs");
|
||||||
var helper = require("../helper");
|
var helper = require("../helper");
|
||||||
var wd = require("../../wd-helper");
|
|
||||||
|
|
||||||
|
|
||||||
describe.skip("history", function() {
|
describe.skip("history", function() {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
var assert = require('assert');
|
|
||||||
var config = require("../config/specs");
|
var config = require("../config/specs");
|
||||||
var geoServer = require("../geojson-server");
|
|
||||||
var helper = require("./helper");
|
var helper = require("./helper");
|
||||||
|
|
||||||
require("./util/webdriverio-ext");
|
require("./util/webdriverio-ext");
|
||||||
|
@ -13,6 +11,9 @@ describe('maputnik', function() {
|
||||||
"geojson:example",
|
"geojson:example",
|
||||||
"raster:raster"
|
"raster:raster"
|
||||||
]));
|
]));
|
||||||
|
browser.execute(function() {
|
||||||
|
localStorage.setItem("survey", true);
|
||||||
|
});
|
||||||
browser.waitForExist(".maputnik-toolbar-link");
|
browser.waitForExist(".maputnik-toolbar-link");
|
||||||
browser.flushReactUpdates();
|
browser.flushReactUpdates();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
var assert = require('assert');
|
|
||||||
var wd = require("../../wd-helper");
|
|
||||||
var config = require("../../config/specs");
|
var config = require("../../config/specs");
|
||||||
var helper = require("../helper");
|
var helper = require("../helper");
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ describe("modals", function() {
|
||||||
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
|
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
|
||||||
})
|
})
|
||||||
|
|
||||||
it("style renderer", function() {
|
it.skip("style renderer", function() {
|
||||||
var selector = wd.$("modal-settings.maputnik:renderer");
|
var selector = wd.$("modal-settings.maputnik:renderer");
|
||||||
browser.selectByValue(selector, "ol3");
|
browser.selectByValue(selector, "ol3");
|
||||||
browser.click(wd.$("modal-settings.name"))
|
browser.click(wd.$("modal-settings.name"))
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
var artifacts = require("../../artifacts");
|
|
||||||
var config = require("../../config/specs");
|
var config = require("../../config/specs");
|
||||||
var helper = require("../helper");
|
var helper = require("../helper");
|
||||||
var wd = require("../../wd-helper");
|
var wd = require("../../wd-helper");
|
||||||
|
|
|
@ -46,10 +46,10 @@ try {
|
||||||
browser.addCommand('flushReactUpdates', function() {
|
browser.addCommand('flushReactUpdates', function() {
|
||||||
browser.executeAsync(function(done) {
|
browser.executeAsync(function(done) {
|
||||||
// For any events to propogate
|
// For any events to propogate
|
||||||
setImmediate(function() {
|
setTimeout(function() {
|
||||||
// For the DOM to be updated.
|
// For the DOM to be updated.
|
||||||
setImmediate(done);
|
setTimeout(done, 0);
|
||||||
})
|
}, 0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue