Tidy of components

- Moved all components into a single directory like nextjs
 - Made component names consistent with each other
 - Made component names consistent with their export class names
 - Added storybook for a few components with the aim to extend this further.
This commit is contained in:
orangemug 2020-06-01 16:09:32 +01:00
parent d07b40ccef
commit 624ccb5b00
88 changed files with 5167 additions and 513 deletions

View file

@ -90,6 +90,7 @@ jobs:
${{ runner.os }}-node- ${{ runner.os }}-node-
- run: npm install - run: npm install
- run: npm run build - run: npm run build
- run: npm run build-storybook
- name: artifacts/editor - name: artifacts/editor
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
@ -101,6 +102,11 @@ jobs:
with: with:
name: editor-profiling name: editor-profiling
path: build/profiling path: build/profiling
- name: artifacts/storybook
uses: actions/upload-artifact@v1
with:
name: storybook
path: build/storybook
# build and test the editor in standalone-chrome # build and test the editor in standalone-chrome
test_selenium_standalone_chrome: test_selenium_standalone_chrome:

17
.storybook/main.js Normal file
View file

@ -0,0 +1,17 @@
const rules = require('../config/webpack.rules');
module.exports = {
stories: ['../stories/**/*.stories.js'],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
webpackFinal: async config => {
// do mutation to the config
return {
...config,
module: {
...config.module,
rules
}
};
},
};

7
.storybook/manager.js Normal file
View file

@ -0,0 +1,7 @@
import { addons } from '@storybook/addons';
import { themes } from '@storybook/theming';
import theme from './maputnik.theme';
addons.setConfig({
theme: theme,
});

View file

@ -0,0 +1,8 @@
import { create } from '@storybook/theming/create';
export default create({
base: 'light',
brandTitle: 'Maputnik',
brandUrl: 'https://github.com/maputnik/editor',
});

4510
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,9 @@
"start-prod": "webpack-dev-server --progress --profile --colors --config config/webpack.production.config.js", "start-prod": "webpack-dev-server --progress --profile --colors --config config/webpack.production.config.js",
"lint-js": "eslint --ext js --ext jsx src test", "lint-js": "eslint --ext js --ext jsx src test",
"lint-css": "stylelint \"src/styles/*.scss\"", "lint-css": "stylelint \"src/styles/*.scss\"",
"lint": "npm run lint-js && npm run lint-css" "lint": "npm run lint-js && npm run lint-css",
"storybook": "start-storybook -h 0.0.0.0 -p 6006",
"build-storybook": "build-storybook -o build/storybook"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -119,6 +121,11 @@
"@babel/preset-flow": "^7.0.0", "@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.6.3", "@babel/preset-react": "^7.6.3",
"@mdi/js": "^5.0.45", "@mdi/js": "^5.0.45",
"@storybook/addon-actions": "^5.3.19",
"@storybook/addon-links": "^5.3.19",
"@storybook/addons": "^5.3.19",
"@storybook/react": "^5.3.19",
"@storybook/theming": "^5.3.19",
"@wdio/cli": "^6.1.14", "@wdio/cli": "^6.1.14",
"@wdio/local-runner": "^6.1.14", "@wdio/local-runner": "^6.1.14",
"@wdio/mocha-framework": "^6.1.14", "@wdio/mocha-framework": "^6.1.14",

View file

@ -7,21 +7,21 @@ import {unset} from 'lodash'
import arrayMove from 'array-move' import arrayMove from 'array-move'
import url from 'url' import url from 'url'
import MapboxGlMap from './map/MapboxGlMap' import MapMapboxGl from './MapMapboxGl'
import OpenLayersMap from './map/OpenLayersMap' import MapOpenLayers from './MapOpenLayers'
import LayerList from './layers/LayerList' import LayerList from './LayerList'
import LayerEditor from './layers/LayerEditor' import LayerEditor from './LayerEditor'
import Toolbar from './Toolbar' import AppToolbar from './AppToolbar'
import AppLayout from './AppLayout' import AppLayout from './AppLayout'
import MessagePanel from './MessagePanel' import MessagePanel from './AppMessagePanel'
import SettingsModal from './modals/SettingsModal' import ModalSettings from './ModalSettings'
import ExportModal from './modals/ExportModal' import ModalExport from './ModalExport'
import SourcesModal from './modals/SourcesModal' import ModalSources from './ModalSources'
import OpenModal from './modals/OpenModal' import ModalOpen from './ModalOpen'
import ShortcutsModal from './modals/ShortcutsModal' import ModalShortcuts from './ModalShortcuts'
import SurveyModal from './modals/SurveyModal' import ModalSurvey from './ModalSurvey'
import DebugModal from './modals/DebugModal' import ModalDebug from './ModalDebug'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata' import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
import {latest, validate} from '@mapbox/mapbox-gl-style-spec' import {latest, validate} from '@mapbox/mapbox-gl-style-spec'
@ -36,7 +36,7 @@ import tokens from '../config/tokens.json'
import isEqual from 'lodash.isequal' import isEqual from 'lodash.isequal'
import Debug from '../libs/debug' import Debug from '../libs/debug'
import queryUtil from '../libs/query-util' import queryUtil from '../libs/query-util'
import {formatLayerId} from './util/format'; import {formatLayerId} from '../util/format';
import MapboxGl from 'mapbox-gl' import MapboxGl from 'mapbox-gl'
@ -668,14 +668,14 @@ export default class App extends React.Component {
// Check if OL code has been loaded? // Check if OL code has been loaded?
if(renderer === 'ol') { if(renderer === 'ol') {
mapElement = <OpenLayersMap mapElement = <MapOpenLayers
{...mapProps} {...mapProps}
onChange={this.onMapChange} onChange={this.onMapChange}
debugToolbox={this.state.openlayersDebugOptions.debugToolbox} debugToolbox={this.state.openlayersDebugOptions.debugToolbox}
onLayerSelect={this.onLayerSelect} onLayerSelect={this.onLayerSelect}
/> />
} else { } else {
mapElement = <MapboxGlMap {...mapProps} mapElement = <MapMapboxGl {...mapProps}
onChange={this.onMapChange} onChange={this.onMapChange}
options={this.state.mapboxGlDebugOptions} options={this.state.mapboxGlDebugOptions}
inspectModeEnabled={this.state.mapState === "inspect"} inspectModeEnabled={this.state.mapState === "inspect"}
@ -741,7 +741,7 @@ export default class App extends React.Component {
const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null
const metadata = this.state.mapStyle.metadata || {} const metadata = this.state.mapStyle.metadata || {}
const toolbar = <Toolbar const toolbar = <AppToolbar
renderer={this._getRenderer()} renderer={this._getRenderer()}
mapState={this.state.mapState} mapState={this.state.mapState}
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
@ -795,7 +795,7 @@ export default class App extends React.Component {
const modals = <div> const modals = <div>
<DebugModal <ModalDebug
renderer={this._getRenderer()} renderer={this._getRenderer()}
mapboxGlDebugOptions={this.state.mapboxGlDebugOptions} mapboxGlDebugOptions={this.state.mapboxGlDebugOptions}
openlayersDebugOptions={this.state.openlayersDebugOptions} openlayersDebugOptions={this.state.openlayersDebugOptions}
@ -805,12 +805,12 @@ export default class App extends React.Component {
onOpenToggle={this.toggleModal.bind(this, 'debug')} onOpenToggle={this.toggleModal.bind(this, 'debug')}
mapView={this.state.mapView} mapView={this.state.mapView}
/> />
<ShortcutsModal <ModalShortcuts
ref={(el) => this.shortcutEl = el} ref={(el) => this.shortcutEl = el}
isOpen={this.state.isOpen.shortcuts} isOpen={this.state.isOpen.shortcuts}
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')} onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
/> />
<SettingsModal <ModalSettings
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged} onStyleChanged={this.onStyleChanged}
onChangeMetadataProperty={this.onChangeMetadataProperty} onChangeMetadataProperty={this.onChangeMetadataProperty}
@ -818,24 +818,24 @@ export default class App extends React.Component {
onOpenToggle={this.toggleModal.bind(this, 'settings')} onOpenToggle={this.toggleModal.bind(this, 'settings')}
openlayersDebugOptions={this.state.openlayersDebugOptions} openlayersDebugOptions={this.state.openlayersDebugOptions}
/> />
<ExportModal <ModalExport
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged} onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.export} isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, 'export')} onOpenToggle={this.toggleModal.bind(this, 'export')}
/> />
<OpenModal <ModalOpen
isOpen={this.state.isOpen.open} isOpen={this.state.isOpen.open}
onStyleOpen={this.openStyle} onStyleOpen={this.openStyle}
onOpenToggle={this.toggleModal.bind(this, 'open')} onOpenToggle={this.toggleModal.bind(this, 'open')}
/> />
<SourcesModal <ModalSources
mapStyle={this.state.mapStyle} mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged} onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.sources} isOpen={this.state.isOpen.sources}
onOpenToggle={this.toggleModal.bind(this, 'sources')} onOpenToggle={this.toggleModal.bind(this, 'sources')}
/> />
<SurveyModal <ModalSurvey
isOpen={this.state.isOpen.survey} isOpen={this.state.isOpen.survey}
onOpenToggle={this.toggleModal.bind(this, 'survey')} onOpenToggle={this.toggleModal.bind(this, 'survey')}
/> />

