maputnik/src/components/modals/SourcesModal.jsx

240 lines
7.3 KiB
React
Raw Normal View History

2016-12-21 16:11:08 +01:00
import React from 'react'
import PropTypes from 'prop-types'
import styleSpec from '@mapbox/mapbox-gl-style-spec'
2016-12-21 16:11:08 +01:00
import Modal from './Modal'
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'
import DeleteIcon from 'react-icons/lib/md/delete'
2016-12-21 16:11:08 +01:00
class PublicSource extends React.Component {
static propTypes = {
id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
onSelect: PropTypes.func.isRequired,
2016-12-21 16:11:08 +01:00
}
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>
}
}
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'
}
if(source.type === 'geojson') return 'geojson'
2017-01-08 19:45:44 +01:00
return null
}
2016-12-28 15:20:07 +01:00
class ActiveSourceTypeEditor extends React.Component {
2016-12-21 16:11:08 +01:00
static propTypes = {
sourceId: PropTypes.string.isRequired,
source: PropTypes.object.isRequired,
onDelete: PropTypes.func.isRequired,
onChange: 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" />
<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)}
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">
<SourceTypeEditor
2017-01-08 19:45:44 +01:00
onChange={this.props.onChange}
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 = {
onAdd: PropTypes.func.isRequired,
}
constructor(props) {
super(props)
this.state = {
mode: 'tilejson_vector',
sourceId: style.generateId(),
source: this.defaultSource('tilejson_vector'),
}
}
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
}
case 'tilejson_vector': return {
type: 'vector',
url: source.url || 'http://localhost:3000/tilejson.json'
}
case 'tilexyz_vector': return {
type: 'vector',
tiles: source.tiles || ['http://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'
}
case 'tilexyz_raster': return {
type: 'raster',
tiles: source.tiles || ['http://localhost:3000/{x}/{y}/{z}.pbf'],
minzoom: source.minzoom || 0,
maxzoom: source.maxzoom || 14
}
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
value={this.state.sourceId}
onChange={v => this.setState({ sourceId: v})}
2016-12-21 17:21:04 +01:00
/>
</InputBlock>
<InputBlock label={"Source Type"} doc={styleSpec.latest.source_tile.type.doc}>
2016-12-21 17:21:04 +01:00
<SelectInput
options={[
['geojson', 'GeoJSON'],
['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
]}
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode}
2016-12-21 17:21:04 +01:00
/>
</InputBlock>
<SourceTypeEditor
2017-01-08 19:45:44 +01:00
onChange={src => this.setState({ source: src })}
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
</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: PropTypes.object.isRequired,
isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired,
onStyleChanged: PropTypes.func.isRequired,
2016-12-22 18:08:42 +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 => {
const source = publicSources[sourceId]
2016-12-21 16:11:08 +01:00
return <PublicSource
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">
2017-01-11 16:26:14 +01:00
<h4>Active Sources</h4>
{activeSources}
2017-01-11 11:35:33 +01:00
</div>
2017-01-11 16:26:14 +01:00
<div className="maputnik-modal-section">
<h4>Choose Public Source</h4>
<p>
Add one of the publicly availble sources to your style.
</p>
<div style={{maxwidth: 500}}>
{tilesetOptions}
</div>
</div>
2017-01-11 11:35:33 +01:00
<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))}
/>
</div>
2016-12-21 16:11:08 +01:00
</Modal>
}
}
export default SourcesModal