mirror of
https://github.com/maputnik/editor.git
synced 2026-02-28 07:20:01 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c286f8d96 | ||
|
|
404b53587f | ||
|
|
e5fbe3b74a | ||
|
|
3f262885ca | ||
|
|
c837179f71 | ||
|
|
9a947658e2 | ||
|
|
2458d4b637 | ||
|
|
e4850805fb | ||
|
|
3a15a3bb06 | ||
|
|
75ca1fa930 | ||
|
|
377840ca24 | ||
|
|
48e9589b58 | ||
|
|
11e9cef834 | ||
|
|
7e3aa09d3e | ||
|
|
e3b7e002b4 | ||
|
|
3b7fb7ae75 | ||
|
|
fab004cdfe | ||
|
|
07523c00f0 | ||
|
|
c15ac14f88 | ||
|
|
8f6006c19f | ||
|
|
16bedcf5b1 | ||
|
|
05349d8ffe | ||
|
|
a1e1895651 | ||
|
|
a111599850 | ||
|
|
121a95cee8 | ||
|
|
decd1f3ea2 | ||
|
|
c632718324 | ||
|
|
9509b59696 |
@@ -8,6 +8,8 @@ targeted at developers and map designers.
|
|||||||
- :link: Design your maps online at **http://maputnik.com/editor/** (all in local storage)
|
- :link: Design your maps online at **http://maputnik.com/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.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
The documentation can be found in the [Wiki](https://github.com/maputnik/editor/wiki). You are welcome to collaborate!
|
The documentation can be found in the [Wiki](https://github.com/maputnik/editor/wiki). You are welcome to collaborate!
|
||||||
@@ -17,8 +19,6 @@ The documentation can be found in the [Wiki](https://github.com/maputnik/editor/
|
|||||||
|
|
||||||
[](https://youtu.be/XoDh0gEnBQo)
|
[](https://youtu.be/XoDh0gEnBQo)
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Develop
|
## Develop
|
||||||
|
|
||||||
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/).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "maputnik",
|
"name": "maputnik",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "A MapboxGL visual style editor",
|
"description": "A MapboxGL visual style editor",
|
||||||
"main": "''",
|
"main": "''",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -30,12 +30,11 @@
|
|||||||
"lodash.isequal": "^4.4.0",
|
"lodash.isequal": "^4.4.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"mapbox-gl": "^0.31.0",
|
"mapbox-gl": "^0.31.0",
|
||||||
"mapbox-gl-inspect": "^1.2.0",
|
"mapbox-gl-inspect": "^1.2.1",
|
||||||
"mapbox-gl-style-spec": "^8.11.0",
|
"mapbox-gl-style-spec": "^8.11.0",
|
||||||
"mousetrap": "^1.6.0",
|
"mousetrap": "^1.6.0",
|
||||||
"ol-mapbox-style": "1.0.1",
|
"ol-mapbox-style": "1.0.1",
|
||||||
"openlayers": "^3.19.1",
|
"openlayers": "^3.19.1",
|
||||||
"randomcolor": "^0.4.4",
|
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-addons-pure-render-mixin": "^15.4.0",
|
"react-addons-pure-render-mixin": "^15.4.0",
|
||||||
"react-autocomplete": "^1.4.0",
|
"react-autocomplete": "^1.4.0",
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export default class Toolbar extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<ExportModal
|
<ExportModal
|
||||||
mapStyle={this.props.mapStyle}
|
mapStyle={this.props.mapStyle}
|
||||||
|
onStyleChanged={this.props.onStyleChanged}
|
||||||
isOpen={this.state.isOpen.export}
|
isOpen={this.state.isOpen.export}
|
||||||
onOpenToggle={this.toggleModal.bind(this, 'export')}
|
onOpenToggle={this.toggleModal.bind(this, 'export')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ export default class ZoomSpecProperty extends React.Component {
|
|||||||
if(this.props.fieldSpec['zoom-function']) {
|
if(this.props.fieldSpec['zoom-function']) {
|
||||||
zoomBtn = <MakeZoomFunctionButton onClick={this.makeZoomFunction.bind(this)} />
|
zoomBtn = <MakeZoomFunctionButton onClick={this.makeZoomFunction.bind(this)} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <InputBlock
|
return <InputBlock
|
||||||
doc={this.props.fieldSpec.doc}
|
doc={this.props.fieldSpec.doc}
|
||||||
label={labelFromFieldName(this.props.fieldName)}
|
label={labelFromFieldName(this.props.fieldName)}
|
||||||
@@ -143,11 +142,10 @@ export default class ZoomSpecProperty extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if(isZoomField(this.props.value)) {
|
const propClass = this.props.fieldSpec.default === this.props.value ? "maputnik-default-property" : "maputnik-modified-property"
|
||||||
return this.renderZoomProperty();
|
return <div className={propClass}>
|
||||||
} else {
|
{isZoomField(this.props.value) ? this.renderZoomProperty() : this.renderProperty()}
|
||||||
return this.renderProperty();
|
</div>
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import StringInput from '../inputs/StringInput'
|
|||||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
import SelectInput from '../inputs/SelectInput'
|
||||||
|
|
||||||
|
function tryParseInt(v) {
|
||||||
|
if (v === '') return v
|
||||||
|
if (isNaN(v)) return v
|
||||||
|
return parseFloat(v)
|
||||||
|
}
|
||||||
|
|
||||||
class SingleFilterEditor extends React.Component {
|
class SingleFilterEditor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
filter: React.PropTypes.array.isRequired,
|
filter: React.PropTypes.array.isRequired,
|
||||||
@@ -17,7 +23,12 @@ class SingleFilterEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFilterPartChanged(filterOp, propertyName, filterArgs) {
|
onFilterPartChanged(filterOp, propertyName, filterArgs) {
|
||||||
const newFilter = [filterOp, propertyName, ...filterArgs]
|
let newFilter = [filterOp, propertyName, ...filterArgs.map(tryParseInt)]
|
||||||
|
if(filterOp === 'has' || filterOp === '!has') {
|
||||||
|
newFilter = [filterOp, propertyName]
|
||||||
|
} else if(filterArgs.length === 0) {
|
||||||
|
newFilter = [filterOp, propertyName, '']
|
||||||
|
}
|
||||||
this.props.onChange(newFilter)
|
this.props.onChange(newFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +53,14 @@ class SingleFilterEditor extends React.Component {
|
|||||||
options={otherFilterOps}
|
options={otherFilterOps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{filterArgs.length > 0 &&
|
||||||
<div className="maputnik-filter-editor-args">
|
<div className="maputnik-filter-editor-args">
|
||||||
<StringInput
|
<StringInput
|
||||||
value={filterArgs.join(',')}
|
value={filterArgs.join(',')}
|
||||||
onChange={ v=> this.onFilterPartChanged(filterOp, propertyName, v.split(','))}
|
onChange={ v=> this.onFilterPartChanged(filterOp, propertyName, v.split(','))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ class CheckboxInput extends React.Component {
|
|||||||
checked={this.props.value}
|
checked={this.props.value}
|
||||||
/>
|
/>
|
||||||
<div className="maputnik-checkbox-box">
|
<div className="maputnik-checkbox-box">
|
||||||
<svg className="maputnik-checkbox-icon" viewBox='0 0 32 32'>
|
<svg style={{
|
||||||
|
display: this.props.value ? 'inline' : 'none'
|
||||||
|
}} className="maputnik-checkbox-icon" viewBox='0 0 32 32'>
|
||||||
<path d='M1 14 L5 10 L13 18 L27 4 L31 8 L13 26 z' />
|
<path d='M1 14 L5 10 L13 18 L27 4 L31 8 L13 26 z' />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import AutocompleteInput from './AutocompleteInput'
|
import AutocompleteInput from './AutocompleteInput'
|
||||||
|
|
||||||
//TODO: Query available font stack dynamically
|
|
||||||
import fontStacks from '../../config/fontstacks.json'
|
|
||||||
|
|
||||||
class FontInput extends React.Component {
|
class FontInput extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: React.PropTypes.array.isRequired,
|
value: React.PropTypes.array.isRequired,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class StringInput extends React.Component {
|
|||||||
placeholder={this.props.default}
|
placeholder={this.props.default}
|
||||||
onChange={e => this.setState({ value: e.target.value })}
|
onChange={e => this.setState({ value: e.target.value })}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
if(this.state.value) this.props.onChange(this.state.value)
|
if(this.state.value!==this.props.value) this.props.onChange(this.state.value)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import PropertyGroup from '../fields/PropertyGroup'
|
|||||||
import LayerEditorGroup from './LayerEditorGroup'
|
import LayerEditorGroup from './LayerEditorGroup'
|
||||||
import LayerTypeBlock from './LayerTypeBlock'
|
import LayerTypeBlock from './LayerTypeBlock'
|
||||||
import LayerIdBlock from './LayerIdBlock'
|
import LayerIdBlock from './LayerIdBlock'
|
||||||
|
import MinZoomBlock from './MinZoomBlock'
|
||||||
|
import MaxZoomBlock from './MaxZoomBlock'
|
||||||
import LayerSourceBlock from './LayerSourceBlock'
|
import LayerSourceBlock from './LayerSourceBlock'
|
||||||
import LayerSourceLayerBlock from './LayerSourceLayerBlock'
|
import LayerSourceLayerBlock from './LayerSourceLayerBlock'
|
||||||
|
|
||||||
@@ -130,6 +132,14 @@ export default class LayerEditor extends React.Component {
|
|||||||
onChange={v => this.changeProperty(null, 'source-layer', v)}
|
onChange={v => this.changeProperty(null, 'source-layer', v)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
<MinZoomBlock
|
||||||
|
value={this.props.layer.minzoom}
|
||||||
|
onChange={v => this.changeProperty(null, 'minzoom', v)}
|
||||||
|
/>
|
||||||
|
<MaxZoomBlock
|
||||||
|
value={this.props.layer.maxzoom}
|
||||||
|
onChange={v => this.changeProperty(null, 'maxzoom', v)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
case 'filter': return <div>
|
case 'filter': return <div>
|
||||||
<div className="maputnik-filter-editor-wrapper">
|
<div className="maputnik-filter-editor-wrapper">
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class LayerListContainer extends React.Component {
|
|||||||
if(lookupKey in this.state.collapsedGroups) {
|
if(lookupKey in this.state.collapsedGroups) {
|
||||||
newGroups[lookupKey] = !this.state.collapsedGroups[lookupKey]
|
newGroups[lookupKey] = !this.state.collapsedGroups[lookupKey]
|
||||||
} else {
|
} else {
|
||||||
newGroups[lookupKey] = true
|
newGroups[lookupKey] = false
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
collapsedGroups: newGroups
|
collapsedGroups: newGroups
|
||||||
@@ -124,7 +124,7 @@ class LayerListContainer extends React.Component {
|
|||||||
|
|
||||||
isCollapsed(groupPrefix, idx) {
|
isCollapsed(groupPrefix, idx) {
|
||||||
const collapsed = this.state.collapsedGroups[[groupPrefix, idx].join('-')]
|
const collapsed = this.state.collapsedGroups[[groupPrefix, idx].join('-')]
|
||||||
return collapsed === undefined ? false : collapsed
|
return collapsed === undefined ? true : collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -147,7 +147,7 @@ class LayerListContainer extends React.Component {
|
|||||||
const groupIdx = findClosestCommonPrefix(this.props.layers, idx)
|
const groupIdx = findClosestCommonPrefix(this.props.layers, idx)
|
||||||
const listItem = <LayerListItem
|
const listItem = <LayerListItem
|
||||||
className={classnames({
|
className={classnames({
|
||||||
'maputnik-layer-list-item-collapsed': this.isCollapsed(groupPrefix, groupIdx),
|
'maputnik-layer-list-item-collapsed': layers.length > 1 && this.isCollapsed(groupPrefix, groupIdx),
|
||||||
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1
|
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1
|
||||||
})}
|
})}
|
||||||
index={idx}
|
index={idx}
|
||||||
|
|||||||
26
src/components/layers/MaxZoomBlock.jsx
Normal file
26
src/components/layers/MaxZoomBlock.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
||||||
|
import InputBlock from '../inputs/InputBlock'
|
||||||
|
import NumberInput from '../inputs/NumberInput'
|
||||||
|
|
||||||
|
class MaxZoomBlock extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
value: React.PropTypes.number.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <InputBlock label={"Max Zoom"} doc={GlSpec.layer.maxzoom.doc}>
|
||||||
|
<NumberInput
|
||||||
|
value={this.props.value}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
min={GlSpec.layer.maxzoom.minimum}
|
||||||
|
max={GlSpec.layer.maxzoom.maximum}
|
||||||
|
default={GlSpec.layer.maxzoom.maximum}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MaxZoomBlock
|
||||||
26
src/components/layers/MinZoomBlock.jsx
Normal file
26
src/components/layers/MinZoomBlock.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
||||||
|
import InputBlock from '../inputs/InputBlock'
|
||||||
|
import NumberInput from '../inputs/NumberInput'
|
||||||
|
|
||||||
|
class MinZoomBlock extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
value: React.PropTypes.number.isRequired,
|
||||||
|
onChange: React.PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <InputBlock label={"Min Zoom"} doc={GlSpec.layer.minzoom.doc}>
|
||||||
|
<NumberInput
|
||||||
|
value={this.props.value}
|
||||||
|
onChange={this.props.onChange}
|
||||||
|
min={GlSpec.layer.minzoom.minimum}
|
||||||
|
max={GlSpec.layer.minzoom.maximum}
|
||||||
|
default={GlSpec.layer.minzoom.minimum}
|
||||||
|
/>
|
||||||
|
</InputBlock>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MinZoomBlock
|
||||||
@@ -23,6 +23,9 @@ function renderProperties(feature) {
|
|||||||
function renderFeature(feature) {
|
function renderFeature(feature) {
|
||||||
return <div key={feature.id}>
|
return <div key={feature.id}>
|
||||||
<div className="maputnik-popup-layer-id">{feature.layer['source-layer']}</div>
|
<div className="maputnik-popup-layer-id">{feature.layer['source-layer']}</div>
|
||||||
|
<InputBlock key={"property-type"} label={"$type"}>
|
||||||
|
<StringInput value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
|
||||||
|
</InputBlock>
|
||||||
{renderProperties(feature)}
|
{renderProperties(feature)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import FeaturePropertyPopup from './FeaturePropertyPopup'
|
|||||||
import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color'
|
import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color'
|
||||||
import style from '../../libs/style.js'
|
import style from '../../libs/style.js'
|
||||||
import tokens from '../../config/tokens.json'
|
import tokens from '../../config/tokens.json'
|
||||||
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
|
import Color from 'color'
|
||||||
import { colorHighlightedLayer } from '../../libs/highlight'
|
import { colorHighlightedLayer } from '../../libs/highlight'
|
||||||
import 'mapbox-gl/dist/mapbox-gl.css'
|
import 'mapbox-gl/dist/mapbox-gl.css'
|
||||||
import '../../mapboxgl.css'
|
import '../../mapboxgl.css'
|
||||||
@@ -112,11 +114,15 @@ export default class MapboxGlMap extends React.Component {
|
|||||||
|
|
||||||
const inspect = new MapboxInspect({
|
const inspect = new MapboxInspect({
|
||||||
popup: new MapboxGl.Popup({
|
popup: new MapboxGl.Popup({
|
||||||
closeButton: false,
|
|
||||||
closeOnClick: false
|
closeOnClick: false
|
||||||
}),
|
}),
|
||||||
showMapPopup: true,
|
showMapPopup: true,
|
||||||
|
showMapPopupOnHover: false,
|
||||||
|
showInspectMapPopupOnHover: true,
|
||||||
showInspectButton: false,
|
showInspectButton: false,
|
||||||
|
assignLayerColor: (layerId, alpha) => {
|
||||||
|
return Color(colors.brightColor(layerId, alpha)).desaturate(0.5).string()
|
||||||
|
},
|
||||||
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) {
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
|||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
import SelectInput from '../inputs/SelectInput'
|
||||||
|
import CheckboxInput from '../inputs/CheckboxInput'
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
import MdFileDownload from 'react-icons/lib/md/file-download'
|
import MdFileDownload from 'react-icons/lib/md/file-download'
|
||||||
|
import style from '../../libs/style.js'
|
||||||
import formatStyle from 'mapbox-gl-style-spec/lib/format'
|
import formatStyle from 'mapbox-gl-style-spec/lib/format'
|
||||||
import GitHub from 'github-api'
|
import GitHub from 'github-api'
|
||||||
|
|
||||||
@@ -15,18 +17,35 @@ import GitHub from 'github-api'
|
|||||||
class Gist extends React.Component {
|
class Gist extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mapStyle: React.PropTypes.object.isRequired,
|
mapStyle: React.PropTypes.object.isRequired,
|
||||||
|
onStyleChanged: React.PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {}
|
this.state = {
|
||||||
|
preview: false,
|
||||||
|
saving: false,
|
||||||
|
latestGist: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
preview: !!(nextProps.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onSave() {
|
onSave() {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
...this.state,
|
||||||
saving: true
|
saving: true
|
||||||
});
|
});
|
||||||
const mapStyleStr = formatStyle(this.props.mapStyle);
|
const preview = this.state.preview && (this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token'];
|
||||||
|
|
||||||
|
const mapStyleStr = preview ?
|
||||||
|
formatStyle(stripAccessTokens(style.replaceAccessToken(this.props.mapStyle))) :
|
||||||
|
formatStyle(stripAccessTokens(this.props.mapStyle));
|
||||||
const styleTitle = this.props.mapStyle.name || 'Style';
|
const styleTitle = this.props.mapStyle.name || 'Style';
|
||||||
const htmlStr = `
|
const htmlStr = `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -56,49 +75,100 @@ class Gist extends React.Component {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
|
const files = {
|
||||||
|
"style.json": {
|
||||||
|
content: mapStyleStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(preview) {
|
||||||
|
files["index.html"] = {
|
||||||
|
content: htmlStr
|
||||||
|
}
|
||||||
|
}
|
||||||
const gh = new GitHub();
|
const gh = new GitHub();
|
||||||
let gist = gh.getGist(); // not a gist yet
|
let gist = gh.getGist(); // not a gist yet
|
||||||
gist.create({
|
gist.create({
|
||||||
public: true,
|
public: true,
|
||||||
description: styleTitle + 'Preview',
|
description: styleTitle,
|
||||||
files: {
|
files: files
|
||||||
"style.json": {
|
|
||||||
content: mapStyleStr
|
|
||||||
},
|
|
||||||
"index.html": {
|
|
||||||
content: htmlStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(function({data}) {
|
}).then(function({data}) {
|
||||||
return gist.read();
|
return gist.read();
|
||||||
}).then(function({data}) {
|
}).then(function({data}) {
|
||||||
this.setState({
|
this.setState({
|
||||||
latestGist: data
|
...this.state,
|
||||||
|
latestGist: data,
|
||||||
|
saving: false,
|
||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPreviewChange(value) {
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
preview: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
changeMetadataProperty(property, value) {
|
||||||
|
const changedStyle = {
|
||||||
|
...this.props.mapStyle,
|
||||||
|
metadata: {
|
||||||
|
...this.props.mapStyle.metadata,
|
||||||
|
[property]: value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.props.onStyleChanged(changedStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPreviewLink() {
|
||||||
|
const gist = this.state.latestGist;
|
||||||
|
const user = gist.user || 'anonymous';
|
||||||
|
const preview = !!gist.files['index.html'];
|
||||||
|
if(preview) {
|
||||||
|
return <span><a target="_blank" href={"https://bl.ocks.org/"+user+"/"+gist.id}>Preview</a>,{' '}</span>
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
renderLatestGist() {
|
renderLatestGist() {
|
||||||
const gist = this.state.latestGist;
|
const gist = this.state.latestGist;
|
||||||
const saving = this.state.saving;
|
const saving = this.state.saving;
|
||||||
if(gist) {
|
if(saving) {
|
||||||
|
return <p>Saving...</p>
|
||||||
|
} else if(gist) {
|
||||||
const user = gist.user || 'anonymous';
|
const user = gist.user || 'anonymous';
|
||||||
return <p>
|
return <p>
|
||||||
Latest saved gist:{' '}
|
Latest saved gist:{' '}
|
||||||
<a target="_blank" href={"https://bl.ocks.org/"+user+"/"+gist.id}>Preview</a>,{' '}
|
{this.renderPreviewLink(this)}
|
||||||
<a target="_blank" href={"https://gist.github.com/"+user+"/"+gist.id}>Source</a>
|
<a target="_blank" href={"https://gist.github.com/"+user+"/"+gist.id}>Source</a>
|
||||||
</p>
|
</p>
|
||||||
} else if(saving) {
|
|
||||||
return <p>Saving...</p>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div>
|
return <div className="maputnik-export-gist">
|
||||||
<Button onClick={this.onSave.bind(this)}>
|
<Button onClick={this.onSave.bind(this)}>
|
||||||
<MdFileDownload />
|
<MdFileDownload />
|
||||||
Save to Gist (anonymous)
|
Save to Gist (anonymous)
|
||||||
</Button>
|
</Button>
|
||||||
|
{' '}
|
||||||
|
<CheckboxInput
|
||||||
|
value={this.state.preview}
|
||||||
|
name='gist-style-preview'
|
||||||
|
onChange={this.onPreviewChange.bind(this)}
|
||||||
|
/>
|
||||||
|
<span> Include preview</span>
|
||||||
|
{this.state.preview ?
|
||||||
|
<div>
|
||||||
|
<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>
|
||||||
|
<a target="_blank" href="https://openmaptiles.com/hosting/">Get your free access token</a>
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
{this.renderLatestGist()}
|
{this.renderLatestGist()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -117,6 +187,7 @@ function stripAccessTokens(mapStyle) {
|
|||||||
class ExportModal extends React.Component {
|
class ExportModal extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mapStyle: React.PropTypes.object.isRequired,
|
mapStyle: React.PropTypes.object.isRequired,
|
||||||
|
onStyleChanged: React.PropTypes.func.isRequired,
|
||||||
isOpen: React.PropTypes.bool.isRequired,
|
isOpen: React.PropTypes.bool.isRequired,
|
||||||
onOpenToggle: React.PropTypes.func.isRequired,
|
onOpenToggle: React.PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
@@ -150,7 +221,7 @@ class ExportModal extends React.Component {
|
|||||||
|
|
||||||
<div className="maputnik-modal-section">
|
<div className="maputnik-modal-section">
|
||||||
<h4>Save style</h4>
|
<h4>Save style</h4>
|
||||||
<Gist mapStyle={this.props.mapStyle} />
|
<Gist mapStyle={this.props.mapStyle} onStyleChanged={this.props.onStyleChanged}/>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,9 @@ class OpenModal extends React.Component {
|
|||||||
<p>
|
<p>
|
||||||
Open one of the publicly available styles to start from.
|
Open one of the publicly available styles to start from.
|
||||||
</p>
|
</p>
|
||||||
|
<div className="maputnik-style-gallery-container">
|
||||||
{styleOptions}
|
{styleOptions}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Modal>
|
</Modal>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,15 @@ class TileURLSourceEditor extends React.Component {
|
|||||||
onChange: React.PropTypes.func.isRequired,
|
onChange: React.PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeTileUrl(idx, value) {
|
||||||
|
const tiles = this.props.source.tiles.slice(0)
|
||||||
|
tiles[idx] = value
|
||||||
|
this.props.onChange({
|
||||||
|
...this.props.source,
|
||||||
|
tiles: tiles
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
renderTileUrls() {
|
renderTileUrls() {
|
||||||
const prefix = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th']
|
const prefix = ['1st', '2nd', '3rd', '4th', '5th', '6th', '7th']
|
||||||
const tiles = this.props.source.tiles || []
|
const tiles = this.props.source.tiles || []
|
||||||
@@ -36,6 +45,7 @@ class TileURLSourceEditor extends React.Component {
|
|||||||
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"} doc={GlSpec.source_tile.tiles.doc}>
|
return <InputBlock key={tileIndex} label={prefix[tileIndex] + " Tile URL"} doc={GlSpec.source_tile.tiles.doc}>
|
||||||
<StringInput
|
<StringInput
|
||||||
value={tileUrl}
|
value={tileUrl}
|
||||||
|
onChange={this.changeTileUrl.bind(this, tileIndex)}
|
||||||
/>
|
/>
|
||||||
</InputBlock>
|
</InputBlock>
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -10,6 +10,5 @@
|
|||||||
"sources": { },
|
"sources": { },
|
||||||
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
"layers": [],
|
"layers": []
|
||||||
"id": "empty-style"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
[
|
|
||||||
"Metropolis Black Italic",
|
|
||||||
"Metropolis Black",
|
|
||||||
"Metropolis Bold Italic",
|
|
||||||
"Metropolis Bold",
|
|
||||||
"Metropolis Extra Bold Italic",
|
|
||||||
"Metropolis Extra Bold",
|
|
||||||
"Metropolis Extra Light Italic",
|
|
||||||
"Metropolis Extra Light",
|
|
||||||
"Metropolis Light Italic",
|
|
||||||
"Metropolis Light",
|
|
||||||
"Metropolis Medium Italic",
|
|
||||||
"Metropolis Medium",
|
|
||||||
"Metropolis Regular Italic",
|
|
||||||
"Metropolis Regular",
|
|
||||||
"Metropolis Semi Bold Italic",
|
|
||||||
"Metropolis Semi Bold",
|
|
||||||
"Metropolis Thin Italic",
|
|
||||||
"Metropolis Thin",
|
|
||||||
"Open Sans Bold Italic",
|
|
||||||
"Open Sans Bold",
|
|
||||||
"Open Sans Extra Bold Italic",
|
|
||||||
"Open Sans Extra Bold",
|
|
||||||
"Open Sans Italic",
|
|
||||||
"Open Sans Light Italic",
|
|
||||||
"Open Sans Light",
|
|
||||||
"Open Sans Regular",
|
|
||||||
"Open Sans Semibold Italic",
|
|
||||||
"Open Sans Semibold",
|
|
||||||
"Klokantech Noto Sans Bold",
|
|
||||||
"Klokantech Noto Sans CJK Bold",
|
|
||||||
"Klokantech Noto Sans CJK Regular",
|
|
||||||
"Klokantech Noto Sans Italic",
|
|
||||||
"Klokantech Noto Sans Regular"
|
|
||||||
]
|
|
||||||
@@ -2,26 +2,26 @@
|
|||||||
{
|
{
|
||||||
"id": "klokantech-basic",
|
"id": "klokantech-basic",
|
||||||
"title": "Klokantech Basic",
|
"title": "Klokantech Basic",
|
||||||
"url": "https://rawgit.com/openmaptiles/klokantech-basic-gl-style/gh-pages/style-cdn.json",
|
"url": "https://rawgit.com/openmaptiles/klokantech-basic-gl-style/master/style.json",
|
||||||
"thumbnail": "https://camo.githubusercontent.com/08dcb3dd384c6083b02e6692c939d68c4114eb33/687474703a2f2f64656d6f2e74696c657365727665722e6f72672f7374796c65732f6b6c6f6b616e746563682d62617369632f7374617469632f382e3631393138342c34372e3333363230332c31302e30372f363030783430304032782e706e67"
|
"thumbnail": "http://maputnik.com/thumbnails/klokantech-basic.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "dark-matter",
|
"id": "dark-matter",
|
||||||
"title": "Dark Matter",
|
"title": "Dark Matter",
|
||||||
"url": "https://rawgit.com/openmaptiles/dark-matter-gl-style/gh-pages/style-cdn.json",
|
"url": "https://rawgit.com/openmaptiles/dark-matter-gl-style/master/style.json",
|
||||||
"thumbnail": "https://camo.githubusercontent.com/258db708523e523782addeecdcc8697368a24df9/687474703a2f2f64656d6f2e74696c657365727665722e6f72672f7374796c65732f6461726b2d6d61747465722f7374617469632f382e3534303538372c34372e3337303535352c31352e30382f363030783430304032782e706e67"
|
"thumbnail": "http://maputnik.com/thumbnails/dark-matter.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "positron",
|
"id": "positron",
|
||||||
"title": "Positron",
|
"title": "Positron",
|
||||||
"url": "https://rawgit.com/openmaptiles/positron-gl-style/gh-pages/style-cdn.json",
|
"url": "https://rawgit.com/openmaptiles/positron-gl-style/master/style.json",
|
||||||
"thumbnail": "https://camo.githubusercontent.com/56df86562b6c36b7cc44ee6e8b91eb4d8e593b66/687474703a2f2f64656d6f2e74696c657365727665722e6f72672f7374796c65732f706f736974726f6e2f7374617469632f31302e3938373235382c34362e3435333135302c342e30322f363030783430304032782e706e67"
|
"thumbnail": "http://maputnik.com/thumbnails/positron.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "osm-bright",
|
"id": "osm-bright",
|
||||||
"title": "OSM Bright",
|
"title": "OSM Bright",
|
||||||
"url": "https://rawgit.com/openmaptiles/osm-bright-gl-style/gh-pages/style-cdn.json",
|
"url": "https://rawgit.com/openmaptiles/osm-bright-gl-style/master/style.json",
|
||||||
"thumbnail": "https://camo.githubusercontent.com/0fdf9922c6b632f903e47b3dfbcfb65e62b25046/687474703a2f2f64656d6f2e74696c657365727665722e6f72672f7374796c65732f6f736d2d6272696768742f7374617469632f382e3234333936372c34362e3931363331352c372e32312f363030783430304032782e706e67"
|
"thumbnail": "http://maputnik.com/thumbnails/osm-bright.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "osm-liberty",
|
"id": "osm-liberty",
|
||||||
@@ -34,5 +34,29 @@
|
|||||||
"title": "Empty Style",
|
"title": "Empty Style",
|
||||||
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
"url": "https://rawgit.com/maputnik/editor/master/src/config/empty-style.json",
|
||||||
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAQAAAAHDYbIAAAAEUlEQVR42mP8/58BDhiJ4wAA974H/U5Xe1oAAAAASUVORK5CYII="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mapbox-satellite",
|
||||||
|
"title": "Mapbox Satellite",
|
||||||
|
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/satellite-v9.json",
|
||||||
|
"thumbnail": "http://maputnik.com/thumbnails/mapbox-satellite.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mapbox-bright",
|
||||||
|
"title": "Mapbox Bright",
|
||||||
|
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/bright-v9.json",
|
||||||
|
"thumbnail": "http://maputnik.com/thumbnails/mapbox-bright.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mapbox-basic",
|
||||||
|
"title": "Mapbox Basic",
|
||||||
|
"url": "https://rawgit.com/mapbox/mapbox-gl-styles/master/styles/basic-v9.json",
|
||||||
|
"thumbnail": "http://maputnik.com/thumbnails/mapbox-basic.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tilezen",
|
||||||
|
"title": "Tilezen",
|
||||||
|
"url": "https://rawgit.com/lukasmartinelli/tilezen-gl-style/master/style.json",
|
||||||
|
"thumbnail": "http://maputnik.com/thumbnails/tilezen.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"openmaptiles": {
|
"openmaptiles": {
|
||||||
"type": "vector",
|
"type": "vector",
|
||||||
"url": "https://free.tilehosting.com/data/v3.json?key=25ItXg7aI5wurYDtttD",
|
"url": "https://free.tilehosting.com/data/v3.json?key={key}",
|
||||||
"title": "OpenMapTiles"
|
"title": "OpenMapTiles"
|
||||||
},
|
},
|
||||||
"tilezen": {
|
"tilezen": {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import randomColor from 'randomcolor'
|
|
||||||
import Color from 'color'
|
|
||||||
|
|
||||||
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
import stylegen from 'mapbox-gl-inspect/lib/stylegen'
|
||||||
import colors from 'mapbox-gl-inspect/lib/colors'
|
import colors from 'mapbox-gl-inspect/lib/colors'
|
||||||
|
|
||||||
@@ -8,6 +5,12 @@ export function colorHighlightedLayer(layer) {
|
|||||||
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
if(!layer || layer.type === 'background' || layer.type === 'raster') return null
|
||||||
|
|
||||||
function changeLayer(l) {
|
function changeLayer(l) {
|
||||||
|
if(l.type === 'circle') {
|
||||||
|
l.paint['circle-radius'] = 3
|
||||||
|
} else if(l.type === 'line') {
|
||||||
|
l.paint['line-width'] = 2
|
||||||
|
}
|
||||||
|
|
||||||
if(layer.filter) {
|
if(layer.filter) {
|
||||||
l.filter = layer.filter
|
l.filter = layer.filter
|
||||||
} else {
|
} else {
|
||||||
@@ -17,7 +20,8 @@ export function colorHighlightedLayer(layer) {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
const color = colors.brightColor(layer.id, 1)
|
const sourceLayerId = layer['source-layer'] || ''
|
||||||
|
const color = colors.brightColor(sourceLayerId, 1)
|
||||||
const layers = []
|
const layers = []
|
||||||
|
|
||||||
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
if(layer.type === "fill" || layer.type === 'fill-extrusion') {
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ function loadJSON(url, defaultValue, cb) {
|
|||||||
|
|
||||||
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
export function downloadGlyphsMetadata(urlTemplate, cb) {
|
||||||
if(!urlTemplate) return cb([])
|
if(!urlTemplate) return cb([])
|
||||||
const url = urlTemplate.replace('{fontstack}/{range}.pbf', 'fontstacks.json')
|
|
||||||
|
// Special handling because Tileserver GL serves the fontstacks metadata differently
|
||||||
|
// https://github.com/klokantech/tileserver-gl/pull/104
|
||||||
|
let url = urlTemplate.replace('/fonts/{fontstack}/{range}.pbf', '/fontstacks.json')
|
||||||
|
url = url.replace('{fontstack}/{range}.pbf', 'fontstacks.json')
|
||||||
|
|
||||||
loadJSON(url, [], cb)
|
loadJSON(url, [], cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,3 +72,7 @@ label:hover {
|
|||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|||||||
5
src/styles/_export.scss
Normal file
5
src/styles/_export.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.maputnik-export-gist {
|
||||||
|
label.maputnik-checkbox-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,3 +129,38 @@
|
|||||||
background-color: $color-gray;
|
background-color: $color-gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PROPERTY
|
||||||
|
.maputnik-default-property {
|
||||||
|
.maputnik-input-block-label {
|
||||||
|
color: darken($color-lowgray, 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-string,
|
||||||
|
.maputnik-number,
|
||||||
|
.maputnik-color,
|
||||||
|
.maputnik-select,
|
||||||
|
.maputnik-checkbox-wrapper {
|
||||||
|
background-color: darken($color-gray, 2%);
|
||||||
|
color: darken($color-lowgray, 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-make-zoom-function svg {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-multibutton .maputnik-button {
|
||||||
|
background-color: darken($color-midgray, 10%);
|
||||||
|
color: darken($color-lowgray, 25%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($color-midgray, 12);
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-multibutton .maputnik-button-selected {
|
||||||
|
background-color: darken($color-midgray, 2%);
|
||||||
|
color: $color-lowgray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,11 @@
|
|||||||
@extend .maputnik-big-button;
|
@extend .maputnik-big-button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maputnik-style-gallery-container {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.maputnik-public-style {
|
.maputnik-public-style {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
@@ -176,3 +181,21 @@
|
|||||||
margin-right: $margin-3;
|
margin-right: $margin-3;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//EXPORT MODAL
|
||||||
|
.maputnik-export-gist {
|
||||||
|
font-size: $font-size-6;
|
||||||
|
|
||||||
|
.maputnik-input-block {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: $color-lowgray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ $toolbar-height: 40px;
|
|||||||
@import 'picker';
|
@import 'picker';
|
||||||
@import 'toolbar';
|
@import 'toolbar';
|
||||||
@import 'modal';
|
@import 'modal';
|
||||||
|
@import 'export';
|
||||||
@import 'layout';
|
@import 'layout';
|
||||||
@import 'layer';
|
@import 'layer';
|
||||||
@import 'input';
|
@import 'input';
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ module.exports = {
|
|||||||
'mapbox-gl/dist/mapbox-gl.js',
|
'mapbox-gl/dist/mapbox-gl.js',
|
||||||
//TODO: Build failure because cannot resolve migrations file
|
//TODO: Build failure because cannot resolve migrations file
|
||||||
//"mapbox-gl-style-spec",
|
//"mapbox-gl-style-spec",
|
||||||
"randomcolor",
|
|
||||||
"lodash.clonedeep",
|
"lodash.clonedeep",
|
||||||
"lodash.throttle",
|
"lodash.throttle",
|
||||||
'color',
|
'color',
|
||||||
|
|||||||
Reference in New Issue
Block a user