View file

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {formatLayerId} from './util/format'; import {formatLayerId} from '../util/format';
class MessagePanel extends React.Component { export default class AppMessagePanel extends React.Component {
static propTypes = { static propTypes = {
errors: PropTypes.array, errors: PropTypes.array,
infos: PropTypes.array, infos: PropTypes.array,
@ -60,5 +60,3 @@ class MessagePanel extends React.Component {
} }
} }
export default MessagePanel

View file

@ -101,7 +101,7 @@ class ToolbarAction extends React.Component {
} }
} }
export default class Toolbar extends React.Component { export default class AppToolbar extends React.Component {
static propTypes = { static propTypes = {
mapStyle: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired,
inspectModeEnabled: PropTypes.bool.isRequired, inspectModeEnabled: PropTypes.bool.isRequired,

View file

@ -1,12 +1,12 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import DocLabel from '../fields/DocLabel' import FieldDocLabel from './FieldDocLabel'
import SpecDoc from './SpecDoc' import Doc from './Doc'
/** Wrap a component with a label */ /** Wrap a component with a label */
class InputBlock extends React.Component { export default class Block extends React.Component {
static propTypes = { static propTypes = {
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
label: PropTypes.oneOfType([ label: PropTypes.oneOfType([
@ -53,7 +53,7 @@ class InputBlock extends React.Component {
> >
{this.props.fieldSpec && {this.props.fieldSpec &&
<div className="maputnik-input-block-label"> <div className="maputnik-input-block-label">
<DocLabel <FieldDocLabel
label={this.props.label} label={this.props.label}
onToggleDoc={this.onToggleDoc} onToggleDoc={this.onToggleDoc}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
@ -85,11 +85,10 @@ class InputBlock extends React.Component {
className="maputnik-doc-inline" className="maputnik-doc-inline"
style={{display: this.state.showDoc ? '' : 'none'}} style={{display: this.state.showDoc ? '' : 'none'}}
> >
<SpecDoc fieldSpec={this.props.fieldSpec} /> <Doc fieldSpec={this.props.fieldSpec} />
</div> </div>
} }
</div> </div>
} }
} }
export default InputBlock

View file

@ -1,10 +1,10 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
class MetadataBlock extends React.Component { export default class BlockComment extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -15,19 +15,18 @@ class MetadataBlock extends React.Component {
doc: "Comments for the current layer. This is non-standard and not in the spec." doc: "Comments for the current layer. This is non-standard and not in the spec."
}; };
return <InputBlock return <Block
label={"Comments"} label={"Comments"}
fieldSpec={fieldSpec} fieldSpec={fieldSpec}
data-wd-key="layer-comment" data-wd-key="layer-comment"
> >
<StringInput <FieldString
multi={true} multi={true}
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
default="Comment..." default="Comment..."
/> />
</InputBlock> </Block>
} }
} }
export default MetadataBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
class LayerIdBlock extends React.Component { export default class BlockId extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
wdKey: PropTypes.string.isRequired, wdKey: PropTypes.string.isRequired,
@ -14,16 +14,15 @@ class LayerIdBlock extends React.Component {
} }
render() { render() {
return <InputBlock label={"ID"} fieldSpec={latest.layer.id} return <Block label={"ID"} fieldSpec={latest.layer.id}
data-wd-key={this.props.wdKey} data-wd-key={this.props.wdKey}
error={this.props.error} error={this.props.error}
> >
<StringInput <FieldString
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
/> />
</InputBlock> </Block>
} }
} }
export default LayerIdBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
class MaxZoomBlock extends React.Component { export default class BlockMaxZoom extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.number, value: PropTypes.number,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -13,11 +13,11 @@ class MaxZoomBlock extends React.Component {
} }
render() { render() {
return <InputBlock label={"Max Zoom"} fieldSpec={latest.layer.maxzoom} return <Block label={"Max Zoom"} fieldSpec={latest.layer.maxzoom}
error={this.props.error} error={this.props.error}
data-wd-key="max-zoom" data-wd-key="max-zoom"
> >
<NumberInput <FieldNumber
allowRange={true} allowRange={true}
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
@ -25,8 +25,7 @@ class MaxZoomBlock extends React.Component {
max={latest.layer.maxzoom.maximum} max={latest.layer.maxzoom.maximum}
default={latest.layer.maxzoom.maximum} default={latest.layer.maxzoom.maximum}
/> />
</InputBlock> </Block>
} }
} }
export default MaxZoomBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
class MinZoomBlock extends React.Component { export default class BlockMinZoom extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.number, value: PropTypes.number,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -13,11 +13,11 @@ class MinZoomBlock extends React.Component {
} }
render() { render() {
return <InputBlock label={"Min Zoom"} fieldSpec={latest.layer.minzoom} return <Block label={"Min Zoom"} fieldSpec={latest.layer.minzoom}
error={this.props.error} error={this.props.error}
data-wd-key="min-zoom" data-wd-key="min-zoom"
> >
<NumberInput <FieldNumber
allowRange={true} allowRange={true}
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
@ -25,8 +25,7 @@ class MinZoomBlock extends React.Component {
max={latest.layer.minzoom.maximum} max={latest.layer.minzoom.maximum}
default={latest.layer.minzoom.minimum} default={latest.layer.minzoom.minimum}
/> />
</InputBlock> </Block>
} }
} }
export default MinZoomBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import AutocompleteInput from '../inputs/AutocompleteInput' import FieldAutocomplete from './FieldAutocomplete'
class LayerSourceBlock extends React.Component { export default class BlockSource extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
wdKey: PropTypes.string, wdKey: PropTypes.string,
@ -20,19 +20,18 @@ class LayerSourceBlock extends React.Component {
} }
render() { render() {
return <InputBlock return <Block
label={"Source"} label={"Source"}
fieldSpec={latest.layer.source} fieldSpec={latest.layer.source}
error={this.props.error} error={this.props.error}
data-wd-key={this.props.wdKey} data-wd-key={this.props.wdKey}
> >
<AutocompleteInput <FieldAutocomplete
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
options={this.props.sourceIds.map(src => [src, src])} options={this.props.sourceIds.map(src => [src, src])}
/> />
</InputBlock> </Block>
} }
} }
export default LayerSourceBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import AutocompleteInput from '../inputs/AutocompleteInput' import FieldAutocomplete from './FieldAutocomplete'
class LayerSourceLayer extends React.Component { export default class BlockSourceLayer extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
@ -20,17 +20,16 @@ class LayerSourceLayer extends React.Component {
} }
render() { render() {
return <InputBlock label={"Source Layer"} fieldSpec={latest.layer['source-layer']} return <Block label={"Source Layer"} fieldSpec={latest.layer['source-layer']}
data-wd-key="layer-source-layer" data-wd-key="layer-source-layer"
> >
<AutocompleteInput <FieldAutocomplete
keepMenuWithinWindowBounds={!!this.props.isFixed} keepMenuWithinWindowBounds={!!this.props.isFixed}
value={this.props.value} value={this.props.value}
onChange={this.props.onChange} onChange={this.props.onChange}
options={this.props.sourceLayerIds.map(l => [l, l])} options={this.props.sourceLayerIds.map(l => [l, l])}
/> />
</InputBlock> </Block>
} }
} }
export default LayerSourceLayer

View file

@ -2,11 +2,11 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
class LayerTypeBlock extends React.Component { export default class BlockType extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
wdKey: PropTypes.string, wdKey: PropTypes.string,
@ -20,18 +20,18 @@ class LayerTypeBlock extends React.Component {
} }
render() { render() {
return <InputBlock label={"Type"} fieldSpec={latest.layer.type} return <Block label={"Type"} fieldSpec={latest.layer.type}
data-wd-key={this.props.wdKey} data-wd-key={this.props.wdKey}
error={this.props.error} error={this.props.error}
> >
{this.props.disabled && {this.props.disabled &&
<StringInput <FieldString
value={this.props.value} value={this.props.value}
disabled={true} disabled={true}
/> />
} }
{!this.props.disabled && {!this.props.disabled &&
<SelectInput <FieldSelect
options={[ options={[
['background', 'Background'], ['background', 'Background'],
['fill', 'Fill'], ['fill', 'Fill'],
@ -47,8 +47,7 @@ class LayerTypeBlock extends React.Component {
value={this.props.value} value={this.props.value}
/> />
} }
</InputBlock> </Block>
} }
} }
export default LayerTypeBlock

View file

@ -1,10 +1,10 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Collapse from 'react-collapse' import Collapse as ReactCollapse from 'react-collapse'
import accessibility from '../../libs/accessibility' import accessibility from '../../libs/accessibility'
export default class CollapseAlt extends React.Component { export default class Collapse extends React.Component {
static propTypes = { static propTypes = {
isActive: PropTypes.bool.isRequired, isActive: PropTypes.bool.isRequired,
children: PropTypes.element.isRequired children: PropTypes.element.isRequired
@ -24,9 +24,9 @@ export default class CollapseAlt extends React.Component {
} }
else { else {
return ( return (
<Collapse isOpened={this.props.isActive}> <ReactCollapse isOpened={this.props.isActive}>
{this.props.children} {this.props.children}
</Collapse> </ReactCollapse>
) )
} }
} }

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
export default class SpecDoc extends React.Component { export default class Doc extends React.Component {
static propTypes = { static propTypes = {
fieldSpec: PropTypes.object.isRequired, fieldSpec: PropTypes.object.isRequired,
} }

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import StringInput from './StringInput' import FieldString from './FieldString'
import NumberInput from './NumberInput' import FieldNumber from './FieldNumber'
class ArrayInput extends React.Component { export default class FieldArray extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.array, value: PropTypes.array,
type: PropTypes.string, type: PropTypes.string,
@ -82,7 +82,7 @@ class ArrayInput extends React.Component {
const inputs = Array(this.props.length).fill(null).map((_, i) => { const inputs = Array(this.props.length).fill(null).map((_, i) => {
if(this.props.type === 'number') { if(this.props.type === 'number') {
return <NumberInput return <FieldNumber
key={i} key={i}
default={containsValues ? undefined : this.props.default[i]} default={containsValues ? undefined : this.props.default[i]}
value={value[i]} value={value[i]}
@ -90,7 +90,7 @@ class ArrayInput extends React.Component {
onChange={this.changeValue.bind(this, i)} onChange={this.changeValue.bind(this, i)}
/> />
} else { } else {
return <StringInput return <FieldString
key={i} key={i}
default={containsValues ? undefined : this.props.default[i]} default={containsValues ? undefined : this.props.default[i]}
value={value[i]} value={value[i]}
@ -106,4 +106,3 @@ class ArrayInput extends React.Component {
} }
} }
export default ArrayInput

View file

@ -6,7 +6,7 @@ import Autocomplete from 'react-autocomplete'
const MAX_HEIGHT = 140; const MAX_HEIGHT = 140;
class AutocompleteInput extends React.Component { export default class FieldAutocomplete extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
options: PropTypes.array, options: PropTypes.array,
@ -95,4 +95,3 @@ class AutocompleteInput extends React.Component {
} }
} }
export default AutocompleteInput

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
class CheckboxInput extends React.Component { export default class FieldCheckbox extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.bool, value: PropTypes.bool,
style: PropTypes.object, style: PropTypes.object,
@ -32,4 +32,3 @@ class CheckboxInput extends React.Component {
} }
} }
export default CheckboxInput

View file

@ -10,7 +10,7 @@ function formatColor(color) {
} }
/*** Number fields with support for min, max and units and documentation*/ /*** Number fields with support for min, max and units and documentation*/
class ColorField extends React.Component { export default class FieldColor extends React.Component {
static propTypes = { static propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
name: PropTypes.string, name: PropTypes.string,
@ -130,4 +130,3 @@ class ColorField extends React.Component {
} }
} }
export default ColorField

View file

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import {MdInfoOutline, MdHighlightOff} from 'react-icons/md' import {MdInfoOutline, MdHighlightOff} from 'react-icons/md'
export default class DocLabel extends React.Component { export default class FieldDocLabel extends React.Component {
static propTypes = { static propTypes = {
label: PropTypes.oneOfType([ label: PropTypes.oneOfType([
PropTypes.object, PropTypes.object,

View file

@ -1,16 +1,16 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import StringInput from './StringInput' import FieldString from './FieldString'
import NumberInput from './NumberInput' import FieldNumber from './FieldNumber'
import Button from '../Button' import Button from './Button'
import {MdDelete} from 'react-icons/md' import {MdDelete} from 'react-icons/md'
import DocLabel from '../fields/DocLabel' import FieldDocLabel from './FieldDocLabel'
import EnumInput from '../inputs/SelectInput' import FieldEnum from './FieldEnum'
import capitalize from 'lodash.capitalize' import capitalize from 'lodash.capitalize'
import UrlInput from '../inputs/UrlInput' import FieldUrl from './FieldUrl'
class DynamicArrayInput extends React.Component { export default class FieldDynamicArray extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.array, value: PropTypes.array,
type: PropTypes.string, type: PropTypes.string,
@ -62,13 +62,13 @@ class DynamicArrayInput extends React.Component {
const deleteValueBtn= <DeleteValueButton onClick={this.deleteValue.bind(this, i)} /> const deleteValueBtn= <DeleteValueButton onClick={this.deleteValue.bind(this, i)} />
let input; let input;
if(this.props.type === 'url') { if(this.props.type === 'url') {
input = <UrlInput input = <FieldUrl
value={v} value={v}
onChange={this.changeValue.bind(this, i)} onChange={this.changeValue.bind(this, i)}
/> />
} }
else if (this.props.type === 'number') { else if (this.props.type === 'number') {
input = <NumberInput input = <FieldNumber
value={v} value={v}
onChange={this.changeValue.bind(this, i)} onChange={this.changeValue.bind(this, i)}
/> />
@ -76,14 +76,14 @@ class DynamicArrayInput extends React.Component {
else if (this.props.type === 'enum') { else if (this.props.type === 'enum') {
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)]); const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)]);
input = <EnumInput input = <FieldEnum
options={options} options={options}
value={v} value={v}
onChange={this.changeValue.bind(this, i)} onChange={this.changeValue.bind(this, i)}
/> />
} }
else { else {
input = <StringInput input = <FieldString
value={v} value={v}
onChange={this.changeValue.bind(this, i)} onChange={this.changeValue.bind(this, i)}
/> />
@ -126,7 +126,7 @@ class DeleteValueButton extends React.Component {
onClick={this.props.onClick} onClick={this.props.onClick}
title="Remove array item" title="Remove array item"
> >
<DocLabel <FieldDocLabel
label={<MdDelete />} label={<MdDelete />}
doc={"Remove array item."} doc={"Remove array item."}
/> />
@ -134,4 +134,3 @@ class DeleteValueButton extends React.Component {
} }
} }
export default DynamicArrayInput

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import MultiButtonInput from '../inputs/MultiButtonInput' import FieldMultiInput from './FieldMultiInput'
function optionsLabelLength(options) { function optionsLabelLength(options) {
@ -13,7 +13,7 @@ function optionsLabelLength(options) {
} }
class EnumInput extends React.Component { export default class FieldEnum extends React.Component {
static propTypes = { static propTypes = {
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
@ -28,14 +28,14 @@ class EnumInput extends React.Component {
const {options, value, onChange, name} = this.props; const {options, value, onChange, name} = this.props;
if(options.length <= 3 && optionsLabelLength(options) <= 20) { if(options.length <= 3 && optionsLabelLength(options) <= 20) {
return <MultiButtonInput return <FieldMultiInput
name={name} name={name}
options={options} options={options}
value={value || this.props.default} value={value || this.props.default}
onChange={onChange} onChange={onChange}
/> />
} else { } else {
return <SelectInput return <FieldSelect
options={options} options={options}
value={value || this.props.default} value={value || this.props.default}
onChange={onChange} onChange={onChange}
@ -43,5 +43,3 @@ class EnumInput extends React.Component {
} }
} }
} }
export default EnumInput

View file

@ -1,8 +1,8 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import AutocompleteInput from './AutocompleteInput' import FieldAutocomplete from './FieldAutocomplete'
class FontInput extends React.Component { export default class FieldFont extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.array, value: PropTypes.array,
default: PropTypes.array, default: PropTypes.array,
@ -39,7 +39,7 @@ class FontInput extends React.Component {
render() { render() {
const inputs = this.values.map((value, i) => { const inputs = this.values.map((value, i) => {
return <AutocompleteInput return <FieldAutocomplete
key={i} key={i}
value={value} value={value}
options={this.props.fonts.map(f => [f, f])} options={this.props.fonts.map(f => [f, f])}
@ -53,4 +53,3 @@ class FontInput extends React.Component {
} }
} }
export default FontInput

View file

@ -106,7 +106,7 @@ function getDataType (value, fieldSpec={}) {
/** Supports displaying spec field for zoom function objects /** Supports displaying spec field for zoom function objects
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property * https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
*/ */
export default class FunctionSpecProperty extends React.Component { export default class FieldFunction extends React.Component {
static propTypes = { static propTypes = {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
fieldName: PropTypes.string.isRequired, fieldName: PropTypes.string.isRequired,

View file

@ -2,8 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames'; import classnames from 'classnames';
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import CodeMirror from 'codemirror'; import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript' import 'codemirror/mode/javascript/javascript'
@ -16,7 +16,7 @@ import stringifyPretty from 'json-stringify-pretty-compact'
import '../util/codemirror-mgl'; import '../util/codemirror-mgl';
class JSONEditor extends React.Component { export default class FieldJsonEditor extends React.Component {
static propTypes = { static propTypes = {
layer: PropTypes.any.isRequired, layer: PropTypes.any.isRequired,
maxHeight: PropTypes.number, maxHeight: PropTypes.number,
@ -173,4 +173,3 @@ class JSONEditor extends React.Component {
} }
} }
export default JSONEditor

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import classnames from 'classnames' import classnames from 'classnames'
import Button from '../Button' import Button from './Button'
class MultiButtonInput extends React.Component { export default class FieldMultiInput extends React.Component {
static propTypes = { static propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
@ -39,4 +39,3 @@ class MultiButtonInput extends React.Component {
} }
} }
export default MultiButtonInput

View file

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
let IDX = 0; let IDX = 0;
class NumberInput extends React.Component { export default class FieldNumber extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.number, value: PropTypes.number,
default: PropTypes.number, default: PropTypes.number,
@ -230,4 +230,3 @@ class NumberInput extends React.Component {
} }
} }
export default NumberInput

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
class SelectInput extends React.Component { export default class FieldSelect extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
@ -31,4 +31,3 @@ class SelectInput extends React.Component {
} }
} }
export default SelectInput

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
class StringInput extends React.Component { export default class FieldString extends React.Component {
static propTypes = { static propTypes = {
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
@ -90,4 +90,3 @@ class StringInput extends React.Component {
} }
} }
export default StringInput

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import AutocompleteInput from './AutocompleteInput' import FieldAutocomplete from './FieldAutocomplete'
class IconInput extends React.Component { export default class FieldSymbol extends React.Component {
static propTypes = { static propTypes = {
value: PropTypes.string, value: PropTypes.string,
icons: PropTypes.array, icons: PropTypes.array,
@ -16,7 +16,7 @@ class IconInput extends React.Component {
} }
render() { render() {
return <AutocompleteInput return <FieldAutocomplete
value={this.props.value} value={this.props.value}
options={this.props.icons.map(f => [f, f])} options={this.props.icons.map(f => [f, f])}
onChange={this.props.onChange} onChange={this.props.onChange}
@ -25,4 +25,3 @@ class IconInput extends React.Component {
} }
} }
export default IconInput

View file

@ -1,7 +1,7 @@
import React, {Fragment} from 'react' import React, {Fragment} from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import StringInput from './StringInput' import FieldString from './FieldString'
import SmallError from '../util/SmallError' import SmallError from './SmallError'
function validate (url) { function validate (url) {
@ -48,7 +48,7 @@ function validate (url) {
return error; return error;
} }
class UrlInput extends React.Component { export default class FieldUrl extends React.Component {
static propTypes = { static propTypes = {
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
@ -88,7 +88,7 @@ class UrlInput extends React.Component {
render () { render () {
return ( return (
<div> <div>
<StringInput <FieldString
{...this.props} {...this.props}
onInput={this.onInput} onInput={this.onInput}
onChange={this.onChange} onChange={this.onChange}
@ -99,4 +99,3 @@ class UrlInput extends React.Component {
} }
} }
export default UrlInput

View file

@ -1,17 +1,16 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { combiningFilterOps } from '../../libs/filterops.js' import { combiningFilterOps } from '../libs/filterops.js'
import {mdiTableRowPlusAfter} from '@mdi/js'; import {mdiTableRowPlusAfter} from '@mdi/js';
import {latest, validate, migrate} from '@mapbox/mapbox-gl-style-spec' import {latest, validate, migrate} from '@mapbox/mapbox-gl-style-spec'
import DocLabel from '../fields/DocLabel' import FieldSelect from './FieldSelect'
import SelectInput from '../inputs/SelectInput' import Block from './Block'
import InputBlock from '../inputs/InputBlock'
import SingleFilterEditor from './SingleFilterEditor' import SingleFilterEditor from './SingleFilterEditor'
import FilterEditorBlock from './FilterEditorBlock' import FilterEditorBlock from './FilterEditorBlock'
import Button from '../Button' import Button from './Button'
import SpecDoc from '../inputs/SpecDoc' import Doc from './Doc'
import ExpressionProperty from '../fields/_ExpressionProperty'; import ExpressionProperty from './_ExpressionProperty';
import {mdiFunctionVariant} from '@mdi/js'; import {mdiFunctionVariant} from '@mdi/js';
@ -94,7 +93,7 @@ function hasNestedCombiningFilter(filter) {
return false return false
} }
export default class CombiningFilterEditor extends React.Component { export default class FilterEditor extends React.Component {
static propTypes = { static propTypes = {
/** Properties of the vector layer and the available fields */ /** Properties of the vector layer and the available fields */
properties: PropTypes.object, properties: PropTypes.object,
@ -244,18 +243,18 @@ export default class CombiningFilterEditor extends React.Component {
return ( return (
<> <>
<InputBlock <Block
key="top" key="top"
fieldSpec={fieldSpec} fieldSpec={fieldSpec}
label={"Filter"} label={"Filter"}
action={actions} action={actions}
> >
<SelectInput <FieldSelect
value={combiningOp} value={combiningOp}
onChange={this.onFilterPartChanged.bind(this, 0)} onChange={this.onFilterPartChanged.bind(this, 0)}
options={[["all", "every filter matches"], ["none", "no filter matches"], ["any", "any filter matches"]]} options={[["all", "every filter matches"], ["none", "no filter matches"], ["any", "any filter matches"]]}
/> />
</InputBlock> </Block>
{editorBlocks} {editorBlocks}
<div <div
key="buttons" key="buttons"
@ -276,7 +275,7 @@ export default class CombiningFilterEditor extends React.Component {
className="maputnik-doc-inline" className="maputnik-doc-inline"
style={{display: this.state.showDoc ? '' : 'none'}} style={{display: this.state.showDoc ? '' : 'none'}}
> >
<SpecDoc fieldSpec={fieldSpec} /> <Doc fieldSpec={fieldSpec} />
</div> </div>
</> </>
); );

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import {MdDelete} from 'react-icons/md' import {MdDelete} from 'react-icons/md'
class FilterEditorBlock extends React.Component { export default class FilterEditorBlock extends React.Component {
static propTypes = { static propTypes = {
onDelete: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired,
children: PropTypes.element.isRequired, children: PropTypes.element.isRequired,
@ -27,4 +27,3 @@ class FilterEditorBlock extends React.Component {
} }
} }
export default FilterEditorBlock

View file

@ -2,7 +2,7 @@ import React from 'react'
import IconBase from 'react-icon-base' import IconBase from 'react-icon-base'
export default class BackgroundIcon extends React.Component { export default class IconBackground extends React.Component {
render() { render() {
return ( return (
<IconBase viewBox="0 0 20 20" {...this.props}> <IconBase viewBox="0 0 20 20" {...this.props}>

View file

@ -2,7 +2,7 @@ import React from 'react'
import IconBase from 'react-icon-base' import IconBase from 'react-icon-base'
export default class FillIcon extends React.Component { export default class IconCircle extends React.Component {
render() { render() {
return ( return (
<IconBase viewBox="0 0 20 20" {...this.props}> <IconBase viewBox="0 0 20 20" {...this.props}>

View file

@ -2,7 +2,7 @@ import React from 'react'
import IconBase from 'react-icon-base' import IconBase from 'react-icon-base'
export default class FillIcon extends React.Component { export default class IconFill extends React.Component {
render() { render() {
return ( return (
<IconBase viewBox="0 0 20 20" {...this.props}> <IconBase viewBox="0 0 20 20" {...this.props}>

View file

@ -0,0 +1,33 @@
import React from 'react'
import PropTypes from 'prop-types'
import IconLine from './IconLine.jsx'
import IconFill from './IconFill.jsx'
import IconSymbol from './IconSymbol.jsx'
import IconBackground from './IconBackground.jsx'
import IconCircle from './IconCircle.jsx'
import IconMissing from './IconMissing.jsx'
export default class IconLayer extends React.Component {
static propTypes = {
type: PropTypes.string.isRequired,
style: PropTypes.object,
}
render() {
const iconProps = { style: this.props.style }
switch(this.props.type) {
case 'fill-extrusion': return <IconBackground {...iconProps} />
case 'raster': return <IconFill {...iconProps} />
case 'hillshade': return <IconFill {...iconProps} />
case 'heatmap': return <IconFill {...iconProps} />
case 'fill': return <IconFill {...iconProps} />
case 'background': return <IconBackground {...iconProps} />
case 'line': return <IconLine {...iconProps} />
case 'symbol': return <IconSymbol {...iconProps} />
case 'circle': return <IconCircle {...iconProps} />
default: return <IconMissing {...iconProps} />
}
}
}

View file

@ -2,7 +2,7 @@ import React from 'react'
import IconBase from 'react-icon-base' import IconBase from 'react-icon-base'
export default class FillIcon extends React.Component { export default class IconLine extends React.Component {
render() { render() {
return ( return (
<IconBase viewBox="0 0 20 20" {...this.props}> <IconBase viewBox="0 0 20 20" {...this.props}>

View file

@ -2,7 +2,7 @@ import React from 'react'
import {MdPriorityHigh} from 'react-icons/md' import {MdPriorityHigh} from 'react-icons/md'
export default class MissingIcon extends React.Component { export default class IconMissing extends React.Component {
render() { render() {
return ( return (
<MdPriorityHigh {...this.props} /> <MdPriorityHigh {...this.props} />

View file

@ -2,7 +2,7 @@ import React from 'react'
import IconBase from 'react-icon-base' import IconBase from 'react-icon-base'
export default class SymbolIcon extends React.Component { export default class IconSymbol extends React.Component {
render() { render() {
return ( return (
<IconBase viewBox="0 0 20 20" {...this.props}> <IconBase viewBox="0 0 20 20" {...this.props}>

View file

@ -2,23 +2,23 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton' import { Wrapper, Button, Menu, MenuItem } from 'react-aria-menubutton'
import JSONEditor from './JSONEditor' import FieldJsonEditor from './FieldJsonEditor'
import FilterEditor from '../filter/FilterEditor' import FilterEditor from './FilterEditor'
import PropertyGroup from '../fields/PropertyGroup' import PropertyGroup from './PropertyGroup'
import LayerEditorGroup from './LayerEditorGroup' import LayerEditorGroup from './LayerEditorGroup'
import LayerTypeBlock from './LayerTypeBlock' import BlockType from './BlockType'
import LayerIdBlock from './LayerIdBlock' import BlockId from './BlockId'
import MinZoomBlock from './MinZoomBlock' import BlockMinZoom from './BlockMinZoom'
import MaxZoomBlock from './MaxZoomBlock' import BlockMaxZoom from './BlockMaxZoom'
import CommentBlock from './CommentBlock' import BlockComment from './BlockComment'
import LayerSourceBlock from './LayerSourceBlock' import BlockSource from './BlockSource'
import LayerSourceLayerBlock from './LayerSourceLayerBlock' import BlockSourceLayer from './BlockSourceLayer'
import {Accordion} from 'react-accessible-accordion'; import {Accordion} from 'react-accessible-accordion';
import {MdMoreVert} from 'react-icons/md' import {MdMoreVert} from 'react-icons/md'
import { changeType, changeProperty } from '../../libs/layer' import { changeType, changeProperty } from '../libs/layer'
import layout from '../../config/layout.json' import layout from '../config/layout.json'
import {formatLayerId} from '../util/format'; import {formatLayerId} from '../util/format';
@ -152,13 +152,13 @@ export default class LayerEditor extends React.Component {
switch(type) { switch(type) {
case 'layer': return <div> case 'layer': return <div>
<LayerIdBlock <BlockId
value={this.props.layer.id} value={this.props.layer.id}
wdKey="layer-editor.layer-id" wdKey="layer-editor.layer-id"
error={errorData.id} error={errorData.id}
onChange={newId => this.props.onLayerIdChange(this.props.layerIndex, this.props.layer.id, newId)} onChange={newId => this.props.onLayerIdChange(this.props.layerIndex, this.props.layer.id, newId)}
/> />
<LayerTypeBlock <BlockType
disabled={true} disabled={true}
error={errorData.type} error={errorData.type}
value={this.props.layer.type} value={this.props.layer.type}
@ -167,7 +167,7 @@ export default class LayerEditor extends React.Component {
changeType(this.props.layer, newType) changeType(this.props.layer, newType)
)} )}
/> />
{this.props.layer.type !== 'background' && <LayerSourceBlock {this.props.layer.type !== 'background' && <BlockSource
error={errorData.source} error={errorData.source}
sourceIds={Object.keys(this.props.sources)} sourceIds={Object.keys(this.props.sources)}
value={this.props.layer.source} value={this.props.layer.source}
@ -175,24 +175,24 @@ export default class LayerEditor extends React.Component {
/> />
} }
{['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.props.layer.type) < 0 && {['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.props.layer.type) < 0 &&
<LayerSourceLayerBlock <BlockSourceLayer
error={errorData['source-layer']} error={errorData['source-layer']}
sourceLayerIds={sourceLayerIds} sourceLayerIds={sourceLayerIds}
value={this.props.layer['source-layer']} value={this.props.layer['source-layer']}
onChange={v => this.changeProperty(null, 'source-layer', v)} onChange={v => this.changeProperty(null, 'source-layer', v)}
/> />
} }
<MinZoomBlock <BlockMinZoom
error={errorData.minzoom} error={errorData.minzoom}
value={this.props.layer.minzoom} value={this.props.layer.minzoom}
onChange={v => this.changeProperty(null, 'minzoom', v)} onChange={v => this.changeProperty(null, 'minzoom', v)}
/> />
<MaxZoomBlock <BlockMaxZoom
error={errorData.maxzoom} error={errorData.maxzoom}
value={this.props.layer.maxzoom} value={this.props.layer.maxzoom}
onChange={v => this.changeProperty(null, 'maxzoom', v)} onChange={v => this.changeProperty(null, 'maxzoom', v)}
/> />
<CommentBlock <BlockComment
error={errorData.comment} error={errorData.comment}
value={comment} value={comment}
onChange={v => this.changeProperty('metadata', 'maputnik:comment', v == "" ? undefined : v)} onChange={v => this.changeProperty('metadata', 'maputnik:comment', v == "" ? undefined : v)}
@ -215,7 +215,7 @@ export default class LayerEditor extends React.Component {
spec={this.props.spec} spec={this.props.spec}
onChange={this.changeProperty.bind(this)} onChange={this.changeProperty.bind(this)}
/> />
case 'jsoneditor': return <JSONEditor case 'jsoneditor': return <FieldJsonEditor
layer={this.props.layer} layer={this.props.layer}
onChange={(layer) => { onChange={(layer) => {
this.props.onLayerChanged( this.props.onLayerChanged(

View file

@ -5,7 +5,7 @@ import lodash from 'lodash';
import LayerListGroup from './LayerListGroup' import LayerListGroup from './LayerListGroup'
import LayerListItem from './LayerListItem' import LayerListItem from './LayerListItem'
import AddModal from '../modals/AddModal' import ModalAdd from './ModalAdd'
import {SortableContainer} from 'react-sortable-hoc'; import {SortableContainer} from 'react-sortable-hoc';
@ -270,7 +270,7 @@ class LayerListContainer extends React.Component {
aria-label="Layers list" aria-label="Layers list"
ref={this.scrollContainerRef} ref={this.scrollContainerRef}
> >
<AddModal <ModalAdd
key={this.state.keys.add} key={this.state.keys.add}
layers={this.props.layers} layers={this.props.layers}
sources={this.props.sources} sources={this.props.sources}

View file

@ -4,13 +4,13 @@ import classnames from 'classnames'
import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icons/md' import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icons/md'
import LayerIcon from '../icons/LayerIcon' import IconLayer from './IconLayer'
import {SortableElement, SortableHandle} from 'react-sortable-hoc' import {SortableElement, SortableHandle} from 'react-sortable-hoc'
const DraggableLabel = SortableHandle((props) => { const DraggableLabel = SortableHandle((props) => {
return <div className="maputnik-layer-list-item-handle"> return <div className="maputnik-layer-list-item-handle">
<LayerIcon <IconLayer
className="layer-handle__icon" className="layer-handle__icon"
type={props.layerType} type={props.layerType}
/> />

View file

@ -3,16 +3,16 @@ import PropTypes from 'prop-types'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import MapboxGl from 'mapbox-gl' import MapboxGl from 'mapbox-gl'
import MapboxInspect from 'mapbox-gl-inspect' import MapboxInspect from 'mapbox-gl-inspect'
import FeatureLayerPopup from './FeatureLayerPopup' import MapMapboxGlLayerPopup from './MapMapboxGlLayerPopup'
import FeaturePropertyPopup from './FeaturePropertyPopup' import MapMapboxGlFeaturePropertyPopup from './MapMapboxGlFeaturePropertyPopup'
import tokens from '../../config/tokens.json' import tokens from '../config/tokens.json'
import colors from 'mapbox-gl-inspect/lib/colors' import colors from 'mapbox-gl-inspect/lib/colors'
import Color from 'color' import Color from 'color'
import ZoomControl from '../../libs/zoomcontrol' import ZoomControl from '../libs/zoomcontrol'
import { colorHighlightedLayer } from '../../libs/highlight' import { colorHighlightedLayer } from '../libs/highlight'
import 'mapbox-gl/dist/mapbox-gl.css' import 'mapbox-gl/dist/mapbox-gl.css'
import '../../mapboxgl.css' import '../mapboxgl.css'
import '../../libs/mapbox-rtl' import '../libs/mapbox-rtl'
const IS_SUPPORTED = MapboxGl.supported(); const IS_SUPPORTED = MapboxGl.supported();
@ -52,7 +52,7 @@ function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
return inspectStyle return inspectStyle
} }
export default class MapboxGlMap extends React.Component { export default class MapMapboxGl extends React.Component {
static propTypes = { static propTypes = {
onDataChange: PropTypes.func, onDataChange: PropTypes.func,
onLayerSelect: PropTypes.func.isRequired, onLayerSelect: PropTypes.func.isRequired,
@ -175,9 +175,9 @@ export default class MapboxGlMap extends React.Component {
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer), buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
renderPopup: features => { renderPopup: features => {
if(this.props.inspectModeEnabled) { if(this.props.inspectModeEnabled) {
return renderPopup(<FeaturePropertyPopup features={features} />, tmpNode); return renderPopup(<MapMapboxGlFeaturePropertyPopup features={features} />, tmpNode);
} else { } else {
return renderPopup(<FeatureLayerPopup features={features} onLayerSelect={this.onLayerSelectById} zoom={this.state.zoom} />, tmpNode); return renderPopup(<MapMapboxGlLayerPopup features={features} onLayerSelect={this.onLayerSelectById} zoom={this.state.zoom} />, tmpNode);
} }
} }
}) })

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
function displayValue(value) { function displayValue(value) {
if (typeof value === 'undefined' || value === null) return value; if (typeof value === 'undefined' || value === null) return value;
@ -15,24 +15,24 @@ function displayValue(value) {
function renderProperties(feature) { function renderProperties(feature) {
return Object.keys(feature.properties).map(propertyName => { return Object.keys(feature.properties).map(propertyName => {
const property = feature.properties[propertyName] const property = feature.properties[propertyName]
return <InputBlock key={propertyName} label={propertyName}> return <Block key={propertyName} label={propertyName}>
<StringInput value={displayValue(property)} style={{backgroundColor: 'transparent'}}/> <FieldString value={displayValue(property)} style={{backgroundColor: 'transparent'}}/>
</InputBlock> </Block>
}) })
} }
function renderFeatureId(feature) { function renderFeatureId(feature) {
return <InputBlock key={"feature-id"} label={"feature_id"}> return <Block key={"feature-id"} label={"feature_id"}>
<StringInput value={displayValue(feature.id)} style={{backgroundColor: 'transparent'}} /> <FieldString value={displayValue(feature.id)} style={{backgroundColor: 'transparent'}} />
</InputBlock> </Block>
} }
function renderFeature(feature, idx) { function renderFeature(feature, idx) {
return <div key={`${feature.sourceLayer}-${idx}`}> return <div key={`${feature.sourceLayer}-${idx}`}>
<div className="maputnik-popup-layer-id">{feature.layer['source']}: {feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</div> <div className="maputnik-popup-layer-id">{feature.layer['source']}: {feature.layer['source-layer']}{feature.inspectModeCounter && <span> × {feature.inspectModeCounter}</span>}</div>
<InputBlock key={"property-type"} label={"$type"}> <Block key={"property-type"} label={"$type"}>
<StringInput value={feature.geometry.type} style={{backgroundColor: 'transparent'}} /> <FieldString value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
</InputBlock> </Block>
{renderFeatureId(feature)} {renderFeatureId(feature)}
{renderProperties(feature)} {renderProperties(feature)}
</div> </div>

View file

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import LayerIcon from '../icons/LayerIcon' import IconLayer from './IconLayer'
import {latest, expression, function as styleFunction} from '@mapbox/mapbox-gl-style-spec' import {latest, expression, function as styleFunction} from '@mapbox/mapbox-gl-style-spec'
function groupFeaturesBySourceLayer(features) { function groupFeaturesBySourceLayer(features) {
@ -96,7 +96,7 @@ class FeatureLayerPopup extends React.Component {
}} }}
> >
{feature.layer.type && {feature.layer.type &&
<LayerIcon type={feature.layer.type} style={{ <IconLayer type={feature.layer.type} style={{
width: 14, width: 14,
height: 14, height: 14,
paddingRight: 3 paddingRight: 3

View file

@ -1,9 +1,9 @@
import React from 'react' import React from 'react'
import {throttle} from 'lodash'; import {throttle} from 'lodash';
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { loadJSON } from '../../libs/urlopen' import { loadJSON } from '../libs/urlopen'
import FeatureLayerPopup from './FeatureLayerPopup'; import MapMapboxGlLayerPopup from './MapMapboxGlLayerPopup';
import 'ol/ol.css' import 'ol/ol.css'
import {apply} from 'ol-mapbox-style'; import {apply} from 'ol-mapbox-style';
@ -24,7 +24,7 @@ function renderCoords (coords) {
} }
} }
export default class OpenLayersMap extends React.Component { export default class MapOpenLayers extends React.Component {
static propTypes = { static propTypes = {
onDataChange: PropTypes.func, onDataChange: PropTypes.func,
mapStyle: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired,
@ -152,7 +152,7 @@ export default class OpenLayersMap extends React.Component {
> >
× ×
</button> </button>
<FeatureLayerPopup <MapMapboxGlLayerPopup
features={this.state.selectedFeatures || []} features={this.state.selectedFeatures || []}
onLayerSelect={this.props.onLayerSelect} onLayerSelect={this.props.onLayerSelect}
/> />

View file

@ -5,7 +5,7 @@ import AriaModal from 'react-aria-modal'
import classnames from 'classnames'; import classnames from 'classnames';
class Modal extends React.Component { export default class Modal extends React.Component {
static propTypes = { static propTypes = {
"data-wd-key": PropTypes.string, "data-wd-key": PropTypes.string,
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
@ -73,4 +73,3 @@ class Modal extends React.Component {
} }
} }
export default Modal

View file

@ -1,15 +1,15 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import Modal from './Modal' import Modal from './Modal'
import LayerTypeBlock from '../layers/LayerTypeBlock' import BlockType from './BlockType'
import LayerIdBlock from '../layers/LayerIdBlock' import BlockId from './BlockId'
import LayerSourceBlock from '../layers/LayerSourceBlock' import BlockSource from './BlockSource'
import LayerSourceLayerBlock from '../layers/LayerSourceLayerBlock' import BlockSourceLayer from './BlockSourceLayer'
class AddModal extends React.Component { export default class ModalAdd extends React.Component {
static propTypes = { static propTypes = {
layers: PropTypes.array.isRequired, layers: PropTypes.array.isRequired,
onLayersChange: PropTypes.func.isRequired, onLayersChange: PropTypes.func.isRequired,
@ -129,20 +129,20 @@ class AddModal extends React.Component {
className="maputnik-add-modal" className="maputnik-add-modal"
> >
<div className="maputnik-add-layer"> <div className="maputnik-add-layer">
<LayerIdBlock <BlockId
value={this.state.id} value={this.state.id}
wdKey="add-layer.layer-id" wdKey="add-layer.layer-id"
onChange={v => { onChange={v => {
this.setState({ id: v }) this.setState({ id: v })
}} }}
/> />
<LayerTypeBlock <BlockType
value={this.state.type} value={this.state.type}
wdKey="add-layer.layer-type" wdKey="add-layer.layer-type"
onChange={v => this.setState({ type: v })} onChange={v => this.setState({ type: v })}
/> />
{this.state.type !== 'background' && {this.state.type !== 'background' &&
<LayerSourceBlock <BlockSource
sourceIds={sources} sourceIds={sources}
wdKey="add-layer.layer-source-block" wdKey="add-layer.layer-source-block"
value={this.state.source} value={this.state.source}
@ -150,7 +150,7 @@ class AddModal extends React.Component {
/> />
} }
{['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 && {['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 &&
<LayerSourceLayerBlock <BlockSourceLayer
isFixed={true} isFixed={true}
sourceLayerIds={layers} sourceLayerIds={layers}
value={this.state['source-layer']} value={this.state['source-layer']}
@ -169,4 +169,3 @@ class AddModal extends React.Component {
} }
} }
export default AddModal

View file

@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
import Modal from './Modal' import Modal from './Modal'
class DebugModal extends React.Component { export default class ModalDebug extends React.Component {
static propTypes = { static propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
renderer: PropTypes.string.isRequired, renderer: PropTypes.string.isRequired,
@ -70,4 +70,3 @@ class DebugModal extends React.Component {
} }
} }
export default DebugModal;

View file

@ -4,18 +4,18 @@ import Slugify from 'slugify'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import {format} from '@mapbox/mapbox-gl-style-spec' import {format} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import CheckboxInput from '../inputs/CheckboxInput' import FieldCheckbox from './FieldCheckbox'
import Button from '../Button' import Button from './Button'
import Modal from './Modal' import Modal from './Modal'
import {MdFileDownload} from 'react-icons/md' import {MdFileDownload} from 'react-icons/md'
import style from '../../libs/style' import style from '../libs/style'
import fieldSpecAdditional from '../../libs/field-spec-additional' import fieldSpecAdditional from '../libs/field-spec-additional'
class ExportModal extends React.Component { export default class ModalExport extends React.Component {
static propTypes = { static propTypes = {
mapStyle: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired, onStyleChanged: PropTypes.func.isRequired,
@ -75,33 +75,33 @@ class ExportModal extends React.Component {
</p> </p>
<div> <div>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.mapbox_access_token.label} label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token} fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
> >
<StringInput <FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']} value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")} onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.maptiler_access_token.label} label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token} fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
> >
<StringInput <FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']} value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")} onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label} label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token} fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
> >
<StringInput <FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']} value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")} onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
/> />
</InputBlock> </Block>
</div> </div>
<Button <Button
@ -117,4 +117,3 @@ class ExportModal extends React.Component {
} }
} }
export default ExportModal

View file

@ -1,11 +1,11 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import Modal from './Modal' import Modal from './Modal'
class LoadingModal extends React.Component { export default class ModalLoading extends React.Component {
static propTypes = { static propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onCancel: PropTypes.func.isRequired, onCancel: PropTypes.func.isRequired,
@ -42,4 +42,3 @@ class LoadingModal extends React.Component {
} }
} }
export default LoadingModal

View file

@ -1,16 +1,16 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import LoadingModal from './LoadingModal' import ModalLoading from './ModalLoading'
import Modal from './Modal' import Modal from './Modal'
import Button from '../Button' import Button from './Button'
import FileReaderInput from 'react-file-reader-input' import FileReaderInput from 'react-file-reader-input'
import UrlInput from '../inputs/UrlInput' import FieldUrl from './FieldUrl'
import {MdFileUpload} from 'react-icons/md' import {MdFileUpload} from 'react-icons/md'
import {MdAddCircleOutline} from 'react-icons/md' import {MdAddCircleOutline} from 'react-icons/md'
import style from '../../libs/style.js' import style from '../libs/style.js'
import publicStyles from '../../config/styles.json' import publicStyles from '../config/styles.json'
class PublicStyle extends React.Component { class PublicStyle extends React.Component {
static propTypes = { static propTypes = {
@ -43,7 +43,7 @@ class PublicStyle extends React.Component {
} }
} }
class OpenModal extends React.Component { export default class ModalOpen extends React.Component {
static propTypes = { static propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired, onOpenToggle: PropTypes.func.isRequired,
@ -211,7 +211,7 @@ class OpenModal extends React.Component {
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>. Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
</p> </p>
<form onSubmit={this.onSubmitUrl}> <form onSubmit={this.onSubmitUrl}>
<UrlInput <FieldUrl
data-wd-key="modal:open.url.input" data-wd-key="modal:open.url.input"
type="text" type="text"
className="maputnik-input" className="maputnik-input"
@ -242,7 +242,7 @@ class OpenModal extends React.Component {
</section> </section>
</Modal> </Modal>
<LoadingModal <ModalLoading
isOpen={!!this.state.activeRequest} isOpen={!!this.state.activeRequest}
title={'Loading style'} title={'Loading style'}
onCancel={(e) => this.onCancelActiveRequest(e)} onCancel={(e) => this.onCancelActiveRequest(e)}
@ -253,4 +253,3 @@ class OpenModal extends React.Component {
} }
} }
export default OpenModal

View file

@ -2,18 +2,18 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import ArrayInput from '../inputs/ArrayInput' import FieldArray from './FieldArray'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import UrlInput from '../inputs/UrlInput' import FieldUrl from './FieldUrl'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import EnumInput from '../inputs/EnumInput' import FieldEnum from './FieldEnum'
import ColorField from '../fields/ColorField' import FieldColor from './FieldColor'
import Modal from './Modal' import Modal from './Modal'
import fieldSpecAdditional from '../../libs/field-spec-additional' import fieldSpecAdditional from '../libs/field-spec-additional'
class SettingsModal extends React.Component { export default class ModalSettings extends React.Component {
static propTypes = { static propTypes = {
mapStyle: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired, onStyleChanged: PropTypes.func.isRequired,
@ -87,108 +87,108 @@ class SettingsModal extends React.Component {
title={'Style Settings'} title={'Style Settings'}
> >
<div className="modal:settings"> <div className="modal:settings">
<InputBlock label={"Name"} fieldSpec={latest.$root.name}> <Block label={"Name"} fieldSpec={latest.$root.name}>
<StringInput {...inputProps} <FieldString {...inputProps}
data-wd-key="modal:settings.name" data-wd-key="modal:settings.name"
value={this.props.mapStyle.name} value={this.props.mapStyle.name}
onChange={this.changeStyleProperty.bind(this, "name")} onChange={this.changeStyleProperty.bind(this, "name")}
/> />
</InputBlock> </Block>
<InputBlock label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}> <Block label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}>
<StringInput {...inputProps} <FieldString {...inputProps}
data-wd-key="modal:settings.owner" data-wd-key="modal:settings.owner"
value={this.props.mapStyle.owner} value={this.props.mapStyle.owner}
onChange={this.changeStyleProperty.bind(this, "owner")} onChange={this.changeStyleProperty.bind(this, "owner")}
/> />
</InputBlock> </Block>
<InputBlock label={"Sprite URL"} fieldSpec={latest.$root.sprite}> <Block label={"Sprite URL"} fieldSpec={latest.$root.sprite}>
<UrlInput {...inputProps} <FieldUrl {...inputProps}
data-wd-key="modal:settings.sprite" data-wd-key="modal:settings.sprite"
value={this.props.mapStyle.sprite} value={this.props.mapStyle.sprite}
onChange={this.changeStyleProperty.bind(this, "sprite")} onChange={this.changeStyleProperty.bind(this, "sprite")}
/> />
</InputBlock> </Block>
<InputBlock label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}> <Block label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}>
<UrlInput {...inputProps} <FieldUrl {...inputProps}
data-wd-key="modal:settings.glyphs" data-wd-key="modal:settings.glyphs"
value={this.props.mapStyle.glyphs} value={this.props.mapStyle.glyphs}
onChange={this.changeStyleProperty.bind(this, "glyphs")} onChange={this.changeStyleProperty.bind(this, "glyphs")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.mapbox_access_token.label} label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token} fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
> >
<StringInput {...inputProps} <FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:mapbox_access_token" data-wd-key="modal:settings.maputnik:mapbox_access_token"
value={metadata['maputnik:mapbox_access_token']} value={metadata['maputnik:mapbox_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")} onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.maptiler_access_token.label} label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token} fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
> >
<StringInput {...inputProps} <FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:openmaptiles_access_token" data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
value={metadata['maputnik:openmaptiles_access_token']} value={metadata['maputnik:openmaptiles_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")} onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label} label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token} fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
> >
<StringInput {...inputProps} <FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:thunderforest_access_token" data-wd-key="modal:settings.maputnik:thunderforest_access_token"
value={metadata['maputnik:thunderforest_access_token']} value={metadata['maputnik:thunderforest_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")} onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
/> />
</InputBlock> </Block>
<InputBlock label={"Center"} fieldSpec={latest.$root.center}> <Block label={"Center"} fieldSpec={latest.$root.center}>
<ArrayInput <FieldArray
length={2} length={2}
type="number" type="number"
value={mapStyle.center} value={mapStyle.center}
default={latest.$root.center.default || [0, 0]} default={latest.$root.center.default || [0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")} onChange={this.changeStyleProperty.bind(this, "center")}
/> />
</InputBlock> </Block>
<InputBlock label={"Zoom"} fieldSpec={latest.$root.zoom}> <Block label={"Zoom"} fieldSpec={latest.$root.zoom}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={mapStyle.zoom} value={mapStyle.zoom}
default={latest.$root.zoom.default || 0} default={latest.$root.zoom.default || 0}
onChange={this.changeStyleProperty.bind(this, "zoom")} onChange={this.changeStyleProperty.bind(this, "zoom")}
/> />
</InputBlock> </Block>
<InputBlock label={"Bearing"} fieldSpec={latest.$root.bearing}> <Block label={"Bearing"} fieldSpec={latest.$root.bearing}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={mapStyle.bearing} value={mapStyle.bearing}
default={latest.$root.bearing.default} default={latest.$root.bearing.default}
onChange={this.changeStyleProperty.bind(this, "bearing")} onChange={this.changeStyleProperty.bind(this, "bearing")}
/> />
</InputBlock> </Block>
<InputBlock label={"Pitch"} fieldSpec={latest.$root.pitch}> <Block label={"Pitch"} fieldSpec={latest.$root.pitch}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={mapStyle.pitch} value={mapStyle.pitch}
default={latest.$root.pitch.default} default={latest.$root.pitch.default}
onChange={this.changeStyleProperty.bind(this, "pitch")} onChange={this.changeStyleProperty.bind(this, "pitch")}
/> />
</InputBlock> </Block>
<InputBlock label={"Light anchor"} fieldSpec={latest.light.anchor}> <Block label={"Light anchor"} fieldSpec={latest.light.anchor}>
<EnumInput <FieldEnum
{...inputProps} {...inputProps}
name="light-anchor" name="light-anchor"
value={light.anchor} value={light.anchor}
@ -196,28 +196,28 @@ class SettingsModal extends React.Component {
default={latest.light.anchor.default} default={latest.light.anchor.default}
onChange={this.changeLightProperty.bind(this, "anchor")} onChange={this.changeLightProperty.bind(this, "anchor")}
/> />
</InputBlock> </Block>
<InputBlock label={"Light color"} fieldSpec={latest.light.color}> <Block label={"Light color"} fieldSpec={latest.light.color}>
<ColorField <FieldColor
{...inputProps} {...inputProps}
value={light.color} value={light.color}
default={latest.light.color.default} default={latest.light.color.default}
onChange={this.changeLightProperty.bind(this, "color")} onChange={this.changeLightProperty.bind(this, "color")}
/> />
</InputBlock> </Block>
<InputBlock label={"Light intensity"} fieldSpec={latest.light.intensity}> <Block label={"Light intensity"} fieldSpec={latest.light.intensity}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={light.intensity} value={light.intensity}
default={latest.light.intensity.default} default={latest.light.intensity.default}
onChange={this.changeLightProperty.bind(this, "intensity")} onChange={this.changeLightProperty.bind(this, "intensity")}
/> />
</InputBlock> </Block>
<InputBlock label={"Light position"} fieldSpec={latest.light.position}> <Block label={"Light position"} fieldSpec={latest.light.position}>
<ArrayInput <FieldArray
{...inputProps} {...inputProps}
type="number" type="number"
length={latest.light.position.length} length={latest.light.position.length}
@ -225,31 +225,31 @@ class SettingsModal extends React.Component {
default={latest.light.position.default} default={latest.light.position.default}
onChange={this.changeLightProperty.bind(this, "position")} onChange={this.changeLightProperty.bind(this, "position")}
/> />
</InputBlock> </Block>
<InputBlock label={"Transition delay"} fieldSpec={latest.transition.delay}> <Block label={"Transition delay"} fieldSpec={latest.transition.delay}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={transition.delay} value={transition.delay}
default={latest.transition.delay.default} default={latest.transition.delay.default}
onChange={this.changeTransitionProperty.bind(this, "delay")} onChange={this.changeTransitionProperty.bind(this, "delay")}
/> />
</InputBlock> </Block>
<InputBlock label={"Transition duration"} fieldSpec={latest.transition.duration}> <Block label={"Transition duration"} fieldSpec={latest.transition.duration}>
<NumberInput <FieldNumber
{...inputProps} {...inputProps}
value={transition.duration} value={transition.duration}
default={latest.transition.duration.default} default={latest.transition.duration.default}
onChange={this.changeTransitionProperty.bind(this, "duration")} onChange={this.changeTransitionProperty.bind(this, "duration")}
/> />
</InputBlock> </Block>
<InputBlock <Block
label={fieldSpecAdditional.maputnik.style_renderer.label} label={fieldSpecAdditional.maputnik.style_renderer.label}
fieldSpec={fieldSpecAdditional.maputnik.style_renderer} fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
> >
<SelectInput {...inputProps} <FieldSelect {...inputProps}
data-wd-key="modal:settings.maputnik:renderer" data-wd-key="modal:settings.maputnik:renderer"
options={[ options={[
['mbgljs', 'MapboxGL JS'], ['mbgljs', 'MapboxGL JS'],
@ -258,7 +258,7 @@ class SettingsModal extends React.Component {
value={metadata['maputnik:renderer'] || 'mbgljs'} value={metadata['maputnik:renderer'] || 'mbgljs'}
onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')} onChange={onChangeMetadataProperty.bind(this, 'maputnik:renderer')}
/> />
</InputBlock> </Block>
@ -267,4 +267,3 @@ class SettingsModal extends React.Component {
} }
} }
export default SettingsModal

View file

@ -4,7 +4,7 @@ import PropTypes from 'prop-types'
import Modal from './Modal' import Modal from './Modal'
class ShortcutsModal extends React.Component { export default class ModalShortcuts extends React.Component {
static propTypes = { static propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired, onOpenToggle: PropTypes.func.isRequired,
@ -130,4 +130,3 @@ class ShortcutsModal extends React.Component {
} }
} }
export default ShortcutsModal

View file

@ -2,15 +2,15 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import Modal from './Modal' import Modal from './Modal'
import Button from '../Button' import Button from './Button'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import SourceTypeEditor from '../sources/SourceTypeEditor' import ModalSourcesTypeEditor from './ModalSourcesTypeEditor'
import style from '../../libs/style' import style from '../libs/style'
import { deleteSource, addSource, changeSource } from '../../libs/source' import { deleteSource, addSource, changeSource } from '../libs/source'
import publicSources from '../../config/tilesets.json' import publicSources from '../config/tilesets.json'
import {MdAddCircleOutline, MdDelete} from 'react-icons/md' import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
@ -69,7 +69,7 @@ function editorMode(source) {
return null return null
} }
class ActiveSourceTypeEditor extends React.Component { class ActiveModalSourcesTypeEditor extends React.Component {
static propTypes = { static propTypes = {
sourceId: PropTypes.string.isRequired, sourceId: PropTypes.string.isRequired,
source: PropTypes.object.isRequired, source: PropTypes.object.isRequired,
@ -92,7 +92,7 @@ class ActiveSourceTypeEditor extends React.Component {
</Button> </Button>
</div> </div>
<div className="maputnik-active-source-type-editor-content"> <div className="maputnik-active-source-type-editor-content">
<SourceTypeEditor <ModalSourcesTypeEditor
onChange={this.props.onChange} onChange={this.props.onChange}
mode={editorMode(this.props.source)} mode={editorMode(this.props.source)}
source={this.props.source} source={this.props.source}
@ -207,14 +207,14 @@ class AddSource extends React.Component {
}; };
return <div className="maputnik-add-source"> return <div className="maputnik-add-source">
<InputBlock label={"Source ID"} fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}}> <Block label={"Source ID"} fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}}>
<StringInput <FieldString
value={this.state.sourceId} value={this.state.sourceId}
onChange={v => this.setState({ sourceId: v})} onChange={v => this.setState({ sourceId: v})}
/> />
</InputBlock> </Block>
<InputBlock label={"Source Type"} fieldSpec={sourceTypeFieldSpec}> <Block label={"Source Type"} fieldSpec={sourceTypeFieldSpec}>
<SelectInput <FieldSelect
options={[ options={[
['geojson_json', 'GeoJSON (JSON)'], ['geojson_json', 'GeoJSON (JSON)'],
['geojson_url', 'GeoJSON (URL)'], ['geojson_url', 'GeoJSON (URL)'],
@ -230,8 +230,8 @@ class AddSource extends React.Component {
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})} onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode} value={this.state.mode}
/> />
</InputBlock> </Block>
<SourceTypeEditor <ModalSourcesTypeEditor
onChange={this.onChangeSource} onChange={this.onChangeSource}
mode={this.state.mode} mode={this.state.mode}
source={this.state.source} source={this.state.source}
@ -246,7 +246,7 @@ class AddSource extends React.Component {
} }
} }
class SourcesModal extends React.Component { export default class ModalSources extends React.Component {
static propTypes = { static propTypes = {
mapStyle: PropTypes.object.isRequired, mapStyle: PropTypes.object.isRequired,
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
@ -264,7 +264,7 @@ class SourcesModal extends React.Component {
const mapStyle = this.props.mapStyle const mapStyle = this.props.mapStyle
const activeSources = Object.keys(mapStyle.sources).map(sourceId => { const activeSources = Object.keys(mapStyle.sources).map(sourceId => {
const source = mapStyle.sources[sourceId] const source = mapStyle.sources[sourceId]
return <ActiveSourceTypeEditor return <ActiveModalSourcesTypeEditor
key={sourceId} key={sourceId}
sourceId={sourceId} sourceId={sourceId}
source={source} source={source}
@ -317,4 +317,3 @@ class SourcesModal extends React.Component {
} }
} }
export default SourcesModal

View file

@ -1,14 +1,14 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec' import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import UrlInput from '../inputs/UrlInput' import FieldUrl from './FieldUrl'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import DynamicArrayInput from '../inputs/DynamicArrayInput' import FieldDynamicArray from './FieldDynamicArray'
import ArrayInput from '../inputs/ArrayInput' import FieldArray from './FieldArray'
import JSONEditor from '../layers/JSONEditor' import FieldJsonEditor from './FieldJsonEditor'
class TileJSONSourceEditor extends React.Component { class TileJSONSourceEditor extends React.Component {
@ -20,15 +20,15 @@ class TileJSONSourceEditor extends React.Component {
render() { render() {
return <div> return <div>
<InputBlock label={"TileJSON URL"} fieldSpec={latest.source_vector.url}> <Block label={"TileJSON URL"} fieldSpec={latest.source_vector.url}>
<UrlInput <FieldUrl
value={this.props.source.url} value={this.props.source.url}
onChange={url => this.props.onChange({ onChange={url => this.props.onChange({
...this.props.source, ...this.props.source,
url: url url: url
})} })}
/> />
</InputBlock> </Block>
{this.props.children} {this.props.children}
</div> </div>
} }
@ -50,36 +50,36 @@ class TileURLSourceEditor extends React.Component {
renderTileUrls() { renderTileUrls() {
const tiles = this.props.source.tiles || []; const tiles = this.props.source.tiles || [];
return <InputBlock label={"Tile URL"} fieldSpec={latest.source_vector.tiles}> return <Block label={"Tile URL"} fieldSpec={latest.source_vector.tiles}>
<DynamicArrayInput <FieldDynamicArray
type="url" type="url"
value={tiles} value={tiles}
onChange={this.changeTileUrls.bind(this)} onChange={this.changeTileUrls.bind(this)}
/> />
</InputBlock> </Block>
} }
render() { render() {
return <div> return <div>
{this.renderTileUrls()} {this.renderTileUrls()}
<InputBlock label={"Min Zoom"} fieldSpec={latest.source_vector.minzoom}> <Block label={"Min Zoom"} fieldSpec={latest.source_vector.minzoom}>
<NumberInput <FieldNumber
value={this.props.source.minzoom || 0} value={this.props.source.minzoom || 0}
onChange={minzoom => this.props.onChange({ onChange={minzoom => this.props.onChange({
...this.props.source, ...this.props.source,
minzoom: minzoom minzoom: minzoom
})} })}
/> />
</InputBlock> </Block>
<InputBlock label={"Max Zoom"} fieldSpec={latest.source_vector.maxzoom}> <Block label={"Max Zoom"} fieldSpec={latest.source_vector.maxzoom}>
<NumberInput <FieldNumber
value={this.props.source.maxzoom || 22} value={this.props.source.maxzoom || 22}
onChange={maxzoom => this.props.onChange({ onChange={maxzoom => this.props.onChange({
...this.props.source, ...this.props.source,
maxzoom: maxzoom maxzoom: maxzoom
})} })}
/> />
</InputBlock> </Block>
{this.props.children} {this.props.children}
</div> </div>
@ -104,26 +104,26 @@ class ImageSourceEditor extends React.Component {
} }
return <div> return <div>
<InputBlock label={"Image URL"} fieldSpec={latest.source_image.url}> <Block label={"Image URL"} fieldSpec={latest.source_image.url}>
<UrlInput <FieldUrl
value={this.props.source.url} value={this.props.source.url}
onChange={url => this.props.onChange({ onChange={url => this.props.onChange({
...this.props.source, ...this.props.source,
url, url,
})} })}
/> />
</InputBlock> </Block>
{["top left", "top right", "bottom right", "bottom left"].map((label, idx) => { {["top left", "top right", "bottom right", "bottom left"].map((label, idx) => {
return ( return (
<InputBlock label={`Coord ${label}`} key={label}> <Block label={`Coord ${label}`} key={label}>
<ArrayInput <FieldArray
length={2} length={2}
type="number" type="number"
value={this.props.source.coordinates[idx]} value={this.props.source.coordinates[idx]}
default={[0, 0]} default={[0, 0]}
onChange={(val) => changeCoord(idx, val)} onChange={(val) => changeCoord(idx, val)}
/> />
</InputBlock> </Block>
); );
})} })}
</div> </div>
@ -155,25 +155,25 @@ class VideoSourceEditor extends React.Component {
} }
return <div> return <div>
<InputBlock label={"Video URL"} fieldSpec={latest.source_video.urls}> <Block label={"Video URL"} fieldSpec={latest.source_video.urls}>
<DynamicArrayInput <FieldDynamicArray
type="string" type="string"
value={this.props.source.urls} value={this.props.source.urls}
default={""} default={""}
onChange={changeUrls} onChange={changeUrls}
/> />
</InputBlock> </Block>
{["top left", "top right", "bottom right", "bottom left"].map((label, idx) => { {["top left", "top right", "bottom right", "bottom left"].map((label, idx) => {
return ( return (
<InputBlock label={`Coord ${label}`} key={label}> <Block label={`Coord ${label}`} key={label}>
<ArrayInput <FieldArray
length={2} length={2}
type="number" type="number"
value={this.props.source.coordinates[idx]} value={this.props.source.coordinates[idx]}
default={[0, 0]} default={[0, 0]}
onChange={val => changeCoord(idx, val)} onChange={val => changeCoord(idx, val)}
/> />
</InputBlock> </Block>
); );
})} })}
</div> </div>
@ -187,27 +187,27 @@ class GeoJSONSourceUrlEditor extends React.Component {
} }
render() { render() {
return <InputBlock label={"GeoJSON URL"} fieldSpec={latest.source_geojson.data}> return <Block label={"GeoJSON URL"} fieldSpec={latest.source_geojson.data}>
<UrlInput <FieldUrl
value={this.props.source.data} value={this.props.source.data}
onChange={data => this.props.onChange({ onChange={data => this.props.onChange({
...this.props.source, ...this.props.source,
data: data data: data
})} })}
/> />
</InputBlock> </Block>
} }
} }
class GeoJSONSourceJSONEditor extends React.Component { class GeoJSONSourceFieldJsonEditor extends React.Component {
static propTypes = { static propTypes = {
source: PropTypes.object.isRequired, source: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
} }
render() { render() {
return <InputBlock label={"GeoJSON"} fieldSpec={latest.source_geojson.data}> return <Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
<JSONEditor <FieldJsonEditor
layer={this.props.source.data} layer={this.props.source.data}
maxHeight={200} maxHeight={200}
mode={{ mode={{
@ -222,11 +222,11 @@ class GeoJSONSourceJSONEditor extends React.Component {
}) })
}} }}
/> />
</InputBlock> </Block>
} }
} }
class SourceTypeEditor extends React.Component { export default class ModalSourcesTypeEditor extends React.Component {
static propTypes = { static propTypes = {
mode: PropTypes.string.isRequired, mode: PropTypes.string.isRequired,
source: PropTypes.object.isRequired, source: PropTypes.object.isRequired,
@ -240,15 +240,15 @@ class SourceTypeEditor extends React.Component {
} }
switch(this.props.mode) { switch(this.props.mode) {
case 'geojson_url': return <GeoJSONSourceUrlEditor {...commonProps} /> case 'geojson_url': return <GeoJSONSourceUrlEditor {...commonProps} />
case 'geojson_json': return <GeoJSONSourceJSONEditor {...commonProps} /> case 'geojson_json': return <GeoJSONSourceFieldJsonEditor {...commonProps} />
case 'tilejson_vector': return <TileJSONSourceEditor {...commonProps} /> case 'tilejson_vector': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_vector': return <TileURLSourceEditor {...commonProps} /> case 'tilexyz_vector': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster': return <TileJSONSourceEditor {...commonProps} /> case 'tilejson_raster': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} /> case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster-dem': return <TileJSONSourceEditor {...commonProps} /> case 'tilejson_raster-dem': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster-dem': return <TileURLSourceEditor {...commonProps}> case 'tilexyz_raster-dem': return <TileURLSourceEditor {...commonProps}>
<InputBlock label={"Encoding"} fieldSpec={latest.source_raster_dem.encoding}> <Block label={"Encoding"} fieldSpec={latest.source_raster_dem.encoding}>
<SelectInput <FieldSelect
options={Object.keys(latest.source_raster_dem.encoding.values)} options={Object.keys(latest.source_raster_dem.encoding.values)}
onChange={encoding => this.props.onChange({ onChange={encoding => this.props.onChange({
...this.props.source, ...this.props.source,
@ -256,7 +256,7 @@ class SourceTypeEditor extends React.Component {
})} })}
value={this.props.source.encoding || latest.source_raster_dem.encoding.default} value={this.props.source.encoding || latest.source_raster_dem.encoding.default}
/> />
</InputBlock> </Block>
</TileURLSourceEditor> </TileURLSourceEditor>
case 'image': return <ImageSourceEditor {...commonProps} /> case 'image': return <ImageSourceEditor {...commonProps} />
case 'video': return <VideoSourceEditor {...commonProps} /> case 'video': return <VideoSourceEditor {...commonProps} />
@ -265,4 +265,3 @@ class SourceTypeEditor extends React.Component {
} }
} }
export default SourceTypeEditor

View file

@ -1,12 +1,12 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import Modal from './Modal' import Modal from './Modal'
import logoImage from 'maputnik-design/logos/logo-color.svg' import logoImage from 'maputnik-design/logos/logo-color.svg'
class SurveyModal extends React.Component { export default class ModalSurvey extends React.Component {
static propTypes = { static propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onOpenToggle: PropTypes.func.isRequired, onOpenToggle: PropTypes.func.isRequired,
@ -36,4 +36,3 @@ class SurveyModal extends React.Component {
} }
} }
export default SurveyModal

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import FunctionSpecField from './FunctionSpecField' import FieldFunction from './FieldFunction'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image'] const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
/** Extract field spec by {@fieldName} from the {@layerType} in the /** Extract field spec by {@fieldName} from the {@layerType} in the
@ -58,7 +58,7 @@ export default class PropertyGroup extends React.Component {
const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName] const fieldValue = fieldName in paint ? paint[fieldName] : layout[fieldName]
const fieldType = fieldName in paint ? 'paint' : 'layout'; const fieldType = fieldName in paint ? 'paint' : 'layout';
return <FunctionSpecField return <FieldFunction
errors={errors} errors={errors}
onChange={this.onPropertyChange} onChange={this.onPropertyChange}
key={fieldName} key={fieldName}

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
class ScrollContainer extends React.Component { export default class ScrollContainer extends React.Component {
static propTypes = { static propTypes = {
children: PropTypes.node children: PropTypes.node
} }
@ -13,4 +13,3 @@ class ScrollContainer extends React.Component {
} }
} }
export default ScrollContainer

View file

@ -1,10 +1,10 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { otherFilterOps } from '../../libs/filterops.js' import { otherFilterOps } from '../libs/filterops.js'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import AutocompleteInput from '../inputs/AutocompleteInput' import FieldAutocomplete from './FieldAutocomplete'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
function tryParseInt(v) { function tryParseInt(v) {
if (v === '') return v if (v === '') return v
@ -35,7 +35,7 @@ function parseFilter(v) {
return v; return v;
} }
class SingleFilterEditor extends React.Component { export default class SingleFilterEditor extends React.Component {
static propTypes = { static propTypes = {
filter: PropTypes.array.isRequired, filter: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -64,14 +64,14 @@ class SingleFilterEditor extends React.Component {
return <div className="maputnik-filter-editor-single"> return <div className="maputnik-filter-editor-single">
<div className="maputnik-filter-editor-property"> <div className="maputnik-filter-editor-property">
<AutocompleteInput <FieldAutocomplete
value={propertyName} value={propertyName}
options={Object.keys(this.props.properties).map(propName => [propName, propName])} options={Object.keys(this.props.properties).map(propName => [propName, propName])}
onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)} onChange={newPropertyName => this.onFilterPartChanged(filterOp, newPropertyName, filterArgs)}
/> />
</div> </div>
<div className="maputnik-filter-editor-operator"> <div className="maputnik-filter-editor-operator">
<SelectInput <FieldSelect
value={filterOp} value={filterOp}
onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)} onChange={newFilterOp => this.onFilterPartChanged(newFilterOp, propertyName, filterArgs)}
options={otherFilterOps} options={otherFilterOps}
@ -79,7 +79,7 @@ class SingleFilterEditor extends React.Component {
</div> </div>
{filterArgs.length > 0 && {filterArgs.length > 0 &&
<div className="maputnik-filter-editor-args"> <div className="maputnik-filter-editor-args">
<StringInput <FieldString
value={filterArgs.join(',')} value={filterArgs.join(',')}
onChange={ v=> this.onFilterPartChanged(filterOp, propertyName, v.split(','))} onChange={ v=> this.onFilterPartChanged(filterOp, propertyName, v.split(','))}
/> />
@ -90,4 +90,3 @@ class SingleFilterEditor extends React.Component {
} }
export default SingleFilterEditor

View file

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import './SmallError.scss'; import './SmallError.scss';
class SmallError extends React.Component { export default class SmallError extends React.Component {
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
} }
@ -17,4 +17,3 @@ class SmallError extends React.Component {
} }
} }
export default SmallError

View file

@ -1,4 +1,4 @@
@import '../../styles/vars'; @import '../styles/vars';
.SmallError { .SmallError {
color: #E57373; color: #E57373;

View file

@ -1,17 +1,17 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import ColorField from './ColorField' import FieldColor from './FieldColor'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
import CheckboxInput from '../inputs/CheckboxInput' import FieldCheckbox from './FieldCheckbox'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import MultiButtonInput from '../inputs/MultiButtonInput' import FieldMultiInput from './FieldMultiInput'
import ArrayInput from '../inputs/ArrayInput' import FieldArray from './FieldArray'
import DynamicArrayInput from '../inputs/DynamicArrayInput' import FieldDynamicArray from './FieldDynamicArray'
import FontInput from '../inputs/FontInput' import FieldFont from './FieldFont'
import IconInput from '../inputs/IconInput' import FieldSymbol from './FieldSymbol'
import EnumInput from '../inputs/EnumInput' import FieldEnum from './FieldEnum'
import capitalize from 'lodash.capitalize' import capitalize from 'lodash.capitalize'
const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image'] const iconProperties = ['background-pattern', 'fill-pattern', 'line-pattern', 'fill-extrusion-pattern', 'icon-image']
@ -62,7 +62,7 @@ export default class SpecField extends React.Component {
function childNodes() { function childNodes() {
switch(this.props.fieldSpec.type) { switch(this.props.fieldSpec.type) {
case 'number': return ( case 'number': return (
<NumberInput <FieldNumber
{...commonProps} {...commonProps}
min={this.props.fieldSpec.minimum} min={this.props.fieldSpec.minimum}
max={this.props.fieldSpec.maximum} max={this.props.fieldSpec.maximum}
@ -71,7 +71,7 @@ export default class SpecField extends React.Component {
case 'enum': case 'enum':
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)]) const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)])
return <EnumInput return <FieldEnum
{...commonProps} {...commonProps}
options={options} options={options}
/> />
@ -79,40 +79,40 @@ export default class SpecField extends React.Component {
case 'formatted': case 'formatted':
case 'string': case 'string':
if(iconProperties.indexOf(this.props.fieldName) >= 0) { if(iconProperties.indexOf(this.props.fieldName) >= 0) {
return <IconInput return <FieldSymbol
{...commonProps} {...commonProps}
icons={this.props.fieldSpec.values} icons={this.props.fieldSpec.values}
/> />
} else { } else {
return <StringInput return <FieldString
{...commonProps} {...commonProps}
/> />
} }
case 'color': return ( case 'color': return (
<ColorField <FieldColor
{...commonProps} {...commonProps}
/> />
) )
case 'boolean': return ( case 'boolean': return (
<CheckboxInput <FieldCheckbox
{...commonProps} {...commonProps}
/> />
) )
case 'array': case 'array':
if(this.props.fieldName === 'text-font') { if(this.props.fieldName === 'text-font') {
return <FontInput return <FieldFont
{...commonProps} {...commonProps}
fonts={this.props.fieldSpec.values} fonts={this.props.fieldSpec.values}
/> />
} else { } else {
if (this.props.fieldSpec.length) { if (this.props.fieldSpec.length) {
return <ArrayInput return <FieldArray
{...commonProps} {...commonProps}
type={this.props.fieldSpec.value} type={this.props.fieldSpec.value}
length={this.props.fieldSpec.length} length={this.props.fieldSpec.length}
/> />
} else { } else {
return <DynamicArrayInput return <FieldDynamicArray
{...commonProps} {...commonProps}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
type={this.props.fieldSpec.value} type={this.props.fieldSpec.value}

View file

@ -2,15 +2,15 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js'; import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
import Button from '../Button' import Button from './Button'
import SpecField from './SpecField' import SpecField from './SpecField'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import SelectInput from '../inputs/SelectInput' import FieldSelect from './FieldSelect'
import DocLabel from './DocLabel' import Doc from './Doc'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import docUid from '../../libs/document-uid' import docUid from '../libs/document-uid'
import sortNumerically from '../../libs/sort-numerically' import sortNumerically from '../libs/sort-numerically'
import {findDefaultFromSpec} from '../util/spec-helper'; import {findDefaultFromSpec} from '../util/spec-helper';
import labelFromFieldName from './_labelFromFieldName' import labelFromFieldName from './_labelFromFieldName'
@ -195,16 +195,16 @@ export default class DataProperty extends React.Component {
let dataInput; let dataInput;
if(this.props.value.type === "categorical") { if(this.props.value.type === "categorical") {
dataInput = <StringInput {...dataProps} /> dataInput = <FieldString {...dataProps} />
} }
else { else {
dataInput = <NumberInput {...dataProps} /> dataInput = <FieldNumber {...dataProps} />
} }
let zoomInput = null; let zoomInput = null;
if(zoomLevel !== undefined) { if(zoomLevel !== undefined) {
zoomInput = <div className="maputnik-data-spec-property-stop-edit"> zoomInput = <div className="maputnik-data-spec-property-stop-edit">
<NumberInput <FieldNumber
value={zoomLevel} value={zoomLevel}
onChange={newZoom => this.changeStop(idx, {zoom: newZoom, value: dataLevel}, value)} onChange={newZoom => this.changeStop(idx, {zoom: newZoom, value: dataLevel}, value)}
min={0} min={0}
@ -223,7 +223,7 @@ export default class DataProperty extends React.Component {
}).join(""); }).join("");
const error = message ? {message} : undefined; const error = message ? {message} : undefined;
return <InputBlock return <Block
error={error} error={error}
key={key} key={key}
action={deleteStopBtn} action={deleteStopBtn}
@ -241,22 +241,22 @@ export default class DataProperty extends React.Component {
onChange={(_, newValue) => this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue)} onChange={(_, newValue) => this.changeStop(idx, {zoom: zoomLevel, value: dataLevel}, newValue)}
/> />
</div> </div>
</InputBlock> </Block>
}) })
} }
return <div className="maputnik-data-spec-block"> return <div className="maputnik-data-spec-block">
<div className="maputnik-data-spec-property"> <div className="maputnik-data-spec-property">
<InputBlock <Block
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
label={labelFromFieldName(this.props.fieldName)} label={labelFromFieldName(this.props.fieldName)}
> >
<div className="maputnik-data-spec-property-group"> <div className="maputnik-data-spec-property-group">
<DocLabel <Doc
label="Type" label="Type"
/> />
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<SelectInput <FieldSelect
value={this.props.value.type} value={this.props.value.type}
onChange={propVal => this.changeDataProperty("type", propVal)} onChange={propVal => this.changeDataProperty("type", propVal)}
title={"Select a type of data scale (default is 'categorical')."} title={"Select a type of data scale (default is 'categorical')."}
@ -265,11 +265,11 @@ export default class DataProperty extends React.Component {
</div> </div>
</div> </div>
<div className="maputnik-data-spec-property-group"> <div className="maputnik-data-spec-property-group">
<DocLabel <Doc
label="Property" label="Property"
/> />
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
<StringInput <FieldString
value={this.props.value.property} value={this.props.value.property}
title={"Input a data property to base styles off of."} title={"Input a data property to base styles off of."}
onChange={propVal => this.changeDataProperty("property", propVal)} onChange={propVal => this.changeDataProperty("property", propVal)}
@ -278,7 +278,7 @@ export default class DataProperty extends React.Component {
</div> </div>
{dataFields && {dataFields &&
<div className="maputnik-data-spec-property-group"> <div className="maputnik-data-spec-property-group">
<DocLabel <Doc
label="Default" label="Default"
/> />
<div className="maputnik-data-spec-property-input"> <div className="maputnik-data-spec-property-input">
@ -291,7 +291,7 @@ export default class DataProperty extends React.Component {
</div> </div>
</div> </div>
} }
</InputBlock> </Block>
</div> </div>
{dataFields && {dataFields &&
<> <>

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import {MdDelete} from 'react-icons/md' import {MdDelete} from 'react-icons/md'

View file

@ -1,14 +1,14 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import Button from '../Button' import Button from './Button'
import {MdDelete, MdUndo} from 'react-icons/md' import {MdDelete, MdUndo} from 'react-icons/md'
import StringInput from '../inputs/StringInput' import FieldString from './FieldString'
import labelFromFieldName from './_labelFromFieldName' import labelFromFieldName from './_labelFromFieldName'
import stringifyPretty from 'json-stringify-pretty-compact' import stringifyPretty from 'json-stringify-pretty-compact'
import JSONEditor from '../layers/JSONEditor' import FieldJsonEditor from './FieldJsonEditor'
export default class ExpressionProperty extends React.Component { export default class ExpressionProperty extends React.Component {
@ -107,14 +107,14 @@ export default class ExpressionProperty extends React.Component {
} }
} }
return <InputBlock return <Block
error={foundErrors} error={foundErrors}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
label={labelFromFieldName(this.props.fieldName)} label={labelFromFieldName(this.props.fieldName)}
action={deleteStopBtn} action={deleteStopBtn}
wideMode={true} wideMode={true}
> >
<JSONEditor <FieldJsonEditor
mode={{name: "mgl"}} mode={{name: "mgl"}}
lint={{ lint={{
context: "expression", context: "expression",
@ -132,6 +132,6 @@ export default class ExpressionProperty extends React.Component {
getValue={getValue} getValue={getValue}
onChange={this.props.onChange} onChange={this.props.onChange}
/> />
</InputBlock> </Block>
} }
} }

View file

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import Button from '../Button' import Button from './Button'
import {MdFunctions, MdInsertChart} from 'react-icons/md' import {MdFunctions, MdInsertChart} from 'react-icons/md'
import {mdiFunctionVariant} from '@mdi/js'; import {mdiFunctionVariant} from '@mdi/js';

View file

@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import SpecField from './SpecField' import SpecField from './SpecField'
import FunctionButtons from './_FunctionButtons' import FunctionButtons from './_FunctionButtons'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import labelFromFieldName from './_labelFromFieldName' import labelFromFieldName from './_labelFromFieldName'
@ -37,13 +37,13 @@ export default class SpecProperty extends React.Component {
const error = errors[fieldType+"."+fieldName]; const error = errors[fieldType+"."+fieldName];
return <InputBlock return <Block
error={error} error={error}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
label={labelFromFieldName(this.props.fieldName)} label={labelFromFieldName(this.props.fieldName)}
action={functionBtn} action={functionBtn}
> >
<SpecField {...this.props} /> <SpecField {...this.props} />
</InputBlock> </Block>
} }
} }

View file

@ -2,16 +2,16 @@ import React from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js'; import {mdiFunctionVariant, mdiTableRowPlusAfter} from '@mdi/js';
import Button from '../Button' import Button from './Button'
import SpecField from './SpecField' import SpecField from './SpecField'
import NumberInput from '../inputs/NumberInput' import FieldNumber from './FieldNumber'
import InputBlock from '../inputs/InputBlock' import Block from './Block'
import DeleteStopButton from './_DeleteStopButton' import DeleteStopButton from './_DeleteStopButton'
import labelFromFieldName from './_labelFromFieldName' import labelFromFieldName from './_labelFromFieldName'
import docUid from '../../libs/document-uid' import docUid from '../libs/document-uid'
import sortNumerically from '../../libs/sort-numerically' import sortNumerically from '../libs/sort-numerically'
/** /**
@ -143,7 +143,7 @@ export default class ZoomProperty extends React.Component {
}).join(""); }).join("");
const error = message ? {message} : undefined; const error = message ? {message} : undefined;
return <InputBlock return <Block
error={error} error={error}
key={key} key={key}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
@ -152,7 +152,7 @@ export default class ZoomProperty extends React.Component {
> >
<div> <div>
<div className="maputnik-zoom-spec-property-stop-edit"> <div className="maputnik-zoom-spec-property-stop-edit">
<NumberInput <FieldNumber
value={zoomLevel} value={zoomLevel}
onChange={changedStop => this.changeZoomStop(idx, changedStop, value)} onChange={changedStop => this.changeZoomStop(idx, changedStop, value)}
min={0} min={0}
@ -168,7 +168,7 @@ export default class ZoomProperty extends React.Component {
/> />
</div> </div>
</div> </div>
</InputBlock> </Block>
}); });
return <div className="maputnik-zoom-spec-property"> return <div className="maputnik-zoom-spec-property">

View file

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

View file

@ -0,0 +1,26 @@
import '../src/styles/index.scss';
import React from 'react';
import {Describe} from './ui';
export default {
title: 'Welcome',
};
export const ToStorybook = () => {
return (
<Describe>
<h1>Maputnik component library</h1>
<p>
This is the Maputnik component library, which shows the uses of some commonly used components from the Maputnik editor. This is a stand alone place where we can better refine them and improve their API separate from their use inside the editor.
</p>
<p>
This should also help us better refine our CSS and make it more modular as currently we rely on the cascade quite a bit in a number of places.
</p>
</Describe>
);
}
ToStorybook.story = {
name: 'Intro',
};

18
stories/Button.stories.js Normal file
View file

@ -0,0 +1,18 @@
import React from 'react';
import Button from '../src/components/Button';
import {action} from '@storybook/addon-actions';
import {Wrapper} from './ui';
export default {
title: 'Button',
component: Button,
};
export const Simple = () => (
<Wrapper>
<Button onClick={action('onClick')}>
Hello Button
</Button>
</Wrapper>
);

View file

@ -0,0 +1,25 @@
import React from 'react';
import {useActionState} from './helper';
import FieldColor from '../src/components/FieldColor';
import {Wrapper} from './ui';
export default {
title: 'FieldColor',
component: FieldColor,
};
export const Simple = () => {
const [color, setColor] = useActionState("onChange", "#ff0000");
return (
<Wrapper>
<FieldColor
name="color"
value={color}
onChange={setColor}
/>
</Wrapper>
);
};

View file

@ -0,0 +1,41 @@
import React from 'react';
import {useActionState} from './helper';
import FieldNumber from '../src/components/FieldNumber';
import {Wrapper} from './ui';
export default {
title: 'FieldNumber',
component: FieldNumber,
};
export const Simple = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<Wrapper>
<FieldNumber
name="number"
value={value}
onChange={setValue}
/>
</Wrapper>
);
};
export const Range = () => {
const [value, setValue] = useActionState("onChange", 1);
return (
<Wrapper>
<FieldNumber
name="number"
value={value}
onChange={setValue}
min={1}
max={24}
allowRange={true}
rangeStep={1}
/>
</Wrapper>
);
};

12
stories/helper.js Normal file
View file

@ -0,0 +1,12 @@
import React, {useState} from 'react';
import {action} from '@storybook/addon-actions';
export function useActionState (name, initialVal) {
const [val, fn] = useState(initialVal);
const actionFn = action(name);
function retFn(val) {
actionFn(val);
return fn(val);
}
return [val, retFn];
}

19
stories/ui.js Normal file
View file

@ -0,0 +1,19 @@
import React from 'react';
export function Describe ({children}) {
return (
<div style={{maxWidth: "600px", margin: "0.8em"}}>
{children}
</div>
)
}
export function Wrapper ({children}) {
return (
<div style={{maxWidth: "180px", margin: "0.4em"}}>
{children}
</div>
);
};