2016-12-21 16:11:08 +01:00
|
|
|
import React from 'react'
|
2017-01-09 22:44:22 +01:00
|
|
|
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
|
2016-12-21 16:11:08 +01:00
|
|
|
import Modal from './Modal'
|
2016-12-21 19:59:37 +01:00
|
|
|
import Button from '../Button'
|
2016-12-21 16:11:08 +01:00
|
|
|
import InputBlock from '../inputs/InputBlock'
|
|
|
|
import StringInput from '../inputs/StringInput'
|
2016-12-21 17:21:04 +01:00
|
|
|
import SelectInput from '../inputs/SelectInput'
|
2016-12-21 17:13:21 +01:00
|
|
|
import SourceTypeEditor from '../sources/SourceTypeEditor'
|
|
|
|
|
2016-12-22 18:08:42 +01:00
|
|
|
import style from '../../libs/style'
|
2017-01-08 19:45:44 +01:00
|
|
|
import { deleteSource, addSource, changeSource } from '../../libs/source'
|
2016-12-21 16:11:08 +01:00
|
|
|
import publicSources from '../../config/tilesets.json'
|
2016-12-21 16:50:34 +01:00
|
|
|
|
|
|
|
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
2016-12-21 19:59:37 +01:00
|
|
|
import DeleteIcon from 'react-icons/lib/md/delete'
|
2016-12-21 16:11:08 +01:00
|
|
|
|
|
|
|
class PublicSource extends React.Component {
|
|
|
|
static propTypes = {
|
|
|
|
id: React.PropTypes.string.isRequired,
|
|
|
|
type: React.PropTypes.string.isRequired,
|
|
|
|
title: React.PropTypes.string.isRequired,
|
|
|
|
onSelect: React.PropTypes.func.isRequired,
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2017-01-11 11:35:33 +01:00
|
|
|
return <div className="maputnik-public-source">
|
|
|
|
<Button
|
|
|
|
className="maputnik-public-source-select"
|
|
|
|
onClick={() => this.props.onSelect(this.props.id)}
|
|
|
|
>
|
|
|
|
<div className="maputnik-public-source-info">
|
|
|
|
<p className="maputnik-public-source-name">{this.props.title}</p>
|
|
|
|
<p className="maputnik-public-source-id">#{this.props.id}</p>
|
|
|
|
</div>
|
|
|
|
<span className="maputnik-space" />
|
|
|
|
<AddIcon />
|
|
|
|
</Button>
|
2016-12-21 16:11:08 +01:00
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 19:59:37 +01:00
|
|
|
function editorMode(source) {
|
2017-01-08 19:45:44 +01:00
|
|
|
if(source.type === 'raster') {
|
|
|
|
if(source.tiles) return 'tilexyz_raster'
|
|
|
|
return 'tilejson_raster'
|
|
|
|
}
|
|
|
|
if(source.type === 'vector') {
|
|
|
|
if(source.tiles) return 'tilexyz_vector'
|
|
|
|
return 'tilejson_vector'
|
2016-12-21 19:59:37 +01:00
|
|
|
}
|
2017-01-08 19:45:44 +01:00
|
|
|
if(source.type === 'geojson') return ' geojson'
|
|
|
|
return null
|
2016-12-21 19:59:37 +01:00
|
|
|
}
|
|
|
|
|
2016-12-28 15:20:07 +01:00
|
|
|
class ActiveSourceTypeEditor extends React.Component {
|
2016-12-21 16:11:08 +01:00
|
|
|
static propTypes = {
|
|
|
|
sourceId: React.PropTypes.string.isRequired,
|
|
|
|
source: React.PropTypes.object.isRequired,
|
2017-01-08 19:45:44 +01:00
|
|
|
onDelete: React.PropTypes.func.isRequired,
|
|
|
|
onChange: React.PropTypes.func.isRequired,
|
2016-12-21 16:11:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2016-12-21 16:50:34 +01:00
|
|
|
const inputProps = { }
|
2017-01-11 11:35:33 +01:00
|
|
|
return <div className="maputnik-active-source-type-editor">
|
|
|
|
<div className="maputnik-active-source-type-editor-header">
|
|
|
|
<span className="maputnik-active-source-type-editor-header-id">#{this.props.sourceId}</span>
|
|
|
|
<span className="maputnik-space" />
|
2016-12-21 19:59:37 +01:00
|
|
|
<Button
|
2017-01-11 11:35:33 +01:00
|
|
|
className="maputnik-active-source-type-editor-header-delete"
|
2017-01-08 19:45:44 +01:00
|
|
|
onClick={()=> this.props.onDelete(this.props.sourceId)}
|
2016-12-21 19:59:37 +01:00
|
|
|
style={{backgroundColor: 'transparent'}}
|
|
|
|
>
|
|
|
|
<DeleteIcon />
|
|
|
|
</Button>
|
2016-12-21 17:13:21 +01:00
|
|
|
</div>
|
2017-01-11 11:35:33 +01:00
|
|
|
<div className="maputnik-active-source-type-editor-content">
|
2016-12-21 19:59:37 +01:00
|
|
|
<SourceTypeEditor
|
2017-01-08 19:45:44 +01:00
|
|
|
onChange={this.props.onChange}
|
2016-12-21 19:59:37 +01:00
|
|
|
mode={editorMode(this.props.source)}
|
|
|
|
source={this.props.source}
|
|
|
|
/>
|
2016-12-21 17:13:21 +01:00
|
|
|
</div>
|
2016-12-21 16:11:08 +01:00
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-21 17:21:04 +01:00
|
|
|
class AddSource extends React.Component {
|
|
|
|
static propTypes = {
|
2017-01-08 19:45:44 +01:00
|
|
|
onAdd: React.PropTypes.func.isRequired,
|
2016-12-21 19:59:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props)
|
|
|
|
this.state = {
|
|
|
|
mode: 'tilejson',
|
2016-12-28 15:57:30 +01:00
|
|
|
sourceId: style.generateId(),
|
|
|
|
source: this.defaultSource('tilejson'),
|
2016-12-21 19:59:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-28 15:57:30 +01:00
|
|
|
defaultSource(mode) {
|
|
|
|
const source = (this.state || {}).source || {}
|
|
|
|
switch(mode) {
|
|
|
|
case 'geojson': return {
|
|
|
|
type: 'geojson',
|
|
|
|
data: source.data || 'http://localhost:3000/geojson.json'
|
2016-12-22 18:08:42 +01:00
|
|
|
}
|
2017-01-08 18:04:30 +01:00
|
|
|
case 'tilejson_vector': return {
|
2016-12-28 15:57:30 +01:00
|
|
|
type: 'vector',
|
|
|
|
url: source.url || 'http://localhost:3000/tilejson.json'
|
|
|
|
}
|
2017-01-08 18:04:30 +01:00
|
|
|
case 'tilexyz_vector': return {
|
2016-12-28 15:57:30 +01:00
|
|
|
type: 'vector',
|
|
|
|
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
|
2017-01-10 15:17:35 +01:00
|
|
|
minZoom: source.minzoom || 0,
|
|
|
|
maxZoom: source.maxzoom || 14
|
2016-12-28 15:57:30 +01:00
|
|
|
}
|
2017-01-08 18:04:30 +01:00
|
|
|
case 'tilejson_raster': return {
|
|
|
|
type: 'raster',
|
|
|
|
url: source.url || 'http://localhost:3000/tilejson.json'
|
|
|
|
}
|
|
|
|
case 'tilexyz_raster': return {
|
|
|
|
type: 'raster',
|
|
|
|
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
|
2017-01-10 15:17:35 +01:00
|
|
|
minzoom: source.minzoom || 0,
|
|
|
|
maxzoom: source.maxzoom || 14
|
2017-01-08 18:04:30 +01:00
|
|
|
}
|
2016-12-28 15:57:30 +01:00
|
|
|
default: return {}
|
|
|
|
}
|
2016-12-22 18:08:42 +01:00
|
|
|
}
|
|
|
|
|
2016-12-21 17:21:04 +01:00
|
|
|
render() {
|
2017-01-11 11:35:33 +01:00
|
|
|
return <div className="maputnik-add-source">
|
2017-01-09 22:44:22 +01:00
|
|
|
<InputBlock label={"Source ID"} doc={"Unique ID that identifies the source and is used in the layer to reference the source."}>
|
2016-12-21 17:21:04 +01:00
|
|
|
<StringInput
|
2016-12-28 15:57:30 +01:00
|
|
|
value={this.state.sourceId}
|
|
|
|
onChange={v => this.setState({ sourceId: v})}
|
2016-12-21 17:21:04 +01:00
|
|
|
/>
|
|
|
|
</InputBlock>
|
2017-01-09 22:44:22 +01:00
|
|
|
<InputBlock label={"Source Type"} doc={GlSpec.source_tile.type.doc}>
|
2016-12-21 17:21:04 +01:00
|
|
|
<SelectInput
|
|
|
|
options={[
|
|
|
|
['geojson', 'GeoJSON'],
|
2017-01-08 18:04:30 +01:00
|
|
|
['tilejson_vector', 'Vector (TileJSON URL)'],
|
|
|
|
['tilexyz_vector', 'Vector (XYZ URLs)'],
|
|
|
|
['tilejson_raster', 'Raster (TileJSON URL)'],
|
|
|
|
['tilexyz_raster', 'Raster (XYZ URL)'],
|
2016-12-21 17:21:04 +01:00
|
|
|
]}
|
2016-12-28 15:57:30 +01:00
|
|
|
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
|
2016-12-21 19:59:37 +01:00
|
|
|
value={this.state.mode}
|
2016-12-21 17:21:04 +01:00
|
|
|
/>
|
|
|
|
</InputBlock>
|
2016-12-21 19:59:37 +01:00
|
|
|
<SourceTypeEditor
|
2017-01-08 19:45:44 +01:00
|
|
|
onChange={src => this.setState({ source: src })}
|
2016-12-21 19:59:37 +01:00
|
|
|
mode={this.state.mode}
|
|
|
|
source={this.state.source}
|
|
|
|
/>
|
2017-01-11 11:35:33 +01:00
|
|
|
<Button
|
|
|
|
className="maputnik-add-source-button"
|
|
|
|
onClick={() => this.props.onAdd(this.state.sourceId, this.state.source)}>
|
2016-12-21 17:21:04 +01:00
|
|
|
Add Source
|
2016-12-21 19:59:37 +01:00
|
|
|
</Button>
|
2016-12-21 17:21:04 +01:00
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
2016-12-21 16:11:08 +01:00
|
|
|
|
|
|
|
class SourcesModal extends React.Component {
|
|
|
|
static propTypes = {
|
|
|
|
mapStyle: React.PropTypes.object.isRequired,
|
|
|
|
isOpen: React.PropTypes.bool.isRequired,
|
2016-12-22 16:35:31 +01:00
|
|
|
onOpenToggle: React.PropTypes.func.isRequired,
|
2016-12-22 18:08:42 +01:00
|
|
|
onStyleChanged: React.PropTypes.func.isRequired,
|
|
|
|
}
|
|
|
|
|
2016-12-28 15:57:30 +01:00
|
|
|
stripTitle(source) {
|
|
|
|
const strippedSource = {...source}
|
|
|
|
delete strippedSource['title']
|
|
|
|
return strippedSource
|
|
|
|
}
|
|
|
|
|
2016-12-21 16:11:08 +01:00
|
|
|
render() {
|
2017-01-08 19:45:44 +01:00
|
|
|
const mapStyle = this.props.mapStyle
|
|
|
|
const activeSources = Object.keys(mapStyle.sources).map(sourceId => {
|
|
|
|
const source = mapStyle.sources[sourceId]
|
2016-12-28 15:20:07 +01:00
|
|
|
return <ActiveSourceTypeEditor
|
2016-12-22 21:06:32 +01:00
|
|
|
key={sourceId}
|
|
|
|
sourceId={sourceId}
|
|
|
|
source={source}
|
2017-01-08 19:45:44 +01:00
|
|
|
onChange={src => this.props.onStyleChanged(changeSource(mapStyle, sourceId, src))}
|
|
|
|
onDelete={() => this.props.onStyleChanged(deleteSource(mapStyle, sourceId))}
|
2016-12-22 21:06:32 +01:00
|
|
|
/>
|
2016-12-21 16:11:08 +01:00
|
|
|
})
|
|
|
|
|
2017-01-08 19:45:44 +01:00
|
|
|
const tilesetOptions = Object.keys(publicSources).filter(sourceId => !(sourceId in mapStyle.sources)).map(sourceId => {
|
2016-12-28 15:57:30 +01:00
|
|
|
const source = publicSources[sourceId]
|
2016-12-21 16:11:08 +01:00
|
|
|
return <PublicSource
|
2016-12-28 15:57:30 +01:00
|
|
|
key={sourceId}
|
|
|
|
id={sourceId}
|
2016-12-22 18:08:42 +01:00
|
|
|
type={source.type}
|
|
|
|
title={source.title}
|
2017-01-08 19:45:44 +01:00
|
|
|
onSelect={() => this.props.onStyleChanged(addSource(mapStyle, sourceId, this.stripTitle(source)))}
|
2016-12-21 16:11:08 +01:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
|
2016-12-21 16:50:34 +01:00
|
|
|
const inputProps = { }
|
2016-12-21 16:11:08 +01:00
|
|
|
return <Modal
|
|
|
|
isOpen={this.props.isOpen}
|
2016-12-22 16:35:31 +01:00
|
|
|
onOpenToggle={this.props.onOpenToggle}
|
2016-12-21 16:11:08 +01:00
|
|
|
title={'Sources'}
|
|
|
|
>
|
2017-01-11 11:35:33 +01:00
|
|
|
<div className="maputnik-modal-section">
|
|
|
|
<h4>Active Sources</h4>
|
2016-12-21 16:11:08 +01:00
|
|
|
{activeSources}
|
2017-01-11 11:35:33 +01:00
|
|
|
</div>
|
|
|
|
<div className="maputnik-modal-section">
|
|
|
|
<h4>Add New Source</h4>
|
|
|
|
<p>Add a new source to your style. You can only choose the source type and id at creation time!</p>
|
|
|
|
<AddSource
|
|
|
|
onAdd={(sourceId, source) => this.props.onStyleChanged(addSource(mapStyle, sourceId, source))}
|
|
|
|
/>
|
2016-12-21 19:59:37 +01:00
|
|
|
</div>
|
2016-12-21 17:21:04 +01:00
|
|
|
|
2017-01-11 11:35:33 +01:00
|
|
|
<div className="maputnik-modal-section">
|
|
|
|
<h4>Choose Public Source</h4>
|
|
|
|
<p>
|
2016-12-22 11:27:53 +01:00
|
|
|
Add one of the publicly availble sources to your style.
|
2017-01-11 11:35:33 +01:00
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
|
2016-12-21 16:11:08 +01:00
|
|
|
<div style={{maxwidth: 500}}>
|
|
|
|
{tilesetOptions}
|
|
|
|
</div>
|
|
|
|
</Modal>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default SourcesModal
|