mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-27 08:55:25 +01:00
Add layout JSON defined property groups
This commit is contained in:
parent
f9f7be1cad
commit
965b2d6e05
4 changed files with 189 additions and 80 deletions
50
src/fields/propertygroup.jsx
Normal file
50
src/fields/propertygroup.jsx
Normal 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>
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
116
src/layout.json
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue