mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-27 09:45:29 +01:00
LayerEditor keeps layer as own state
This commit is contained in:
parent
ca04f60393
commit
3cf8a6939d
4 changed files with 119 additions and 51 deletions
26
src/app.jsx
26
src/app.jsx
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
22
src/map.jsx
22
src/map.jsx
|
@ -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}}/>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
src/style.js
32
src/style.js
|
@ -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 []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue