Restructure and rename components

This commit is contained in:
Lukas Martinelli
2016-12-20 11:44:22 +01:00
parent 461a001552
commit fde60ac3e0
46 changed files with 365 additions and 425 deletions

View File

@@ -0,0 +1,130 @@
import React from 'react'
import Immutable from 'immutable'
import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space'
import Tabs from 'react-simpletabs'
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 PureRenderMixin from 'react-addons-pure-render-mixin';
import ScrollContainer from '../ScrollContainer'
import layout from '../../config/layout.json'
import theme from '../../config/rebass.js'
class UnsupportedLayer extends React.Component {
render() {
return <div></div>
}
}
/** Layer editor supporting multiple types of layers. */
export default class LayerEditor extends React.Component {
static propTypes = {
layer: React.PropTypes.object.isRequired,
sources: React.PropTypes.instanceOf(Immutable.Map),
vectorLayers: React.PropTypes.instanceOf(Immutable.Map),
onLayerChanged: React.PropTypes.func,
onLayerDestroyed: React.PropTypes.func,
}
static defaultProps = {
onLayerChanged: () => {},
onLayerDestroyed: () => {},
}
static childContextTypes = {
reactIconBase: React.PropTypes.object
}
constructor(props) {
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
getChildContext () {
return {
reactIconBase: {
size: theme.fontSizes[4],
color: theme.colors.lowgray,
}
}
}
onPropertyChange(group, property, newValue) {
const layer = this.props.layer
const changedLayer = layer.setIn([group, property], newValue)
this.props.onLayerChanged(changedLayer)
}
onFilterChange(newValue) {
let layer = this.props.layer
const changedLayer = layer.set('filter', newValue)
this.props.onLayerChanged(changedLayer)
}
toggleVisibility() {
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
this.onLayoutChanged('visibility', 'visible')
} else {
this.onLayoutChanged('visibility', 'none')
}
}
render() {
const layerType = this.props.layer.get('type')
const groups = layout[layerType].groups
const propertyGroups = groups.map(group => {
return <PropertyGroup
key={this.props.group}
layer={this.props.layer}
groupFields={Immutable.OrderedSet(group.fields)}
onChange={this.onPropertyChange.bind(this)}
/>
})
console.log(this.props.layer.toJSON())
let visibleIcon = <MdVisibilityOff />
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
visibleIcon = <MdVisibility />
}
return <div style={{
padding: theme.scale[0],
}}>
<Toolbar>
<NavItem style={{fontWeight: 400}}>
{this.props.layer.get('id')}
</NavItem>
<Space auto x={1} />
<NavItem onClick={this.toggleVisibility.bind(this)}>
{visibleIcon}
</NavItem>
<NavItem onClick={(e) => this.props.onLayerDestroyed(this.props.layer)}>
<MdDelete />
</NavItem>
</Toolbar>
{propertyGroups}
<FilterEditor
filter={this.props.layer.get('filter', Immutable.List()).toJSON()}
properties={this.props.vectorLayers.get(this.props.layer.get('source-layer'))}
onChange={f => this.onFilterChange(Immutable.fromJS(f))}
/>
{this.props.layer.get('type') !== 'background'
&& <SourceEditor
source={this.props.layer.get('source')}
sourceLayer={this.props.layer.get('source-layer')}
sources={this.props.sources}
onSourceChange={console.log}
onSourceLayerChange={console.log}
/>}
</div>
}
}

View File

