mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-11-10 08:17:46 +01:00
Restructure and rename components
This commit is contained in:
parent
461a001552
commit
fde60ac3e0
46 changed files with 365 additions and 425 deletions
|
@ -35,7 +35,6 @@
|
|||
"react-color": "^2.10.0",
|
||||
"react-dom": "^15.4.0",
|
||||
"react-file-reader-input": "^1.1.0",
|
||||
"react-height": "^2.1.1",
|
||||
"react-icon-base": "^2.0.4",
|
||||
"react-icons": "^2.2.1",
|
||||
"react-simpletabs": "^0.7.0",
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import React from 'react'
|
||||
import {saveAs} from 'file-saver'
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
import Drawer from 'rebass/dist/Drawer'
|
||||
import Container from 'rebass/dist/Container'
|
||||
import Block from 'rebass/dist/Block'
|
||||
import Fixed from 'rebass/dist/Fixed'
|
||||
|
||||
import { MapboxGlMap } from './gl.jsx'
|
||||
import { OpenLayers3Map } from './ol3.jsx'
|
||||
import { LayerList } from './layers/list.jsx'
|
||||
import { LayerEditor } from './layers/editor.jsx'
|
||||
import {Toolbar} from './toolbar.jsx'
|
||||
import style from './style.js'
|
||||
import { loadDefaultStyle, SettingsStore, StyleStore } from './stylestore.js'
|
||||
import { ApiStyleStore } from './apistore.js'
|
||||
import MapboxGlMap from './map/MapboxGlMap.jsx'
|
||||
import OpenLayers3Map from './map/OpenLayers3Map.jsx'
|
||||
import LayerList from './layers/LayerList.jsx'
|
||||
import LayerEditor from './layers/LayerEditor.jsx'
|
||||
import Toolbar from './Toolbar.jsx'
|
||||
|
||||
import LayerWatcher from './layerwatcher.js'
|
||||
import theme from './theme.js'
|
||||
import { colors, fullHeight } from './theme.js'
|
||||
import './index.css'
|
||||
import style from '../libs/style.js'
|
||||
import { loadDefaultStyle, SettingsStore, StyleStore } from '../libs/stylestore.js'
|
||||
import { ApiStyleStore } from '../libs/apistore.js'
|
||||
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 {
|
||||
static childContextTypes = {
|
||||
|
@ -160,7 +160,7 @@ export default class App extends React.Component {
|
|||
width: 300,
|
||||
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>
|
||||
{this.mapRenderer()}
|
||||
</div>
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react'
|
||||
import theme from './theme.js'
|
||||
import scrollbars from './scrollbars.scss'
|
||||
|
||||
const ScrollContainer = (props) => {
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import React from 'react'
|
||||
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 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 MdFindInPage from 'react-icons/lib/md/find-in-page'
|
||||
|
||||
import SettingsModal from './modals/settings.jsx'
|
||||
import TilesetsModal from './modals/tilesets.jsx'
|
||||
import style from './style.js'
|
||||
import { fullHeight } from './theme.js'
|
||||
import theme from './theme.js';
|
||||
import SettingsModal from './modals/SettingsModal'
|
||||
import TilesetsModal from './modals/TilesetsModal'
|
||||
|
||||
import style from '../libs/style'
|
||||
import colors from '../config/colors';
|
||||
|
||||
const InlineBlock = props => <div style={{display: "inline-block", ...props.style}}>
|
||||
{props.children}
|
||||
</div>
|
||||
|
||||
export class Toolbar extends React.Component {
|
||||
export default class Toolbar extends React.Component {
|
||||
static propTypes = {
|
||||
mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
onStyleChanged: React.PropTypes.func.isRequired,
|
||||
|
@ -107,7 +107,7 @@ export class Toolbar extends React.Component {
|
|||
zIndex: 100,
|
||||
left: 0,
|
||||
top: 0,
|
||||
backgroundColor: theme.colors.black
|
||||
backgroundColor: colors.black
|
||||
}}>
|
||||
<SettingsModal
|
||||
mapStyle={this.props.mapStyle}
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import inputStyle from './input.js'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
class BooleanField extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -14,7 +14,7 @@ class BooleanField extends React.Component {
|
|||
return <input
|
||||
type="checkbox"
|
||||
style={{
|
||||
...inputStyle.checkbox,
|
||||
...input.checkbox,
|
||||
...this.props.style
|
||||
}}
|
||||
value={this.props.value}
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react'
|
||||
import Color from 'color'
|
||||
import inputStyle from './input.js'
|
||||
import ChromePicker from 'react-color/lib/components/chrome/Chrome'
|
||||
|
||||
import input from '../../config/input.js'
|
||||
|
||||
function formatColor(color) {
|
||||
const rgb = color.rgb
|
||||
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${rgb.a})`
|
||||
|
@ -53,7 +54,7 @@ class ColorField extends React.Component {
|
|||
</div>
|
||||
|
||||
return <div style={{
|
||||
...inputStyle.property,
|
||||
...input.property,
|
||||
position: 'relative',
|
||||
display: 'inline',
|
||||
}}>
|
||||
|
@ -61,7 +62,7 @@ class ColorField extends React.Component {
|
|||
<input
|
||||
onClick={this.togglePicker.bind(this)}
|
||||
style={{
|
||||
...inputStyle.select,
|
||||
...input.select,
|
||||
...this.props.style
|
||||
}}
|
||||
name={this.props.name}
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import inputStyle from './input.js'
|
||||
import input from '../../config/input.js'
|
||||
|
||||
class EnumField extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -22,7 +22,7 @@ class EnumField extends React.Component {
|
|||
|
||||
return <select
|
||||
style={{
|
||||
...inputStyle.select,
|
||||
...input.select,
|
||||
...this.props.style
|
||||
}}
|
||||
value={this.props.value}
|
|
@ -1,5 +1,5 @@
|
|||
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*/
|
||||
class NumberField extends React.Component {
|
||||
|
@ -27,7 +27,7 @@ class NumberField extends React.Component {
|
|||
render() {
|
||||
return <input
|
||||
style={{
|
||||
...inputStyle.input,
|
||||
...input.input,
|
||||
...this.props.style
|
||||
}}
|
||||
type="number"
|
|
@ -1,11 +1,13 @@
|
|||
import React from 'react'
|
||||
import { ZoomSpecField } from './spec.jsx'
|
||||
import Immutable from 'immutable'
|
||||
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) {
|
||||
const paint = GlSpec['paint_' + layerType] || {}
|
||||
const layout = GlSpec['layout_' + layerType] || {}
|
||||
|
@ -25,10 +27,8 @@ export default class PropertyGroup extends React.Component {
|
|||
|
||||
render() {
|
||||
const fields = this.props.groupFields.map(fieldName => {
|
||||
console.log(fieldName)
|
||||
const fieldSpec = getFieldSpec(this.props.layer.get('type'), fieldName)
|
||||
const fieldValue = this.props.layer.getIn(['paint', fieldName], this.props.layer.getIn(['layout', fieldName]))
|
||||
|
||||
return <ZoomSpecField
|
||||
onChange={this.props.onChange}
|
||||
key={fieldName}
|
||||
|
@ -39,10 +39,10 @@ export default class PropertyGroup extends React.Component {
|
|||
}).toIndexedSeq()
|
||||
|
||||
return <div style={{
|
||||
padding: theme.scale[2],
|
||||
padding: margins[2],
|
||||
paddingRight: 0,
|
||||
backgroundColor: theme.colors.black,
|
||||
marginBottom: theme.scale[2],
|
||||
backgroundColor: colors.black,
|
||||
marginBottom: margins[2],
|
||||
}}>
|
||||
{fields}
|
||||
</div>
|
|
@ -3,87 +3,19 @@ import Immutable from 'immutable'
|
|||
import color from 'color'
|
||||
|
||||
import GlSpec from 'mapbox-gl-style-spec/reference/latest.min.js'
|
||||
import NumberField from './number'
|
||||
import EnumField from './enum'
|
||||
import BooleanField from './boolean'
|
||||
import ColorField from './color'
|
||||
import StringField from './string'
|
||||
import inputStyle from './input.js'
|
||||
import theme from '../theme.js'
|
||||
import NumberField from './NumberField'
|
||||
import EnumField from './EnumField'
|
||||
import BooleanField from './BooleanField'
|
||||
import ColorField from './ColorField'
|
||||
import StringField from './StringField'
|
||||
|
||||
import input from '../../config/input.js'
|
||||
import theme from '../../config/rebass.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 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) {
|
||||
let label = fieldName.split('-').slice(1).join(' ')
|
||||
if(label.length > 0) {
|
||||
|
@ -92,13 +24,15 @@ function labelFromFieldName(fieldName) {
|
|||
return label
|
||||
}
|
||||
|
||||
|
||||
/** Display any field from the Mapbox GL style spec and
|
||||
* choose the correct field component based on the @{fieldSpec}
|
||||
* to display @{value}. */
|
||||
class SpecField extends React.Component {
|
||||
export default class SpecField extends React.Component {
|
||||
static propTypes = {
|
||||
...specFieldProps,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
fieldName: React.PropTypes.string.isRequired,
|
||||
fieldSpec: React.PropTypes.object.isRequired,
|
||||
|
||||
value: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
|
@ -1,5 +1,5 @@
|
|||
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*/
|
||||
class StringField extends React.Component {
|
||||
|
@ -20,7 +20,7 @@ class StringField extends React.Component {
|
|||
render() {
|
||||
return <input
|
||||
style={{
|
||||
...inputStyle.input,
|
||||
...input.input,
|
||||
...this.props.style
|
||||
}}
|
||||
name={this.props.name}
|
98
src/components/fields/ZoomSpecField.jsx
Normal file
98
src/components/fields/ZoomSpecField.jsx
Normal 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
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react'
|
||||
import Immutable from 'immutable'
|
||||
import { PropertyGroup } from '../fields/spec'
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
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 setFilterOps = ['in', '!in']
|
||||
|
@ -26,9 +27,9 @@ class CombiningOperatorSelect extends React.Component {
|
|||
return <div>
|
||||
<select
|
||||
style={{
|
||||
...inputStyle.select,
|
||||
...input.select,
|
||||
width: '20.5%',
|
||||
margin: theme.scale[0],
|
||||
margin: margins[0],
|
||||
}}
|
||||
value={this.props.value}
|
||||
onChange={e => this.props.onChange(e.target.value)}
|
||||
|
@ -36,9 +37,9 @@ class CombiningOperatorSelect extends React.Component {
|
|||
{options}
|
||||
</select>
|
||||
<label style={{
|
||||
...inputStyle.label,
|
||||
...input.label,
|
||||
width: '60%',
|
||||
marginLeft: theme.scale[0],
|
||||
marginLeft: margins[0],
|
||||
}}>
|
||||
of the filters matches
|
||||
</label>
|
||||
|
@ -58,9 +59,9 @@ class OperatorSelect extends React.Component {
|
|||
})
|
||||
return <select
|
||||
style={{
|
||||
...inputStyle.select,
|
||||
...input.select,
|
||||
width: '15%',
|
||||
margin: theme.scale[0]
|
||||
margin: margins[0]
|
||||
}}
|
||||
value={this.props.value}
|
||||
onChange={e => this.props.onChange(e.target.value)}
|
||||
|
@ -74,6 +75,7 @@ class SingleFilterEditor extends React.Component {
|
|||
static propTypes = {
|
||||
filter: React.PropTypes.array.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
}
|
||||
|
||||
onFilterPartChanged(filterOp, propertyName, filterArgs) {
|
||||
|
@ -88,24 +90,28 @@ class SingleFilterEditor extends React.Component {
|
|||
const filterArgs = f.slice(2)
|
||||
|
||||
return <div>
|
||||
<input
|
||||
<select
|
||||
style={{
|
||||
...inputStyle.input,
|
||||
...input.select,
|
||||
width: '17%',
|
||||
margin: theme.scale[0]
|
||||
margin: margins[0]
|
||||
}}
|
||||
value={propertyName}
|
||||
onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)}
|
||||
/>
|
||||
>
|
||||
{this.props.properties.keySeq().map(propName => {
|
||||
return <option key={propName} value={propName}>{propName}</option>
|
||||
}).toIndexedSeq()}
|
||||
</select>
|
||||
<OperatorSelect
|
||||
value={filterOp}
|
||||
onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)}
|
||||
/>
|
||||
<input
|
||||
style={{
|
||||
...inputStyle.input,
|
||||
...input.input,
|
||||
width: '53%',
|
||||
margin: theme.scale[0]
|
||||
margin: margins[0]
|
||||
}}
|
||||
value={filterArgs.join(',')}
|
||||
onChange={e => {
|
||||
|
@ -118,6 +124,8 @@ class SingleFilterEditor extends React.Component {
|
|||
|
||||
export default class CombiningFilterEditor extends React.Component {
|
||||
static propTypes = {
|
||||
/** Properties of the vector layer and the available fields */
|
||||
properties: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
filter: React.PropTypes.array.isRequired,
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
}
|
||||
|
@ -154,15 +162,16 @@ export default class CombiningFilterEditor extends React.Component {
|
|||
const filterEditors = filters.map((f, idx) => {
|
||||
return <SingleFilterEditor
|
||||
key={idx}
|
||||
properties={this.props.properties}
|
||||
filter={f}
|
||||
onChange={this.onFilterPartChanged.bind(this, idx + 1)}
|
||||
/>
|
||||
})
|
||||
|
||||
return <div style={{
|
||||
padding: theme.scale[2],
|
||||
padding: margins[2],
|
||||
paddingRight: 0,
|
||||
backgroundColor: theme.colors.black
|
||||
backgroundColor: colors.black
|
||||
}}>
|
||||
<CombiningOperatorSelect
|
||||
value={combiningOp}
|
26
src/components/icons/LayerIcon.jsx
Normal file
26
src/components/icons/LayerIcon.jsx
Normal 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
|
|
@ -6,18 +6,19 @@ import NavItem from 'rebass/dist/NavItem'
|
|||
import Space from 'rebass/dist/Space'
|
||||
import Tabs from 'react-simpletabs'
|
||||
|
||||
import theme from '../theme.js'
|
||||
import SourceEditor from './source.jsx'
|
||||
import FilterEditor from '../filter/editor.jsx'
|
||||
import PropertyGroup from '../fields/propertygroup.jsx'
|
||||
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.jsx'
|
||||
import layout from '../layout.json'
|
||||
import ScrollContainer from '../ScrollContainer'
|
||||
|
||||
import layout from '../../config/layout.json'
|
||||
import theme from '../../config/rebass.js'
|
||||
|
||||
class UnsupportedLayer extends React.Component {
|
||||
render() {
|
||||
|
@ -26,10 +27,11 @@ class UnsupportedLayer extends React.Component {
|
|||
}
|
||||
|
||||
/** Layer editor supporting multiple types of layers. */
|
||||
export class LayerEditor extends React.Component {
|
||||
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,
|
||||
}
|
||||
|
@ -82,12 +84,14 @@ export class LayerEditor extends React.Component {
|
|||
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 />
|
||||
|
@ -110,6 +114,7 @@ export class LayerEditor extends React.Component {
|
|||
{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'
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import Immutable from 'immutable'
|
||||
|
||||
import Heading from 'rebass/dist/Heading'
|
||||
|
@ -6,11 +7,10 @@ import Toolbar from 'rebass/dist/Toolbar'
|
|||
import NavItem from 'rebass/dist/NavItem'
|
||||
import Space from 'rebass/dist/Space'
|
||||
|
||||
import { LayerEditor } from './editor.jsx'
|
||||
import LayerListItem from './listitem.jsx'
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import theme from '../theme.js'
|
||||
import ScrollContainer from '../scrollcontainer.jsx'
|
||||
import LayerListItem from './LayerListItem'
|
||||
import ScrollContainer from '../ScrollContainer'
|
||||
|
||||
import { margins } from '../../config/scales.js'
|
||||
|
||||
import {SortableContainer, SortableHandle, arrayMove} from 'react-sortable-hoc';
|
||||
|
||||
|
@ -55,14 +55,14 @@ class LayerListContainer extends React.Component {
|
|||
/>
|
||||
})
|
||||
return <ScrollContainer>
|
||||
<ul style={{ padding: theme.scale[1], margin: 0 }}>
|
||||
<ul style={{ padding: margins[1], margin: 0 }}>
|
||||
{layerPanels}
|
||||
</ul>
|
||||
</ScrollContainer>
|
||||
}
|
||||
}
|
||||
|
||||
export class LayerList extends React.Component {
|
||||
export default class LayerList extends React.Component {
|
||||
static propTypes = {...layerListPropTypes}
|
||||
|
||||
constructor(props) {
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react'
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin'
|
||||
import Radium from 'radium'
|
||||
import Immutable from 'immutable'
|
||||
import Color from 'color'
|
||||
|
@ -8,12 +9,12 @@ import Toolbar from 'rebass/dist/Toolbar'
|
|||
import NavItem from 'rebass/dist/NavItem'
|
||||
import Space from 'rebass/dist/Space'
|
||||
|
||||
import LayerIcon from '../icons/layer.jsx'
|
||||
import { LayerEditor } from './editor.jsx'
|
||||
import scrollbars from '../scrollbars.scss'
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import theme from '../theme.js'
|
||||
import {SortableElement, SortableHandle} from 'react-sortable-hoc';
|
||||
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
|
||||
|
@ -48,8 +49,8 @@ class LayerListItem extends React.Component {
|
|||
onClick={() => this.props.onLayerSelected(this.props.layerId)}
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
color: theme.colors.lowgray,
|
||||
fontSize: theme.fontSizes[5],
|
||||
color: colors.lowgray,
|
||||
fontSize: fontSizes[5],
|
||||
borderBottom: 1,
|
||||
borderLeft: 2,
|
||||
borderRight: 0,
|
||||
|
@ -59,11 +60,11 @@ class LayerListItem extends React.Component {
|
|||
zIndex: 2000,
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
padding: theme.scale[1],
|
||||
borderColor: Color(theme.colors.gray).lighten(0.1).string(),
|
||||
backgroundColor: theme.colors.gray,
|
||||
padding: margins[1],
|
||||
borderColor: Color(colors.gray).lighten(0.1).string(),
|
||||
backgroundColor: colors.gray,
|
||||
":hover": {
|
||||
backgroundColor: Color(theme.colors.gray).lighten(0.15).string(),
|
||||
backgroundColor: Color(colors.gray).lighten(0.15).string(),
|
||||
}
|
||||
}}>
|
||||
<LayerTypeDragHandle type={this.props.layerType} />
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react'
|
||||
import Immutable from 'immutable'
|
||||
import { PropertyGroup } from '../fields/spec'
|
||||
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 */
|
||||
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>
|
||||
}).toIndexedSeq()
|
||||
|
||||
console.log(this.props.sources)
|
||||
return <div>
|
||||
<div style={inputStyle.property}>
|
||||
<label style={inputStyle.label}>Source</label>
|
||||
<div style={input.property}>
|
||||
<label style={input.label}>Source</label>
|
||||
<select
|
||||
style={inputStyle.select}
|
||||
style={input.select}
|
||||
value={this.props.source}
|
||||
onChange={(e) => this.onSourceChange(e.target.value)}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
</div>
|
||||
<div style={inputStyle.property}>
|
||||
<label style={inputStyle.label}>Source Layer</label>
|
||||
<div style={input.property}>
|
||||
<label style={input.label}>Source Layer</label>
|
||||
<select
|
||||
style={inputStyle.select}
|
||||
style={input.select}
|
||||
value={this.props.sourceLayer}
|
||||
onChange={(e) => this.onSourceLayerChange(e.target.value)}
|
||||
>
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react'
|
||||
import { fullHeight } from './theme.js'
|
||||
import Immutable from 'immutable'
|
||||
|
||||
export class Map extends React.Component {
|
||||
export default class Map extends React.Component {
|
||||
static propTypes = {
|
||||
mapStyle: React.PropTypes.instanceOf(Immutable.Map).isRequired,
|
||||
accessToken: React.PropTypes.string,
|
||||
|
@ -17,7 +16,10 @@ export class Map extends React.Component {
|
|||
return <div
|
||||
ref={x => this.container = x}
|
||||
style={{
|
||||
...fullHeight,
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
}}></div>
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react'
|
||||
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'
|
||||
|
||||
export class MapboxGlMap extends Map {
|
||||
import Map from './Map.jsx'
|
||||
import style from '../../libs/style.js'
|
||||
|
||||
export default class MapboxGlMap extends Map {
|
||||
static propTypes = {
|
||||
onMapLoaded: React.PropTypes.func,
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react'
|
||||
import style from './style.js'
|
||||
import { Map } from './map.jsx'
|
||||
import ol from 'openlayers'
|
||||
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) {
|
||||
super(props)
|
||||
|
|
@ -14,8 +14,8 @@ import Input from 'rebass/dist/Input'
|
|||
import Toolbar from 'rebass/dist/Toolbar'
|
||||
import NavItem from 'rebass/dist/NavItem'
|
||||
|
||||
import publicTilesets from '../tilesets.json'
|
||||
import theme from '../theme.js'
|
||||
import publicTilesets from '../../config/tilesets.json'
|
||||
import theme from '../../config/rebass'
|
||||
|
||||
class TilesetsModal extends React.Component {
|
||||
static propTypes = {
|
29
src/config/colors.js
Normal file
29
src/config/colors.js
Normal 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
|
|
@ -1,6 +1,4 @@
|
|||
/** Common input styling */
|
||||
|
||||
import theme from '../theme.js'
|
||||
import theme from './rebass.js'
|
||||
|
||||
const base = {
|
||||
display: 'inline-block',
|
66
src/config/rebass.js
Normal file
66
src/config/rebass.js
Normal 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
2
src/config/scales.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const margins = [3, 5, 10, 30, 40]
|
||||
export const fontSizes = [26, 20, 16, 14, 12, 10]
|
|
@ -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
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import App from './app.jsx';
|
||||
import './index.css'
|
||||
import App from './components/App';
|
||||
|
||||
ReactDOM.render(<App/>, document.querySelector("#app"));
|
||||
|
|
|
@ -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>
|
||||
}
|
||||
}
|
115
src/theme.js
115
src/theme.js
|
@ -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
|
|
@ -32,15 +32,14 @@ module.exports = {
|
|||
'mapbox-gl',
|
||||
//TODO: Cannot resolve migrations file?
|
||||
//"mapbox-gl-style-spec",
|
||||
"radium",
|
||||
"randomcolor",
|
||||
'react',
|
||||
"react-collapse",
|
||||
"react-dom",
|
||||
"react-color",
|
||||
"react-file-reader-input",
|
||||
"react-height",
|
||||
//TODO: Icons raise multi vendor errors?
|
||||
//"react-icons",
|
||||
"react-motion",
|
||||
"rebass",
|
||||
// Open Layers
|
||||
'openlayers',
|
||||
|
|
Loading…
Reference in a new issue