mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2025-01-14 20:13:27 +01:00
Merge remote-tracking branch 'upstream/master' into feature/add-range-slider
This commit is contained in:
commit
59e070f463
23 changed files with 340 additions and 130 deletions
|
@ -50,11 +50,6 @@ templates:
|
|||
path: /tmp/artifacts
|
||||
destination: /artifacts
|
||||
jobs:
|
||||
build-linux-node-v8:
|
||||
docker:
|
||||
- image: node:8
|
||||
working_directory: ~/repo-linux-node-v8
|
||||
steps: *build-steps
|
||||
build-linux-node-v10:
|
||||
docker:
|
||||
- image: node:10
|
||||
|
@ -71,14 +66,6 @@ jobs:
|
|||
- image: node:13
|
||||
working_directory: ~/repo-linux-node-v13
|
||||
steps: *build-steps
|
||||
build-osx-node-v8:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
dependencies:
|
||||
override:
|
||||
- brew install node@8
|
||||
working_directory: ~/repo-osx-node-v8
|
||||
steps: *build-steps
|
||||
build-osx-node-v10:
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
|
@ -108,11 +95,9 @@ workflows:
|
|||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build-linux-node-v8
|
||||
- build-linux-node-v10
|
||||
- build-linux-node-v12
|
||||
- build-linux-node-v13
|
||||
- build-osx-node-v8
|
||||
- build-osx-node-v10
|
||||
- build-osx-node-v12
|
||||
- build-osx-node-v13
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
image: Visual Studio 2015
|
||||
image: Visual Studio 2019
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "10"
|
||||
- nodejs_version: "12"
|
||||
- nodejs_version: "13"
|
||||
|
@ -18,7 +17,7 @@ install:
|
|||
Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) $env:platform
|
||||
}
|
||||
- md public
|
||||
- npm --vs2015 install --global windows-build-tools
|
||||
- npm install --global windows-build-tools
|
||||
- npm install
|
||||
build_script:
|
||||
- npm run build
|
||||
|
|
9
package-lock.json
generated
9
package-lock.json
generated
|
@ -9070,12 +9070,9 @@
|
|||
}
|
||||
},
|
||||
"react-collapse": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-collapse/-/react-collapse-4.0.3.tgz",
|
||||
"integrity": "sha512-OO4NhtEqFtz+1ma31J1B7+ezdRnzHCZiTGSSd/Pxoks9hxrZYhzFEddeYt05A/1477xTtdrwo7xEa2FLJyWGCQ==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.8"
|
||||
}
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-collapse/-/react-collapse-5.0.1.tgz",
|
||||
"integrity": "sha512-cN2tkxBWizhPQ2JHfe0aUSJtmMthKA17NZkTElpiQ2snQAAi1hssXZ2fv88rAPNNvG5ss4t0PbOZT0TIl9Lk3Q=="
|
||||
},
|
||||
"react-color": {
|
||||
"version": "2.17.3",
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
"react-aria-modal": "^4.0.0",
|
||||
"react-autobind": "^1.0.6",
|
||||
"react-autocomplete": "^1.8.1",
|
||||
"react-collapse": "^4.0.3",
|
||||
"react-collapse": "^5.0.1",
|
||||
"react-color": "^2.17.3",
|
||||
"react-dom": "^16.10.2",
|
||||
"react-file-reader-input": "^2.0.0",
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
}
|
||||
|
||||
.cm-s-maputnik .CodeMirror-cursor {
|
||||
border-left: solid thin #8e8e8e !important;
|
||||
border-left: solid thin #f0f0f0 !important;
|
||||
}
|
||||
|
||||
.cm-s-maputnik.CodeMirror-focused div.CodeMirror-selected {
|
||||
|
@ -47,5 +47,11 @@
|
|||
}
|
||||
|
||||
.cm-s-maputnik .CodeMirror-matchingbracket {
|
||||
text-decoration: underline; color: white !important;
|
||||
background-color: #f0f0f0;
|
||||
color: #565659 !important;
|
||||
}
|
||||
|
||||
.cm-s-maputnik .CodeMirror-nonmatchingbracket {
|
||||
background-color: #bb0000;
|
||||
color: white !important;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,25 @@ function isDataField(value) {
|
|||
return typeof value === 'object' && value.stops && typeof value.property !== 'undefined'
|
||||
}
|
||||
|
||||
/**
|
||||
* If we don't have a default value just make one up
|
||||
*/
|
||||
function findDefaultFromSpec (spec) {
|
||||
if (spec.hasOwnProperty('default')) {
|
||||
return spec.default;
|
||||
}
|
||||
|
||||
const defaults = {
|
||||
'color': '#000000',
|
||||
'string': '',
|
||||
'boolean': false,
|
||||
'number': 0,
|
||||
'array': [],
|
||||
}
|
||||
|
||||
return defaults[spec.type] || '';
|
||||
}
|
||||
|
||||
/** Supports displaying spec field for zoom function objects
|
||||
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
||||
*/
|
||||
|
@ -82,8 +101,8 @@ export default class FunctionSpecProperty extends React.Component {
|
|||
makeZoomFunction = () => {
|
||||
const zoomFunc = {
|
||||
stops: [
|
||||
[6, this.props.value],
|
||||
[10, this.props.value]
|
||||
[6, this.props.value || findDefaultFromSpec(this.props.fieldSpec)],
|
||||
[10, this.props.value || findDefaultFromSpec(this.props.fieldSpec)]
|
||||
]
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, zoomFunc)
|
||||
|
@ -96,8 +115,8 @@ export default class FunctionSpecProperty extends React.Component {
|
|||
property: "",
|
||||
type: functionType,
|
||||
stops: [
|
||||
[{zoom: 6, value: stopValue}, this.props.value || stopValue],
|
||||
[{zoom: 10, value: stopValue}, this.props.value || stopValue]
|
||||
[{zoom: 6, value: stopValue}, this.props.value || findDefaultFromSpec(this.props.fieldSpec)],
|
||||
[{zoom: 10, value: stopValue}, this.props.value || findDefaultFromSpec(this.props.fieldSpec)]
|
||||
]
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, dataFunc)
|
||||
|
|
|
@ -11,7 +11,7 @@ import ArrayInput from '../inputs/ArrayInput'
|
|||
import DynamicArrayInput from '../inputs/DynamicArrayInput'
|
||||
import FontInput from '../inputs/FontInput'
|
||||
import IconInput from '../inputs/IconInput'
|
||||
import EnumInput from '../inputs/SelectInput'
|
||||
import EnumInput from '../inputs/EnumInput'
|
||||
import capitalize from 'lodash.capitalize'
|
||||
|
||||
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
|
||||
|
@ -75,6 +75,7 @@ export default class SpecField extends React.Component {
|
|||
{...commonProps}
|
||||
options={options}
|
||||
/>
|
||||
case 'resolvedImage':
|
||||
case 'formatted':
|
||||
case 'string':
|
||||
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
||||
|
|
|
@ -8,11 +8,32 @@ import StringInput from '../inputs/StringInput'
|
|||
import SelectInput from '../inputs/SelectInput'
|
||||
import DocLabel from './DocLabel'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import docUid from '../../libs/document-uid'
|
||||
import sortNumerically from '../../libs/sort-numerically'
|
||||
|
||||
import labelFromFieldName from './_labelFromFieldName'
|
||||
import DeleteStopButton from './_DeleteStopButton'
|
||||
|
||||
|
||||
|
||||
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 DataProperty extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
|
@ -29,6 +50,30 @@ export default class DataProperty extends React.Component {
|
|||
]),
|
||||
}
|
||||
|
||||
state = {
|
||||
refs: {}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const newRefs = setStopRefs(this.props, this.state);
|
||||
|
||||
if(newRefs) {
|
||||
this.setState({
|
||||
refs: newRefs
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
const newRefs = setStopRefs(props, state);
|
||||
if(newRefs) {
|
||||
return {
|
||||
refs: newRefs
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getFieldFunctionType(fieldSpec) {
|
||||
if (fieldSpec.expression.interpolated) {
|
||||
return "exponential"
|
||||
|
@ -48,14 +93,42 @@ export default class DataProperty extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
// Order the stops altering the refs to reflect their new position.
|
||||
orderStopsByZoom(stops) {
|
||||
const mappedWithRef = stops
|
||||
.map((stop, idx) => {
|
||||
return {
|
||||
ref: this.state.refs[idx],
|
||||
data: stop
|
||||
}
|
||||
})
|
||||
// Sort by zoom
|
||||
.sort((a, b) => sortNumerically(a.data[0].zoom, b.data[0].zoom));
|
||||
|
||||
// Fetch the new position of the stops
|
||||
const newRefs = {};
|
||||
mappedWithRef
|
||||
.forEach((stop, idx) =>{
|
||||
newRefs[idx] = stop.ref;
|
||||
})
|
||||
|
||||
this.setState({
|
||||
refs: newRefs
|
||||
});
|
||||
|
||||
return mappedWithRef.map((item) => item.data);
|
||||
}
|
||||
|
||||
changeStop(changeIdx, stopData, value) {
|
||||
const stops = this.props.value.stops.slice(0)
|
||||
const changedStop = stopData.zoom === undefined ? stopData.value : stopData
|
||||
stops[changeIdx] = [changedStop, value]
|
||||
|
||||
const orderedStops = this.orderStopsByZoom(stops);
|
||||
|
||||
const changedValue = {
|
||||
...this.props.value,
|
||||
stops: stops,
|
||||
stops: orderedStops,
|
||||
}
|
||||
this.props.onChange(this.props.fieldName, changedValue)
|
||||
}
|
||||
|
@ -77,6 +150,7 @@ export default class DataProperty extends React.Component {
|
|||
|
||||
const dataFields = this.props.value.stops.map((stop, idx) => {
|
||||
const zoomLevel = typeof stop[0] === 'object' ? stop[0].zoom : undefined;
|
||||
const key = this.state.refs[idx];
|
||||
const dataLevel = typeof stop[0] === 'object' ? stop[0].value : stop[0];
|
||||
const value = stop[1]
|
||||
const deleteStopBtn = <DeleteStopButton onClick={this.props.onDeleteStop.bind(this, idx)} />
|
||||
|
@ -107,7 +181,7 @@ export default class DataProperty extends React.Component {
|
|||
</div>
|
||||
}
|
||||
|
||||
return <InputBlock key={idx} action={deleteStopBtn} label="">
|
||||
return <InputBlock key={key} action={deleteStopBtn} label="">
|
||||
{zoomInput}
|
||||
<div className="maputnik-data-spec-property-stop-data">
|
||||
{dataInput}
|
||||
|
|
|
@ -8,10 +8,15 @@ class StringInput extends React.Component {
|
|||
style: PropTypes.object,
|
||||
default: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onInput: PropTypes.func,
|
||||
multi: PropTypes.bool,
|
||||
required: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onInput: () => {},
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -57,7 +62,9 @@ class StringInput extends React.Component {
|
|||
this.setState({
|
||||
editing: true,
|
||||
value: e.target.value
|
||||
})
|
||||
}, () => {
|
||||
this.props.onInput(this.state.value);
|
||||
});
|
||||
},
|
||||
onBlur: () => {
|
||||
if(this.state.value!==this.props.value) {
|
||||
|
|
77
src/components/inputs/UrlInput.jsx
Normal file
77
src/components/inputs/UrlInput.jsx
Normal file
|
@ -0,0 +1,77 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import StringInput from './StringInput'
|
||||
import SmallError from '../util/SmallError'
|
||||
|
||||
|
||||
function validate (url) {
|
||||
let error;
|
||||
const getProtocol = (url) => {
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.protocol;
|
||||
}
|
||||
catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
const protocol = getProtocol(url);
|
||||
if (
|
||||
protocol &&
|
||||
protocol === "http:" &&
|
||||
window.location.protocol === "https:"
|
||||
) {
|
||||
error = (
|
||||
<SmallError>
|
||||
CORS policy won't allow fetching resources served over http from https, use a <code>https://</code> domain
|
||||
</SmallError>
|
||||
);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
class UrlInput extends React.Component {
|
||||
static propTypes = {
|
||||
"data-wd-key": PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
default: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onInput: PropTypes.func,
|
||||
multi: PropTypes.bool,
|
||||
required: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
onInput: () => {},
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: validate(props.value)
|
||||
};
|
||||
}
|
||||
|
||||
onInput = (url) => {
|
||||
this.setState({
|
||||
error: validate(url)
|
||||
});
|
||||
this.props.onInput(url);
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<StringInput
|
||||
{...this.props}
|
||||
onInput={this.onInput}
|
||||
/>
|
||||
{this.state.error}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default UrlInput
|
|
@ -7,6 +7,7 @@ import CodeMirror from 'codemirror';
|
|||
|
||||
import 'codemirror/mode/javascript/javascript'
|
||||
import 'codemirror/addon/lint/lint'
|
||||
import 'codemirror/addon/edit/matchbrackets'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/addon/lint/lint.css'
|
||||
import '../../codemirror-maputnik.css'
|
||||
|
@ -47,6 +48,7 @@ class JSONEditor extends React.Component {
|
|||
viewportMargin: Infinity,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
matchBrackets: true,
|
||||
gutters: ["CodeMirror-lint-markers"],
|
||||
scrollbarStyle: "null",
|
||||
});
|
||||
|
@ -105,17 +107,6 @@ class JSONEditor extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const codeMirrorOptions = {
|
||||
mode: {name: "javascript", json: true},
|
||||
tabSize: 2,
|
||||
theme: 'maputnik',
|
||||
viewportMargin: Infinity,
|
||||
lineNumbers: true,
|
||||
lint: true,
|
||||
gutters: ["CodeMirror-lint-markers"],
|
||||
scrollbarStyle: "null",
|
||||
}
|
||||
|
||||
const style = {};
|
||||
if (this.props.maxHeight) {
|
||||
style.maxHeight = this.props.maxHeight;
|
||||
|
|
|
@ -19,8 +19,7 @@ const IS_SUPPORTED = MapboxGl.supported();
|
|||
|
||||
function renderPopup(popup, mountNode) {
|
||||
ReactDOM.render(popup, mountNode);
|
||||
var content = mountNode.innerHTML;
|
||||
return content;
|
||||
return mountNode;
|
||||
}
|
||||
|
||||
function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
|
||||
|
@ -87,11 +86,9 @@ export default class MapboxGlMap extends React.Component {
|
|||
const metadata = props.mapStyle.metadata || {}
|
||||
MapboxGl.accessToken = metadata['maputnik:mapbox_access_token'] || tokens.mapbox
|
||||
|
||||
if(!props.inspectModeEnabled) {
|
||||
//Mapbox GL now does diffing natively so we don't need to calculate
|
||||
//the necessary operations ourselves!
|
||||
this.state.map.setStyle(props.mapStyle, { diff: true})
|
||||
}
|
||||
//Mapbox GL now does diffing natively so we don't need to calculate
|
||||
//the necessary operations ourselves!
|
||||
this.state.map.setStyle(props.mapStyle, {diff: true})
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
@ -102,6 +99,9 @@ export default class MapboxGlMap extends React.Component {
|
|||
this.updateMapFromProps(this.props);
|
||||
|
||||
if(this.props.inspectModeEnabled !== prevProps.inspectModeEnabled) {
|
||||
// HACK: Fix for <https://github.com/maputnik/editor/issues/576>, while we wait for a proper fix.
|
||||
// eslint-disable-next-line
|
||||
this.state.inspect._popupBlocked = false;
|
||||
this.state.inspect.toggleInspector()
|
||||
}
|
||||
if(this.props.inspectModeEnabled) {
|
||||
|
@ -123,6 +123,7 @@ export default class MapboxGlMap extends React.Component {
|
|||
container: this.container,
|
||||
style: this.props.mapStyle,
|
||||
hash: true,
|
||||
maxZoom: 24
|
||||
}
|
||||
|
||||
const map = new MapboxGl.Map(mapOpts);
|
||||
|
@ -180,6 +181,10 @@ export default class MapboxGlMap extends React.Component {
|
|||
})
|
||||
})
|
||||
|
||||
map.on("error", e => {
|
||||
console.log("ERROR", e);
|
||||
})
|
||||
|
||||
map.on("zoom", e => {
|
||||
this.setState({
|
||||
zoom: map.getZoom()
|
||||
|
|
|
@ -4,6 +4,7 @@ import LoadingModal from './LoadingModal'
|
|||
import Modal from './Modal'
|
||||
import Button from '../Button'
|
||||
import FileReaderInput from 'react-file-reader-input'
|
||||
import UrlInput from '../inputs/UrlInput'
|
||||
|
||||
import {MdFileUpload} from 'react-icons/md'
|
||||
import {MdAddCircleOutline} from 'react-icons/md'
|
||||
|
@ -122,9 +123,8 @@ class OpenModal extends React.Component {
|
|||
})
|
||||
}
|
||||
|
||||
onOpenUrl = () => {
|
||||
const url = this.styleUrlElement.value;
|
||||
this.onStyleSelect(url);
|
||||
onOpenUrl = (url) => {
|
||||
this.onStyleSelect(this.state.styleUrl);
|
||||
}
|
||||
|
||||
onUpload = (_, files) => {
|
||||
|
@ -160,9 +160,9 @@ class OpenModal extends React.Component {
|
|||
this.props.onOpenToggle();
|
||||
}
|
||||
|
||||
onChangeUrl = () => {
|
||||
onChangeUrl = (url) => {
|
||||
this.setState({
|
||||
styleUrl: this.styleUrlElement.value
|
||||
styleUrl: url,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -209,14 +209,13 @@ class OpenModal extends React.Component {
|
|||
<p>
|
||||
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
||||
</p>
|
||||
<input
|
||||
<UrlInput
|
||||
data-wd-key="open-modal.url.input"
|
||||
type="text"
|
||||
ref={(input) => this.styleUrlElement = input}
|
||||
className="maputnik-input"
|
||||
placeholder="Enter URL..."
|
||||
default="Enter URL..."
|
||||
value={this.state.styleUrl}
|
||||
onChange={this.onChangeUrl}
|
||||
onInput={this.onChangeUrl}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
|
|
|
@ -6,6 +6,7 @@ import InputBlock from '../inputs/InputBlock'
|
|||
import ArrayInput from '../inputs/ArrayInput'
|
||||
import NumberInput from '../inputs/NumberInput'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import UrlInput from '../inputs/UrlInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import EnumInput from '../inputs/EnumInput'
|
||||
import ColorField from '../fields/ColorField'
|
||||
|
@ -84,7 +85,7 @@ class SettingsModal extends React.Component {
|
|||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Style Settings'}
|
||||
>
|
||||
<div style={{minWidth: 350}}>
|
||||
<div className="modal-settings">
|
||||
<InputBlock label={"Name"} doc={latest.$root.name.doc}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.name"
|
||||
|
@ -100,7 +101,7 @@ class SettingsModal extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Sprite URL"} doc={latest.$root.sprite.doc}>
|
||||
<StringInput {...inputProps}
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.sprite"
|
||||
value={this.props.mapStyle.sprite}
|
||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
||||
|
@ -108,7 +109,7 @@ class SettingsModal extends React.Component {
|
|||
</InputBlock>
|
||||
|
||||
<InputBlock label={"Glyphs URL"} doc={latest.$root.glyphs.doc}>
|
||||
<StringInput {...inputProps}
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.glyphs"
|
||||
value={this.props.mapStyle.glyphs}
|
||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||
|
|
|
@ -112,10 +112,12 @@ class AddSource extends React.Component {
|
|||
|
||||
defaultSource(mode) {
|
||||
const source = (this.state || {}).source || {}
|
||||
const {protocol} = window.location;
|
||||
|
||||
switch(mode) {
|
||||
case 'geojson_url': return {
|
||||
type: 'geojson',
|
||||
data: 'http://localhost:3000/geojson.json'
|
||||
data: `${protocol}//localhost:3000/geojson.json`
|
||||
}
|
||||
case 'geojson_json': return {
|
||||
type: 'geojson',
|
||||
|
@ -123,31 +125,31 @@ class AddSource extends React.Component {
|
|||
}
|
||||
case 'tilejson_vector': return {
|
||||
type: 'vector',
|
||||
url: source.url || 'http://localhost:3000/tilejson.json'
|
||||
url: source.url || `${protocol}//localhost:3000/tilejson.json`
|
||||
}
|
||||
case 'tilexyz_vector': return {
|
||||
type: 'vector',
|
||||
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
|
||||
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
|
||||
minZoom: source.minzoom || 0,
|
||||
maxZoom: source.maxzoom || 14
|
||||
}
|
||||
case 'tilejson_raster': return {
|
||||
type: 'raster',
|
||||
url: source.url || 'http://localhost:3000/tilejson.json'
|
||||
url: source.url || `${protocol}//localhost:3000/tilejson.json`
|
||||
}
|
||||
case 'tilexyz_raster': return {
|
||||
type: 'raster',
|
||||
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
|
||||
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
|
||||
minzoom: source.minzoom || 0,
|
||||
maxzoom: source.maxzoom || 14
|
||||
}
|
||||
case 'tilejson_raster-dem': return {
|
||||
type: 'raster-dem',
|
||||
url: source.url || 'http://localhost:3000/tilejson.json'
|
||||
url: source.url || `${protocol}//localhost:3000/tilejson.json`
|
||||
}
|
||||
case 'tilexyz_raster-dem': return {
|
||||
type: 'raster-dem',
|
||||
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
|
||||
tiles: source.tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`],
|
||||
minzoom: source.minzoom || 0,
|
||||
maxzoom: source.maxzoom || 14
|
||||
}
|
||||
|
@ -155,6 +157,15 @@ class AddSource extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
onAdd = () => {
|
||||
const {source, sourceId} = this.state;
|
||||
this.props.onAdd(sourceId, source);
|
||||
}
|
||||
|
||||
onChangeSource = (source) => {
|
||||
this.setState({source});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="maputnik-add-source">
|
||||
<InputBlock label={"Source ID"} doc={"Unique ID that identifies the source and is used in the layer to reference the source."}>
|
||||
|
@ -180,13 +191,14 @@ class AddSource extends React.Component {
|
|||
/>
|
||||
</InputBlock>
|
||||
<SourceTypeEditor
|
||||
onChange={src => this.setState({ source: src })}
|
||||
onChange={this.onChangeSource}
|
||||
mode={this.state.mode}
|
||||
source={this.state.source}
|
||||
/>
|
||||
<Button
|
||||
className="maputnik-add-source-button"
|
||||
onClick={() => this.props.onAdd(this.state.sourceId, this.state.source)}>
|
||||
onClick={this.onAdd}
|
||||
>
|
||||
Add Source
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
|||
import {latest} from '@mapbox/mapbox-gl-style-spec'
|
||||
import InputBlock from '../inputs/InputBlock'
|
||||
import StringInput from '../inputs/StringInput'
|
||||
import UrlInput from '../inputs/UrlInput'
|
||||
import NumberInput from '../inputs/NumberInput'
|
||||
import SelectInput from '../inputs/SelectInput'
|
||||
import JSONEditor from '../layers/JSONEditor'
|
||||
|
@ -18,7 +19,7 @@ class TileJSONSourceEditor extends React.Component {
|
|||
render() {
|
||||
return <div>
|
||||
<InputBlock label={"TileJSON URL"} doc={latest.source_vector.url.doc}>
|
||||
<StringInput
|
||||
<UrlInput
|
||||
value={this.props.source.url}
|
||||
onChange={url => this.props.onChange({
|
||||
...this.props.source,
|
||||
|
@ -52,7 +53,7 @@ class TileURLSourceEditor extends React.Component {
|
|||
const tiles = this.props.source.tiles || []
|
||||
return tiles.map((tileUrl, tileIndex) => {
|
||||
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"} doc={latest.source_vector.tiles.doc}>
|
||||
<StringInput
|
||||
<UrlInput
|
||||
value={tileUrl}
|
||||
onChange={this.changeTileUrl.bind(this, tileIndex)}
|
||||
/>
|
||||
|
@ -95,7 +96,7 @@ class GeoJSONSourceUrlEditor extends React.Component {
|
|||
|
||||
render() {
|
||||
return <InputBlock label={"GeoJSON URL"} doc={latest.source_geojson.data.doc}>
|
||||
<StringInput
|
||||
<UrlInput
|
||||
value={this.props.source.data}
|
||||
onChange={data => this.props.onChange({
|
||||
...this.props.source,
|
||||
|
|
20
src/components/util/SmallError.jsx
Normal file
20
src/components/util/SmallError.jsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import './SmallError.scss';
|
||||
|
||||
|
||||
class SmallError extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="SmallError">
|
||||
Error: {this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SmallError
|
7
src/components/util/SmallError.scss
Normal file
7
src/components/util/SmallError.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
@import '../../styles/vars';
|
||||
|
||||
.SmallError {
|
||||
color: #E57373;
|
||||
font-size: $font-size-6;
|
||||
margin-top: $margin-2
|
||||
}
|
|
@ -1,40 +1,40 @@
|
|||
[
|
||||
{
|
||||
"id": "klokantech-basic",
|
||||
"title": "Klokantech Basic",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/klokantech-basic-gl-style@e142f83/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/klokantech-basic.png"
|
||||
},
|
||||
{
|
||||
"id": "dark-matter",
|
||||
"title": "Dark Matter",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/dark-matter-gl-style@1dcc1d3/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/dark-matter.png"
|
||||
},
|
||||
{
|
||||
"id": "positron",
|
||||
"title": "Positron",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/positron-gl-style@2877814/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/positron.png"
|
||||
},
|
||||
{
|
||||
"id": "osm-bright",
|
||||
"title": "OSM Bright",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/osm-bright-gl-style@500e26e/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/osm-bright.png"
|
||||
},
|
||||
{
|
||||
"id": "toner-gl-style",
|
||||
"title": "Toner",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/toner-gl-style@bb49571/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/toner.png"
|
||||
},
|
||||
{
|
||||
"id": "osm-liberty",
|
||||
"title": "OSM Liberty",
|
||||
"url": "https://maputnik.github.io/osm-liberty/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/osm-liberty.png"
|
||||
},
|
||||
{
|
||||
"id": "klokantech-basic",
|
||||
"title": "Klokantech Basic",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/klokantech-basic-gl-style@v1.9/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/klokantech-basic.png"
|
||||
},
|
||||
{
|
||||
"id": "dark-matter",
|
||||
"title": "Dark Matter",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/dark-matter-gl-style@v1.8/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/dark-matter.png"
|
||||
},
|
||||
{
|
||||
"id": "positron",
|
||||
"title": "Positron",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/positron-gl-style@v1.8/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/positron.png"
|
||||
},
|
||||
{
|
||||
"id": "osm-bright",
|
||||
"title": "OSM Bright",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/osm-bright-gl-style@v1.9/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/osm-bright.png"
|
||||
},
|
||||
{
|
||||
"id": "toner-gl-style",
|
||||
"title": "Toner",
|
||||
"url": "https://cdn.jsdelivr.net/gh/openmaptiles/toner-gl-style@dcb6e64/style.json",
|
||||
"thumbnail": "https://maputnik.github.io/thumbnails/toner.png"
|
||||
},
|
||||
{
|
||||
"id": "os-zoomstack-outdoor",
|
||||
"title": "Zoomstack Outdoor",
|
||||
|
|
|
@ -280,3 +280,7 @@
|
|||
color: $color-green;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.modal-settings {
|
||||
width: 400px;
|
||||
}
|
||||
|
|
|
@ -7,3 +7,7 @@
|
|||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ReactCollapse--collapse {
|
||||
transition: height 180ms;
|
||||
}
|
||||
|
|
23
src/styles/_vars.scss
Normal file
23
src/styles/_vars.scss
Normal file
|
@ -0,0 +1,23 @@
|
|||
$color-black: #191b20;
|
||||
$color-gray: #222429;
|
||||
$color-midgray: #303237;
|
||||
$color-lowgray: #a4a4a4;
|
||||
$color-white: #f0f0f0;
|
||||
$color-red: #cf4a4a;
|
||||
$color-green: #53b972;
|
||||
$margin-1: 3px;
|
||||
$margin-2: 5px;
|
||||
$margin-3: 10px;
|
||||
$margin-4: 30px;
|
||||
$margin-5: 40px;
|
||||
$font-size-1: 24px;
|
||||
$font-size-2: 20px;
|
||||
$font-size-3: 18px;
|
||||
$font-size-4: 16px;
|
||||
$font-size-5: 14px;
|
||||
$font-size-6: 12px;
|
||||
$font-family: Roboto, sans-serif;
|
||||
|
||||
$toolbar-height: 40px;
|
||||
$toolbar-offset: 0;
|
||||
|
|
@ -1,26 +1,4 @@
|
|||
$color-black: #191b20;
|
||||
$color-gray: #222429;
|
||||
$color-midgray: #303237;
|
||||
$color-lowgray: #a4a4a4;
|
||||
$color-white: #f0f0f0;
|
||||
$color-red: #cf4a4a;
|
||||
$color-green: #53b972;
|
||||
$margin-1: 3px;
|
||||
$margin-2: 5px;
|
||||
$margin-3: 10px;
|
||||
$margin-4: 30px;
|
||||
$margin-5: 40px;
|
||||
$font-size-1: 24px;
|
||||
$font-size-2: 20px;
|
||||
$font-size-3: 18px;
|
||||
$font-size-4: 16px;
|
||||
$font-size-5: 14px;
|
||||
$font-size-6: 12px;
|
||||
$font-family: Roboto, sans-serif;
|
||||
|
||||
$toolbar-height: 40px;
|
||||
$toolbar-offset: 0;
|
||||
|
||||
@import 'vars';
|
||||
@import 'mixins';
|
||||
@import 'reset';
|
||||
@import 'base';
|
||||
|
|
Loading…
Reference in a new issue