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

View file

@ -8,17 +8,25 @@ import theme from './theme.js'
import scrollbars from './scrollbars.scss'
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() {
const paint = this.props.layer.paint
return <div>
<Input name="fill-color" label="Fill color" onChange={this.props.changePaint} value={this.props.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-translate" label="Fill translate" onChange={this.props.changePaint} value={this.props.paint["fill-translate"]} />
<Input name="fill-translate-anchor" label="Fill translate anchor" onChange={this.props.changePaint} value={this.props.paint["fill-translate-anchor"]} />
<Checkbox name="fill-antialias" label="Antialias" onChange={this.props.changePaint} checked={this.props.paint["fill-antialias"]} />
<Input name="fill-opacity" label="Opacity" onChange={this.props.changePaint} value={this.props.paint["fill-opacity"]} />
<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.onPaintChanged.bind(this, "fill-outline-color")} value={paint["fill-outline-color"]} />
<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.onPaintChanged.bind(this, "fill-translate-anchor")} value={paint["fill-translate-anchor"]} />
<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.onPaintChanged.bind(this, "fill-opacity")} value={paint["fill-opacity"]} />
</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 {
static propTypes = {
layer: React.PropTypes.object.isRequired,
styleManager: React.PropTypes.object.isRequired
}
static childContextTypes = {
reactIconBase: React.PropTypes.object
}
constructor(props) {
super(props);
this.toggleLayer = this.toggleLayer.bind(this);
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() {
this.setState({isOpened: !this.state.isOpened})
}
render() {
let layer = <FillLayer paint={this.props.layer.paint}/>
if (this.props.layer.type === "line") {
layer = <LineLayer />
} else if (this.props.layer.type === "symbol") {
layer = <SymbolLayer />
layerFromType(type) {
if (type === "fill") {
return <FillLayer layer={this.state.layer} onPaintChanged={this.onPaintChanged.bind(this)} />
}
if (type === "line") {
return <LineLayer />
}
if (type === "symbol") {
return <SymbolLayer />
}
return <NoLayer />
}
render() {
return <div style={{
padding: theme.scale[0],
borderBottom: 1,
@ -77,9 +119,9 @@ export class LayerPanel extends React.Component {
borderStyle: "solid",
borderColor: theme.borderColor,
}}>
<Toolbar onClick={this.toggleLayer}>
<Toolbar onClick={this.toggleLayer.bind(this)}>
<NavItem>
#{this.props.layer.id}
#{this.state.layer.id}
</NavItem>
<Space auto x={1} />
<NavItem>
@ -91,7 +133,7 @@ export class LayerPanel extends React.Component {
</Toolbar>
<Collapse isOpened={this.state.isOpened}>
<div style={{padding: theme.scale[2], paddingRight: 0}}>
{layer}
{this.layerFromType(this.state.layer.type)}
</div>
</Collapse>
</div>
@ -99,10 +141,16 @@ export class LayerPanel extends React.Component {
}
export class LayerEditor extends React.Component {
static propTypes = {
styleManager: React.PropTypes.object.isRequired
}
render() {
const layerPanels = this.props.layers.map(layer => {
return <LayerPanel key={layer.id} layer={layer} />
const layers = this.props.styleManager.layers()
const layerPanels = layers.map(layer => {
return <LayerPanel key={layer.id} layer={layer} styleManager={this.props.styleManager} />
});
return <div>
<Toolbar style={{marginRight: 20}}>
<NavItem>

View file

@ -4,14 +4,29 @@ import ReactMapboxGl, { ZoomControl } from "react-mapbox-gl"
import theme from './theme.js'
export class Map extends React.Component {
static propTypes = {
styleManager: React.PropTypes.object.isRequired
}
constructor(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() {
if (this.props.mapStyle) {
if (this.props.styleManager.mapStyle) {
return <ReactMapboxGl
style={this.props.mapStyle}
onStyleLoad={this.onMapLoaded.bind(this)}
style={this.props.styleManager.mapStyle}
accessToken="pk.eyJ1IjoibW9yZ2Vua2FmZmVlIiwiYSI6IjIzcmN0NlkifQ.0LRTNgCc-envt9d5MzR75w">
<ZoomControl/>
</ReactMapboxGl>
@ -19,4 +34,3 @@ export class Map extends React.Component {
return <div style={{backgroundColor: theme.colors.black}}/>
}
}

View file

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