mirror of
https://github.com/maputnik/editor.git
synced 2026-02-11 07:00:00 +00:00
Restructure and rename components
This commit is contained in:
28
src/components/fields/BooleanField.jsx
Normal file
28
src/components/fields/BooleanField.jsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from 'react'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
class BooleanField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.bool,
|
||||
doc: React.PropTypes.string,
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
render() {
|
||||
return <input
|
||||
type="checkbox"
|
||||
style={{
|
||||
...input.checkbox,
|
||||
...this.props.style
|
||||
}}
|
||||
value={this.props.value}
|
||||
onChange={e => {this.props.onChange(!this.props.value)}}
|
||||
checked={this.props.value}
|
||||
>
|
||||
</input>
|
||||
}
|
||||
}
|
||||
|
||||
export default BooleanField
|
||||
77
src/components/fields/ColorField.jsx
Normal file
77
src/components/fields/ColorField.jsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react'
|
||||
import Color from 'color'
|
||||
import ChromePicker from 'react-color/lib/components/chrome/Chrome'
|
||||
|
||||
import input from '../../config/input.js'
|
||||
|
||||
function formatColor(color) {
|
||||
const rgb = color.rgb
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
|
||||
}
|
||||
|
||||
/*** Number fields with support for min, max and units and documentation*/
|
||||
class ColorField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.string,
|
||||
default: React.PropTypes.number,
|
||||
doc: React.PropTypes.string,
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
pickerOpened: false
|
||||
}
|
||||
}
|
||||
|
||||
togglePicker() {
|
||||
this.setState({ pickerOpened: !this.state.pickerOpened })
|
||||
}
|
||||
|
||||
render() {
|
||||
const picker = <div style={{
|
||||
position: 'absolute',
|
||||
left: 163,
|
||||
top: 0,
|
||||
}}>
|
||||
<ChromePicker
|
||||
color={this.props.value ? Color(this.props.value).object() : null}
|
||||
onChange={c => this.props.onChange(formatColor(c))}
|
||||
/>
|
||||
<div
|
||||
onClick={this.togglePicker.bind(this)}
|
||||
style={{
|
||||
zIndex: -1,
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px',
|
||||
}} />
|
||||
</div>
|
||||
|
||||
return <div style={{
|
||||
...input.property,
|
||||
position: 'relative',
|
||||
display: 'inline',
|
||||
}}>
|
||||
{this.state.pickerOpened && picker}
|
||||
<input
|
||||
onClick={this.togglePicker.bind(this)}
|
||||
style={{
|
||||
...input.select,
|
||||
...this.props.style
|
||||
}}
|
||||
name={this.props.name}
|
||||
placeholder={this.props.default}
|
||||
value={this.props.value ? this.props.value : ""}
|
||||
onChange={(e) => this.props.onChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default ColorField
|
||||
36
src/components/fields/EnumField.jsx
Normal file
36
src/components/fields/EnumField.jsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
class EnumField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.string,
|
||||
allowedValues: React.PropTypes.array.isRequired,
|
||||
doc: React.PropTypes.string,
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
return this.props.onChange(e.target.value)
|
||||
}
|
||||
|
||||
render() {
|
||||
const options = this.props.allowedValues.map(val => {
|
||||
return <option key={val} value={val}>{val}</option>
|
||||
})
|
||||
|
||||
return <select
|
||||
style={{
|
||||
...input.select,
|
||||
...this.props.style
|
||||
}}
|
||||
value={this.props.value}
|
||||
onChange={this.onChange.bind(this)}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
}
|
||||
}
|
||||
|
||||
export default EnumField
|
||||
42
src/components/fields/NumberField.jsx
Normal file
42
src/components/fields/NumberField.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import React from 'react'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
/*** Number fields with support for min, max and units and documentation*/
|
||||
class NumberField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.number,
|
||||
default: React.PropTypes.number,
|
||||
unit: React.PropTypes.string,
|
||||
min: React.PropTypes.number,
|
||||
max: React.PropTypes.number,
|
||||
doc: React.PropTypes.string,
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
const value = parseFloat(e.target.value)
|
||||
/*TODO: we can do range validation already here?
|
||||
if(this.props.min && value < this.props.min) return
|
||||
if(this.props.max && value > this.props.max) return
|
||||
*/
|
||||
this.props.onChange(value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <input
|
||||
style={{
|
||||
...input.input,
|
||||
...this.props.style
|
||||
}}
|
||||
type="number"
|
||||
name={this.props.name}
|
||||
placeholder={this.props.default}
|
||||
value={this.props.value}
|
||||
onChange={this.onChange.bind(this)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
export default NumberField
|
||||
50
src/components/fields/PropertyGroup.jsx
Normal file
50
src/components/fields/PropertyGroup.jsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import Immutable from 'immutable'
|
||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
|
||||
|
||||
import ZoomSpecField from './ZoomSpecField'
|
||||
import colors from '../../config/colors'
|
||||
import { margins } from '../../config/scales'
|
||||
|
||||
/** Extract field spec by {@fieldName} from the {@layerType} in the
|
||||
* style specification from either the paint or layout group */
|
||||
function getFieldSpec(layerType, fieldName) {
|
||||
const paint = GlSpec['paint_' + layerType] || {}
|
||||
const layout = GlSpec['layout_' + layerType] || {}
|
||||
if (fieldName in paint) {
|
||||
return paint[fieldName]
|
||||
} else {
|
||||
return layout[fieldName]
|
||||
}
|
||||
}
|
||||
|
||||
export default class PropertyGroup extends React.Component {
|
||||
static propTypes = {
|
||||
layer: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
groupFields: React.PropTypes.instanceOf(Immutable.OrderedSet).isRequired,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const fields = this.props.groupFields.map(fieldName => {
|
||||
const fieldSpec = getFieldSpec(this.props.layer.get('type'), fieldName)
|
||||
const fieldValue = this.props.layer.getIn(['paint', fieldName], this.props.layer.getIn(['layout', fieldName]))
|
||||
return <ZoomSpecField
|
||||
onChange={this.props.onChange}
|
||||
key={fieldName}
|
||||
fieldName={fieldName}
|
||||
value={fieldValue}
|
||||
fieldSpec={fieldSpec}
|
||||
/>
|
||||
}).toIndexedSeq()
|
||||
|
||||
return <div style={{
|
||||
padding: margins[2],
|
||||
paddingRight: 0,
|
||||
backgroundColor: colors.black,
|
||||
marginBottom: margins[2],
|
||||
}}>
|
||||
{fields}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
103
src/components/fields/SpecField.jsx
Normal file
103
src/components/fields/SpecField.jsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import React from 'react'
|
||||
import Immutable from 'immutable'
|
||||
import color from 'color'
|
||||
|
||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
|
||||
import NumberField from './NumberField'
|
||||
import EnumField from './EnumField'
|
||||
import BooleanField from './BooleanField'
|
||||
import ColorField from './ColorField'
|
||||
import StringField from './StringField'
|
||||
|
||||
import input from '../../config/input.js'
|
||||
import theme from '../../config/rebass.js'
|
||||
|
||||
function isZoomField(value) {
|
||||
return Immutable.Map.isMap(value)
|
||||
}
|
||||
|
||||
function labelFromFieldName(fieldName) {
|
||||
let label = fieldName.split('-').slice(1).join(' ')
|
||||
if(label.length > 0) {
|
||||
label = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
}
|
||||
return label
|
||||
}
|
||||
|
||||
/** Display any field from the Mapbox GL style spec and
|
||||
* choose the correct field component based on the @{fieldSpec}
|
||||
* to display @{value}. */
|
||||
export default class SpecField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
fieldName: React.PropTypes.string.isRequired,
|
||||
fieldSpec: React.PropTypes.object.isRequired,
|
||||
|
||||
value: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
||||
]),
|
||||
/** Override the style of the field */
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
onValueChanged(property, value) {
|
||||
return this.props.onChange(property, value)
|
||||
}
|
||||
|
||||
render() {
|
||||
switch(this.props.fieldSpec.type) {
|
||||
case 'number': return (
|
||||
<NumberField
|
||||
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
|
||||
value={this.props.value}
|
||||
name={this.props.fieldName}
|
||||
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}
|
||||
style={this.props.style}
|
||||
/>
|
||||
)
|
||||
case 'enum': return (
|
||||
<EnumField
|
||||
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
|
||||
value={this.props.value}
|
||||
name={this.props.fieldName}
|
||||
allowedValues={Object.keys(this.props.fieldSpec.values)}
|
||||
doc={this.props.fieldSpec.doc}
|
||||
style={this.props.style}
|
||||
/>
|
||||
)
|
||||
case 'string': return (
|
||||
<StringField
|
||||
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
|
||||
value={this.props.value}
|
||||
name={this.props.fieldName}
|
||||
doc={this.props.fieldSpec.doc}
|
||||
style={this.props.style}
|
||||
/>
|
||||
)
|
||||
case 'color': return (
|
||||
<ColorField
|
||||
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
|
||||
value={this.props.value}
|
||||
name={this.props.fieldName}
|
||||
doc={this.props.fieldSpec.doc}
|
||||
style={this.props.style}
|
||||
/>
|
||||
)
|
||||
case 'boolean': return (
|
||||
<BooleanField
|
||||
onChange={this.onValueChanged.bind(this, this.props.fieldName)}
|
||||
value={this.props.value}
|
||||
name={this.props.fieldName}
|
||||
doc={this.props.fieldSpec.doc}
|
||||
style={this.props.style}
|
||||
/>
|
||||
)
|
||||
default: return null
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/components/fields/StringField.jsx
Normal file
34
src/components/fields/StringField.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
/*** Number fields with support for min, max and units and documentation*/
|
||||
class StringField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
name: React.PropTypes.string.isRequired,
|
||||
value: React.PropTypes.string,
|
||||
default: React.PropTypes.number,
|
||||
doc: React.PropTypes.string,
|
||||
style: React.PropTypes.object,
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
const value = e.target.value
|
||||
return this.props.onChange(value === "" ? null: value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <input
|
||||
style={{
|
||||
...input.input,
|
||||
...this.props.style
|
||||
}}
|
||||
name={this.props.name}
|
||||
placeholder={this.props.default}
|
||||
value={this.props.value ? this.props.value : ""}
|
||||
onChange={this.onChange.bind(this)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
export default StringField
|
||||
98
src/components/fields/ZoomSpecField.jsx
Normal file
98
src/components/fields/ZoomSpecField.jsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import React from 'react'
|
||||
import Immutable from 'immutable'
|
||||
import Color from 'color'
|
||||
|
||||
import NumberField from './NumberField'
|
||||
import EnumField from './EnumField'
|
||||
import BooleanField from './BooleanField'
|
||||
import ColorField from './ColorField'
|
||||
import StringField from './StringField'
|
||||
import SpecField from './SpecField'
|
||||
|
||||
import input from '../../config/input.js'
|
||||
import colors from '../../config/colors.js'
|
||||
import { margins } from '../../config/scales.js'
|
||||
|
||||
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
|
||||
*/
|
||||
export default class ZoomSpecField extends React.Component {
|
||||
static propTypes = {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
fieldName: React.PropTypes.string.isRequired,
|
||||
fieldSpec: React.PropTypes.object.isRequired,
|
||||
|
||||
value: React.PropTypes.oneOfType([
|
||||
React.PropTypes.object,
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
||||
React.PropTypes.bool,
|
||||
]),
|
||||
}
|
||||
|
||||
render() {
|
||||
const label = <label style={input.label}>
|
||||
{labelFromFieldName(this.props.fieldName)}
|
||||
</label>
|
||||
|
||||
if(isZoomField(this.props.value)) {
|
||||
const zoomFields = this.props.value.get('stops').map(stop => {
|
||||
const zoomLevel = stop.get(0)
|
||||
const value = stop.get(1)
|
||||
|
||||
return <div style={input.property} key={zoomLevel}>
|
||||
{label}
|
||||
<SpecField {...this.props}
|
||||
value={value}
|
||||
style={{
|
||||
width: '33%'
|
||||
}}
|
||||
/>
|
||||
|
||||
<input
|
||||
style={{
|
||||
...input.input,
|
||||
width: '10%',
|
||||
marginLeft: margins[0],
|
||||
}}
|
||||
type="number"
|
||||
value={zoomLevel}
|
||||
min={0}
|
||||
max={22}
|
||||
/>
|
||||
</div>
|
||||
}).toSeq()
|
||||
return <div style={{
|
||||
border: 1,
|
||||
borderStyle: 'solid',
|
||||
borderColor: Color(colors.gray).lighten(0.1).string(),
|
||||
padding: margins[1],
|
||||
}}>
|
||||
{zoomFields}
|
||||
</div>
|
||||
} else {
|
||||
return <div style={input.property}>
|
||||
{label}
|
||||
<SpecField {...this.props} />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function labelFromFieldName(fieldName) {
|
||||
let label = fieldName.split('-').slice(1).join(' ')
|
||||
if(label.length > 0) {
|
||||
label = label.charAt(0).toUpperCase() + label.slice(1);
|
||||
}
|
||||
return label
|
||||
}
|
||||
Reference in New Issue
Block a user