Add layout JSON defined property groups

This commit is contained in:
Lukas Martinelli 2016-12-19 20:15:30 +01:00
parent f9f7be1cad
commit 965b2d6e05
4 changed files with 189 additions and 80 deletions

View file

@ -0,0 +1,50 @@
import React from 'react'
import { ZoomSpecField } from './spec.jsx'
import Immutable from 'immutable'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import theme from '../theme.js'
console.log(ZoomSpecField)
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 => {
console.log(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: theme.scale[2],
paddingRight: 0,
backgroundColor: theme.colors.black,
marginBottom: theme.scale[2],
}}>
{fields}
</div>
}
}

View file

@ -24,7 +24,7 @@ const specFieldProps = {
/** 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 {
export class ZoomSpecField extends React.Component {
static propTypes = {
...specFieldProps,
value: React.PropTypes.oneOfType([
@ -167,30 +167,3 @@ class SpecField extends React.Component {
}
}
}
export class PropertyGroup extends React.Component {
static propTypes = {
onChange: React.PropTypes.func.isRequired,
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
layerType: React.PropTypes.oneOf(['fill', 'background', 'line', 'symbol']).isRequired,
groupType: React.PropTypes.oneOf(['paint', 'layout']).isRequired,
}
render() {
const layerTypeSpec = GlSpec[this.props.groupType + "_" + this.props.layerType]
const specFields = Object.keys(layerTypeSpec).filter(propName => propName !== 'visibility').map(propName => {
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>
}
}

View file

@ -9,7 +9,7 @@ import Tabs from 'react-simpletabs'
import theme from '../theme.js'
import SourceEditor from './source.jsx'
import FilterEditor from '../filter/editor.jsx'
import { PropertyGroup } from '../fields/spec.jsx'
import PropertyGroup from '../fields/propertygroup.jsx'
import MdVisibility from 'react-icons/lib/md/visibility'
import MdVisibilityOff from 'react-icons/lib/md/visibility-off'
@ -17,6 +17,7 @@ import MdDelete from 'react-icons/lib/md/delete'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import ScrollContainer from '../scrollcontainer.jsx'
import layout from '../layout.json'
class UnsupportedLayer extends React.Component {
render() {
@ -56,30 +57,13 @@ export class LayerEditor extends React.Component {
}
}
onPaintChanged(property, newValue) {
let layer = this.props.layer
//TODO: by using immutable records we can avoid this checking if object exists
//
if(!layer.has('paint')) {
layer = layer.set('paint', Immutable.Map())
}
const changedLayer = layer.setIn(['paint', property], newValue)
onPropertyChange(group, property, newValue) {
const layer = this.props.layer
const changedLayer = layer.setIn([group, property], newValue)
this.props.onLayerChanged(changedLayer)
}
onLayoutChanged(property, newValue) {
let layer = this.props.layer
//TODO: by using immutable records we can avoid this checking if object exists
if(!layer.has('layout')) {
layer = layer.set('layout', Immutable.Map())
}
const changedLayer = layer.setIn(['layout', property], newValue)
this.props.onLayerChanged(changedLayer)
}
onFilterChanged(newValue) {
onFilterChange(newValue) {
let layer = this.props.layer
const changedLayer = layer.set('filter', newValue)
this.props.onLayerChanged(changedLayer)
@ -94,17 +78,26 @@ export class LayerEditor extends React.Component {
}
render() {
const layerType = this.props.layer.get('type')
const groups = layout[layerType].groups
const propertyGroups = groups.map(group => {
return <PropertyGroup
layer={this.props.layer}
groupFields={Immutable.OrderedSet(group.fields)}
onChange={this.onPropertyChange.bind(this)}
/>
})
let visibleIcon = <MdVisibilityOff />
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
visibleIcon = <MdVisibility />
}
return <div style={{
padding: theme.scale[0],
}}>
<Toolbar>
<NavItem style={{fontWeight: 400}}>
#{this.props.layer.get('id')}
{this.props.layer.get('id')}
</NavItem>
<Space auto x={1} />
<NavItem onClick={this.toggleVisibility.bind(this)}>
@ -114,42 +107,19 @@ export class LayerEditor extends React.Component {
<MdDelete />
</NavItem>
</Toolbar>
<Tabs>
<Tabs.Panel title='Paint'>
<div style={{padding: theme.scale[2], paddingRight: 0, backgroundColor: theme.colors.black}}>
<PropertyGroup
onChange={this.onPaintChanged.bind(this)}
layerType={this.props.layer.get('type')}
groupType="paint"
properties={this.props.layer.get('paint', Immutable.Map())}
/>
</div>
</Tabs.Panel>
<Tabs.Panel title='Layout'>
<div style={{padding: theme.scale[2], paddingRight: 0, backgroundColor: theme.colors.black}}>
<PropertyGroup
onChange={this.onLayoutChanged.bind(this)}
layerType={this.props.layer.get('type')}
groupType="layout"
properties={this.props.layer.get('layout', Immutable.Map())}
/>
</div>
</Tabs.Panel>
<Tabs.Panel title='Data'>
{propertyGroups}
<FilterEditor
filter={this.props.layer.get('filter', Immutable.List()).toJSON()}
onChange={f => this.onFilterChanged(Immutable.fromJS(f))}
onChange={f => this.onFilterChange(Immutable.fromJS(f))}
/>
{this.props.layer.get('type') !== 'background' && <SourceEditor
{this.props.layer.get('type') !== 'background'
&& <SourceEditor
source={this.props.layer.get('source')}
sourceLayer={this.props.layer.get('source-layer')}
sources={this.props.sources}
onSourceChange={console.log}
onSourceLayerChange={console.log}
/>}
</Tabs.Panel>
</Tabs>
</div>
}
}

116
src/layout.json Normal file
View file

@ -0,0 +1,116 @@
{
"line": {
"groups": [
{
"title": "Paint",
"fields": [
"line-opacity",
"line-color",
"line-width",
"line-offset",
"line-blur",
"line-pattern"
]
},
{
"title": "Secondary",
"fields": [
"line-translate",
"line-translate-anchor",
"line-cap",
"line-join",
"line-miter-limit",
"line-round-limit",
"line-dasharray",
"line-gap-width"
]
}
]
},
"background": {
"groups": [
{
"title": "primary",
"fields": [
"background-color",
"background-pattern",
"background-opacity"
]
}
]
},
"fill": {
"groups": [
{
"title": "primary",
"fields": [
"fill-opacity",
"fill-color",
"fill-antialias",
"fill-outline-color",
"fill-pattern",
"fill-translate",
"fill-translate-anchor"
]
}
]
},
"symbol": {
"groups": [
{
"title": "primary",
"fields": [
"text-field",
"text-font",
"text-size",
"text-line-height"
]
},
{
"title": "placement",
"fields": [
"symbol-placement",
"symbol-spacing",
"symbol-avoid-edges",
"text-padding",
"text-allow-overlap",
"text-ignore-placement"
]
},
{
"title": "layout",
"fields": [
"text-pitch-alignment",
"text-rotation-alignment",
"text-max-width",
"text-letter-spacing",
"text-justify",
"text-anchor",
"text-max-angle",
"text-rotate",
"text-keep-upright",
"text-transform",
"text-offset",
"text-optional"
]
},
{
"title": "icon",
"fields": [
"icon-allow-overlap",
"icon-ignore-placement",
"icon-optional",
"icon-rotation-alignment",
"icon-size",
"icon-text-fit",
"icon-text-fit-padding",
"icon-image",
"icon-rotate",
"icon-padding",
"icon-keep-upright",
"icon-offset"
]
}
]
}
}