mirror of
https://github.com/maputnik/editor.git
synced 2025-12-06 14:20:02 +00:00
Compare commits
40 Commits
v1.3.0-bet
...
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b966fae926 | ||
|
|
f1ddf4e57e | ||
|
|
64e65dc7d3 | ||
|
|
1e07a88aed | ||
|
|
6e49cc65a9 | ||
|
|
06d579118a | ||
|
|
f0e4b5b930 | ||
|
|
088127a9a5 | ||
|
|
98c235bc21 | ||
|
|
70f1f9ffac | ||
|
|
c5ea9494df | ||
|
|
9a34db7008 | ||
|
|
988b7fca0f | ||
|
|
bdc6489db4 | ||
|
|
49b096b601 | ||
|
|
31d83f6a26 | ||
|
|
03e52b7a72 | ||
|
|
551e950c39 | ||
|
|
a7620f83a6 | ||
|
|
0384181ee1 | ||
|
|
fd59f42819 | ||
|
|
cc51774259 | ||
|
|
5a19245ee0 | ||
|
|
45f45b7547 | ||
|
|
530bfaf3b3 | ||
|
|
6ea70ab9cf | ||
|
|
a0e2d68dae | ||
|
|
1447e8bfb5 | ||
|
|
c0480a50ea | ||
|
|
09ba2be416 | ||
|
|
115ce3305d | ||
|
|
960b2022ed | ||
|
|
252b442ca9 | ||
|
|
03b9ddda9c | ||
|
|
968d7d7fda | ||
|
|
b211f1cd12 | ||
|
|
870d4349f4 | ||
|
|
d88bc59720 | ||
|
|
45bdf53a41 | ||
|
|
00e94212bd |
6378
package-lock.json
generated
6378
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "maputnik",
|
||||
"version": "1.3.0-beta",
|
||||
"version": "1.5.0",
|
||||
"description": "A MapboxGL visual style editor",
|
||||
"main": "''",
|
||||
"scripts": {
|
||||
@@ -21,8 +21,8 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/maputnik/editor#readme",
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.1.2",
|
||||
"@mapbox/mapbox-gl-style-spec": "^12.0.0",
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.2.0",
|
||||
"@mapbox/mapbox-gl-style-spec": "^13.1.0",
|
||||
"classnames": "^2.2.5",
|
||||
"codemirror": "^5.37.0",
|
||||
"color": "^3.0.0",
|
||||
@@ -34,7 +34,7 @@
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"mapbox-gl": "^0.45.0",
|
||||
"mapbox-gl": "^0.47.0",
|
||||
"mapbox-gl-inspect": "^1.3.1",
|
||||
"maputnik-design": "github:maputnik/design",
|
||||
"mousetrap": "^1.6.1",
|
||||
|
||||
@@ -22,7 +22,7 @@ import SurveyModal from './modals/SurveyModal'
|
||||
|
||||
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
||||
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 { undoMessages, redoMessages } from '../libs/diffmessage'
|
||||
import { loadDefaultStyle, StyleStore } from '../libs/stylestore'
|
||||
@@ -35,7 +35,7 @@ import Debug from '../libs/debug'
|
||||
import queryUtil from '../libs/query-util'
|
||||
|
||||
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) {
|
||||
@@ -176,7 +176,8 @@ export default class App extends React.Component {
|
||||
survey: localStorage.hasOwnProperty('survey') ? false : true
|
||||
},
|
||||
mapOptions: {
|
||||
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries")
|
||||
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
|
||||
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes")
|
||||
},
|
||||
mapFilter: queryObj["color-blindness-emulation"],
|
||||
}
|
||||
@@ -365,7 +366,7 @@ export default class App extends React.Component {
|
||||
if(!this.state.sources.hasOwnProperty(key) && val.type === "vector" && val.hasOwnProperty("url")) {
|
||||
let url = val.url;
|
||||
try {
|
||||
url = mapboxUtil.normalizeSourceURL(url, MapboxGl.accessToken);
|
||||
url = normalizeSourceURL(url, MapboxGl.accessToken);
|
||||
} catch(err) {
|
||||
console.warn("Failed to normalizeSourceURL: ", err);
|
||||
}
|
||||
@@ -407,7 +408,7 @@ export default class App extends React.Component {
|
||||
|
||||
mapRenderer() {
|
||||
const mapProps = {
|
||||
mapStyle: style.replaceAccessToken(this.state.mapStyle, {allowFallback: true}),
|
||||
mapStyle: style.replaceAccessTokens(this.state.mapStyle, {allowFallback: true}),
|
||||
options: this.state.mapOptions,
|
||||
onDataChange: (e) => {
|
||||
this.layerWatcher.analyzeMap(e.map)
|
||||
@@ -430,9 +431,10 @@ export default class App extends React.Component {
|
||||
onLayerSelect={this.onLayerSelect.bind(this)} />
|
||||
}
|
||||
|
||||
const elementStyle = {
|
||||
"filter": `url('#${this.state.mapFilter}')`
|
||||
};
|
||||
const elementStyle = {};
|
||||
if(this.state.mapFilter) {
|
||||
elementStyle.filter = `url('#${this.state.mapFilter}')`;
|
||||
}
|
||||
|
||||
return <div style={elementStyle}>
|
||||
{mapElement}
|
||||
|
||||
@@ -16,7 +16,7 @@ export default class FunctionButtons extends React.Component {
|
||||
|
||||
render() {
|
||||
let makeZoomButton, makeDataButton
|
||||
if (this.props.fieldSpec['zoom-function']) {
|
||||
if (this.props.fieldSpec.expression.parameters.includes('zoom')) {
|
||||
makeZoomButton = <Button
|
||||
className="maputnik-make-zoom-function"
|
||||
onClick={this.props.onZoomClick}
|
||||
|
||||
@@ -66,6 +66,7 @@ export default class MapboxGlMap extends React.Component {
|
||||
onDataChange: () => {},
|
||||
onLayerSelect: () => {},
|
||||
mapboxAccessToken: tokens.mapbox,
|
||||
options: {},
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
@@ -103,6 +104,7 @@ export default class MapboxGlMap extends React.Component {
|
||||
}
|
||||
|
||||
map.showTileBoundaries = this.props.options.showTileBoundaries;
|
||||
map.showCollisionBoxes = this.props.options.showCollisionBoxes;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -116,6 +118,7 @@ export default class MapboxGlMap extends React.Component {
|
||||
const map = new MapboxGl.Map(mapOpts);
|
||||
|
||||
map.showTileBoundaries = mapOpts.showTileBoundaries;
|
||||
map.showCollisionBoxes = mapOpts.showCollisionBoxes;
|
||||
|
||||
const zoom = new ZoomControl;
|
||||
map.addControl(zoom, 'top-right');
|
||||
|
||||
@@ -10,7 +10,7 @@ import Button from '../Button'
|
||||
import Modal from './Modal'
|
||||
import MdFileDownload from 'react-icons/lib/md/file-download'
|
||||
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'
|
||||
|
||||
@@ -49,7 +49,7 @@ class Gist extends React.Component {
|
||||
const mapboxToken = (this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token'];
|
||||
|
||||
const mapStyleStr = preview ?
|
||||
styleSpec.format(stripAccessTokens(style.replaceAccessToken(this.props.mapStyle))) :
|
||||
styleSpec.format(stripAccessTokens(style.replaceAccessTokens(this.props.mapStyle))) :
|
||||
styleSpec.format(stripAccessTokens(this.props.mapStyle));
|
||||
const styleTitle = this.props.mapStyle.name || 'Style';
|
||||
const htmlStr = `
|
||||
@@ -235,10 +235,24 @@ class ExportModal extends React.Component {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
changeMetadataProperty(property, value) {
|
||||
const changedStyle = {
|
||||
...this.props.mapStyle,
|
||||
metadata: {
|
||||
...this.props.mapStyle.metadata,
|
||||
[property]: value
|
||||
}
|
||||
}
|
||||
this.props.onStyleChanged(changedStyle)
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return <Modal
|
||||
data-wd-key="export-modal"
|
||||
@@ -252,6 +266,28 @@ class ExportModal extends React.Component {
|
||||
<p>
|
||||
Download a JSON style to your computer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<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>
|
||||
<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)}>
|
||||
<MdFileDownload />
|
||||
Download
|
||||
|
||||
@@ -94,6 +94,14 @@ class SettingsModal extends React.Component {
|
||||
/>
|
||||
</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."}>
|
||||
<SelectInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:renderer"
|
||||
|
||||
@@ -236,9 +236,12 @@ class SourcesModal extends React.Component {
|
||||
<p>
|
||||
Add one of the publicly available sources to your style.
|
||||
</p>
|
||||
<div style={{maxwidth: 500}}>
|
||||
<div className="maputnik-public-sources" style={{maxwidth: 500}}>
|
||||
{tilesetOptions}
|
||||
</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 className="maputnik-modal-section">
|
||||
|
||||
@@ -193,7 +193,8 @@
|
||||
"raster-brightness-max",
|
||||
"raster-saturation",
|
||||
"raster-contrast",
|
||||
"raster-fade-duration"
|
||||
"raster-fade-duration",
|
||||
"raster-resampling"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -8,5 +8,15 @@
|
||||
"type": "vector",
|
||||
"url": "https://free.tilehosting.com/data/v3.json?key={key}",
|
||||
"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",
|
||||
"openmaptiles": "Og58UhhtiiTaLVlPtPgs"
|
||||
"openmaptiles": "Og58UhhtiiTaLVlPtPgs",
|
||||
"thunderforest": "b71f7f0ba4064f5eb9e903859a9cf5c6"
|
||||
}
|
||||
|
||||
@@ -54,18 +54,28 @@ function indexOfLayer(layers, layerId) {
|
||||
return null
|
||||
}
|
||||
|
||||
function replaceAccessToken(mapStyle, opts={}) {
|
||||
const omtSource = mapStyle.sources.openmaptiles
|
||||
if(!omtSource) return mapStyle
|
||||
if(!omtSource.hasOwnProperty("url")) return mapStyle
|
||||
function getAccessToken(sourceName, mapStyle, opts) {
|
||||
if(sourceName === "thunderforest_transport" || sourceName === "thunderforest_outdoors") {
|
||||
sourceName = "thunderforest"
|
||||
}
|
||||
|
||||
const metadata = mapStyle.metadata || {}
|
||||
let accessToken = metadata['maputnik:openmaptiles_access_token'];
|
||||
let accessToken = metadata[`maputnik:${sourceName}_access_token`]
|
||||
|
||||
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) {
|
||||
// Early exit.
|
||||
return mapStyle;
|
||||
@@ -73,16 +83,31 @@ function replaceAccessToken(mapStyle, opts={}) {
|
||||
|
||||
const changedSources = {
|
||||
...mapStyle.sources,
|
||||
openmaptiles: {
|
||||
...omtSource,
|
||||
url: omtSource.url.replace('{key}', accessToken)
|
||||
[sourceName]: {
|
||||
...source,
|
||||
url: source.url.replace('{key}', accessToken)
|
||||
}
|
||||
}
|
||||
const changedStyle = {
|
||||
...mapStyle,
|
||||
glyphs: mapStyle.glyphs ? mapStyle.glyphs.replace('{key}', accessToken) : mapStyle.glyphs,
|
||||
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/)) {
|
||||
changedStyle = {
|
||||
...changedStyle,
|
||||
glyphs: mapStyle.glyphs.replace('{key}', getAccessToken("openmaptiles", mapStyle, opts))
|
||||
}
|
||||
}
|
||||
|
||||
return changedStyle
|
||||
}
|
||||
@@ -92,5 +117,5 @@ export default {
|
||||
emptyStyle,
|
||||
indexOfLayer,
|
||||
generateId,
|
||||
replaceAccessToken,
|
||||
replaceAccessTokens,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
// The UI is 100% height so prevent bounce scroll on OSX
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: calc(100% - #{$toolbar-height + $toolbar-offset});
|
||||
width: 75%;
|
||||
width: calc(
|
||||
100%
|
||||
- 200px /* layer list */
|
||||
- 350px /* layer editor */
|
||||
);
|
||||
}
|
||||
|
||||
// DOC LABEL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//SCROLLING
|
||||
.maputnik-scroll-container {
|
||||
overflow-x: visible;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
@@ -125,6 +125,10 @@
|
||||
}
|
||||
|
||||
//SOURCE MODAL
|
||||
.maputnik-public-sources {
|
||||
margin-bottom: 1.5%;
|
||||
}
|
||||
|
||||
.maputnik-public-source {
|
||||
vertical-align: top;
|
||||
margin-top: 1.5%;
|
||||
@@ -150,6 +154,7 @@
|
||||
|
||||
.maputnik-public-source-id {
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.maputnik-active-source-type-editor {
|
||||
|
||||
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>
|
||||
.react-collapse-container {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
|
||||
@@ -38,6 +38,7 @@ $toolbar-offset: 0;
|
||||
@import 'popup';
|
||||
@import 'map';
|
||||
@import 'react-collapse';
|
||||
@import 'react-codemirror';
|
||||
|
||||
/**
|
||||
* Hacks for webdriverio isVisibleWithinViewport
|
||||
|
||||
@@ -13,6 +13,9 @@ describe('maputnik', function() {
|
||||
"geojson:example",
|
||||
"raster:raster"
|
||||
]));
|
||||
browser.execute(function() {
|
||||
localStorage.setItem("survey", true);
|
||||
});
|
||||
browser.waitForExist(".maputnik-toolbar-link");
|
||||
browser.flushReactUpdates();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user