diff --git a/src/components/inputs/StringInput.jsx b/src/components/inputs/StringInput.jsx index e160d69..87bdd31 100644 --- a/src/components/inputs/StringInput.jsx +++ b/src/components/inputs/StringInput.jsx @@ -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) { diff --git a/src/components/inputs/UrlInput.jsx b/src/components/inputs/UrlInput.jsx new file mode 100644 index 0000000..335d3ff --- /dev/null +++ b/src/components/inputs/UrlInput.jsx @@ -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 = ( + + CORS policy won't allow fetching resources served over http from https, use a https:// domain + + ); + } + + 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 ( +
+ + {this.state.error} +
+ ); + } +} + +export default UrlInput diff --git a/src/components/map/MapboxGlMap.jsx b/src/components/map/MapboxGlMap.jsx index 87ee61b..7047d6a 100644 --- a/src/components/map/MapboxGlMap.jsx +++ b/src/components/map/MapboxGlMap.jsx @@ -181,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() diff --git a/src/components/modals/OpenModal.jsx b/src/components/modals/OpenModal.jsx index d2c6477..67f289d 100644 --- a/src/components/modals/OpenModal.jsx +++ b/src/components/modals/OpenModal.jsx @@ -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 {

Load from a URL. Note that the URL must have CORS enabled.

- this.styleUrlElement = input} className="maputnik-input" - placeholder="Enter URL..." + default="Enter URL..." value={this.state.styleUrl} - onChange={this.onChangeUrl} + onInput={this.onChangeUrl} />
diff --git a/src/components/sources/SourceTypeEditor.jsx b/src/components/sources/SourceTypeEditor.jsx index 83672e1..8ed4672 100644 --- a/src/components/sources/SourceTypeEditor.jsx +++ b/src/components/sources/SourceTypeEditor.jsx @@ -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
- 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 - @@ -95,7 +96,7 @@ class GeoJSONSourceUrlEditor extends React.Component { render() { return - this.props.onChange({ ...this.props.source, diff --git a/src/components/util/SmallError.jsx b/src/components/util/SmallError.jsx new file mode 100644 index 0000000..03d9c78 --- /dev/null +++ b/src/components/util/SmallError.jsx @@ -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 ( +
+ Error: {this.props.children} +
+ ); + } +} + +export default SmallError diff --git a/src/components/util/SmallError.scss b/src/components/util/SmallError.scss new file mode 100644 index 0000000..1111282 --- /dev/null +++ b/src/components/util/SmallError.scss @@ -0,0 +1,7 @@ +@import '../../styles/vars'; + +.SmallError { + color: #E57373; + font-size: $font-size-6; + margin-top: $margin-2 +} diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 2f6b1b7..f15cf79 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -280,3 +280,7 @@ color: $color-green; margin-top: 16px; } + +.modal-settings { + width: 400px; +} diff --git a/src/styles/_vars.scss b/src/styles/_vars.scss new file mode 100644 index 0000000..7cb3d17 --- /dev/null +++ b/src/styles/_vars.scss @@ -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; + diff --git a/src/styles/index.scss b/src/styles/index.scss index 49af915..759b73c 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -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';