maputnik/src/components/modals/SourcesModal.jsx

272 lines
7.4 KiB
React
Raw Normal View History

2016-12-21 16:11:08 +01:00
import React from 'react'
import Modal from './Modal'
import Heading from '../Heading'
import Button from '../Button'
2016-12-22 11:27:53 +01:00
import Paragraph from '../Paragraph'
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'
2016-12-21 16:11:08 +01:00
import publicSources from '../../config/tilesets.json'
import colors from '../../config/colors'
2016-12-21 16:50:34 +01:00
import { margins, fontSizes } from '../../config/scales'
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: React.PropTypes.string.isRequired,
type: React.PropTypes.string.isRequired,
title: React.PropTypes.string.isRequired,
onSelect: React.PropTypes.func.isRequired,
}
render() {
return <div style={{
verticalAlign: 'top',
marginTop: margins[2],
marginRight: margins[2],
2016-12-21 16:50:34 +01:00
backgroundColor: colors.gray,
2016-12-21 16:11:08 +01:00
display: 'inline-block',
width: 240,
2016-12-21 16:50:34 +01:00
fontSize: fontSizes[4],
color: colors.lowgray,
2016-12-21 16:11:08 +01:00
}}>
2016-12-22 18:08:42 +01:00
<Button
onClick={() => this.props.onSelect(this.props.id)}
style={{
backgroundColor: 'transparent',
padding: margins[2],
display: 'flex',
flexDirection: 'row',
}}
>
2016-12-21 16:50:34 +01:00
<div>
<span style={{fontWeight: 700}}>{this.props.title}</span>
<br/>
2016-12-21 17:13:21 +01:00
<span style={{fontSize: fontSizes[5]}}>#{this.props.id}</span>
2016-12-21 16:50:34 +01:00
</div>
2016-12-21 16:11:08 +01:00
<span style={{flexGrow: 1}} />
<AddIcon />
</Button>
2016-12-21 16:11:08 +01:00
</div>
}
}
function editorMode(source) {
if(source.type === 'geojson') return ' geojson'
if(source.type === 'vector' && source.tiles) {
return 'tilexyz'
}
return 'tilejson'
}
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,
onSourceDelete: React.PropTypes.func.isRequired,
onSourceChange: React.PropTypes.func.isRequired,
2016-12-21 16:11:08 +01:00
}
render() {
2016-12-21 16:50:34 +01:00
const inputProps = { }
2016-12-21 16:11:08 +01:00
return <div style={{
}}>
2016-12-21 17:13:21 +01:00
<div style={{
backgroundColor: colors.gray,
color: colors.lowgray,
padding: margins[1],
display: 'flex',
fontSize: fontSizes[4],
flexDirection: 'row',
}}>
<span style={{fontWeight: 700, fontSize: fontSizes[4], lineHeight: 2}}>#{this.props.sourceId}</span>
2016-12-21 17:13:21 +01:00
<span style={{flexGrow: 1}} />
<Button
2016-12-28 15:20:07 +01:00
onClick={()=> this.props.onSourceDelete(this.props.sourceId)}
style={{backgroundColor: 'transparent'}}
>
<DeleteIcon />
</Button>
2016-12-21 17:13:21 +01:00
</div>
<div style={{
borderColor: colors.gray,
borderWidth: 2,
borderStyle: 'solid',
padding: margins[1],
}}>
<SourceTypeEditor
onChange={this.props.onSourceChange}
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 = {
onSourceAdd: React.PropTypes.func.isRequired,
}
constructor(props) {
super(props)
this.state = {
mode: 'tilejson',
sourceId: style.generateId(),
source: this.defaultSource('tilejson'),
}
}
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': return {
type: 'vector',
url: source.url || 'http://localhost:3000/tilejson.json'
}
case 'tilexyz': return {
type: 'vector',
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
}
onSourceChange(source) {
this.setState({
source: source
})
2016-12-21 17:21:04 +01:00
}
render() {
return <div>
<InputBlock label={"Source ID"}>
<StringInput
value={this.state.sourceId}
onChange={v => this.setState({ sourceId: v})}
2016-12-21 17:21:04 +01:00
/>
</InputBlock>
<InputBlock label={"Source Type"}>
<SelectInput
options={[
['geojson', 'GeoJSON'],
['tilejson', 'Vector (TileJSON URL)'],
['tilexyz', 'Vector (XYZ URLs)'],
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
onChange={this.onSourceChange.bind(this)}
mode={this.state.mode}
source={this.state.source}
/>
<Button onClick={() => this.props.onSourceAdd(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: 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,
}
onSourceAdd(sourceId, source) {
2016-12-22 18:08:42 +01:00
const changedSources = {
...this.props.mapStyle.sources,
[sourceId]: source
2016-12-22 18:08:42 +01:00
}
const changedStyle = {
...this.props.mapStyle,
sources: changedSources
}
this.props.onStyleChanged(changedStyle)
2016-12-21 16:11:08 +01:00
}
2016-12-28 15:20:07 +01:00
deleteSource(sourceId) {
const remainingSources = { ...this.props.mapStyle.sources}
delete remainingSources[sourceId]
const changedStyle = {
...this.props.mapStyle,
sources: remainingSources
}
this.props.onStyleChanged(changedStyle)
}
stripTitle(source) {
const strippedSource = {...source}
delete strippedSource['title']
return strippedSource
}
2016-12-21 16:11:08 +01:00
render() {
const activeSources = Object.keys(this.props.mapStyle.sources).map(sourceId => {
const source = this.props.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}
2016-12-28 15:20:07 +01:00
onSourceDelete={this.deleteSource.bind(this)}
2016-12-22 21:06:32 +01:00
/>
2016-12-21 16:11:08 +01:00
})
const tilesetOptions = Object.keys(publicSources).filter(sourceId => !(sourceId in this.props.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}
onSelect={() => this.onSourceAdd(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'}
>
<Heading level={4}>Active Sources</Heading>
{activeSources}
<Heading level={4}>Add New Source</Heading>
<div style={{maxWidth: 300}}>
<p style={{color: colors.lowgray, fontSize: fontSizes[5]}}>Add a new source to your style. You can only choose the source type and id at creation time!</p>
2016-12-22 18:08:42 +01:00
<AddSource onSourceAdd={this.onSourceAdd.bind(this)} />
</div>
2016-12-21 17:21:04 +01:00
2016-12-21 16:11:08 +01:00
<Heading level={4}>Choose Public Source</Heading>
2016-12-22 11:27:53 +01:00
<Paragraph>
Add one of the publicly availble sources to your style.
</Paragraph>
2016-12-21 16:11:08 +01:00
<div style={{maxwidth: 500}}>
{tilesetOptions}
</div>
</Modal>
}
}
export default SourcesModal