maputnik/src/fields/spec.jsx

190 lines
5.3 KiB
React
Raw Normal View History

2016-09-12 19:44:28 +02:00
import React from 'react'
import Immutable from 'immutable'
2016-12-17 17:40:44 +01:00
import color from 'color'
2016-09-12 19:44:28 +02:00
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import NumberField from './number'
import EnumField from './enum'
2016-12-18 20:08:20 +01:00
import BooleanField from './boolean'
2016-09-12 19:47:28 +02:00
import ColorField from './color'
2016-09-12 19:54:02 +02:00
import StringField from './string'
2016-12-17 17:40:44 +01:00
import inputStyle from './input.js'
import theme from '../theme.js'
2016-09-12 19:44:28 +02:00
2016-12-17 17:40:44 +01:00
function isZoomField(value) {
return Immutable.Map.isMap(value)
}
const specFieldProps = {
onChange: React.PropTypes.func.isRequired,
fieldName: React.PropTypes.string.isRequired,
fieldSpec: React.PropTypes.object.isRequired,
}
/** Supports displaying spec field for zoom function objects
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
*/
class ZoomSpecField extends React.Component {
static propTypes = {
...specFieldProps,
value: React.PropTypes.oneOfType([
2016-09-12 19:44:28 +02:00
React.PropTypes.object,
React.PropTypes.string,
React.PropTypes.number,
2016-12-18 20:08:20 +01:00
React.PropTypes.bool,
2016-12-17 17:40:44 +01:00
]),
2016-09-12 19:44:28 +02:00
}
2016-12-17 17:40:44 +01:00
render() {
if(isZoomField(this.props.value)) {
const zoomFields = this.props.value.get('stops').map(stop => {
const zoomLevel = stop.get(0)
const value = stop.get(1)
2016-12-17 17:40:44 +01:00
return <div key={zoomLevel}>
<b><span style={inputStyle.label}>Zoom Level {zoomLevel}</span></b>
2016-12-19 16:21:22 +01:00
<SpecField {...this.props}
value={value}
style={{
width: '20%'
}}
/>
<input
style={{
...inputStyle.input,
width: '10%'
}}
type="number"
value={zoomLevel}
min={0}
max={22}
/>
2016-12-17 17:40:44 +01:00
</div>
}).toSeq()
return <div style={{
border: 1,
borderStyle: 'solid',
borderColor: color(theme.colors.gray).lighten(0.1).string(),
2016-12-17 17:40:44 +01:00
padding: theme.scale[1],
}}>
{zoomFields}
</div>
} else {
return <SpecField {...this.props} />
}
}
}
2016-12-17 20:36:43 +01:00
function labelFromFieldName(fieldName) {
let label = fieldName.split('-').slice(1).join(' ')
if(label.length > 0) {
label = label.charAt(0).toUpperCase() + label.slice(1);
}
return label
}
2016-12-19 16:21:22 +01:00
2016-12-17 17:40:44 +01:00
/** Display any field from the Mapbox GL style spec and
* choose the correct field component based on the @{fieldSpec}
* to display @{value}. */
class SpecField extends React.Component {
static propTypes = {
...specFieldProps,
value: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
]),
2016-12-19 16:21:22 +01:00
/** Override the style of the field */
style: React.PropTypes.object,
2016-12-17 17:40:44 +01:00
}
onValueChanged(property, value) {
return this.props.onChange(property, value)
}
render() {
2016-12-17 20:36:43 +01:00
const label = labelFromFieldName(this.props.fieldName)
2016-12-17 17:40:44 +01:00
switch(this.props.fieldSpec.type) {
case 'number': return (
<NumberField
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
value={this.props.value}
2016-12-17 20:36:43 +01:00
name={label}
2016-12-17 17:40:44 +01:00
default={this.props.fieldSpec.default}
min={this.props.fieldSpec.min}
max={this.props.fieldSpec.max}
unit={this.props.fieldSpec.unit}
doc={this.props.fieldSpec.doc}
2016-12-19 16:21:22 +01:00
style={this.props.style}
2016-12-17 17:40:44 +01:00
/>
)
case 'enum': return (
<EnumField
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
value={this.props.value}
2016-12-17 20:36:43 +01:00
name={label}
allowedValues={Object.keys(this.props.fieldSpec.values)}
2016-12-17 17:40:44 +01:00
doc={this.props.fieldSpec.doc}
2016-12-19 16:21:22 +01:00
style={this.props.style}
2016-12-17 17:40:44 +01:00
/>
)
case 'string': return (
<StringField
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
value={this.props.value}
2016-12-17 20:36:43 +01:00
name={label}
2016-12-17 17:40:44 +01:00
doc={this.props.fieldSpec.doc}
2016-12-19 16:21:22 +01:00
style={this.props.style}
2016-12-17 17:40:44 +01:00
/>
)
case 'color': return (
<ColorField
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
value={this.props.value}
2016-12-17 20:36:43 +01:00
name={label}
2016-12-18 20:08:20 +01:00
doc={this.props.fieldSpec.doc}
2016-12-19 16:21:22 +01:00
style={this.props.style}
2016-12-18 20:08:20 +01:00
/>
)
case 'boolean': return (
<BooleanField
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
value={this.props.value}
name={label}
2016-12-17 17:40:44 +01:00
doc={this.props.fieldSpec.doc}
2016-12-19 16:21:22 +01:00
style={this.props.style}
2016-12-17 17:40:44 +01:00
/>
)
default: return null
}
}
2016-09-12 19:44:28 +02:00
}
export class PropertyGroup extends React.Component {
2016-12-17 17:40:44 +01:00
static propTypes = {
onChange: React.PropTypes.func.isRequired,
2016-12-17 17:40:44 +01:00
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
layerType: React.PropTypes.oneOf(['fill', 'background', 'line', 'symbol']).isRequired,
groupType: React.PropTypes.oneOf(['paint', 'layout']).isRequired,
2016-09-12 19:44:28 +02:00
}
2016-12-17 17:40:44 +01:00
render() {
const layerTypeSpec = GlSpec[this.props.groupType + "_" + this.props.layerType]
2016-12-17 20:36:43 +01:00
const specFields = Object.keys(layerTypeSpec).filter(propName => propName !== 'visibility').map(propName => {
2016-12-17 17:40:44 +01:00
const fieldSpec = layerTypeSpec[propName]
const propValue = this.props.properties.get(propName)
return <ZoomSpecField
onChange={this.props.onChange}
key={propName}
value={propValue}
fieldName={propName}
fieldSpec={fieldSpec}
/>
})
return <div>
{specFields}
</div>
}
2016-09-12 19:44:28 +02:00
}