@@ -0,0 +1,93 @@
import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Immutable from 'immutable'
import Heading from 'rebass/dist/Heading'
import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space'
import LayerListItem from './LayerListItem'
import ScrollContainer from '../ScrollContainer'
import { margins } from '../../config/scales.js'
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
const layerListPropTypes = {
layers: React.PropTypes.instanceOf(Immutable.OrderedMap),
onLayersChanged: React.PropTypes.func.isRequired,
onLayerSelected: React.PropTypes.func,
}
// List of collapsible layer editors
@SortableContainer
class LayerListContainer extends React.Component {
static propTypes = {...layerListPropTypes}
static defaultProps = {
onLayerSelected: () => {},
}
constructor(props) {
super(props)
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
onLayerDestroyed(deletedLayer) {
const remainingLayers = this.props.layers.delete(deletedLayer.get('id'))
this.props.onLayersChanged(remainingLayers)
}
onLayerChanged(layer) {
const changedLayers = this.props.layers.set(layer.get('id'), layer)
this.props.onLayersChanged(changedLayers)
}
render() {
const layerPanels = this.props.layers.toIndexedSeq().map((layer, index) => {
const layerId = layer.get('id')
return <LayerListItem
index={index}
key={layerId}
layerId={layerId}
layerType={layer.get('type')}
onLayerSelected={this.props.onLayerSelected}
/>
})
return <ScrollContainer>
<ul style={{ padding: margins[1], margin: 0 }}>
{layerPanels}
</ul>
</ScrollContainer>
}
}
export default class LayerList extends React.Component {
static propTypes = {...layerListPropTypes}
constructor(props) {
super(props)
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
onSortEnd(move) {
const { oldIndex, newIndex } = move
if(oldIndex === newIndex) return
//TODO: Implement this more performant for immutable collections
// instead of converting back and forth
let layers = this.props.layers.toArray()
layers = arrayMove(layers, oldIndex, newIndex)
layers = Immutable.OrderedMap(layers.map(l => [l.get('id'), l]))
this.props.onLayersChanged(layers)
}
render() {
return <LayerListContainer
{...this.props}
onSortEnd={this.onSortEnd.bind(this)}
useDragHandle={true}
/>
}
}

View File

@@ -0,0 +1,76 @@
import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin'
import Radium from 'radium'
import Immutable from 'immutable'
import Color from 'color'
import Heading from 'rebass/dist/Heading'
import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space'
import LayerIcon from '../icons/LayerIcon'
import LayerEditor from './LayerEditor'
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
import colors from '../../config/colors.js'
import { fontSizes, margins } from '../../config/scales.js'
@SortableHandle
class LayerTypeDragHandle extends React.Component {
static propTypes = LayerIcon.propTypes
render() {
return <LayerIcon
{...this.props}
style={{width: 15, height: 15, paddingRight: 3}}
/>
}
}
@SortableElement
@Radium
class LayerListItem extends React.Component {
static propTypes = {
layerId: React.PropTypes.string.isRequired,
layerType: React.PropTypes.string.isRequired,
onLayerSelected: React.PropTypes.func.isRequired,
}
constructor(props) {
super(props)
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
render() {
return <li
key={this.props.layerId}
onClick={() => this.props.onLayerSelected(this.props.layerId)}
style={{
fontWeight: 400,
color: colors.lowgray,
fontSize: fontSizes[5],
borderBottom: 1,
borderLeft: 2,
borderRight: 0,
borderStyle: "solid",
userSelect: 'none',
listStyle: 'none',
zIndex: 2000,
cursor: 'pointer',
position: 'relative',
padding: margins[1],
borderColor: Color(colors.gray).lighten(0.1).string(),
backgroundColor: colors.gray,
":hover": {
backgroundColor: Color(colors.gray).lighten(0.15).string(),
}
}}>
<LayerTypeDragHandle type={this.props.layerType} />
<span>{this.props.layerId}</span>
</li>
}
}
export default Radium(LayerListItem);

View File

@@ -0,0 +1,61 @@
import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Immutable from 'immutable'
import PropertyGroup from '../fields/PropertyGroup'
import input from '../../config/input.js'
/** Choose tileset (source) and the source layer */
export default class SourceEditor extends React.Component {
static propTypes = {
source: React.PropTypes.string.isRequired,
sourceLayer: React.PropTypes.string.isRequired,
onSourceChange: React.PropTypes.func.isRequired,
onSourceLayerChange: React.PropTypes.func.isRequired,
/** List of available sources in the style
* https://www.mapbox.com/mapbox-gl-style-spec/#root-sources */
sources: React.PropTypes.instanceOf(Immutable.Map).isRequired,
}
constructor(props) {
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
render() {
const options = this.props.sources.map((source, sourceId)=> {
return <option key={sourceId} value={sourceId}>{sourceId}</option>
}).toIndexedSeq()
const layerOptions = this.props.sources.get(this.props.source, Immutable.Set()).map(vectorLayerId => {
const id = vectorLayerId
return <option key={id} value={id}>{id}</option>
}).toIndexedSeq()
return <div>
<div style={input.property}>
<label style={input.label}>Source</label>
<select
style={input.select}
value={this.props.source}
onChange={(e) => this.onSourceChange(e.target.value)}
>
{options}
</select>
</div>
<div style={input.property}>
<label style={input.label}>Source Layer</label>
<select
style={input.select}
value={this.props.sourceLayer}
onChange={(e) => this.onSourceLayerChange(e.target.value)}
>
{layerOptions}
</select>
</div>
</div>
}
}