mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-28 01:35: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
|
/** Supports displaying spec field for zoom function objects
|
||||||
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
|
* 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 = {
|
static propTypes = {
|
||||||
...specFieldProps,
|
...specFieldProps,
|
||||||
value: React.PropTypes.oneOfType([
|
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 theme from '../theme.js'
|
||||||
import SourceEditor from './source.jsx'
|
import SourceEditor from './source.jsx'
|
||||||
import FilterEditor from '../filter/editor.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 MdVisibility from 'react-icons/lib/md/visibility'
|
||||||
import MdVisibilityOff from 'react-icons/lib/md/visibility-off'
|
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 PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||||
|
|
||||||
import ScrollContainer from '../scrollcontainer.jsx'
|
import ScrollContainer from '../scrollcontainer.jsx'
|
||||||
|
import layout from '../layout.json'
|
||||||
|
|
||||||
class UnsupportedLayer extends React.Component {
|
class UnsupportedLayer extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -56,30 +57,13 @@ export class LayerEditor extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPaintChanged(property, newValue) {
|
onPropertyChange(group, property, newValue) {
|
||||||
let layer = this.props.layer
|
const layer = this.props.layer
|
||||||
//TODO: by using immutable records we can avoid this checking if object exists
|
const changedLayer = layer.setIn([group, property], newValue)
|
||||||
//
|
|
||||||
if(!layer.has('paint')) {
|
|
||||||
layer = layer.set('paint', Immutable.Map())
|
|
||||||
}
|
|
||||||
|
|
||||||
const changedLayer = layer.setIn(['paint', property], newValue)
|
|
||||||
this.props.onLayerChanged(changedLayer)
|
this.props.onLayerChanged(changedLayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLayoutChanged(property, newValue) {
|
onFilterChange(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) {
|
|
||||||
let layer = this.props.layer
|
let layer = this.props.layer
|
||||||
const changedLayer = layer.set('filter', newValue)
|
const changedLayer = layer.set('filter', newValue)
|
||||||
this.props.onLayerChanged(changedLayer)
|
this.props.onLayerChanged(changedLayer)
|
||||||
|
@ -94,17 +78,26 @@ export class LayerEditor extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 />
|
let visibleIcon = <MdVisibilityOff />
|
||||||
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
|
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
|
||||||
visibleIcon = <MdVisibility />
|
visibleIcon = <MdVisibility />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div style={{
|
return <div style={{
|
||||||
padding: theme.scale[0],
|
padding: theme.scale[0],
|
||||||
}}>
|
}}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<NavItem style={{fontWeight: 400}}>
|
<NavItem style={{fontWeight: 400}}>
|
||||||
#{this.props.layer.get('id')}
|
{this.props.layer.get('id')}
|
||||||
</NavItem>
|
</NavItem>
|
||||||
<Space auto x={1} />
|
<Space auto x={1} />
|
||||||
<NavItem onClick={this.toggleVisibility.bind(this)}>
|
<NavItem onClick={this.toggleVisibility.bind(this)}>
|
||||||
|
@ -114,42 +107,19 @@ export class LayerEditor extends React.Component {
|
||||||
<MdDelete />
|
<MdDelete />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<Tabs>
|
{propertyGroups}
|
||||||
<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'>
|
|
||||||
<FilterEditor
|
<FilterEditor
|
||||||
filter={this.props.layer.get('filter', Immutable.List()).toJSON()}
|
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')}
|
source={this.props.layer.get('source')}
|
||||||
sourceLayer={this.props.layer.get('source-layer')}
|
sourceLayer={this.props.layer.get('source-layer')}
|
||||||
sources={this.props.sources}
|
sources={this.props.sources}
|
||||||
onSourceChange={console.log}
|
onSourceChange={console.log}
|
||||||
onSourceLayerChange={console.log}
|
onSourceLayerChange={console.log}
|
||||||
/>}
|
/>}
|
||||||
</Tabs.Panel>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
</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