LayerEditor keeps layer as own state

This commit is contained in:
lukasmartinelli 2016-09-09 15:49:23 +02:00
parent ca04f60393
commit 3cf8a6939d
4 changed files with 119 additions and 51 deletions

View file

@ -4,17 +4,21 @@ import { Drawer, Container, Block, Fixed } from 'rebass'
import {Map} from './map.jsx' import {Map} from './map.jsx'
import {Toolbar} from './toolbar.jsx' import {Toolbar} from './toolbar.jsx'
import { LayerEditor } from './layers.jsx' import { LayerEditor } from './layers.jsx'
import { StyleManager } from './style.js'
import theme from './theme.js' import theme from './theme.js'
import layout from './layout.scss' import layout from './layout.scss'
import 'react-virtualized/styles.css'; import 'react-virtualized/styles.css'
export class WorkspaceDrawer extends React.Component { export class WorkspaceDrawer extends React.Component {
static propTypes = {
styleManager: React.PropTypes.object.isRequired
}
render() { render() {
let editor = null let editor = null
if(this.props.styleManager.mapStyle) {
if(this.props.mapStyle) { editor = <LayerEditor styleManager={this.props.styleManager}/>
editor = <LayerEditor layers={this.props.mapStyle.layers}/>
} }
return <div style={{ return <div style={{
@ -40,14 +44,13 @@ export default class App extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.updateStyle = this.updateStyle.bind(this);
this.state = { this.state = {
mapStyle: null styleManager: new StyleManager(),
} }
} }
updateStyle(newStyle) { onStyleUpload(newStyle) {
this.setState({ mapStyle: newStyle }) this.setState({ styleManager: new StyleManager(newStyle) })
} }
getChildContext () { getChildContext () {
@ -60,12 +63,11 @@ export default class App extends React.Component {
} }
render() { render() {
console.log(this.state.mapStyle)
return <div style={{ fontFamily: theme.fontFamily, color: theme.color }}> return <div style={{ fontFamily: theme.fontFamily, color: theme.color }}>
<Toolbar onStyleUpload={this.updateStyle} /> <Toolbar onStyleUpload={this.onStyleUpload.bind(this)} />
<WorkspaceDrawer mapStyle={this.state.mapStyle} /> <WorkspaceDrawer styleManager={this.state.styleManager}/>
<div className={layout.map}> <div className={layout.map}>
<Map mapStyle={this.state.mapStyle} /> <Map styleManager={this.state.styleManager} />
</div> </div>
</div> </div>
} }

View file

@ -8,17 +8,25 @@ import theme from './theme.js'
import scrollbars from './scrollbars.scss' import scrollbars from './scrollbars.scss'
export class FillLayer extends React.Component { export class FillLayer extends React.Component {
static propTypes = {
layer: React.PropTypes.object.isRequired,
onPaintChanged: React.PropTypes.func.isRequired
}
onPaintChanged(property, e) {
this.props.onPaintChanged(property, e.target.value)
}
render() { render() {
const paint = this.props.layer.paint
return <div> return <div>
<Input name="fill-color" label="Fill color" onChange={this.props.changePaint} value={this.props.paint["fill-color"]} /> <Input name="fill-color" label="Fill color" onChange={this.onPaintChanged.bind(this, "fill-color")} value={paint["fill-color"]} />
<Input name="fill-outline-color" label="Fill outline color" onChange={this.props.changePaint} value={this.props.paint["fill-outline-color"]} /> <Input name="fill-outline-color" label="Fill outline color" onChange={this.onPaintChanged.bind(this, "fill-outline-color")} value={paint["fill-outline-color"]} />
<Input name="fill-translate" label="Fill translate" onChange={this.props.changePaint} value={this.props.paint["fill-translate"]} /> <Input name="fill-translate" label="Fill translate" onChange={this.onPaintChanged.bind(this, "fill-translate")} value={paint["fill-translate"]} />
<Input name="fill-translate-anchor" label="Fill translate anchor" onChange={this.props.changePaint} value={this.props.paint["fill-translate-anchor"]} /> <Input name="fill-translate-anchor" label="Fill translate anchor" onChange={this.onPaintChanged.bind(this, "fill-translate-anchor")} value={paint["fill-translate-anchor"]} />
<Checkbox name="fill-antialias" label="Antialias" onChange={this.props.changePaint} checked={this.props.paint["fill-antialias"]} /> <Checkbox name="fill-antialias" label="Antialias" onChange={this.onPaintChanged.bind(this, "fill-antialias")} checked={paint["fill-antialias"]} />
<Input name="fill-opacity" label="Opacity" onChange={this.props.changePaint} value={this.props.paint["fill-opacity"]} /> <Input name="fill-opacity" label="Opacity" onChange={this.onPaintChanged.bind(this, "fill-opacity")} value={paint["fill-opacity"]} />
</div> </div>
} }
} }
@ -34,16 +42,30 @@ export class SymbolLayer extends React.Component {
} }
} }
export class NoLayer extends React.Component {
render() {
return <div></div>
}
}
export class LayerPanel extends React.Component { export class LayerPanel extends React.Component {
static propTypes = {
layer: React.PropTypes.object.isRequired,
styleManager: React.PropTypes.object.isRequired
}
static childContextTypes = { static childContextTypes = {
reactIconBase: React.PropTypes.object reactIconBase: React.PropTypes.object
} }
constructor(props) { constructor(props) {
super(props); super(props);
this.toggleLayer = this.toggleLayer.bind(this);
this.state = { this.state = {
isOpened: false isOpened: false,
//TODO: Is that bad practice?
//however I want to keep the layer state local herere
//otherwise the style always would, propagate around?
layer: this.props.layer
} }
} }
@ -56,18 +78,38 @@ export class LayerPanel extends React.Component {
} }
} }
onPaintChanged(property, newValue) {
let layer = this.state.layer
layer.paint[property] = newValue;
this.props.styleManager.changeStyle({
command: 'setPaintProperty',
args: [layer.id, property, newValue]
})
this.setState({ layer });
}
toggleLayer() { toggleLayer() {
this.setState({isOpened: !this.state.isOpened}) this.setState({isOpened: !this.state.isOpened})
} }
render() { layerFromType(type) {
let layer = <FillLayer paint={this.props.layer.paint}/> if (type === "fill") {
if (this.props.layer.type === "line") { return <FillLayer layer={this.state.layer} onPaintChanged={this.onPaintChanged.bind(this)} />
layer = <LineLayer />
} else if (this.props.layer.type === "symbol") {
layer = <SymbolLayer />
} }
if (type === "line") {
return <LineLayer />
}
if (type === "symbol") {
return <SymbolLayer />
}
return <NoLayer />
}
render() {
return <div style={{ return <div style={{
padding: theme.scale[0], padding: theme.scale[0],
borderBottom: 1, borderBottom: 1,
@ -77,9 +119,9 @@ export class LayerPanel extends React.Component {
borderStyle: "solid", borderStyle: "solid",
borderColor: theme.borderColor, borderColor: theme.borderColor,
}}> }}>
<Toolbar onClick={this.toggleLayer}> <Toolbar onClick={this.toggleLayer.bind(this)}>
<NavItem> <NavItem>
#{this.props.layer.id} #{this.state.layer.id}
</NavItem> </NavItem>
<Space auto x={1} /> <Space auto x={1} />
<NavItem> <NavItem>
@ -91,7 +133,7 @@ export class LayerPanel extends React.Component {
</Toolbar> </Toolbar>
<Collapse isOpened={this.state.isOpened}> <Collapse isOpened={this.state.isOpened}>
<div style={{padding: theme.scale[2], paddingRight: 0}}> <div style={{padding: theme.scale[2], paddingRight: 0}}>
{layer} {this.layerFromType(this.state.layer.type)}
</div> </div>
</Collapse> </Collapse>
</div> </div>
@ -99,10 +141,16 @@ export class LayerPanel extends React.Component {
} }
export class LayerEditor extends React.Component { export class LayerEditor extends React.Component {
static propTypes = {
styleManager: React.PropTypes.object.isRequired
}
render() { render() {
const layerPanels = this.props.layers.map(layer => { const layers = this.props.styleManager.layers()
return <LayerPanel key={layer.id} layer={layer} /> const layerPanels = layers.map(layer => {
return <LayerPanel key={layer.id} layer={layer} styleManager={this.props.styleManager} />
}); });
return <div> return <div>
<Toolbar style={{marginRight: 20}}> <Toolbar style={{marginRight: 20}}>
<NavItem> <NavItem>

View file

@ -4,14 +4,29 @@ import ReactMapboxGl, { ZoomControl } from "react-mapbox-gl"
import theme from './theme.js' import theme from './theme.js'
export class Map extends React.Component { export class Map extends React.Component {
static propTypes = {
styleManager: React.PropTypes.object.isRequired
}
constructor(props) { constructor(props) {
super(props) super(props)
this.map = null
}
onStyleChange(change) {
this.map[change.command].apply(this.map, change.args);
}
onMapLoaded(map) {
this.map = map;
this.props.styleManager.onStyleChange(this.onStyleChange.bind(this))
} }
render() { render() {
if (this.props.mapStyle) { if (this.props.styleManager.mapStyle) {
return <ReactMapboxGl return <ReactMapboxGl
style={this.props.mapStyle} onStyleLoad={this.onMapLoaded.bind(this)}
style={this.props.styleManager.mapStyle}
accessToken="pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6IjIzcmN0NlkifQ.0LRTNgCc-envt9d5MzR75w"> accessToken="pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6IjIzcmN0NlkifQ.0LRTNgCc-envt9d5MzR75w">
<ZoomControl/> <ZoomControl/>
</ReactMapboxGl> </ReactMapboxGl>
@ -19,4 +34,3 @@ export class Map extends React.Component {
return <div style={{backgroundColor: theme.colors.black}}/> return <div style={{backgroundColor: theme.colors.black}}/>
} }
} }

View file

@ -1,27 +1,31 @@
import React from 'react'; import React from 'react';
// A wrapper around Mapbox GL style // A wrapper around Mapbox GL style to publish
export class Style { // and subscribe to map changes
constructor() { export class StyleManager {
this.styleHistory = []; constructor(mapStyle) {
this.renderers = []; this.commandHistory = [];
this.subscribers = [];
this.mapStyle = mapStyle;
} }
load(style) { onStyleChange(cb) {
this.currentStyle = style; this.subscribers.push(cb);
} }
onRender(cb) { changeStyle(command) {
this.renderers.push(cb); this.commandHistory.push(command)
this.subscribers.forEach(f => f(command))
console.log(command)
} }
update(style) { layer(layerId) {
this.styleHistory.push(this.currentStyle); console.log(this.mapStyle)
this.currentStyle = style; return this.mapStyle.layers[layerId]
this.renderers.forEach(r => r(this.currentStyle))
} }
layers() { layers() {
return this.currentStyle.layers; if(this.mapStyle) return this.mapStyle.layers
return []
} }
} }