Support copy, delete and toggle visibility

This commit is contained in:
Lukas Martinelli 2016-12-20 20:21:35 +01:00
parent 6d9484ec5e
commit dc097e9f9a
6 changed files with 87 additions and 50 deletions

View file

@ -21,6 +21,7 @@
"dependencies": {
"color": "^1.0.3",
"file-saver": "^1.3.2",
"lodash.clonedeep": "^4.5.0",
"lodash.throttle": "^4.1.1",
"lodash.topairs": "^4.3.0",
"mapbox-gl": "mapbox/mapbox-gl-js#6c24b9621d2aa770eda67fb5638b4d78087b5624",

View file

@ -75,12 +75,12 @@ export default class App extends React.Component {
this.setState({ accessToken: newToken })
}
onLayersChanged(changedLayers) {
onLayersChange(changedLayers) {
const changedStyle = {
...this.state.mapStyle,
layers: [changedLayers]
layers: changedLayers
}
this.setState({ mapStyle: newStyle })
this.setState({ mapStyle: changedStyle })
}
onLayerChanged(layer) {
@ -112,14 +112,9 @@ export default class App extends React.Component {
}
}
onLayerSelected(layerId) {
const layers = this.state.mapStyle.layers
for (let i = 0; i < layers.length; i++) {
if(layers[i].id === layerId) {
this.setState({ selectedLayerIndex: i })
break
}
}
onLayerSelect(layerId) {
const idx = style.indexOfLayer(this.state.mapStyle.layers, layerId)
this.setState({ selectedLayerIndex: idx })
}
render() {
@ -135,17 +130,17 @@ export default class App extends React.Component {
/>
const layerList = <LayerList
onLayersChanged={this.onLayersChanged.bind(this)}
onLayerSelected={this.onLayerSelected.bind(this)}
onLayersChange={this.onLayersChange.bind(this)}
onLayerSelect={this.onLayerSelect.bind(this)}
selectedLayerIndex={this.state.selectedLayerIndex}
layers={layers}
/>
const layerEditor = selectedLayer ? <LayerEditor
layer={selectedLayer}
onLayerChanged={this.onLayerChanged.bind(this)}
sources={this.layerWatcher.sources}
vectorLayers={this.layerWatcher.vectorLayers}
onLayerChanged={this.onLayerChanged.bind(this)}
/> : null
return <Layout

View file

@ -9,10 +9,6 @@ import SourceEditor from './SourceEditor'
import FilterEditor from '../filter/FilterEditor'
import PropertyGroup from '../fields/PropertyGroup'
import MdVisibility from 'react-icons/lib/md/visibility'
import MdVisibilityOff from 'react-icons/lib/md/visibility-off'
import MdDelete from 'react-icons/lib/md/delete'
import ScrollContainer from '../ScrollContainer'
import layout from '../../config/layout.json'
@ -77,14 +73,6 @@ export default class LayerEditor extends React.Component {
this.props.onLayerChanged(changedLayer)
}
toggleVisibility() {
if(this.props.layer.has('layout') && this.props.layer.layout.visibility === 'none') {
this.onLayoutChanged('visibility', 'visible')
} else {
this.onLayoutChanged('visibility', 'none')
}
}
render() {
const layerType = this.props.layer.type
const groups = layout[layerType].groups

View file

@ -1,5 +1,6 @@
import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import cloneDeep from 'lodash.clonedeep'
import Heading from 'rebass/dist/Heading'
import Toolbar from 'rebass/dist/Toolbar'
@ -9,6 +10,7 @@ import Space from 'rebass/dist/Space'
import LayerListItem from './LayerListItem'
import ScrollContainer from '../ScrollContainer'
import style from '../../libs/style.js'
import { margins } from '../../config/scales.js'
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
@ -16,8 +18,8 @@ import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
const layerListPropTypes = {
layers: React.PropTypes.array.isRequired,
selectedLayerIndex: React.PropTypes.number.isRequired,
onLayersChanged: React.PropTypes.func.isRequired,
onLayerSelected: React.PropTypes.func,
onLayersChange: React.PropTypes.func.isRequired,
onLayerSelect: React.PropTypes.func,
}
// List of collapsible layer editors
@ -25,7 +27,7 @@ const layerListPropTypes = {
class LayerListContainer extends React.Component {
static propTypes = {...layerListPropTypes}
static defaultProps = {
onLayerSelected: () => {},
onLayerSelect: () => {},
}
constructor(props) {
@ -33,9 +35,34 @@ class LayerListContainer extends React.Component {
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
onLayerDestroyed(deletedLayer) {
const remainingLayers = this.props.layers.delete(deletedLayer.id)
this.props.onLayersChanged(remainingLayers)
onLayerDestroy(layerId) {
const remainingLayers = this.props.layers.slice(0)
const idx = style.indexOfLayer(remainingLayers, layerId)
remainingLayers.splice(idx, 1);
this.props.onLayersChange(remainingLayers)
}
onLayerCopy(layerId) {
const changedLayers = this.props.layers.slice(0)
const idx = style.indexOfLayer(changedLayers, layerId)
const clonedLayer = cloneDeep(changedLayers[idx])
clonedLayer.id = clonedLayer.id + "-copy"
changedLayers.splice(idx, 0, clonedLayer)
this.props.onLayersChange(changedLayers)
}
onLayerVisibilityToggle(layerId) {
const changedLayers = this.props.layers.slice(0)
const idx = style.indexOfLayer(changedLayers, layerId)
const layer = { ...changedLayers[idx] }
const changedLayout = 'layout' in layer ? {...layer.layout} : {}
changedLayout.visibility = changedLayout.visibility === 'none' ? 'visible' : 'none'
layer.layout = changedLayout
changedLayers[idx] = layer
this.props.onLayersChange(changedLayers)
}
render() {
@ -46,8 +73,12 @@ class LayerListContainer extends React.Component {
key={layerId}
layerId={layerId}
layerType={layer.type}
visibility={(layer.layout || {}).visibility}
isSelected={index === this.props.selectedLayerIndex}
onLayerSelected={this.props.onLayerSelected}
onLayerSelect={this.props.onLayerSelect}
onLayerDestroy={this.onLayerDestroy.bind(this)}
onLayerCopy={this.onLayerCopy.bind(this)}
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
/>
})
return <ScrollContainer>
@ -71,7 +102,7 @@ export default class LayerList extends React.Component {
if(oldIndex === newIndex) return
let layers = this.props.layers.slice(0)
layers = arrayMove(layers, oldIndex, newIndex)
this.props.onLayersChanged(layers)
this.props.onLayersChange(layers)
}
render() {

View file

@ -35,6 +35,7 @@ class IconAction extends React.Component {
static propTypes = {
action: React.PropTypes.string.isRequired,
active: React.PropTypes.bool,
onClick: React.PropTypes.func.isRequired,
}
constructor(props) {
@ -44,12 +45,19 @@ class IconAction extends React.Component {
renderIcon() {
const iconStyle = {
fill: this.props.active ? (this.state.hover ? colors.lowgray : colors.midgray) : colors.gray,
fill: colors.gray
}
if(this.props.active) {
iconStyle.fill = colors.midgray
}
if(this.state.hover) {
iconStyle.fill = colors.lowgray
}
switch(this.props.action) {
case 'copy': return <CopyIcon style={iconStyle} />
case 'show': return <VisibilityOnIcon style={iconStyle} />
case 'show': return <VisibilityIcon style={iconStyle} />
case 'hide': return <VisibilityOffIcon style={iconStyle} />
case 'delete': return <DeleteIcon style={iconStyle} />
default: return null
@ -57,17 +65,18 @@ class IconAction extends React.Component {
}
render() {
return <div
return <a
style={{
display: "inline",
marginLeft: margins[0],
...this.props.style
}}
onClick={this.props.onClick}
onMouseOver={e => this.setState({hover: true})}
onMouseOut={e => this.setState({hover: false})}
>
{this.renderIcon()}
</div>
</a>
}
}
@ -77,18 +86,20 @@ class LayerListItem extends React.Component {
layerId: React.PropTypes.string.isRequired,
layerType: React.PropTypes.string.isRequired,
isSelected: React.PropTypes.bool,
visibility: React.PropTypes.bool,
visibility: React.PropTypes.string,
onLayerSelected: React.PropTypes.func.isRequired,
onLayerDestroyed: React.PropTypes.func,
onLayerVisibilityToggled: React.PropTypes.func,
onLayerSelect: React.PropTypes.func.isRequired,
onLayerCopy: React.PropTypes.func,
onLayerDestroy: React.PropTypes.func,
onLayerVisibilityToggle: React.PropTypes.func,
}
static defaultProps = {
isSelected: false,
visibility: true,
onLayerDestroyed: () => {},
onLayerVisibilityToggled: () => {},
visibility: 'visible',
onLayerCopy: () => {},
onLayerDestroy: () => {},
onLayerVisibilityToggle: () => {},
}
static childContextTypes = {
@ -143,7 +154,7 @@ class LayerListItem extends React.Component {
return <li
key={this.props.layerId}
onClick={e => this.props.onLayerSelected(this.props.layerId)}
onClick={e => this.props.onLayerSelect(this.props.layerId)}
onMouseOver={e => this.setState({hover: true})}
onMouseOut={e => this.setState({hover: false})}
style={itemStyle}>
@ -161,15 +172,16 @@ class LayerListItem extends React.Component {
<span style={{flexGrow: 1}} />
<IconAction {...iconProps}
action={'delete'}
onClick={e => this.props.onLayerDestroyed(this.props.layerId)}
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
/>
<IconAction {...iconProps}
action={'copy'}
onClick={e => this.props.onLayerVisibilityToggled(this.props.layerId)}
onClick={e => this.props.onLayerCopy(this.props.layerId)}
/>
<IconAction {...iconProps}
action={this.props.visibility ? 'hide' : 'show'}
onClick={e => this.props.onLayerVisibilityToggled(this.props.layerId)}
active={this.state.hover || this.props.isSelected || this.props.visibility === 'none'}
action={this.props.visibility === 'visible' ? 'hide' : 'show'}
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
/>
</div>
</li>

View file

@ -24,7 +24,17 @@ function ensureMetadataExists(style) {
return ensureHasId(ensureHasTimestamp(style))
}
function indexOfLayer(layers, layerId) {
for (let i = 0; i < layers.length; i++) {
if(layers[i].id === layerId) {
return i
}
}
return null
}
export default {
ensureMetadataExists,
emptyStyle,
indexOfLayer,
}