mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-29 07:50:28 +01:00
Merge remote-tracking branch 'upstream/master' into feature/add-range-slider
This commit is contained in:
commit
b1d097a40f
30 changed files with 1523 additions and 1468 deletions
|
@ -49,11 +49,6 @@ templates:
|
||||||
path: /tmp/artifacts
|
path: /tmp/artifacts
|
||||||
destination: /artifacts
|
destination: /artifacts
|
||||||
jobs:
|
jobs:
|
||||||
build-linux-node-v6:
|
|
||||||
docker:
|
|
||||||
- image: node:6
|
|
||||||
working_directory: ~/repo-linux-node-v6
|
|
||||||
steps: *build-steps
|
|
||||||
build-linux-node-v8:
|
build-linux-node-v8:
|
||||||
docker:
|
docker:
|
||||||
- image: node:8
|
- image: node:8
|
||||||
|
@ -65,13 +60,10 @@ jobs:
|
||||||
- image: node:10
|
- image: node:10
|
||||||
working_directory: ~/repo-linux-node-v10
|
working_directory: ~/repo-linux-node-v10
|
||||||
steps: *build-steps
|
steps: *build-steps
|
||||||
build-osx-node-v6:
|
build-linux-node-v11:
|
||||||
macos:
|
docker:
|
||||||
xcode: "9.0"
|
- image: node:11
|
||||||
dependencies:
|
working_directory: ~/repo-linux-node-v11
|
||||||
override:
|
|
||||||
- brew install node@6
|
|
||||||
working_directory: ~/repo-osx-node-v6
|
|
||||||
steps: *build-steps
|
steps: *build-steps
|
||||||
build-osx-node-v8:
|
build-osx-node-v8:
|
||||||
macos:
|
macos:
|
||||||
|
@ -89,15 +81,22 @@ jobs:
|
||||||
- brew install node@10
|
- brew install node@10
|
||||||
working_directory: ~/repo-osx-node-v10
|
working_directory: ~/repo-osx-node-v10
|
||||||
steps: *build-steps
|
steps: *build-steps
|
||||||
|
build-osx-node-v11:
|
||||||
|
macos:
|
||||||
|
xcode: "9.0"
|
||||||
|
dependencies:
|
||||||
|
override:
|
||||||
|
- brew install node@11
|
||||||
|
working_directory: ~/repo-osx-node-v11
|
||||||
|
steps: *build-steps
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build:
|
build:
|
||||||
jobs:
|
jobs:
|
||||||
- build-linux-node-v6
|
|
||||||
- build-linux-node-v8
|
- build-linux-node-v8
|
||||||
- build-linux-node-v10
|
- build-linux-node-v10
|
||||||
- build-osx-node-v6
|
- build-linux-node-v11
|
||||||
- build-osx-node-v8
|
- build-osx-node-v8
|
||||||
- build-osx-node-v10
|
- build-osx-node-v10
|
||||||
|
- build-osx-node-v11
|
||||||
|
|
12
Dockerfile
12
Dockerfile
|
@ -1,4 +1,9 @@
|
||||||
FROM node:10
|
FROM node:10-slim
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
git \
|
||||||
|
python \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
EXPOSE 8888
|
EXPOSE 8888
|
||||||
|
|
||||||
|
@ -9,7 +14,8 @@ COPY . ${HOME}/
|
||||||
|
|
||||||
WORKDIR ${HOME}
|
WORKDIR ${HOME}
|
||||||
|
|
||||||
RUN npm install -d --dev
|
RUN npm install -d
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
CMD npm run start -- --host 0.0.0.0
|
WORKDIR ${HOME}/build/build
|
||||||
|
CMD python -m SimpleHTTPServer 8888
|
||||||
|
|
43
README.md
43
README.md
|
@ -1,18 +1,18 @@
|
||||||
# Maputnik
|
# Maputnik
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/maputnik/editor.svg?branch=master)][travis]
|
[![Build Status](https://circleci.com/gh/maputnik/editor/tree/master.svg?style=shield)][circleci]
|
||||||
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/anelbgv6jdb3qnh9/branch/master?svg=true)][appveyor]
|
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/anelbgv6jdb3qnh9/branch/master?svg=true)][appveyor]
|
||||||
[![Dependency Status](https://david-dm.org/maputnik/editor.svg)][dm-prod]
|
[![Dependency Status](https://david-dm.org/maputnik/editor.svg)][dm-prod]
|
||||||
[![Dev Dependency Status](https://david-dm.org/maputnik/editor/dev-status.svg)][dm-dev]
|
[![Dev Dependency Status](https://david-dm.org/maputnik/editor/dev-status.svg)][dm-dev]
|
||||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg)][license]
|
[![License](https://img.shields.io/badge/license-MIT-blue.svg)][license]
|
||||||
|
|
||||||
[travis]: https://travis-ci.org/maputnik/editor
|
[circleci]: https://circleci.com/gh/maputnik/editor/tree/master
|
||||||
[appveyor]: https://ci.appveyor.com/project/lukasmartinelli/editor
|
[appveyor]: https://ci.appveyor.com/project/lukasmartinelli/editor
|
||||||
[dm-prod]: https://david-dm.org/maputnik/editor
|
[dm-prod]: https://david-dm.org/maputnik/editor
|
||||||
[dm-dev]: https://david-dm.org/maputnik/editor#info=devDependencies
|
[dm-dev]: https://david-dm.org/maputnik/editor?type=dev
|
||||||
[license]: https://tldrlegal.com/license/mit-license
|
[license]: https://tldrlegal.com/license/mit-license
|
||||||
|
|
||||||
<img width="200" align="right" alt="Maputnik" src="src/img/maputnik.png" />
|
<img width="200" align="right" alt="Maputnik" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/src/img/maputnik.png" />
|
||||||
|
|
||||||
A free and open visual editor for the [Mapbox GL styles](https://www.mapbox.com/mapbox-gl-style-spec/)
|
A free and open visual editor for the [Mapbox GL styles](https://www.mapbox.com/mapbox-gl-style-spec/)
|
||||||
targeted at developers and map designers.
|
targeted at developers and map designers.
|
||||||
|
@ -20,7 +20,7 @@ targeted at developers and map designers.
|
||||||
- :link: Design your maps online at **<https://maputnik.github.io/editor/>** (all in local storage)
|
- :link: Design your maps online at **<https://maputnik.github.io/editor/>** (all in local storage)
|
||||||
- :link: Use the [Maputnik CLI](https://github.com/maputnik/editor/wiki/Maputnik-CLI) for local style development
|
- :link: Use the [Maputnik CLI](https://github.com/maputnik/editor/wiki/Maputnik-CLI) for local style development
|
||||||
|
|
||||||
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 independence is an OSS map designer.
|
||||||
|
|
||||||
|
|
||||||
## Donations
|
## Donations
|
||||||
|
@ -40,10 +40,7 @@ The documentation can be found in the [Wiki](https://github.com/maputnik/editor/
|
||||||
|
|
||||||
Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/).
|
Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/).
|
||||||
|
|
||||||
We ensure building and developing Maputnik works with
|
We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
|
||||||
|
|
||||||
- Linux, OSX and Windows
|
|
||||||
- Node >4
|
|
||||||
|
|
||||||
Install the deps, start the dev server and open the web browser on `http://localhost:8888/`.
|
Install the deps, start the dev server and open the web browser on `http://localhost:8888/`.
|
||||||
|
|
||||||
|
@ -54,12 +51,18 @@ npm install
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
The build process will watch for changes to the filesystem, rebuild and autoreload the editor. However note this from the webpack-dev-server docs
|
If you want Maputnik to be accessible externally use the [`--host` option](https://webpack.js.org/configuration/dev-server/#devserverhost):
|
||||||
|
|
||||||
> webpack uses the file system to get notified of file changes. In some cases this does not work. For example, when using Network File System (NFS). Vagrant also has a lot of problems with this.
|
```bash
|
||||||
Snippet from <https://webpack.js.org/configuration/dev-server/#devserver-watchoptions->
|
# start externally accessible dev server
|
||||||
|
npm start -- --host 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
To enable polling add `export WEBPACK_DEV_SERVER_POLLING=1` to your enviroment.
|
The build process will watch for changes to the filesystem, rebuild and autoreload the editor. However note this from the [webpack-dev-server docs](https://webpack.js.org/configuration/dev-server/):
|
||||||
|
|
||||||
|
> webpack uses the file system to get notified of file changes. In some cases this does not work. For example, when using Network File System (NFS). Vagrant also has a lot of problems with this. ([snippet source](https://webpack.js.org/configuration/dev-server/#devserverwatchoptions-))
|
||||||
|
|
||||||
|
To enable polling add `export WEBPACK_DEV_SERVER_POLLING=1` to your environment.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm run build
|
npm run build
|
||||||
|
@ -79,7 +82,7 @@ For testing we use [webdriverio](http://webdriver.io) and [selenium-standalone](
|
||||||
|
|
||||||
[selenium-standalone](https://github.com/vvo/selenium-standalone) starts a server that will launch browsers on your local machine. We use chrome so you **must** have chrome installed on your machine.
|
[selenium-standalone](https://github.com/vvo/selenium-standalone) starts a server that will launch browsers on your local machine. We use chrome so you **must** have chrome installed on your machine.
|
||||||
|
|
||||||
Now open and terminal and run the following. This will install the drivers on your local machine
|
Now open a terminal and run the following. This will install the drivers on your local machine
|
||||||
|
|
||||||
```
|
```
|
||||||
./node_modules/.bin/selenium-standalone install
|
./node_modules/.bin/selenium-standalone install
|
||||||
|
@ -115,13 +118,13 @@ Thanks to the supporters of the **[Kickstarter campaign](https://www.kickstarter
|
||||||
- [Terranodo](http://terranodo.io/)
|
- [Terranodo](http://terranodo.io/)
|
||||||
|
|
||||||
<a href="https://getwemap.com/">
|
<a href="https://getwemap.com/">
|
||||||
<img width="33%" alt="Wemap" style="display:inline" src="media/sponsors/wemap.jpg" />
|
<img width="33%" alt="Wemap" style="display:inline" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/wemap.jpg" />
|
||||||
</a>
|
</a>
|
||||||
<a href="http://terranodo.io/">
|
<a href="http://terranodo.io/">
|
||||||
<img width="33%" alt="Terranodo" style="display:inline" src="media/sponsors/terranodo.png" />
|
<img width="33%" alt="Terranodo" style="display:inline" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/terranodo.png" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.orbiconinformatik.dk/">
|
<a href="https://www.orbiconinformatik.dk/">
|
||||||
<img width="32%" alt="Terranodo" style="display:inline" src="media/sponsors/orbicon_informatik.png" />
|
<img width="32%" alt="Terranodo" style="display:inline" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/orbicon_informatik.png" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -133,13 +136,13 @@ Thanks to the supporters of the **[Kickstarter campaign](https://www.kickstarter
|
||||||
- [Dreipol](https://www.dreipol.ch/)
|
- [Dreipol](https://www.dreipol.ch/)
|
||||||
|
|
||||||
<a href="https://www.klokantech.com/">
|
<a href="https://www.klokantech.com/">
|
||||||
<img width="18%" alt="Klokan Technologies" style="display:inline-block" src="media/sponsors/klokantech.png" />
|
<img width="18%" alt="Klokan Technologies" style="display:inline-block" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/klokantech.png" />
|
||||||
</a>
|
</a>
|
||||||
<a href="http://www.geofabrik.de/">
|
<a href="http://www.geofabrik.de/">
|
||||||
<img width="18%" alt="Geofabrik" style="display:inline-block" src="media/sponsors/geofabrik.png" />
|
<img width="18%" alt="Geofabrik" style="display:inline-block" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/geofabrik.png" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://www.dreipol.ch/">
|
<a href="https://www.dreipol.ch/">
|
||||||
<img width="18%" alt="Dreipol" style="display:inline-block" src="media/sponsors/dreipol.png" />
|
<img width="18%" alt="Dreipol" style="display:inline-block" src="https://cdn.jsdelivr.net/gh/maputnik/editor@1.5.0/media/sponsors/dreipol.png" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2015
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- nodejs_version: "8"
|
- nodejs_version: "8"
|
||||||
- nodejs_version: "9"
|
|
||||||
- nodejs_version: "10"
|
- nodejs_version: "10"
|
||||||
|
- nodejs_version: "11"
|
||||||
platform:
|
platform:
|
||||||
- x86
|
- x86
|
||||||
- x64
|
- x64
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node $env:nodejs_version
|
- ps: Install-Product node $env:nodejs_version
|
||||||
- md public
|
- md public
|
||||||
- npm install --global --production windows-build-tools
|
- npm --vs2015 install --global windows-build-tools
|
||||||
- npm install
|
- npm install
|
||||||
build_script:
|
build_script:
|
||||||
- npm run build
|
- npm run build
|
||||||
test_script:
|
test_script:
|
||||||
- npm run lint
|
- npm run lint
|
||||||
|
- npm run lint-styles
|
||||||
|
|
2487
package-lock.json
generated
2487
package-lock.json
generated
File diff suppressed because it is too large
Load diff
14
package.json
14
package.json
|
@ -10,7 +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\""
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.1.2",
|
"@babel/runtime": "^7.1.2",
|
||||||
"@mapbox/mapbox-gl-rtl-text": "^0.2.1",
|
"@mapbox/mapbox-gl-rtl-text": "^0.2.1",
|
||||||
"@mapbox/mapbox-gl-style-spec": "^13.3.0",
|
"@mapbox/mapbox-gl-style-spec": "^13.6.0",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"codemirror": "^5.40.2",
|
"codemirror": "^5.40.2",
|
||||||
"color": "^3.0.0",
|
"color": "^3.0.0",
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"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.50.0-beta.1",
|
"mapbox-gl": "^0.53.1",
|
||||||
"mapbox-gl-inspect": "^1.3.1",
|
"mapbox-gl-inspect": "^1.3.1",
|
||||||
"maputnik-design": "github:maputnik/design",
|
"maputnik-design": "github:maputnik/design",
|
||||||
"ol": "^5.2.0",
|
"ol": "^5.2.0",
|
||||||
|
@ -121,18 +121,18 @@
|
||||||
"istanbul-lib-coverage": "^2.0.1",
|
"istanbul-lib-coverage": "^2.0.1",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"node-sass": "^4.9.3",
|
"node-sass": "^4.10.0",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
"react-hot-loader": "^4.3.11",
|
"react-hot-loader": "^4.3.11",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "^7.1.0",
|
||||||
"selenium-standalone": "^6.15.3",
|
"selenium-standalone": "^6.15.3",
|
||||||
"style-loader": "^0.23.0",
|
"style-loader": "^0.23.0",
|
||||||
"stylelint": "^9.6.0",
|
"stylelint": "^10.0.0",
|
||||||
"stylelint-config-recommended-scss": "^3.2.0",
|
"stylelint-config-recommended-scss": "^3.2.0",
|
||||||
"stylelint-scss": "^3.3.1",
|
"stylelint-scss": "^3.5.4",
|
||||||
"transform-loader": "^0.2.4",
|
"transform-loader": "^0.2.4",
|
||||||
"uuid": "^3.3.2",
|
"uuid": "^3.3.2",
|
||||||
"wdio-mocha-framework": "^0.6.3",
|
"wdio-mocha-framework": "^0.6.4",
|
||||||
"wdio-selenium-standalone-service": "0.0.10",
|
"wdio-selenium-standalone-service": "0.0.10",
|
||||||
"wdio-spec-reporter": "^0.1.5",
|
"wdio-spec-reporter": "^0.1.5",
|
||||||
"webdriverio": "^4.13.2",
|
"webdriverio": "^4.13.2",
|
||||||
|
|
|
@ -49,6 +49,26 @@ function normalizeSourceURL (url, apiToken="") {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFetchAccessToken(url, mapStyle) {
|
||||||
|
const matchesTilehosting = url.match(/\.tilehosting\.com/);
|
||||||
|
const matchesMaptiler = url.match(/\.maptiler\.com/);
|
||||||
|
const matchesThunderforest = url.match(/\.thunderforest\.com/);
|
||||||
|
if (matchesTilehosting || matchesMaptiler) {
|
||||||
|
const accessToken = style.getAccessToken("openmaptiles", mapStyle, {allowFallback: true})
|
||||||
|
if (accessToken) {
|
||||||
|
return url.replace('{key}', accessToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (matchesThunderforest) {
|
||||||
|
const accessToken = style.getAccessToken("thunderforest", mapStyle, {allowFallback: true})
|
||||||
|
if (accessToken) {
|
||||||
|
return url.replace('{key}', accessToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateRootSpec(spec, fieldName, newValues) {
|
function updateRootSpec(spec, fieldName, newValues) {
|
||||||
return {
|
return {
|
||||||
|
@ -73,57 +93,48 @@ export default class App extends React.Component {
|
||||||
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false)
|
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const keyCodes = {
|
|
||||||
"esc": 27,
|
|
||||||
"?": 191,
|
|
||||||
"o": 79,
|
|
||||||
"e": 69,
|
|
||||||
"s": 83,
|
|
||||||
"d": 68,
|
|
||||||
"i": 73,
|
|
||||||
"m": 77,
|
|
||||||
}
|
|
||||||
|
|
||||||
const shortcuts = [
|
const shortcuts = [
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["?"],
|
key: "?",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.toggleModal("shortcuts");
|
this.toggleModal("shortcuts");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["o"],
|
key: "o",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.toggleModal("open");
|
this.toggleModal("open");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["e"],
|
key: "e",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.toggleModal("export");
|
this.toggleModal("export");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["d"],
|
key: "d",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.toggleModal("sources");
|
this.toggleModal("sources");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["s"],
|
key: "s",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.toggleModal("settings");
|
this.toggleModal("settings");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["i"],
|
key: "i",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.setMapState("inspect");
|
this.setMapState(
|
||||||
|
this.state.mapState === "map" ? "inspect" : "map"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keyCode: keyCodes["m"],
|
key: "m",
|
||||||
handler: () => {
|
handler: () => {
|
||||||
document.querySelector(".mapboxgl-canvas").focus();
|
document.querySelector(".mapboxgl-canvas").focus();
|
||||||
}
|
}
|
||||||
|
@ -131,16 +142,17 @@ export default class App extends React.Component {
|
||||||
]
|
]
|
||||||
|
|
||||||
document.body.addEventListener("keyup", (e) => {
|
document.body.addEventListener("keyup", (e) => {
|
||||||
if(e.keyCode === keyCodes["esc"]) {
|
if(e.key === "Escape") {
|
||||||
e.target.blur();
|
e.target.blur();
|
||||||
document.body.focus();
|
document.body.focus();
|
||||||
}
|
}
|
||||||
else if(document.activeElement === document.body) {
|
else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
|
||||||
const shortcut = shortcuts.find((shortcut) => {
|
const shortcut = shortcuts.find((shortcut) => {
|
||||||
return (shortcut.keyCode === e.keyCode)
|
return (shortcut.key === e.key)
|
||||||
})
|
})
|
||||||
|
|
||||||
if(shortcut) {
|
if(shortcut) {
|
||||||
|
this.setModal("shortcuts", false);
|
||||||
shortcut.handler(e);
|
shortcut.handler(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +207,8 @@ export default class App extends React.Component {
|
||||||
},
|
},
|
||||||
mapOptions: {
|
mapOptions: {
|
||||||
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
|
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
|
||||||
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes")
|
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes"),
|
||||||
|
showOverdrawInspector: queryUtil.asBool(queryObj, "show-overdraw-inspector")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,6 +418,12 @@ export default class App extends React.Component {
|
||||||
console.warn("Failed to normalizeSourceURL: ", err);
|
console.warn("Failed to normalizeSourceURL: ", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
url = setFetchAccessToken(url, this.state.mapStyle)
|
||||||
|
} catch(err) {
|
||||||
|
console.warn("Failed to setFetchAccessToken: ", err);
|
||||||
|
}
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
})
|
})
|
||||||
|
@ -488,17 +507,21 @@ export default class App extends React.Component {
|
||||||
this.setState({ selectedLayerIndex: idx })
|
this.setState({ selectedLayerIndex: idx })
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleModal(modalName) {
|
setModal(modalName, value) {
|
||||||
|
if(modalName === 'survey' && value === false) {
|
||||||
|
localStorage.setItem('survey', '');
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isOpen: {
|
isOpen: {
|
||||||
...this.state.isOpen,
|
...this.state.isOpen,
|
||||||
[modalName]: !this.state.isOpen[modalName]
|
[modalName]: value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if(modalName === 'survey') {
|
toggleModal(modalName) {
|
||||||
localStorage.setItem('survey', '');
|
this.setModal(modalName, !this.state.isOpen[modalName]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -553,6 +576,7 @@ export default class App extends React.Component {
|
||||||
|
|
||||||
const modals = <div>
|
const modals = <div>
|
||||||
<ShortcutsModal
|
<ShortcutsModal
|
||||||
|
ref={(el) => this.shortcutEl = el}
|
||||||
isOpen={this.state.isOpen.shortcuts}
|
isOpen={this.state.isOpen.shortcuts}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
|
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -198,7 +198,7 @@ export default class Toolbar extends React.Component {
|
||||||
<ToolbarSelect wdKey="nav:inspect">
|
<ToolbarSelect wdKey="nav:inspect">
|
||||||
<MdFindInPage />
|
<MdFindInPage />
|
||||||
<IconText>View </IconText>
|
<IconText>View </IconText>
|
||||||
<select onChange={(e) => this.handleSelection(e.target.value)}>
|
<select onChange={(e) => this.handleSelection(e.target.value)} value={currentView.id}>
|
||||||
{views.map((item) => {
|
{views.map((item) => {
|
||||||
return (
|
return (
|
||||||
<option key={item.id} value={item.id}>
|
<option key={item.id} value={item.id}>
|
||||||
|
|
|
@ -81,6 +81,7 @@ export default class SpecField extends React.Component {
|
||||||
options={options}
|
options={options}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
case 'formatted':
|
||||||
case 'string':
|
case 'string':
|
||||||
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
||||||
return <IconInput
|
return <IconInput
|
||||||
|
|
|
@ -54,7 +54,8 @@ class AutocompleteInput extends React.Component {
|
||||||
menuStyle={{
|
menuStyle={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
maxHeight: this.state.maxHeight
|
maxHeight: this.state.maxHeight,
|
||||||
|
zIndex: '998'
|
||||||
}}
|
}}
|
||||||
wrapperProps={{
|
wrapperProps={{
|
||||||
className: "maputnik-autocomplete",
|
className: "maputnik-autocomplete",
|
||||||
|
|
|
@ -5,7 +5,7 @@ import AutocompleteInput from './AutocompleteInput'
|
||||||
|
|
||||||
class IconInput extends React.Component {
|
class IconInput extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.array,
|
value: PropTypes.string,
|
||||||
icons: PropTypes.array,
|
icons: PropTypes.array,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -29,20 +29,11 @@ class JSONEditor extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props, state) {
|
componentDidUpdate(prevProps) {
|
||||||
return {
|
if (prevProps.layer !== this.props.layer) {
|
||||||
code: JSON.stringify(props.layer, null, 2)
|
this.setState({
|
||||||
};
|
code: JSON.stringify(this.props.layer, null, 2)
|
||||||
}
|
})
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
|
||||||
try {
|
|
||||||
const parsedLayer = JSON.parse(this.state.code)
|
|
||||||
// If the structure is still the same do not update
|
|
||||||
// because it affects editing experience by reformatting all the time
|
|
||||||
return nextState.code !== JSON.stringify(parsedLayer, null, 2)
|
|
||||||
} catch(err) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ class LayerListContainer extends React.Component {
|
||||||
areAllGroupsExpanded: false,
|
areAllGroupsExpanded: false,
|
||||||
isOpen: {
|
isOpen: {
|
||||||
add: false,
|
add: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleModal(modalName) {
|
toggleModal(modalName) {
|
||||||
|
@ -66,12 +66,12 @@ class LayerListContainer extends React.Component {
|
||||||
this.groupedLayers().forEach(layers => {
|
this.groupedLayers().forEach(layers => {
|
||||||
const groupPrefix = layerPrefix(layers[0].id)
|
const groupPrefix = layerPrefix(layers[0].id)
|
||||||
const lookupKey = [groupPrefix, idx].join('-')
|
const lookupKey = [groupPrefix, idx].join('-')
|
||||||
|
|
||||||
|
|
||||||
if (layers.length > 1) {
|
if (layers.length > 1) {
|
||||||
newGroups[lookupKey] = this.state.areAllGroupsExpanded
|
newGroups[lookupKey] = this.state.areAllGroupsExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.forEach((layer) => {
|
layers.forEach((layer) => {
|
||||||
idx += 1
|
idx += 1
|
||||||
})
|
})
|
||||||
|
@ -204,6 +204,7 @@ export default class LayerList extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return <LayerListContainerSortable
|
return <LayerListContainerSortable
|
||||||
{...this.props}
|
{...this.props}
|
||||||
|
helperClass='sortableHelper'
|
||||||
onSortEnd={this.props.onMoveLayer.bind(this)}
|
onSortEnd={this.props.onMoveLayer.bind(this)}
|
||||||
useDragHandle={true}
|
useDragHandle={true}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,23 +7,16 @@ import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icon
|
||||||
import LayerIcon from '../icons/LayerIcon'
|
import LayerIcon from '../icons/LayerIcon'
|
||||||
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
||||||
|
|
||||||
class LayerTypeDragHandle extends React.Component {
|
|
||||||
static propTypes = LayerIcon.propTypes
|
|
||||||
|
|
||||||
render() {
|
const DraggableLabel = SortableHandle((props) => {
|
||||||
return <LayerIcon
|
return <div className="maputnik-layer-list-item-handle">
|
||||||
{...this.props}
|
<LayerIcon
|
||||||
style={{
|
className="layer-handle__icon"
|
||||||
cursor: 'move',
|
type={props.layerType}
|
||||||
width: 14,
|
|
||||||
height: 14,
|
|
||||||
paddingRight: 3,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
}
|
<span className="maputnik-layer-list-item-id">{props.layerId}</span>
|
||||||
}
|
</div>
|
||||||
|
});
|
||||||
const LayerTypeDragHandleSortable = SortableHandle((props) => <LayerTypeDragHandle {...props} />)
|
|
||||||
|
|
||||||
class IconAction extends React.Component {
|
class IconAction extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -111,8 +104,7 @@ class LayerListItem extends React.Component {
|
||||||
"maputnik-layer-list-item-selected": this.props.isSelected,
|
"maputnik-layer-list-item-selected": this.props.isSelected,
|
||||||
[this.props.className]: true,
|
[this.props.className]: true,
|
||||||
})}>
|
})}>
|
||||||
<LayerTypeDragHandleSortable type={this.props.layerType} />
|
<DraggableLabel {...this.props} />
|
||||||
<span className="maputnik-layer-list-item-id">{this.props.layerId}</span>
|
|
||||||
<span style={{flexGrow: 1}} />
|
<span style={{flexGrow: 1}} />
|
||||||
<IconAction
|
<IconAction
|
||||||
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import LayerIcon from '../icons/LayerIcon'
|
import LayerIcon from '../icons/LayerIcon'
|
||||||
|
import {latest, expression, function as styleFunction} from '@mapbox/mapbox-gl-style-spec'
|
||||||
|
|
||||||
function groupFeaturesBySourceLayer(features) {
|
function groupFeaturesBySourceLayer(features) {
|
||||||
const sources = {}
|
const sources = {}
|
||||||
|
@ -28,7 +29,59 @@ function groupFeaturesBySourceLayer(features) {
|
||||||
class FeatureLayerPopup extends React.Component {
|
class FeatureLayerPopup extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onLayerSelect: PropTypes.func.isRequired,
|
onLayerSelect: PropTypes.func.isRequired,
|
||||||
features: PropTypes.array
|
features: PropTypes.array,
|
||||||
|
zoom: PropTypes.number,
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFeatureColor(feature, zoom) {
|
||||||
|
try {
|
||||||
|
const paintProps = feature.layer.paint;
|
||||||
|
let propName;
|
||||||
|
|
||||||
|
if(paintProps.hasOwnProperty("text-color") && paintProps["text-color"]) {
|
||||||
|
propName = "text-color";
|
||||||
|
}
|
||||||
|
else if (paintProps.hasOwnProperty("fill-color") && paintProps["fill-color"]) {
|
||||||
|
propName = "fill-color";
|
||||||
|
}
|
||||||
|
else if (paintProps.hasOwnProperty("line-color") && paintProps["line-color"]) {
|
||||||
|
propName = "line-color";
|
||||||
|
}
|
||||||
|
else if (paintProps.hasOwnProperty("fill-extrusion-color") && paintProps["fill-extrusion-color"]) {
|
||||||
|
propName = "fill-extrusion-color";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(propName) {
|
||||||
|
const propertySpec = latest["paint_"+feature.layer.type][propName];
|
||||||
|
|
||||||
|
let color = feature.layer.paint[propName];
|
||||||
|
|
||||||
|
if(typeof(color) === "object") {
|
||||||
|
if(color.stops) {
|
||||||
|
color = styleFunction.convertFunction(color, propertySpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exprResult = expression.createExpression(color, propertySpec);
|
||||||
|
const val = exprResult.value.evaluate({
|
||||||
|
zoom: zoom
|
||||||
|
}, feature);
|
||||||
|
return val.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Default color
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is quite complex, just incase there's an edgecase we're missing
|
||||||
|
// always return black if we get an unexpected error.
|
||||||
|
catch (err) {
|
||||||
|
console.error("Unable to get feature color, error:", err);
|
||||||
|
return "black";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -36,21 +89,31 @@ class FeatureLayerPopup extends React.Component {
|
||||||
|
|
||||||
const items = Object.keys(sources).map(vectorLayerId => {
|
const items = Object.keys(sources).map(vectorLayerId => {
|
||||||
const layers = sources[vectorLayerId].map((feature, idx) => {
|
const layers = sources[vectorLayerId].map((feature, idx) => {
|
||||||
return <label
|
const featureColor = this._getFeatureColor(feature, this.props.zoom);
|
||||||
key={idx}
|
|
||||||
className="maputnik-popup-layer"
|
return <div
|
||||||
onClick={() => {
|
key={idx}
|
||||||
this.props.onLayerSelect(feature.layer.id)
|
className="maputnik-popup-layer"
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<LayerIcon type={feature.layer.type} style={{
|
<div
|
||||||
width: 14,
|
className="maputnik-popup-layer__swatch"
|
||||||
height: 14,
|
style={{background: featureColor}}
|
||||||
paddingRight: 3
|
></div>
|
||||||
}}/>
|
<label
|
||||||
{feature.layer.id}
|
className="maputnik-popup-layer__label"
|
||||||
{feature.counter && <span> × {feature.counter}</span>}
|
onClick={() => {
|
||||||
</label>
|
this.props.onLayerSelect(feature.layer.id)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LayerIcon type={feature.layer.type} style={{
|
||||||
|
width: 14,
|
||||||
|
height: 14,
|
||||||
|
paddingRight: 3
|
||||||
|
}}/>
|
||||||
|
{feature.layer.id}
|
||||||
|
{feature.counter && <span> × {feature.counter}</span>}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
})
|
})
|
||||||
return <div key={vectorLayerId}>
|
return <div key={vectorLayerId}>
|
||||||
<div className="maputnik-popup-layer-id">{vectorLayerId}</div>
|
<div className="maputnik-popup-layer-id">{vectorLayerId}</div>
|
||||||
|
|
|
@ -22,7 +22,7 @@ function renderProperties(feature) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFeature(feature) {
|
function renderFeature(feature) {
|
||||||
return <div key={feature.id}>
|
return <div key={`${feature.sourceLayer}-${feature.id}`}>
|
||||||
<div className="maputnik-popup-layer-id">{feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</div>
|
<div className="maputnik-popup-layer-id">{feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</div>
|
||||||
<InputBlock key={"property-type"} label={"$type"}>
|
<InputBlock key={"property-type"} label={"$type"}>
|
||||||
<StringInput value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
|
<StringInput value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
|
||||||
|
@ -43,7 +43,7 @@ function removeDuplicatedFeatures(features) {
|
||||||
if(featureIndex === -1) {
|
if(featureIndex === -1) {
|
||||||
uniqueFeatures.push(feature)
|
uniqueFeatures.push(feature)
|
||||||
} else {
|
} else {
|
||||||
if(uniqueFeatures[featureIndex].hasOwnProperty('counter')) {
|
if(uniqueFeatures[featureIndex].hasOwnProperty('inspectModeCounter')) {
|
||||||
uniqueFeatures[featureIndex].inspectModeCounter++
|
uniqueFeatures[featureIndex].inspectModeCounter++
|
||||||
} else {
|
} else {
|
||||||
uniqueFeatures[featureIndex].inspectModeCounter = 2
|
uniqueFeatures[featureIndex].inspectModeCounter = 2
|
||||||
|
|
|
@ -17,10 +17,10 @@ import '../../libs/mapbox-rtl'
|
||||||
|
|
||||||
const IS_SUPPORTED = MapboxGl.supported();
|
const IS_SUPPORTED = MapboxGl.supported();
|
||||||
|
|
||||||
function renderPropertyPopup(features) {
|
function renderPopup(popup, mountNode) {
|
||||||
var mountNode = document.createElement('div');
|
ReactDOM.render(popup, mountNode);
|
||||||
ReactDOM.render(<FeaturePropertyPopup features={features} />, mountNode)
|
var content = mountNode.innerHTML;
|
||||||
return mountNode.innerHTML;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
||||||
|
@ -77,9 +77,6 @@ export default class MapboxGlMap extends React.Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
map: null,
|
map: null,
|
||||||
inspect: null,
|
inspect: null,
|
||||||
isPopupOpen: false,
|
|
||||||
popupX: 0,
|
|
||||||
popupY: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +94,16 @@ export default class MapboxGlMap extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
let should = false;
|
||||||
|
try {
|
||||||
|
should = JSON.stringify(this.props) !== JSON.stringify(nextProps) || JSON.stringify(this.state) !== JSON.stringify(nextState);
|
||||||
|
} catch(e) {
|
||||||
|
// no biggie, carry on
|
||||||
|
}
|
||||||
|
return should;
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if(!IS_SUPPORTED) return;
|
if(!IS_SUPPORTED) return;
|
||||||
|
|
||||||
|
@ -114,6 +121,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
if (map) {
|
if (map) {
|
||||||
map.showTileBoundaries = this.props.options.showTileBoundaries;
|
map.showTileBoundaries = this.props.options.showTileBoundaries;
|
||||||
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
|
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
|
||||||
|
map.showOverdrawInspector = this.props.options.showOverdrawInspector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +139,7 @@ export default class MapboxGlMap extends React.Component {
|
||||||
|
|
||||||
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
||||||
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
||||||
|
map.showOverdrawInspector = mapOpts.showOverdrawInspector;
|
||||||
|
|
||||||
const zoom = new ZoomControl;
|
const zoom = new ZoomControl;
|
||||||
map.addControl(zoom, 'top-right');
|
map.addControl(zoom, 'top-right');
|
||||||
|
@ -138,6 +147,8 @@ export default class MapboxGlMap extends React.Component {
|
||||||
const nav = new MapboxGl.NavigationControl();
|
const nav = new MapboxGl.NavigationControl();
|
||||||
map.addControl(nav, 'top-right');
|
map.addControl(nav, 'top-right');
|
||||||
|
|
||||||
|
const tmpNode = document.createElement('div');
|
||||||
|
|
||||||
const inspect = new MapboxInspect({
|
const inspect = new MapboxInspect({
|
||||||
popup: new MapboxGl.Popup({
|
popup: new MapboxGl.Popup({
|
||||||
closeOnClick: false
|
closeOnClick: false
|
||||||
|
@ -153,18 +164,23 @@ export default class MapboxGlMap extends React.Component {
|
||||||
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
|
||||||
renderPopup: features => {
|
renderPopup: features => {
|
||||||
if(this.props.inspectModeEnabled) {
|
if(this.props.inspectModeEnabled) {
|
||||||
return renderPropertyPopup(features)
|
return renderPopup(<FeaturePropertyPopup features={features} />, tmpNode);
|
||||||
} else {
|
} else {
|
||||||
var mountNode = document.createElement('div');
|
return renderPopup(<FeatureLayerPopup features={features} onLayerSelect={this.props.onLayerSelect} zoom={this.state.zoom} />, tmpNode);
|
||||||
ReactDOM.render(<FeatureLayerPopup features={features} onLayerSelect={this.props.onLayerSelect} />, mountNode)
|
|
||||||
return mountNode
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
map.addControl(inspect)
|
map.addControl(inspect)
|
||||||
|
|
||||||
map.on("style.load", () => {
|
map.on("style.load", () => {
|
||||||
this.setState({ map, inspect });
|
this.setState({
|
||||||
|
map,
|
||||||
|
inspect,
|
||||||
|
zoom: map.getZoom()
|
||||||
|
});
|
||||||
|
if(this.props.inspectModeEnabled) {
|
||||||
|
inspect.toggleInspector();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
map.on("data", e => {
|
map.on("data", e => {
|
||||||
|
@ -173,6 +189,12 @@ export default class MapboxGlMap extends React.Component {
|
||||||
map: this.state.map
|
map: this.state.map
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
map.on("zoom", e => {
|
||||||
|
this.setState({
|
||||||
|
zoom: map.getZoom()
|
||||||
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -2,25 +2,25 @@
|
||||||
{
|
{
|
||||||
"id": "klokantech-basic",
|
"id": "klokantech-basic",
|
||||||
"title": "Klokantech Basic",
|
"title": "Klokantech Basic",
|
||||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/klokantech-basic-gl-style@v1.7/style.json",
|
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/klokantech-basic-gl-style@v1.8/style.json",
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/klokantech-basic.png"
|
"thumbnail": "https://maputnik.github.io/thumbnails/klokantech-basic.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dark-matter",
|
"id": "dark-matter",
|
||||||
"title": "Dark Matter",
|
"title": "Dark Matter",
|
||||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/dark-matter-gl-style@v1.6/style.json",
|
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/dark-matter-gl-style@v1.7/style.json",
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/dark-matter.png"
|
"thumbnail": "https://maputnik.github.io/thumbnails/dark-matter.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "positron",
|
"id": "positron",
|
||||||
"title": "Positron",
|
"title": "Positron",
|
||||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/positron-gl-style@v1.6/style.json",
|
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/positron-gl-style@v1.7/style.json",
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/positron.png"
|
"thumbnail": "https://maputnik.github.io/thumbnails/positron.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "osm-bright",
|
"id": "osm-bright",
|
||||||
"title": "OSM Bright",
|
"title": "OSM Bright",
|
||||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/osm-bright-gl-style@v1.7/style.json",
|
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/osm-bright-gl-style@v1.8/style.json",
|
||||||
"thumbnail": "https://maputnik.github.io/thumbnails/osm-bright.png"
|
"thumbnail": "https://maputnik.github.io/thumbnails/osm-bright.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
{
|
{
|
||||||
"id": "empty-style",
|
"id": "empty-style",
|
||||||
"title": "Empty Style",
|
"title": "Empty Style",
|
||||||
"url": "https://cdn.jsdelivr.net/gh/maputnik/editor@v1.5.0/src/config/empty-style.json",
|
"url": "https://cdn.jsdelivr.net/gh/maputnik/editor@9cf74ca405d2be0608b57db8109cf3a6af5b9f49/src/config/empty-style.json",
|
||||||
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"openmaptiles": {
|
"openmaptiles": {
|
||||||
"type": "vector",
|
"type": "vector",
|
||||||
"url": "https://maps.tilehosting.com/data/v3.json?key={key}",
|
"url": "https://api.maptiler.com/tiles/v3/tiles.json?key={key}",
|
||||||
"title": "OpenMapTiles"
|
"title": "OpenMapTiles"
|
||||||
},
|
},
|
||||||
"thunderforest_transport": {
|
"thunderforest_transport": {
|
||||||
|
|
|
@ -3,9 +3,9 @@ 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("raw-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([data]);
|
const blob = new window.Blob([data], {
|
||||||
const objectUrl = window.URL.createObjectURL(blob, {
|
|
||||||
type: "text/javascript"
|
type: "text/javascript"
|
||||||
});
|
});
|
||||||
|
const objectUrl = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
MapboxGl.setRTLTextPlugin(objectUrl);
|
MapboxGl.setRTLTextPlugin(objectUrl);
|
||||||
|
|
|
@ -101,7 +101,7 @@ function replaceAccessTokens(mapStyle, opts={}) {
|
||||||
changedStyle = replaceSourceAccessToken(changedStyle, sourceName, opts);
|
changedStyle = replaceSourceAccessToken(changedStyle, sourceName, opts);
|
||||||
})
|
})
|
||||||
|
|
||||||
if (mapStyle.glyphs && mapStyle.glyphs.match(/\.tilehosting\.com/)) {
|
if (mapStyle.glyphs && (mapStyle.glyphs.match(/\.tilehosting\.com/) || mapStyle.glyphs.match(/\.maptiler\.com/))) {
|
||||||
const newAccessToken = getAccessToken("openmaptiles", mapStyle, opts);
|
const newAccessToken = getAccessToken("openmaptiles", mapStyle, opts);
|
||||||
if (newAccessToken) {
|
if (newAccessToken) {
|
||||||
changedStyle = {
|
changedStyle = {
|
||||||
|
@ -119,5 +119,6 @@ export default {
|
||||||
emptyStyle,
|
emptyStyle,
|
||||||
indexOfLayer,
|
indexOfLayer,
|
||||||
generateId,
|
generateId,
|
||||||
|
getAccessToken,
|
||||||
replaceAccessTokens,
|
replaceAccessTokens,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,22 @@
|
||||||
border-top-color: rgb(28, 31, 36);
|
border-top-color: rgb(28, 31, 36);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip {
|
||||||
|
border-bottom-color: rgb(28, 31, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip {
|
||||||
|
border-bottom-color: rgb(28, 31, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip {
|
||||||
|
border-top-color: rgb(28, 31, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip {
|
||||||
|
border-top-color: rgb(28, 31, 36);
|
||||||
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-content {
|
.mapboxgl-popup-content {
|
||||||
background-color: rgb(28, 31, 36);
|
background-color: rgb(28, 31, 36);
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
src: url('../fonts/Roboto-Regular.ttf') format('truetype');
|
src: url('../fonts/Roboto-Regular.ttf') format('truetype');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
@ -10,6 +11,7 @@
|
||||||
src: url('../fonts/Roboto-Medium.ttf') format('truetype');
|
src: url('../fonts/Roboto-Medium.ttf') format('truetype');
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|
|
@ -24,6 +24,15 @@
|
||||||
padding-bottom: $margin-5;
|
padding-bottom: $margin-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-item-handle {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: $color-lowgray;
|
color: $color-lowgray;
|
||||||
|
@ -36,7 +45,7 @@
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 5px 10px;
|
padding: 5px;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
max-height: 50px;
|
max-height: 50px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -62,6 +71,7 @@
|
||||||
background: initial;
|
background: initial;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
|
height: 15px;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
fill: darken($color-lowgray, 20);
|
fill: darken($color-lowgray, 20);
|
||||||
|
@ -111,6 +121,8 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-group-header {
|
&-group-header {
|
||||||
|
@ -160,7 +172,7 @@
|
||||||
// PROPERTY
|
// PROPERTY
|
||||||
.maputnik-default-property {
|
.maputnik-default-property {
|
||||||
.maputnik-input-block-label {
|
.maputnik-input-block-label {
|
||||||
color: darken($color-lowgray, 25%);
|
color: darken($color-lowgray, 20%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-string,
|
.maputnik-string,
|
||||||
|
@ -169,7 +181,7 @@
|
||||||
.maputnik-select,
|
.maputnik-select,
|
||||||
.maputnik-checkbox-wrapper {
|
.maputnik-checkbox-wrapper {
|
||||||
background-color: darken($color-gray, 2%);
|
background-color: darken($color-gray, 2%);
|
||||||
color: darken($color-lowgray, 25%);
|
color: darken($color-lowgray, 20%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-make-zoom-function svg {
|
.maputnik-make-zoom-function svg {
|
||||||
|
@ -178,7 +190,7 @@
|
||||||
|
|
||||||
.maputnik-multibutton .maputnik-button {
|
.maputnik-multibutton .maputnik-button {
|
||||||
background-color: darken($color-midgray, 10%);
|
background-color: darken($color-midgray, 10%);
|
||||||
color: darken($color-lowgray, 25%);
|
color: darken($color-lowgray, 20%);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: lighten($color-midgray, 12);
|
background-color: lighten($color-midgray, 12);
|
||||||
|
@ -195,6 +207,11 @@
|
||||||
.more-menu {
|
.more-menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
&__menu {
|
&__menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
@ -229,3 +246,10 @@
|
||||||
min-width: 28px;
|
min-width: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone of the element which is sorted
|
||||||
|
.sortableHelper {
|
||||||
|
font-family: $font-family;
|
||||||
|
z-index: 9999;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
|
@ -247,12 +247,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-modal-survey {
|
.maputnik-modal-survey {
|
||||||
width: 372px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-modal-survey__logo {
|
.maputnik-modal-survey__logo {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
height: 128px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maputnik-modal-survey__description {
|
.maputnik-modal-survey__description {
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
.maputnik-popup-layer {
|
.maputnik-popup-layer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-popup-layer__swatch {
|
||||||
|
display: inline-block;
|
||||||
|
width: 5px;
|
||||||
|
align-content: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-popup-layer__label {
|
||||||
display: block;
|
display: block;
|
||||||
color: $color-lowgray;
|
color: $color-lowgray;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -12,4 +12,8 @@ div:not(.maputnik-toolbar__actions) {
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Styling for Firefox
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: #666 #26282e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@
|
||||||
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
|
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
|
||||||
color: $color-black !important;
|
color: $color-black !important;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
|
border-width: 0;
|
||||||
|
|
||||||
option {
|
option {
|
||||||
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
|
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
$color-black: #1c1f24;
|
$color-black: #191b20;
|
||||||
$color-gray: #26282e;
|
$color-gray: #222429;
|
||||||
$color-midgray: #36383e;
|
$color-midgray: #303237;
|
||||||
$color-lowgray: #8e8e8e;
|
$color-lowgray: #a4a4a4;
|
||||||
$color-white: #f0f0f0;
|
$color-white: #f0f0f0;
|
||||||
$color-red: #cf4a4a;
|
$color-red: #cf4a4a;
|
||||||
$color-green: #53b972;
|
$color-green: #53b972;
|
||||||
|
|
|
@ -162,7 +162,7 @@ describe("modals", function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("open map tiles access token", function() {
|
it("maptiler access token", function() {
|
||||||
var apiKey = "testing123";
|
var apiKey = "testing123";
|
||||||
browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey);
|
browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey);
|
||||||
browser.click(wd.$("modal-settings.name"))
|
browser.click(wd.$("modal-settings.name"))
|
||||||
|
@ -172,14 +172,24 @@ describe("modals", function() {
|
||||||
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
|
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip("style renderer", function() {
|
it("thunderforest access token", function() {
|
||||||
var selector = wd.$("modal-settings.maputnik:renderer");
|
var apiKey = "testing123";
|
||||||
browser.selectByValue(selector, "ol3");
|
browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey);
|
||||||
browser.click(wd.$("modal-settings.name"))
|
browser.click(wd.$("modal-settings.name"))
|
||||||
browser.flushReactUpdates();
|
browser.flushReactUpdates();
|
||||||
|
|
||||||
var styleObj = helper.getStyleStore(browser);
|
var styleObj = helper.getStyleStore(browser);
|
||||||
assert.equal(styleObj.metadata["maputnik:renderer"], "ol3");
|
assert.equal(styleObj.metadata["maputnik:thunderforest_access_token"], apiKey);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("style renderer", function() {
|
||||||
|
var selector = wd.$("modal-settings.maputnik:renderer");
|
||||||
|
browser.selectByValue(selector, "ol");
|
||||||
|
browser.click(wd.$("modal-settings.name"))
|
||||||
|
browser.flushReactUpdates();
|
||||||
|
|
||||||
|
var styleObj = helper.getStyleStore(browser);
|
||||||
|
assert.equal(styleObj.metadata["maputnik:renderer"], "ol");
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue