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

@ -35,7 +35,6 @@
"react-color": "^2.10.0", "react-color": "^2.10.0",
"react-dom": "^15.4.0", "react-dom": "^15.4.0",
"react-file-reader-input": "^1.1.0", "react-file-reader-input": "^1.1.0",
"react-height": "^2.1.1",
"react-icon-base": "^2.0.4", "react-icon-base": "^2.0.4",
"react-icons": "^2.2.1", "react-icons": "^2.2.1",
"react-simpletabs": "^0.7.0", "react-simpletabs": "^0.7.0",

View file

@ -1,24 +1,24 @@
import React from 'react' import React from 'react'
import {saveAs} from 'file-saver' import { saveAs } from 'file-saver'
import Drawer from 'rebass/dist/Drawer' import Drawer from 'rebass/dist/Drawer'
import Container from 'rebass/dist/Container' import Container from 'rebass/dist/Container'
import Block from 'rebass/dist/Block' import Block from 'rebass/dist/Block'
import Fixed from 'rebass/dist/Fixed' import Fixed from 'rebass/dist/Fixed'
import { MapboxGlMap } from './gl.jsx' import MapboxGlMap from './map/MapboxGlMap.jsx'
import { OpenLayers3Map } from './ol3.jsx' import OpenLayers3Map from './map/OpenLayers3Map.jsx'
import { LayerList } from './layers/list.jsx' import LayerList from './layers/LayerList.jsx'
import { LayerEditor } from './layers/editor.jsx' import LayerEditor from './layers/LayerEditor.jsx'
import {Toolbar} from './toolbar.jsx' import Toolbar from './Toolbar.jsx'
import style from './style.js'
import { loadDefaultStyle, SettingsStore, StyleStore } from './stylestore.js'
import { ApiStyleStore } from './apistore.js'
import LayerWatcher from './layerwatcher.js' import style from '../libs/style.js'
import theme from './theme.js' import { loadDefaultStyle, SettingsStore, StyleStore } from '../libs/stylestore.js'
import { colors, fullHeight } from './theme.js' import { ApiStyleStore } from '../libs/apistore.js'
import './index.css' import LayerWatcher from '../libs/layerwatcher.js'
import theme from '../config/rebass.js'
import colors from '../config/colors.js'
export default class App extends React.Component { export default class App extends React.Component {
static childContextTypes = { static childContextTypes = {
@ -160,7 +160,7 @@ export default class App extends React.Component {
width: 300, width: 300,
backgroundColor: colors.gray} backgroundColor: colors.gray}
}> }>
{selectedLayer && <LayerEditor layer={selectedLayer} onLayerChanged={this.onLayerChanged.bind(this)} sources={this.layerWatcher.sources}/>} {selectedLayer && <LayerEditor layer={selectedLayer} onLayerChanged={this.onLayerChanged.bind(this)} sources={this.layerWatcher.sources} vectorLayers={this.layerWatcher.vectorLayers}/>}
</div> </div>
{this.mapRenderer()} {this.mapRenderer()}
</div> </div>

View file

@ -1,5 +1,4 @@
import React from 'react' import React from 'react'
import theme from './theme.js'
import scrollbars from './scrollbars.scss' import scrollbars from './scrollbars.scss'
const ScrollContainer = (props) => { const ScrollContainer = (props) => {

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react'
import Immutable from 'immutable' import Immutable from 'immutable'
import FileReaderInput from 'react-file-reader-input'; import FileReaderInput from 'react-file-reader-input'
import Button from 'rebass/dist/Button' import Button from 'rebass/dist/Button'
import Text from 'rebass/dist/Text' import Text from 'rebass/dist/Text'
@ -25,17 +25,17 @@ import MdFontDownload from 'react-icons/lib/md/font-download'
import MdHelpOutline from 'react-icons/lib/md/help-outline' import MdHelpOutline from 'react-icons/lib/md/help-outline'
import MdFindInPage from 'react-icons/lib/md/find-in-page' import MdFindInPage from 'react-icons/lib/md/find-in-page'
import SettingsModal from './modals/settings.jsx' import SettingsModal from './modals/SettingsModal'
import TilesetsModal from './modals/tilesets.jsx' import TilesetsModal from './modals/TilesetsModal'
import style from './style.js'
import { fullHeight } from './theme.js' import style from '../libs/style'
import theme from './theme.js'; import colors from '../config/colors';
const InlineBlock = props => <div style={{display: "inline-block", ...props.style}}> const InlineBlock = props => <div style={{display: "inline-block", ...props.style}}>
{props.children} {props.children}
</div> </div>
export class Toolbar extends React.Component { export default class Toolbar extends React.Component {
static propTypes = { static propTypes = {
mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired, mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired,
onStyleChanged: React.PropTypes.func.isRequired, onStyleChanged: React.PropTypes.func.isRequired,
@ -107,7 +107,7 @@ export class Toolbar extends React.Component {
zIndex: 100, zIndex: 100,
left: 0, left: 0,
top: 0, top: 0,
backgroundColor: theme.colors.black backgroundColor: colors.black
}}> }}>
<SettingsModal <SettingsModal
mapStyle={this.props.mapStyle} mapStyle={this.props.mapStyle}

View file

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import inputStyle from './input.js' import input from '../../config/input.js'
class BooleanField extends React.Component { class BooleanField extends React.Component {
static propTypes = { static propTypes = {
@ -14,7 +14,7 @@ class BooleanField extends React.Component {
return <input return <input
type="checkbox" type="checkbox"
style={{ style={{
...inputStyle.checkbox, ...input.checkbox,
...this.props.style ...this.props.style
}} }}
value={this.props.value} value={this.props.value}

View file

@ -1,8 +1,9 @@
import React from 'react' import React from 'react'
import Color from 'color' import Color from 'color'
import inputStyle from './input.js'
import ChromePicker from 'react-color/lib/components/chrome/Chrome' import ChromePicker from 'react-color/lib/components/chrome/Chrome'
import input from '../../config/input.js'
function formatColor(color) { function formatColor(color) {
const rgb = color.rgb const rgb = color.rgb
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})` return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
@ -53,7 +54,7 @@ class ColorField extends React.Component {
</div> </div>
return <div style={{ return <div style={{
...inputStyle.property, ...input.property,
position: 'relative', position: 'relative',
display: 'inline', display: 'inline',
}}> }}>
@ -61,7 +62,7 @@ class ColorField extends React.Component {
<input <input
onClick={this.togglePicker.bind(this)} onClick={this.togglePicker.bind(this)}
style={{ style={{
...inputStyle.select, ...input.select,
...this.props.style ...this.props.style
}} }}
name={this.props.name} name={this.props.name}

View file

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import inputStyle from './input.js' import input from '../../config/input.js'
class EnumField extends React.Component { class EnumField extends React.Component {
static propTypes = { static propTypes = {
@ -22,7 +22,7 @@ class EnumField extends React.Component {
return <select return <select
style={{ style={{
...inputStyle.select, ...input.select,
...this.props.style ...this.props.style
}} }}
value={this.props.value} value={this.props.value}

View file

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import inputStyle from './input.js' import input from '../../config/input.js'
/*** Number fields with support for min, max and units and documentation*/ /*** Number fields with support for min, max and units and documentation*/
class NumberField extends React.Component { class NumberField extends React.Component {
@ -27,7 +27,7 @@ class NumberField extends React.Component {
render() { render() {
return <input return <input
style={{ style={{
...inputStyle.input, ...input.input,
...this.props.style ...this.props.style
}} }}
type="number" type="number"

View file

@ -1,11 +1,13 @@
import React from 'react' import React from 'react'
import { ZoomSpecField } from './spec.jsx'
import Immutable from 'immutable' import Immutable from 'immutable'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js' import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import theme from '../theme.js'
console.log(ZoomSpecField) import ZoomSpecField from './ZoomSpecField'
import colors from '../../config/colors'
import { margins } from '../../config/scales'
/** Extract field spec by {@fieldName} from the {@layerType} in the
* style specification from either the paint or layout group */
function getFieldSpec(layerType, fieldName) { function getFieldSpec(layerType, fieldName) {
const paint = GlSpec['paint_' + layerType] || {} const paint = GlSpec['paint_' + layerType] || {}
const layout = GlSpec['layout_' + layerType] || {} const layout = GlSpec['layout_' + layerType] || {}
@ -25,10 +27,8 @@ export default class PropertyGroup extends React.Component {
render() { render() {
const fields = this.props.groupFields.map(fieldName => { const fields = this.props.groupFields.map(fieldName => {
console.log(fieldName)
const fieldSpec = getFieldSpec(this.props.layer.get('type'), fieldName) const fieldSpec = getFieldSpec(this.props.layer.get('type'), fieldName)
const fieldValue = this.props.layer.getIn(['paint', fieldName], this.props.layer.getIn(['layout', fieldName])) const fieldValue = this.props.layer.getIn(['paint', fieldName], this.props.layer.getIn(['layout', fieldName]))
return <ZoomSpecField return <ZoomSpecField
onChange={this.props.onChange} onChange={this.props.onChange}
key={fieldName} key={fieldName}
@ -39,10 +39,10 @@ export default class PropertyGroup extends React.Component {
}).toIndexedSeq() }).toIndexedSeq()
return <div style={{ return <div style={{
padding: theme.scale[2], padding: margins[2],
paddingRight: 0, paddingRight: 0,
backgroundColor: theme.colors.black, backgroundColor: colors.black,
marginBottom: theme.scale[2], marginBottom: margins[2],
}}> }}>
{fields} {fields}
</div> </div>

View file

@ -3,87 +3,19 @@ import Immutable from 'immutable'
import color from 'color' import color from 'color'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js' import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import NumberField from './number' import NumberField from './NumberField'
import EnumField from './enum' import EnumField from './EnumField'
import BooleanField from './boolean' import BooleanField from './BooleanField'
import ColorField from './color' import ColorField from './ColorField'
import StringField from './string' import StringField from './StringField'
import inputStyle from './input.js'
import theme from '../theme.js' import input from '../../config/input.js'
import theme from '../../config/rebass.js'
function isZoomField(value) { function isZoomField(value) {
return Immutable.Map.isMap(value) return Immutable.Map.isMap(value)
} }
const specFieldProps = {
onChange: React.PropTypes.func.isRequired,
fieldName: React.PropTypes.string.isRequired,
fieldSpec: React.PropTypes.object.isRequired,
}
/** Supports displaying spec field for zoom function objects
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
*/
export class ZoomSpecField extends React.Component {
static propTypes = {
...specFieldProps,
value: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.bool,
]),
}
render() {
const label = <label style={inputStyle.label}>
{labelFromFieldName(this.props.fieldName)}
</label>
if(isZoomField(this.props.value)) {
const zoomFields = this.props.value.get('stops').map(stop => {
const zoomLevel = stop.get(0)
const value = stop.get(1)
return <div style={inputStyle.property} key={zoomLevel}>
{label}
<SpecField {...this.props}
value={value}
style={{
width: '33%'
}}
/>
<input
style={{
...inputStyle.input,
width: '10%',
marginLeft: theme.scale[0],
}}
type="number"
value={zoomLevel}
min={0}
max={22}
/>
</div>
}).toSeq()
return <div style={{
border: 1,
borderStyle: 'solid',
borderColor: color(theme.colors.gray).lighten(0.1).string(),
padding: theme.scale[1],
}}>
{zoomFields}
</div>
} else {
return <div style={inputStyle.property}>
{label}
<SpecField {...this.props} />
</div>
}
}
}
function labelFromFieldName(fieldName) { function labelFromFieldName(fieldName) {
let label = fieldName.split('-').slice(1).join(' ') let label = fieldName.split('-').slice(1).join(' ')
if(label.length > 0) { if(label.length > 0) {
@ -92,13 +24,15 @@ function labelFromFieldName(fieldName) {
return label return label
} }
/** Display any field from the Mapbox GL style spec and /** Display any field from the Mapbox GL style spec and
* choose the correct field component based on the @{fieldSpec} * choose the correct field component based on the @{fieldSpec}
* to display @{value}. */ * to display @{value}. */
class SpecField extends React.Component { export default class SpecField extends React.Component {
static propTypes = { static propTypes = {
...specFieldProps, onChange: React.PropTypes.func.isRequired,
fieldName: React.PropTypes.string.isRequired,
fieldSpec: React.PropTypes.object.isRequired,
value: React.PropTypes.oneOfType([ value: React.PropTypes.oneOfType([
React.PropTypes.string, React.PropTypes.string,
React.PropTypes.number, React.PropTypes.number,

View file

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import inputStyle from './input.js' import input from '../../config/input.js'
/*** Number fields with support for min, max and units and documentation*/ /*** Number fields with support for min, max and units and documentation*/
class StringField extends React.Component { class StringField extends React.Component {
@ -20,7 +20,7 @@ class StringField extends React.Component {
render() { render() {
return <input return <input
style={{ style={{
...inputStyle.input, ...input.input,
...this.props.style ...this.props.style
}} }}
name={this.props.name} name={this.props.name}

View file

@ -0,0 +1,98 @@
import React from 'react'
import Immutable from 'immutable'
import Color from 'color'
import NumberField from './NumberField'
import EnumField from './EnumField'
import BooleanField from './BooleanField'
import ColorField from './ColorField'
import StringField from './StringField'
import SpecField from './SpecField'
import input from '../../config/input.js'
import colors from '../../config/colors.js'
import { margins } from '../../config/scales.js'
function isZoomField(value) {
return Immutable.Map.isMap(value)
}
const specFieldProps = {
onChange: React.PropTypes.func.isRequired,
fieldName: React.PropTypes.string.isRequired,
fieldSpec: React.PropTypes.object.isRequired,
}
/** Supports displaying spec field for zoom function objects
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
*/
export default class ZoomSpecField extends React.Component {
static propTypes = {
onChange: React.PropTypes.func.isRequired,
fieldName: React.PropTypes.string.isRequired,
fieldSpec: React.PropTypes.object.isRequired,
value: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.bool,
]),
}
render() {
const label = <label style={input.label}>
{labelFromFieldName(this.props.fieldName)}
</label>
if(isZoomField(this.props.value)) {
const zoomFields = this.props.value.get('stops').map(stop => {
const zoomLevel = stop.get(0)
const value = stop.get(1)
return <div style={input.property} key={zoomLevel}>
{label}
<SpecField {...this.props}
value={value}
style={{
width: '33%'
}}
/>
<input
style={{
...input.input,
width: '10%',
marginLeft: margins[0],
}}
type="number"
value={zoomLevel}
min={0}
max={22}
/>
</div>
}).toSeq()
return <div style={{
border: 1,
borderStyle: 'solid',
borderColor: Color(colors.gray).lighten(0.1).string(),
padding: margins[1],
}}>
{zoomFields}
</div>
} else {
return <div style={input.property}>
{label}
<SpecField {...this.props} />
</div>
}
}
}
function labelFromFieldName(fieldName) {
let label = fieldName.split('-').slice(1).join(' ')
if(label.length > 0) {
label = label.charAt(0).toUpperCase() + label.slice(1);
}
return label
}

View file

@ -1,10 +1,11 @@
import React from 'react' import React from 'react'
import Immutable from 'immutable' import Immutable from 'immutable'
import { PropertyGroup } from '../fields/spec'
import PureRenderMixin from 'react-addons-pure-render-mixin'; import PureRenderMixin from 'react-addons-pure-render-mixin';
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js' import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
import inputStyle from '../fields/input.js'
import theme from '../theme.js' import input from '../../config/input.js'
import colors from '../../config/colors.js'
import { margins } from '../../config/scales.js'
const combiningFilterOps = ['all', 'any', 'none'] const combiningFilterOps = ['all', 'any', 'none']
const setFilterOps = ['in', '!in'] const setFilterOps = ['in', '!in']
@ -26,9 +27,9 @@ class CombiningOperatorSelect extends React.Component {
return <div> return <div>
<select <select
style={{ style={{
...inputStyle.select, ...input.select,
width: '20.5%', width: '20.5%',
margin: theme.scale[0], margin: margins[0],
}} }}
value={this.props.value} value={this.props.value}
onChange={e => this.props.onChange(e.target.value)} onChange={e => this.props.onChange(e.target.value)}
@ -36,9 +37,9 @@ class CombiningOperatorSelect extends React.Component {
{options} {options}
</select> </select>
<label style={{ <label style={{
...inputStyle.label, ...input.label,
width: '60%', width: '60%',
marginLeft: theme.scale[0], marginLeft: margins[0],
}}> }}>
of the filters matches of the filters matches
</label> </label>
@ -58,9 +59,9 @@ class OperatorSelect extends React.Component {
}) })
return <select return <select
style={{ style={{
...inputStyle.select, ...input.select,
width: '15%', width: '15%',
margin: theme.scale[0] margin: margins[0]
}} }}
value={this.props.value} value={this.props.value}
onChange={e => this.props.onChange(e.target.value)} onChange={e => this.props.onChange(e.target.value)}
@ -74,6 +75,7 @@ class SingleFilterEditor extends React.Component {
static propTypes = { static propTypes = {
filter: React.PropTypes.array.isRequired, filter: React.PropTypes.array.isRequired,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func.isRequired,
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
} }
onFilterPartChanged(filterOp, propertyName, filterArgs) { onFilterPartChanged(filterOp, propertyName, filterArgs) {
@ -88,24 +90,28 @@ class SingleFilterEditor extends React.Component {
const filterArgs = f.slice(2) const filterArgs = f.slice(2)
return <div> return <div>
<input <select
style={{ style={{
...inputStyle.input, ...input.select,
width: '17%', width: '17%',
margin: theme.scale[0] margin: margins[0]
}} }}
value={propertyName} value={propertyName}
onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)} onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)}
/> >
{this.props.properties.keySeq().map(propName => {
return <option key={propName} value={propName}>{propName}</option>
}).toIndexedSeq()}
</select>
<OperatorSelect <OperatorSelect
value={filterOp} value={filterOp}
onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)} onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)}
/> />
<input <input
style={{ style={{
...inputStyle.input, ...input.input,
width: '53%', width: '53%',
margin: theme.scale[0] margin: margins[0]
}} }}
value={filterArgs.join(',')} value={filterArgs.join(',')}
onChange={e => { onChange={e => {
@ -118,6 +124,8 @@ class SingleFilterEditor extends React.Component {
export default class CombiningFilterEditor extends React.Component { export default class CombiningFilterEditor extends React.Component {
static propTypes = { static propTypes = {
/** Properties of the vector layer and the available fields */
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
filter: React.PropTypes.array.isRequired, filter: React.PropTypes.array.isRequired,
onChange: React.PropTypes.func.isRequired, onChange: React.PropTypes.func.isRequired,
} }
@ -154,15 +162,16 @@ export default class CombiningFilterEditor extends React.Component {
const filterEditors = filters.map((f, idx) => { const filterEditors = filters.map((f, idx) => {
return <SingleFilterEditor return <SingleFilterEditor
key={idx} key={idx}
properties={this.props.properties}
filter={f} filter={f}
onChange={this.onFilterPartChanged.bind(this, idx + 1)} onChange={this.onFilterPartChanged.bind(this, idx + 1)}
/> />
}) })
return <div style={{ return <div style={{
padding: theme.scale[2], padding: margins[2],
paddingRight: 0, paddingRight: 0,
backgroundColor: theme.colors.black backgroundColor: colors.black
}}> }}>
<CombiningOperatorSelect <CombiningOperatorSelect
value={combiningOp} value={combiningOp}

View file

@ -0,0 +1,26 @@
import React from 'react'
import LineIcon from './LineIcon.jsx'
import FillIcon from './FillIcon.jsx'
import SymbolIcon from './SymbolIcon.jsx'
import BackgroundIcon from './BackgroundIcon.jsx'
class LayerIcon extends React.Component {
static propTypes = {
type: React.PropTypes.string.isRequired,
style: React.PropTypes.object,
}
render() {
const iconProps = { style: this.props.style }
switch(this.props.type) {
case 'fill': return <FillIcon {...iconProps} />
case 'background': return <BackgroundIcon {...iconProps} />
case 'line': return <LineIcon {...iconProps} />
case 'symbol': return <SymbolIcon {...iconProps} />
default: return null
}
}
}
export default LayerIcon

View file

@ -6,18 +6,19 @@ import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space' import Space from 'rebass/dist/Space'
import Tabs from 'react-simpletabs' import Tabs from 'react-simpletabs'
import theme from '../theme.js' import SourceEditor from './SourceEditor'
import SourceEditor from './source.jsx' import FilterEditor from '../filter/FilterEditor'
import FilterEditor from '../filter/editor.jsx' import PropertyGroup from '../fields/PropertyGroup'
import PropertyGroup from '../fields/propertygroup.jsx'
import MdVisibility from 'react-icons/lib/md/visibility' import MdVisibility from 'react-icons/lib/md/visibility'
import MdVisibilityOff from 'react-icons/lib/md/visibility-off' import MdVisibilityOff from 'react-icons/lib/md/visibility-off'
import MdDelete from 'react-icons/lib/md/delete' import MdDelete from 'react-icons/lib/md/delete'
import PureRenderMixin from 'react-addons-pure-render-mixin'; import PureRenderMixin from 'react-addons-pure-render-mixin';
import ScrollContainer from '../scrollcontainer.jsx' import ScrollContainer from '../ScrollContainer'
import layout from '../layout.json'
import layout from '../../config/layout.json'
import theme from '../../config/rebass.js'
class UnsupportedLayer extends React.Component { class UnsupportedLayer extends React.Component {
render() { render() {
@ -26,10 +27,11 @@ class UnsupportedLayer extends React.Component {
} }
/** Layer editor supporting multiple types of layers. */ /** Layer editor supporting multiple types of layers. */
export class LayerEditor extends React.Component { export default class LayerEditor extends React.Component {
static propTypes = { static propTypes = {
layer: React.PropTypes.object.isRequired, layer: React.PropTypes.object.isRequired,
sources: React.PropTypes.instanceOf(Immutable.Map), sources: React.PropTypes.instanceOf(Immutable.Map),
vectorLayers: React.PropTypes.instanceOf(Immutable.Map),
onLayerChanged: React.PropTypes.func, onLayerChanged: React.PropTypes.func,
onLayerDestroyed: React.PropTypes.func, onLayerDestroyed: React.PropTypes.func,
} }
@ -82,12 +84,14 @@ export class LayerEditor extends React.Component {
const groups = layout[layerType].groups const groups = layout[layerType].groups
const propertyGroups = groups.map(group => { const propertyGroups = groups.map(group => {
return <PropertyGroup return <PropertyGroup
key={this.props.group}
layer={this.props.layer} layer={this.props.layer}
groupFields={Immutable.OrderedSet(group.fields)} groupFields={Immutable.OrderedSet(group.fields)}
onChange={this.onPropertyChange.bind(this)} onChange={this.onPropertyChange.bind(this)}
/> />
}) })
console.log(this.props.layer.toJSON())
let visibleIcon = <MdVisibilityOff /> let visibleIcon = <MdVisibilityOff />
if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') { if(this.props.layer.has('layout') && this.props.layer.getIn(['layout', 'visibility']) === 'none') {
visibleIcon = <MdVisibility /> visibleIcon = <MdVisibility />
@ -110,6 +114,7 @@ export class LayerEditor extends React.Component {
{propertyGroups} {propertyGroups}
<FilterEditor <FilterEditor
filter={this.props.layer.get('filter', Immutable.List()).toJSON()} 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))} onChange={f => this.onFilterChange(Immutable.fromJS(f))}
/> />
{this.props.layer.get('type') !== 'background' {this.props.layer.get('type') !== 'background'

View file

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import Immutable from 'immutable' import Immutable from 'immutable'
import Heading from 'rebass/dist/Heading' import Heading from 'rebass/dist/Heading'
@ -6,11 +7,10 @@ import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem' import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space' import Space from 'rebass/dist/Space'
import { LayerEditor } from './editor.jsx' import LayerListItem from './LayerListItem'
import LayerListItem from './listitem.jsx' import ScrollContainer from '../ScrollContainer'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import theme from '../theme.js' import { margins } from '../../config/scales.js'
import ScrollContainer from '../scrollcontainer.jsx'
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc'; import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
@ -55,14 +55,14 @@ class LayerListContainer extends React.Component {
/> />
}) })
return <ScrollContainer> return <ScrollContainer>
<ul style={{ padding: theme.scale[1], margin: 0 }}> <ul style={{ padding: margins[1], margin: 0 }}>
{layerPanels} {layerPanels}
</ul> </ul>
</ScrollContainer> </ScrollContainer>
} }
} }
export class LayerList extends React.Component { export default class LayerList extends React.Component {
static propTypes = {...layerListPropTypes} static propTypes = {...layerListPropTypes}
constructor(props) { constructor(props) {

View file

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin'
import Radium from 'radium' import Radium from 'radium'
import Immutable from 'immutable' import Immutable from 'immutable'
import Color from 'color' import Color from 'color'
@ -8,12 +9,12 @@ import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem' import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space' import Space from 'rebass/dist/Space'
import LayerIcon from '../icons/layer.jsx' import LayerIcon from '../icons/LayerIcon'
import { LayerEditor } from './editor.jsx' import LayerEditor from './LayerEditor'
import scrollbars from '../scrollbars.scss' import {SortableElement, SortableHandle} from 'react-sortable-hoc'
import PureRenderMixin from 'react-addons-pure-render-mixin';
import theme from '../theme.js' import colors from '../../config/colors.js'
import {SortableElement, SortableHandle} from 'react-sortable-hoc'; import { fontSizes, margins } from '../../config/scales.js'
@SortableHandle @SortableHandle
@ -48,8 +49,8 @@ class LayerListItem extends React.Component {
onClick={() => this.props.onLayerSelected(this.props.layerId)} onClick={() => this.props.onLayerSelected(this.props.layerId)}
style={{ style={{
fontWeight: 400, fontWeight: 400,
color: theme.colors.lowgray, color: colors.lowgray,
fontSize: theme.fontSizes[5], fontSize: fontSizes[5],
borderBottom: 1, borderBottom: 1,
borderLeft: 2, borderLeft: 2,
borderRight: 0, borderRight: 0,
@ -59,11 +60,11 @@ class LayerListItem extends React.Component {
zIndex: 2000, zIndex: 2000,
cursor: 'pointer', cursor: 'pointer',
position: 'relative', position: 'relative',
padding: theme.scale[1], padding: margins[1],
borderColor: Color(theme.colors.gray).lighten(0.1).string(), borderColor: Color(colors.gray).lighten(0.1).string(),
backgroundColor: theme.colors.gray, backgroundColor: colors.gray,
":hover": { ":hover": {
backgroundColor: Color(theme.colors.gray).lighten(0.15).string(), backgroundColor: Color(colors.gray).lighten(0.15).string(),
} }
}}> }}>
<LayerTypeDragHandle type={this.props.layerType} /> <LayerTypeDragHandle type={this.props.layerType} />

View file

@ -1,8 +1,9 @@
import React from 'react' import React from 'react'
import Immutable from 'immutable'
import { PropertyGroup } from '../fields/spec'
import PureRenderMixin from 'react-addons-pure-render-mixin'; import PureRenderMixin from 'react-addons-pure-render-mixin';
import inputStyle from '../fields/input.js' import Immutable from 'immutable'
import PropertyGroup from '../fields/PropertyGroup'
import input from '../../config/input.js'
/** Choose tileset (source) and the source layer */ /** Choose tileset (source) and the source layer */
export default class SourceEditor extends React.Component { export default class SourceEditor extends React.Component {
@ -33,22 +34,21 @@ export default class SourceEditor extends React.Component {
return <option key={id} value={id}>{id}</option> return <option key={id} value={id}>{id}</option>
}).toIndexedSeq() }).toIndexedSeq()
console.log(this.props.sources)
return <div> return <div>
<div style={inputStyle.property}> <div style={input.property}>
<label style={inputStyle.label}>Source</label> <label style={input.label}>Source</label>
<select <select
style={inputStyle.select} style={input.select}
value={this.props.source} value={this.props.source}
onChange={(e) => this.onSourceChange(e.target.value)} onChange={(e) => this.onSourceChange(e.target.value)}
> >
{options} {options}
</select> </select>
</div> </div>
<div style={inputStyle.property}> <div style={input.property}>
<label style={inputStyle.label}>Source Layer</label> <label style={input.label}>Source Layer</label>
<select <select
style={inputStyle.select} style={input.select}
value={this.props.sourceLayer} value={this.props.sourceLayer}
onChange={(e) => this.onSourceLayerChange(e.target.value)} onChange={(e) => this.onSourceLayerChange(e.target.value)}
> >

View file

@ -1,8 +1,7 @@
import React from 'react' import React from 'react'
import { fullHeight } from './theme.js'
import Immutable from 'immutable' import Immutable from 'immutable'
export class Map extends React.Component { export default class Map extends React.Component {
static propTypes = { static propTypes = {
mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired, mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired,
accessToken: React.PropTypes.string, accessToken: React.PropTypes.string,
@ -17,7 +16,10 @@ export class Map extends React.Component {
return <div return <div
ref={x => this.container = x} ref={x => this.container = x}
style={{ style={{
...fullHeight, position: "fixed",
top: 0,
bottom: 0,
height: "100%",
width: "100%", width: "100%",
}}></div> }}></div>
} }

View file

@ -1,12 +1,11 @@
import React from 'react' import React from 'react'
import MapboxGl from 'mapbox-gl' import MapboxGl from 'mapbox-gl'
import { fullHeight } from './theme.js'
import style from './style.js'
import { Map } from './map.jsx'
import Immutable from 'immutable'
import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color' import validateColor from 'mapbox-gl-style-spec/lib/validate/validate_color'
export class MapboxGlMap extends Map { import Map from './Map.jsx'
import style from '../../libs/style.js'
export default class MapboxGlMap extends Map {
static propTypes = { static propTypes = {
onMapLoaded: React.PropTypes.func, onMapLoaded: React.PropTypes.func,
} }

View file

@ -1,10 +1,11 @@
import React from 'react' import React from 'react'
import style from './style.js'
import { Map } from './map.jsx'
import ol from 'openlayers' import ol from 'openlayers'
import olms from 'ol-mapbox-style' import olms from 'ol-mapbox-style'
export class OpenLayers3Map extends Map { import Map from './Map'
import style from '../../libs/style.js'
export default class OpenLayers3Map extends Map {
constructor(props) { constructor(props) {
super(props) super(props)

View file

@ -14,8 +14,8 @@ import Input from 'rebass/dist/Input'
import Toolbar from 'rebass/dist/Toolbar' import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem' import NavItem from 'rebass/dist/NavItem'
import publicTilesets from '../tilesets.json' import publicTilesets from '../../config/tilesets.json'
import theme from '../theme.js' import theme from '../../config/rebass'
class TilesetsModal extends React.Component { class TilesetsModal extends React.Component {
static propTypes = { static propTypes = {

29
src/config/colors.js Normal file
View file

@ -0,0 +1,29 @@
const baseColors = {
black: '#1c1f24',
gray: '#26282e',
midgray: '#36383e',
lowgray: '#8e8e8e',
white: '#fff',
blue: '#00d9f7',
green: '#B4C7AD',
orange: '#fb3',
red: '#f04',
}
const themeColors = {
primary: baseColors.gray,
secondary: baseColors.midgray,
default: baseColors.gray,
info: baseColors.blue,
success: baseColors.green,
warning: baseColors.orange,
error: baseColors.red
}
const colors = {
...baseColors,
...themeColors
}
export default colors

View file

@ -1,6 +1,4 @@
/** Common input styling */ import theme from './rebass.js'
import theme from '../theme.js'
const base = { const base = {
display: 'inline-block', display: 'inline-block',

66
src/config/rebass.js Normal file
View file

@ -0,0 +1,66 @@
import colors from './colors'
import { margins, fontSizes } from './scales'
const border = {
borderColor: colors.black,
borderRadius: 0,
}
const dark = {
name: 'Dark',
color: colors.white,
fontFamily: 'Roboto, sans-serif',
scale: margins,
fontSizes: fontSizes,
colors,
inverted: colors.midGray,
...border,
Block: {
backgroundColor: colors.gray,
...border,
borderLeft: 0,
borderRight: 0,
marginBottom: 0,
paddingBottom: 0,
},
PanelHeader: {
marginRight: -10,
marginBottom: 0,
fontSize: fontSizes[5],
fontWeight: 400,
color: colors.white,
},
Panel: {
backgroundColor: colors.gray,
},
Button: {
color: '#00d9f7',
},
Menu: {
color: '#00d9f7',
backgroundColor: '#000'
},
Message: {
color: '#111',
opacity: 15/16
},
Header: {
fontWeight: 400,
},
ButtonCircle : {
},
Toolbar: {
fontWeight: 400,
minHeight: margins[3]
},
Label: {
fontWeight: 300,
},
Input: {
fontWeight: 300,
fontSize: fontSizes[5],
}
}
export default dark

2
src/config/scales.js Normal file
View file

@ -0,0 +1,2 @@
export const margins = [3, 5, 10, 30, 40]
export const fontSizes = [26, 20, 16, 14, 12, 10]

View file

@ -1,35 +0,0 @@
import React from 'react'
import LineIcon from './line.jsx'
import FillIcon from './fill.jsx'
import SymbolIcon from './symbol.jsx'
import BackgroundIcon from './background.jsx'
class LayerIcon extends React.Component {
static propTypes = {
type: React.PropTypes.string.isRequired,
style: React.PropTypes.object,
}
render() {
if (this.props.type === "fill") {
return <FillIcon style={this.props.style} />
}
if (this.props.type === "background") {
return <BackgroundIcon style={this.props.style} />
}
if (this.props.type === "line") {
return <LineIcon style={this.props.style} />
}
if (this.props.type === "symbol") {
return <SymbolIcon style={this.props.style} />
}
return null
}
}
export default LayerIcon

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from './app.jsx'; import './index.css'
import App from './components/App';
ReactDOM.render(<App/>, document.querySelector("#app")); ReactDOM.render(<App/>, document.querySelector("#app"));

View file

@ -1,79 +0,0 @@
import React from 'react'
import theme from './theme.js'
import Heading from 'rebass/dist/Heading'
import Container from 'rebass/dist/Container'
import Input from 'rebass/dist/Input'
import Toolbar from 'rebass/dist/Toolbar'
import NavItem from 'rebass/dist/NavItem'
import Space from 'rebass/dist/Space'
import Button from 'rebass/dist/Button'
import Immutable from 'immutable'
import PureRenderMixin from 'react-addons-pure-render-mixin';
/** Edit global settings within a style such as the name */
export class SettingsEditor extends React.Component {
static propTypes = {
mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired,
onStyleChanged: React.PropTypes.func.isRequired,
accessToken: React.PropTypes.string,
onAccessTokenChanged: React.PropTypes.func,
onReset: React.PropTypes.func
}
constructor(props) {
super(props);
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
}
onChange(property, e) {
const changedStyle = this.props.mapStyle.set(property, e.target.value)
this.props.onStyleChanged(changedStyle)
}
render() {
return <div>
<Toolbar style={{marginRight: 20}}>
<NavItem>
<Heading>Settings</Heading>
</NavItem>
</Toolbar>
<Container>
<Input
name="access-token"
label="Mapbox GL access token"
value={this.props.accessToken}
onChange={e => this.props.onAccessTokenChanged(e.target.value)}
/>
<Input
name="name"
label="Name"
value={this.props.mapStyle.get('name')}
onChange={this.onChange.bind(this, "name")}
/>
<Input
name="owner"
label="Owner"
value={this.props.mapStyle.get('owner')}
onChange={this.onChange.bind(this, "owner")}
/>
<Input
name="sprite"
label="Sprite URL"
value={this.props.mapStyle.get('sprite')}
onChange={this.onChange.bind(this, "sprite")}
/>
<Input
name="glyphs"
label="Glyphs URL"
value={this.props.mapStyle.get('glyphs')}
onChange={this.onChange.bind(this, "glyphs")}
/>
<Button
onClick={this.props.onReset}
>Reset style</Button>
</Container>
</div>
}
}

View file

@ -1,115 +0,0 @@
const caps = {
textTransform: 'uppercase',
letterSpacing: '.2em'
}
export const fullHeight = {
position: "fixed",
top: 0,
bottom: 0,
height: "100%",
}
const baseColors = {
black: '#1c1f24',
gray: '#26282e',
midgray: '#36383e',
lowgray: '#8e8e8e',
white: '#fff',
blue: '#00d9f7',
green: '#B4C7AD',
orange: '#fb3',
red: '#f04',
}
const themeColors = {
primary: baseColors.gray,
secondary: baseColors.midgray,
default: baseColors.gray,
info: baseColors.blue,
success: baseColors.green,
warning: baseColors.orange,
error: baseColors.red
}
export const colors = {
...baseColors,
...themeColors
}
export const inputBase = {
display: 'block',
border: '1px solid rgb(36, 36, 36)',
height: 30,
width: '100%',
paddingLeft: 5,
paddingRight: 5,
backgroundColor: colors.gray,
}
const scale = [3, 5, 10, 30, 40]
const fontSizes = [26, 20, 16, 14, 12, 10]
const border = {
borderColor: colors.black,
borderRadius: 0,
}
const dark = {
name: 'Dark',
color: colors.white,
fontFamily: 'Roboto, sans-serif',
scale,
fontSizes,
colors,
inverted: colors.midGray,
...border,
Block: {
backgroundColor: colors.gray,
...border,
borderLeft: 0,
borderRight: 0,
marginBottom: 0,
paddingBottom: 0,
},
PanelHeader: {
marginRight: -10,
marginBottom: 0,
fontSize: fontSizes[5],
fontWeight: 400,
color: colors.white,
},
Panel: {
backgroundColor: colors.gray,
},
Button: {
color: '#00d9f7',
},
Menu: {
color: '#00d9f7',
backgroundColor: '#000'
},
Message: {
color: '#111',
opacity: 15/16
},
Header: {
fontWeight: 400,
},
ButtonCircle : {
},
Toolbar: {
fontWeight: 400,
minHeight: scale[3]
},
Label: {
fontWeight: 300,
},
Input: {
fontWeight: 300,
fontSize: fontSizes[5],
},
}
export default dark

View file

@ -32,15 +32,14 @@ module.exports = {
'mapbox-gl', 'mapbox-gl',
//TODO: Cannot resolve migrations file? //TODO: Cannot resolve migrations file?
//"mapbox-gl-style-spec", //"mapbox-gl-style-spec",
"radium",
"randomcolor", "randomcolor",
'react', 'react',
"react-collapse",
"react-dom", "react-dom",
"react-color",
"react-file-reader-input", "react-file-reader-input",
"react-height",
//TODO: Icons raise multi vendor errors? //TODO: Icons raise multi vendor errors?
//"react-icons", //"react-icons",
"react-motion",
"rebass", "rebass",
// Open Layers // Open Layers
'openlayers', 'openlayers',