diff --git a/README.md b/README.md
index 536b0802..073c1f39 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,6 @@ A free and open visual editor for the [Mapbox GL styles](https://www.mapbox.com/
targeted at developers and map designers.
- :link: Design your maps online at **** (all in local storage)
-- :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.
@@ -40,10 +39,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/).
-We ensure building and developing Maputnik works with
-
-- Linux, OSX and Windows
-- Node >4
+We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
Install the deps, start the dev server and open the web browser on `http://localhost:8888/`.
@@ -79,7 +75,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.
-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
diff --git a/appveyor.yml b/appveyor.yml
index 9d469f89..bfe10868 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,7 +2,6 @@ image: Visual Studio 2017
environment:
matrix:
- nodejs_version: "8"
- - nodejs_version: "9"
- nodejs_version: "10"
platform:
- x86
diff --git a/src/components/App.jsx b/src/components/App.jsx
index df8983ca..6fc5ce91 100644
--- a/src/components/App.jsx
+++ b/src/components/App.jsx
@@ -23,7 +23,7 @@ import SurveyModal from './modals/SurveyModal'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
import {latest, validate} from '@mapbox/mapbox-gl-style-spec'
import style from '../libs/style'
-import { initialStyleUrl, loadStyleUrl } from '../libs/urlopen'
+import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
import { undoMessages, redoMessages } from '../libs/diffmessage'
import { StyleStore } from '../libs/stylestore'
import { ApiStyleStore } from '../libs/apistore'
@@ -73,57 +73,48 @@ export default class App extends React.Component {
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 = [
{
- keyCode: keyCodes["?"],
+ key: "?",
handler: () => {
this.toggleModal("shortcuts");
}
},
{
- keyCode: keyCodes["o"],
+ key: "o",
handler: () => {
this.toggleModal("open");
}
},
{
- keyCode: keyCodes["e"],
+ key: "e",
handler: () => {
this.toggleModal("export");
}
},
{
- keyCode: keyCodes["d"],
+ key: "d",
handler: () => {
this.toggleModal("sources");
}
},
{
- keyCode: keyCodes["s"],
+ key: "s",
handler: () => {
this.toggleModal("settings");
}
},
{
- keyCode: keyCodes["i"],
+ key: "i",
handler: () => {
- this.setMapState("inspect");
+ this.setMapState(
+ this.state.mapState === "map" ? "inspect" : "map"
+ );
}
},
{
- keyCode: keyCodes["m"],
+ key: "m",
handler: () => {
document.querySelector(".mapboxgl-canvas").focus();
}
@@ -131,26 +122,31 @@ export default class App extends React.Component {
]
document.body.addEventListener("keyup", (e) => {
- if(e.keyCode === keyCodes["esc"]) {
+ if(e.key === "Escape") {
e.target.blur();
document.body.focus();
}
- else if(document.activeElement === document.body) {
+ else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
const shortcut = shortcuts.find((shortcut) => {
- return (shortcut.keyCode === e.keyCode)
+ return (shortcut.key === e.key)
})
if(shortcut) {
+ this.setModal("shortcuts", false);
shortcut.handler(e);
}
}
})
const styleUrl = initialStyleUrl()
- if(styleUrl) {
+ if(styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?")) {
this.styleStore = new StyleStore()
loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle))
+ removeStyleQuerystring()
} else {
+ if(styleUrl) {
+ removeStyleQuerystring()
+ }
this.styleStore.init(err => {
if(err) {
console.log('Falling back to local storage for storing styles')
@@ -191,7 +187,8 @@ export default class App extends React.Component {
},
mapOptions: {
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")
},
}
@@ -484,17 +481,21 @@ export default class App extends React.Component {
this.setState({ selectedLayerIndex: idx })
}
- toggleModal(modalName) {
+ setModal(modalName, value) {
+ if(modalName === 'survey' && value === false) {
+ localStorage.setItem('survey', '');
+ }
+
this.setState({
isOpen: {
...this.state.isOpen,
- [modalName]: !this.state.isOpen[modalName]
+ [modalName]: value
}
})
+ }
- if(modalName === 'survey') {
- localStorage.setItem('survey', '');
- }
+ toggleModal(modalName) {
+ this.setModal(modalName, !this.state.isOpen[modalName]);
}
render() {
@@ -549,6 +550,7 @@ export default class App extends React.Component {
const modals =
this.shortcutEl = el}
isOpen={this.state.isOpen.shortcuts}
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
/>
diff --git a/src/components/inputs/AutocompleteInput.jsx b/src/components/inputs/AutocompleteInput.jsx
index 70ac561f..8f4ed3e2 100644
--- a/src/components/inputs/AutocompleteInput.jsx
+++ b/src/components/inputs/AutocompleteInput.jsx
@@ -54,7 +54,8 @@ class AutocompleteInput extends React.Component {
menuStyle={{
position: "fixed",
overflow: "auto",
- maxHeight: this.state.maxHeight
+ maxHeight: this.state.maxHeight,
+ zIndex: '998'
}}
wrapperProps={{
className: "maputnik-autocomplete",
diff --git a/src/components/layers/LayerList.jsx b/src/components/layers/LayerList.jsx
index 9f62856d..0b3af807 100644
--- a/src/components/layers/LayerList.jsx
+++ b/src/components/layers/LayerList.jsx
@@ -46,7 +46,7 @@ class LayerListContainer extends React.Component {
areAllGroupsExpanded: false,
isOpen: {
add: false,
- }
+ }
}
toggleModal(modalName) {
@@ -66,12 +66,12 @@ class LayerListContainer extends React.Component {
this.groupedLayers().forEach(layers => {
const groupPrefix = layerPrefix(layers[0].id)
const lookupKey = [groupPrefix, idx].join('-')
-
+
if (layers.length > 1) {
newGroups[lookupKey] = this.state.areAllGroupsExpanded
}
-
+
layers.forEach((layer) => {
idx += 1
})
@@ -204,6 +204,7 @@ export default class LayerList extends React.Component {
render() {
return
diff --git a/src/components/map/MapboxGlMap.jsx b/src/components/map/MapboxGlMap.jsx
index 6236243b..dd4885f8 100644
--- a/src/components/map/MapboxGlMap.jsx
+++ b/src/components/map/MapboxGlMap.jsx
@@ -114,6 +114,7 @@ export default class MapboxGlMap extends React.Component {
if (map) {
map.showTileBoundaries = this.props.options.showTileBoundaries;
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
+ map.showOverdrawInspector = this.props.options.showOverdrawInspector;
}
}
@@ -131,6 +132,7 @@ export default class MapboxGlMap extends React.Component {
map.showTileBoundaries = mapOpts.showTileBoundaries;
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
+ map.showOverdrawInspector = mapOpts.showOverdrawInspector;
const zoom = new ZoomControl;
map.addControl(zoom, 'top-right');
diff --git a/src/libs/urlopen.js b/src/libs/urlopen.js
index 20b7aa3b..885d3c3a 100644
--- a/src/libs/urlopen.js
+++ b/src/libs/urlopen.js
@@ -1,4 +1,5 @@
import url from 'url'
+import querystring from 'querystring'
import style from './style.js'
export function initialStyleUrl() {
@@ -24,6 +25,23 @@ export function loadStyleUrl(styleUrl, cb) {
})
}
+export function removeStyleQuerystring() {
+ const initialUrl = url.parse(window.location.href, true)
+ let qs = querystring.parse(window.location.search.slice(1))
+ delete qs["style"]
+ if(Object.getOwnPropertyNames(qs).length === 0) {
+ qs = ""
+ } else {
+ qs = "?" + querystring.stringify(qs)
+ }
+ let newUrlHash = initialUrl.hash
+ if(newUrlHash === null) {
+ newUrlHash = ""
+ }
+ const newUrl = initialUrl.protocol + "//" + initialUrl.host + initialUrl.pathname + qs + newUrlHash
+ window.history.replaceState({}, document.title, newUrl)
+}
+
export function loadJSON(url, defaultValue, cb) {
fetch(url, {
mode: 'cors',
diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss
index e3b396b9..11c60fbc 100644
--- a/src/styles/_layer.scss
+++ b/src/styles/_layer.scss
@@ -206,6 +206,11 @@
.more-menu {
position: relative;
+ svg {
+ width: 22px;
+ height: 22px;
+ }
+
&__menu {
position: absolute;
z-index: 9999;
@@ -241,3 +246,9 @@
}
}
+// Clone of the element which is sorted
+.sortableHelper {
+ font-family: $font-family;
+ z-index: 9999;
+ border: none;
+}
diff --git a/src/styles/_toolbar.scss b/src/styles/_toolbar.scss
index 734c8ad0..e54c1e7e 100644
--- a/src/styles/_toolbar.scss
+++ b/src/styles/_toolbar.scss
@@ -100,10 +100,18 @@
background: inherit;
border-width: 0;
@extend .maputnik-toolbar-link;
-}
-.maputnik-toolbar-select select {
- margin-left: 4px;
+ select {
+ // HACK:
+ color: $color-black !important;
+ margin-left: 4px;
+ border-width: 0;
+
+ option {
+ // HACK:
+ color: $color-black !important;
+ }
+ }
}
.maputnik-icon-text {
diff --git a/test/functional/history/index.js b/test/functional/history/index.js
index e788e7e4..11cf8af9 100644
--- a/test/functional/history/index.js
+++ b/test/functional/history/index.js
@@ -13,6 +13,7 @@ describe.skip("history", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
helper.modal.addLayer.open();
diff --git a/test/functional/index.js b/test/functional/index.js
index 3c9f5ec6..53e61958 100644
--- a/test/functional/index.js
+++ b/test/functional/index.js
@@ -11,6 +11,7 @@ describe('maputnik', function() {
"geojson:example",
"raster:raster"
]));
+ browser.alertAccept();
browser.execute(function() {
localStorage.setItem("survey", true);
});
diff --git a/test/functional/layers/index.js b/test/functional/layers/index.js
index 012b6729..048a7951 100644
--- a/test/functional/layers/index.js
+++ b/test/functional/layers/index.js
@@ -11,6 +11,7 @@ describe("layers", function() {
"geojson:example",
"raster:raster"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -449,6 +450,7 @@ describe("layers", function() {
browser.url(config.baseUrl+"?debug&style="+getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
helper.modal.addLayer.open();
var aId = helper.modal.addLayer.fill({
diff --git a/test/functional/map/index.js b/test/functional/map/index.js
index 2df53934..816a30c7 100644
--- a/test/functional/map/index.js
+++ b/test/functional/map/index.js
@@ -9,6 +9,7 @@ describe("map", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
])+"#"+zoomLevel+"/41.3805/2.1635");
+ browser.alertAccept();
browser.waitUntil(function () {
return (
@@ -22,6 +23,7 @@ describe("map", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
])+"#"+zoomLevel+"/41.3805/2.1635");
+ browser.alertAccept();
browser.click(".mapboxgl-ctrl-zoom-in")
browser.waitUntil(function () {
diff --git a/test/functional/modals/index.js b/test/functional/modals/index.js
index 0403b220..3726e97b 100644
--- a/test/functional/modals/index.js
+++ b/test/functional/modals/index.js
@@ -99,6 +99,7 @@ describe("modals", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.selectByValue(wd.$("nav:inspect", "select"), "inspect");
})
@@ -161,7 +162,7 @@ describe("modals", function() {
})
})
- it("open map tiles access token", function() {
+ it("maptiler access token", function() {
var apiKey = "testing123";
browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey);
browser.click(wd.$("modal-settings.name"))
@@ -171,14 +172,24 @@ describe("modals", function() {
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
})
- it.skip("style renderer", function() {
- var selector = wd.$("modal-settings.maputnik:renderer");
- browser.selectByValue(selector, "ol3");
+ it("thunderforest access token", function() {
+ var apiKey = "testing123";
+ browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey);
browser.click(wd.$("modal-settings.name"))
browser.flushReactUpdates();
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");
})
})
diff --git a/test/functional/screenshots/index.js b/test/functional/screenshots/index.js
index 093802ea..ef976c4b 100644
--- a/test/functional/screenshots/index.js
+++ b/test/functional/screenshots/index.js
@@ -18,6 +18,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -28,6 +29,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -41,6 +43,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -54,6 +57,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -67,6 +71,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();
@@ -80,6 +85,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example"
]));
+ browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates();