mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2025-01-14 20:13:27 +01:00
Group layers #66
This commit is contained in:
parent
76d2d06e77
commit
d0f047d88a
6 changed files with 142 additions and 38 deletions
20
src/components/layers/Collapser.jsx
Normal file
20
src/components/layers/Collapser.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react'
|
||||||
|
import CollapseOpenIcon from 'react-icons/lib/md/arrow-drop-down'
|
||||||
|
import CollapseCloseIcon from 'react-icons/lib/md/arrow-drop-up'
|
||||||
|
|
||||||
|
export default class Collapser extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
isCollapsed: React.PropTypes.bool.isRequired,
|
||||||
|
style: React.PropTypes.object,
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const iconStyle = {
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
...this.props.style,
|
||||||
|
}
|
||||||
|
return this.props.isCollapsed ? <CollapseCloseIcon style={iconStyle}/> : <CollapseOpenIcon style={iconStyle} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Collapse from 'react-collapse'
|
import Collapse from 'react-collapse'
|
||||||
import CollapseOpenIcon from 'react-icons/lib/md/arrow-drop-down'
|
import Collapser from './Collapser'
|
||||||
import CollapseCloseIcon from 'react-icons/lib/md/arrow-drop-up'
|
|
||||||
|
|
||||||
class Collapser extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
isCollapsed: React.PropTypes.bool.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const iconStyle = {
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
}
|
|
||||||
return this.props.isCollapsed ? <CollapseCloseIcon style={iconStyle}/> : <CollapseOpenIcon style={iconStyle} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class LayerEditorGroup extends React.Component {
|
export default class LayerEditorGroup extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
||||||
import cloneDeep from 'lodash.clonedeep'
|
import cloneDeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
|
import LayerListGroup from './LayerListGroup'
|
||||||
import LayerListItem from './LayerListItem'
|
import LayerListItem from './LayerListItem'
|
||||||
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
import AddIcon from 'react-icons/lib/md/add-circle-outline'
|
||||||
import AddModal from '../modals/AddModal'
|
import AddModal from '../modals/AddModal'
|
||||||
|
@ -9,6 +10,8 @@ import AddModal from '../modals/AddModal'
|
||||||
import style from '../../libs/style.js'
|
import style from '../../libs/style.js'
|
||||||
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
|
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
|
||||||
|
|
||||||
|
const layerPrefix = name => name.replace(' ', '-').replace('_', '-').split('-')[0]
|
||||||
|
|
||||||
const layerListPropTypes = {
|
const layerListPropTypes = {
|
||||||
layers: React.PropTypes.array.isRequired,
|
layers: React.PropTypes.array.isRequired,
|
||||||
selectedLayerIndex: React.PropTypes.number.isRequired,
|
selectedLayerIndex: React.PropTypes.number.isRequired,
|
||||||
|
@ -73,22 +76,69 @@ class LayerListContainer extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
groupedLayers() {
|
||||||
const layerPanels = this.props.layers.map((layer, index) => {
|
const groups = []
|
||||||
const layerId = layer.id
|
for (let i = 0; i < this.props.layers.length; i++) {
|
||||||
return <LayerListItem
|
const previousLayer = this.props.layers[i-1]
|
||||||
index={index}
|
const layer = this.props.layers[i]
|
||||||
key={layerId}
|
if(previousLayer && layerPrefix(previousLayer.id) == layerPrefix(layer.id)) {
|
||||||
layerId={layerId}
|
const lastGroup = groups[groups.length - 1]
|
||||||
layerType={layer.type}
|
lastGroup.push(layer)
|
||||||
visibility={(layer.layout || {}).visibility}
|
} else {
|
||||||
isSelected={index === this.props.selectedLayerIndex}
|
groups.push([layer])
|
||||||
onLayerSelect={this.props.onLayerSelect}
|
}
|
||||||
onLayerDestroy={this.onLayerDestroy.bind(this)}
|
}
|
||||||
onLayerCopy={this.onLayerCopy.bind(this)}
|
return groups
|
||||||
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
|
}
|
||||||
/>
|
|
||||||
|
toggleLayerGroup(groupPrefix) {
|
||||||
|
groupedLayers().forEach(layers => {
|
||||||
|
if(groupPrefix === layerPrefix(layers[0].id)) {
|
||||||
|
layers.forEach(layer => {
|
||||||
|
//HACK
|
||||||
|
//In this case it is ok to modify the metadata
|
||||||
|
//because no one else depends on it
|
||||||
|
layer.metadata = {
|
||||||
|
...layer.metadata,
|
||||||
|
'maputnik:visible': false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const listItems = []
|
||||||
|
let idx = 0
|
||||||
|
this.groupedLayers().forEach(layers => {
|
||||||
|
if(layers.length > 1) {
|
||||||
|
const groupPrefix = layerPrefix(layers[0].id)
|
||||||
|
const grp = <LayerListGroup
|
||||||
|
key={'group-'+groupPrefix}
|
||||||
|
title={groupPrefix}
|
||||||
|
isActive={true}
|
||||||
|
/>
|
||||||
|
listItems.push(grp)
|
||||||
|
}
|
||||||
|
|
||||||
|
layers.forEach(layer => {
|
||||||
|
const listItem = <LayerListItem
|
||||||
|
index={idx}
|
||||||
|
key={layer.id}
|
||||||
|
layerId={layer.id}
|
||||||
|
layerType={layer.type}
|
||||||
|
visibility={(layer.layout || {}).visibility}
|
||||||
|
isSelected={idx === this.props.selectedLayerIndex}
|
||||||
|
onLayerSelect={this.props.onLayerSelect}
|
||||||
|
onLayerDestroy={this.onLayerDestroy.bind(this)}
|
||||||
|
onLayerCopy={this.onLayerCopy.bind(this)}
|
||||||
|
onLayerVisibilityToggle={this.onLayerVisibilityToggle.bind(this)}
|
||||||
|
/>
|
||||||
|
listItems.push(listItem)
|
||||||
|
idx += 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return <div className="maputnik-layer-list">
|
return <div className="maputnik-layer-list">
|
||||||
<AddModal
|
<AddModal
|
||||||
layers={this.props.layers}
|
layers={this.props.layers}
|
||||||
|
@ -107,7 +157,7 @@ class LayerListContainer extends React.Component {
|
||||||
</Button>
|
</Button>
|
||||||
</header>
|
</header>
|
||||||
<ul className="maputnik-layer-list-container">
|
<ul className="maputnik-layer-list-container">
|
||||||
{layerPanels}
|
{listItems}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
26
src/components/layers/LayerListGroup.jsx
Normal file
26
src/components/layers/LayerListGroup.jsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react'
|
||||||
|
import Collapser from './Collapser'
|
||||||
|
|
||||||
|
export default class LayerEditorGroup extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
title: React.PropTypes.string.isRequired,
|
||||||
|
children: React.PropTypes.element.isRequired,
|
||||||
|
isActive: React.PropTypes.bool.isRequired,
|
||||||
|
onActiveToggle: React.PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div className="maputnik-layer-list-group">
|
||||||
|
<div className="maputnik-layer-list-group-header"
|
||||||
|
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||||
|
>
|
||||||
|
<span className="maputnik-layer-list-group-title">{this.props.title}</span>
|
||||||
|
<span className="maputnik-space" />
|
||||||
|
<Collapser
|
||||||
|
style={{ height: 14, width: 14 }}
|
||||||
|
isCollapsed={this.props.isActive}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,10 +80,6 @@ class LayerListItem extends React.Component {
|
||||||
reactIconBase: React.PropTypes.object
|
reactIconBase: React.PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
}
|
|
||||||
|
|
||||||
getChildContext() {
|
getChildContext() {
|
||||||
return {
|
return {
|
||||||
reactIconBase: { size: 14 }
|
reactIconBase: { size: 14 }
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background-color: $color-black;
|
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
display: flex;
|
display: flex;
|
||||||
display: -ms-flexbox;
|
display: -ms-flexbox;
|
||||||
|
@ -69,6 +68,35 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-group {
|
||||||
|
//border-bottom: 2px solid $color-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-group-header {
|
||||||
|
font-size: $font-size-6;
|
||||||
|
color: $color-lowgray;
|
||||||
|
background-color: lighten($color-black, 2);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: $margin-2;
|
||||||
|
display: flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
@include vendor-prefix(flex-direction, row)
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-group-title {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-group-content {
|
||||||
|
margin-left: $margin-3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue