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-
- run: npm install
- run: npm run build
- run: npm run build-storybook
- name: artifacts/editor
uses: actions/upload-artifact@v1
with:
@ -101,6 +102,11 @@ jobs:
with:
name: editor-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
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",
"lint-js": "eslint --ext js --ext jsx src test",
"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": {
"type": "git",
@ -119,6 +121,11 @@
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.6.3",
"@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/local-runner": "^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 url from 'url'
import MapboxGlMap from './map/MapboxGlMap'
import OpenLayersMap from './map/OpenLayersMap'
import LayerList from './layers/LayerList'
import LayerEditor from './layers/LayerEditor'
import Toolbar from './Toolbar'
import MapMapboxGl from './MapMapboxGl'
import MapOpenLayers from './MapOpenLayers'
import LayerList from './LayerList'
import LayerEditor from './LayerEditor'
import AppToolbar from './AppToolbar'
import AppLayout from './AppLayout'
import MessagePanel from './MessagePanel'
import MessagePanel from './AppMessagePanel'
import SettingsModal from './modals/SettingsModal'
import ExportModal from './modals/ExportModal'
import SourcesModal from './modals/SourcesModal'
import OpenModal from './modals/OpenModal'
import ShortcutsModal from './modals/ShortcutsModal'
import SurveyModal from './modals/SurveyModal'
import DebugModal from './modals/DebugModal'
import ModalSettings from './ModalSettings'
import ModalExport from './ModalExport'
import ModalSources from './ModalSources'
import ModalOpen from './ModalOpen'
import ModalShortcuts from './ModalShortcuts'
import ModalSurvey from './ModalSurvey'
import ModalDebug from './ModalDebug'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
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 Debug from '../libs/debug'
import queryUtil from '../libs/query-util'
import {formatLayerId} from './util/format';
import {formatLayerId} from '../util/format';
import MapboxGl from 'mapbox-gl'
@ -668,14 +668,14 @@ export default class App extends React.Component {
// Check if OL code has been loaded?
if(renderer === 'ol') {
mapElement = <OpenLayersMap
mapElement = <MapOpenLayers
{...mapProps}
onChange={this.onMapChange}
debugToolbox={this.state.openlayersDebugOptions.debugToolbox}
onLayerSelect={this.onLayerSelect}
/>
} else {
mapElement = <MapboxGlMap {...mapProps}
mapElement = <MapMapboxGl {...mapProps}
onChange={this.onMapChange}
options={this.state.mapboxGlDebugOptions}
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 metadata = this.state.mapStyle.metadata || {}
const toolbar = <Toolbar
const toolbar = <AppToolbar
renderer={this._getRenderer()}
mapState={this.state.mapState}
mapStyle={this.state.mapStyle}
@ -795,7 +795,7 @@ export default class App extends React.Component {
const modals = <div>
<DebugModal
<ModalDebug
renderer={this._getRenderer()}
mapboxGlDebugOptions={this.state.mapboxGlDebugOptions}
openlayersDebugOptions={this.state.openlayersDebugOptions}
@ -805,12 +805,12 @@ export default class App extends React.Component {
onOpenToggle={this.toggleModal.bind(this, 'debug')}
mapView={this.state.mapView}
/>
<ShortcutsModal
<ModalShortcuts
ref={(el) => this.shortcutEl = el}
isOpen={this.state.isOpen.shortcuts}
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
/>
<SettingsModal
<ModalSettings
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
onChangeMetadataProperty={this.onChangeMetadataProperty}
@ -818,24 +818,24 @@ export default class App extends React.Component {
onOpenToggle={this.toggleModal.bind(this, 'settings')}
openlayersDebugOptions={this.state.openlayersDebugOptions}
/>
<ExportModal
<ModalExport
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, 'export')}
/>
<OpenModal
<ModalOpen
isOpen={this.state.isOpen.open}
onStyleOpen={this.openStyle}
onOpenToggle={this.toggleModal.bind(this, 'open')}
/>
<SourcesModal
<ModalSources
mapStyle={this.state.mapStyle}
onStyleChanged={this.onStyleChanged}
isOpen={this.state.isOpen.sources}
onOpenToggle={this.toggleModal.bind(this, 'sources')}
/>
<SurveyModal
<ModalSurvey
isOpen={this.state.isOpen.survey}
onOpenToggle={this.toggleModal.bind(this, 'survey')}
/>

View file

@ -1,8 +1,8 @@
import React from 'react'
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 = {
errors: 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 = {
mapStyle: PropTypes.object.isRequired,
inspectModeEnabled: PropTypes.bool.isRequired,

View file

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

View file

@ -1,10 +1,10 @@
import React from 'react'
import PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import Block from './Block'
import FieldString from './FieldString'
class MetadataBlock extends React.Component {
export default class BlockComment extends React.Component {
static propTypes = {
value: PropTypes.string,
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."
};
return <InputBlock
return <Block
label={"Comments"}
fieldSpec={fieldSpec}
data-wd-key="layer-comment"
>
<StringInput
<FieldString
multi={true}
value={this.props.value}
onChange={this.props.onChange}
default="Comment..."
/>
</InputBlock>
</Block>
}
}
export default MetadataBlock

View file

@ -2,10 +2,10 @@ import React from 'react'
import PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import Block from './Block'
import FieldString from './FieldString'
class LayerIdBlock extends React.Component {
export default class BlockId extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
wdKey: PropTypes.string.isRequired,
@ -14,16 +14,15 @@ class LayerIdBlock extends React.Component {
}
render() {
return <InputBlock label={"ID"} fieldSpec={latest.layer.id}
return <Block label={"ID"} fieldSpec={latest.layer.id}
data-wd-key={this.props.wdKey}
error={this.props.error}
>
<StringInput
<FieldString
value={this.props.value}
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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import NumberInput from '../inputs/NumberInput'
import Block from './Block'
import FieldNumber from './FieldNumber'
class MaxZoomBlock extends React.Component {
export default class BlockMaxZoom extends React.Component {
static propTypes = {
value: PropTypes.number,
onChange: PropTypes.func.isRequired,
@ -13,11 +13,11 @@ class MaxZoomBlock extends React.Component {
}
render() {
return <InputBlock label={"Max Zoom"} fieldSpec={latest.layer.maxzoom}
return <Block label={"Max Zoom"} fieldSpec={latest.layer.maxzoom}
error={this.props.error}
data-wd-key="max-zoom"
>
<NumberInput
<FieldNumber
allowRange={true}
value={this.props.value}
onChange={this.props.onChange}
@ -25,8 +25,7 @@ class MaxZoomBlock extends React.Component {
max={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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import NumberInput from '../inputs/NumberInput'
import Block from './Block'
import FieldNumber from './FieldNumber'
class MinZoomBlock extends React.Component {
export default class BlockMinZoom extends React.Component {
static propTypes = {
value: PropTypes.number,
onChange: PropTypes.func.isRequired,
@ -13,11 +13,11 @@ class MinZoomBlock extends React.Component {
}
render() {
return <InputBlock label={"Min Zoom"} fieldSpec={latest.layer.minzoom}
return <Block label={"Min Zoom"} fieldSpec={latest.layer.minzoom}
error={this.props.error}
data-wd-key="min-zoom"
>
<NumberInput
<FieldNumber
allowRange={true}
value={this.props.value}
onChange={this.props.onChange}
@ -25,8 +25,7 @@ class MinZoomBlock extends React.Component {
max={latest.layer.minzoom.maximum}
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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import AutocompleteInput from '../inputs/AutocompleteInput'
import Block from './Block'
import FieldAutocomplete from './FieldAutocomplete'
class LayerSourceBlock extends React.Component {
export default class BlockSource extends React.Component {
static propTypes = {
value: PropTypes.string,
wdKey: PropTypes.string,
@ -20,19 +20,18 @@ class LayerSourceBlock extends React.Component {
}
render() {
return <InputBlock
return <Block
label={"Source"}
fieldSpec={latest.layer.source}
error={this.props.error}
data-wd-key={this.props.wdKey}
>
<AutocompleteInput
<FieldAutocomplete
value={this.props.value}
onChange={this.props.onChange}
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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import AutocompleteInput from '../inputs/AutocompleteInput'
import Block from './Block'
import FieldAutocomplete from './FieldAutocomplete'
class LayerSourceLayer extends React.Component {
export default class BlockSourceLayer extends React.Component {
static propTypes = {
value: PropTypes.string,
onChange: PropTypes.func,
@ -20,17 +20,16 @@ class LayerSourceLayer extends React.Component {
}
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"
>
<AutocompleteInput
<FieldAutocomplete
keepMenuWithinWindowBounds={!!this.props.isFixed}
value={this.props.value}
onChange={this.props.onChange}
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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import SelectInput from '../inputs/SelectInput'
import StringInput from '../inputs/StringInput'
import Block from './Block'
import FieldSelect from './FieldSelect'
import FieldString from './FieldString'
class LayerTypeBlock extends React.Component {
export default class BlockType extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
wdKey: PropTypes.string,
@ -20,18 +20,18 @@ class LayerTypeBlock extends React.Component {
}
render() {
return <InputBlock label={"Type"} fieldSpec={latest.layer.type}
return <Block label={"Type"} fieldSpec={latest.layer.type}
data-wd-key={this.props.wdKey}
error={this.props.error}
>
{this.props.disabled &&
<StringInput
<FieldString
value={this.props.value}
disabled={true}
/>
}
{!this.props.disabled &&
<SelectInput
<FieldSelect
options={[
['background', 'Background'],
['fill', 'Fill'],
@ -47,8 +47,7 @@ class LayerTypeBlock extends React.Component {
value={this.props.value}
/>
}
</InputBlock>
</Block>
}
}
export default LayerTypeBlock

View file

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

View file

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

View file

@ -1,9 +1,9 @@
import React from 'react'
import PropTypes from 'prop-types'
import StringInput from './StringInput'
import NumberInput from './NumberInput'
import FieldString from './FieldString'
import FieldNumber from './FieldNumber'
class ArrayInput extends React.Component {
export default class FieldArray extends React.Component {
static propTypes = {
value: PropTypes.array,
type: PropTypes.string,
@ -82,7 +82,7 @@ class ArrayInput extends React.Component {
const inputs = Array(this.props.length).fill(null).map((_, i) => {
if(this.props.type === 'number') {
return <NumberInput
return <FieldNumber
key={i}
default={containsValues ? undefined : this.props.default[i]}
value={value[i]}
@ -90,7 +90,7 @@ class ArrayInput extends React.Component {
onChange={this.changeValue.bind(this, i)}
/>
} else {
return <StringInput
return <FieldString
key={i}
default={containsValues ? undefined : this.props.default[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;
class AutocompleteInput extends React.Component {
export default class FieldAutocomplete extends React.Component {
static propTypes = {
value: PropTypes.string,
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 PropTypes from 'prop-types'
class CheckboxInput extends React.Component {
export default class FieldCheckbox extends React.Component {
static propTypes = {
value: PropTypes.bool,
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*/
class ColorField extends React.Component {
export default class FieldColor extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
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'
export default class DocLabel extends React.Component {
export default class FieldDocLabel extends React.Component {
static propTypes = {
label: PropTypes.oneOfType([
PropTypes.object,

View file

@ -1,16 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import StringInput from './StringInput'
import NumberInput from './NumberInput'
import Button from '../Button'
import FieldString from './FieldString'
import FieldNumber from './FieldNumber'
import Button from './Button'
import {MdDelete} from 'react-icons/md'
import DocLabel from '../fields/DocLabel'
import EnumInput from '../inputs/SelectInput'
import FieldDocLabel from './FieldDocLabel'
import FieldEnum from './FieldEnum'
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 = {
value: PropTypes.array,
type: PropTypes.string,
@ -62,13 +62,13 @@ class DynamicArrayInput extends React.Component {
const deleteValueBtn= <DeleteValueButton onClick={this.deleteValue.bind(this, i)} />
let input;
if(this.props.type === 'url') {
input = <UrlInput
input = <FieldUrl
value={v}
onChange={this.changeValue.bind(this, i)}
/>
}
else if (this.props.type === 'number') {
input = <NumberInput
input = <FieldNumber
value={v}
onChange={this.changeValue.bind(this, i)}
/>
@ -76,14 +76,14 @@ class DynamicArrayInput extends React.Component {
else if (this.props.type === 'enum') {
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)]);
input = <EnumInput
input = <FieldEnum
options={options}
value={v}
onChange={this.changeValue.bind(this, i)}
/>
}
else {
input = <StringInput
input = <FieldString
value={v}
onChange={this.changeValue.bind(this, i)}
/>
@ -126,7 +126,7 @@ class DeleteValueButton extends React.Component {
onClick={this.props.onClick}
title="Remove array item"
>
<DocLabel
<FieldDocLabel
label={<MdDelete />}
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 PropTypes from 'prop-types'
import SelectInput from '../inputs/SelectInput'
import MultiButtonInput from '../inputs/MultiButtonInput'
import FieldSelect from './FieldSelect'
import FieldMultiInput from './FieldMultiInput'
function optionsLabelLength(options) {
@ -13,7 +13,7 @@ function optionsLabelLength(options) {
}
class EnumInput extends React.Component {
export default class FieldEnum extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
value: PropTypes.string,
@ -28,14 +28,14 @@ class EnumInput extends React.Component {
const {options, value, onChange, name} = this.props;
if(options.length <= 3 && optionsLabelLength(options) <= 20) {
return <MultiButtonInput
return <FieldMultiInput
name={name}
options={options}
value={value || this.props.default}
onChange={onChange}
/>
} else {
return <SelectInput
return <FieldSelect
options={options}
value={value || this.props.default}
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 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 = {
value: PropTypes.array,
default: PropTypes.array,
@ -39,7 +39,7 @@ class FontInput extends React.Component {
render() {
const inputs = this.values.map((value, i) => {
return <AutocompleteInput
return <FieldAutocomplete
key={i}
value={value}
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
* 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 = {
onChange: PropTypes.func.isRequired,
fieldName: PropTypes.string.isRequired,

View file

@ -2,8 +2,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames';
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import Block from './Block'
import FieldString from './FieldString'
import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript'
@ -16,7 +16,7 @@ import stringifyPretty from 'json-stringify-pretty-compact'
import '../util/codemirror-mgl';
class JSONEditor extends React.Component {
export default class FieldJsonEditor extends React.Component {
static propTypes = {
layer: PropTypes.any.isRequired,
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 PropTypes from 'prop-types'
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 = {
name: 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;
class NumberInput extends React.Component {
export default class FieldNumber extends React.Component {
static propTypes = {
value: 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 PropTypes from 'prop-types'
class SelectInput extends React.Component {
export default class FieldSelect extends React.Component {
static propTypes = {
value: PropTypes.string.isRequired,
"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 PropTypes from 'prop-types'
class StringInput extends React.Component {
export default class FieldString extends React.Component {
static propTypes = {
"data-wd-key": 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 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 = {
value: PropTypes.string,
icons: PropTypes.array,
@ -16,7 +16,7 @@ class IconInput extends React.Component {
}
render() {
return <AutocompleteInput
return <FieldAutocomplete
value={this.props.value}
options={this.props.icons.map(f => [f, f])}
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 PropTypes from 'prop-types'
import StringInput from './StringInput'
import SmallError from '../util/SmallError'
import FieldString from './FieldString'
import SmallError from './SmallError'
function validate (url) {
@ -48,7 +48,7 @@ function validate (url) {
return error;
}
class UrlInput extends React.Component {
export default class FieldUrl extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
value: PropTypes.string,
@ -88,7 +88,7 @@ class UrlInput extends React.Component {
render () {
return (
<div>
<StringInput
<FieldString
{...this.props}
onInput={this.onInput}
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 PropTypes from 'prop-types'
import { combiningFilterOps } from '../../libs/filterops.js'
import { combiningFilterOps } from '../libs/filterops.js'
import {mdiTableRowPlusAfter} from '@mdi/js';
import {latest, validate, migrate} from '@mapbox/mapbox-gl-style-spec'
import DocLabel from '../fields/DocLabel'
import SelectInput from '../inputs/SelectInput'
import InputBlock from '../inputs/InputBlock'
import FieldSelect from './FieldSelect'
import Block from './Block'
import SingleFilterEditor from './SingleFilterEditor'
import FilterEditorBlock from './FilterEditorBlock'
import Button from '../Button'
import SpecDoc from '../inputs/SpecDoc'
import ExpressionProperty from '../fields/_ExpressionProperty';
import Button from './Button'
import Doc from './Doc'
import ExpressionProperty from './_ExpressionProperty';
import {mdiFunctionVariant} from '@mdi/js';
@ -94,7 +93,7 @@ function hasNestedCombiningFilter(filter) {
return false
}
export default class CombiningFilterEditor extends React.Component {
export default class FilterEditor extends React.Component {
static propTypes = {
/** Properties of the vector layer and the available fields */
properties: PropTypes.object,
@ -244,18 +243,18 @@ export default class CombiningFilterEditor extends React.Component {
return (
<>
<InputBlock
<Block
key="top"
fieldSpec={fieldSpec}
label={"Filter"}
action={actions}
>
<SelectInput
<FieldSelect
value={combiningOp}
onChange={this.onFilterPartChanged.bind(this, 0)}
options={[["all", "every filter matches"], ["none", "no filter matches"], ["any", "any filter matches"]]}
/>
</InputBlock>
</Block>
{editorBlocks}
<div
key="buttons"
@ -276,7 +275,7 @@ export default class CombiningFilterEditor extends React.Component {
className="maputnik-doc-inline"
style={{display: this.state.showDoc ? '' : 'none'}}
>
<SpecDoc fieldSpec={fieldSpec} />
<Doc fieldSpec={fieldSpec} />
</div>
</>
);

View file

@ -1,9 +1,9 @@
import React from 'react'
import PropTypes from 'prop-types'
import Button from '../Button'
import Button from './Button'
import {MdDelete} from 'react-icons/md'
class FilterEditorBlock extends React.Component {
export default class FilterEditorBlock extends React.Component {
static propTypes = {
onDelete: PropTypes.func.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'
export default class BackgroundIcon extends React.Component {
export default class IconBackground extends React.Component {
render() {
return (
<IconBase viewBox="0 0 20 20" {...this.props}>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,13 +4,13 @@ import classnames from 'classnames'
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'
const DraggableLabel = SortableHandle((props) => {
return <div className="maputnik-layer-list-item-handle">
<LayerIcon
<IconLayer
className="layer-handle__icon"
type={props.layerType}
/>

View file

@ -3,16 +3,16 @@ import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import MapboxGl from 'mapbox-gl'
import MapboxInspect from 'mapbox-gl-inspect'
import FeatureLayerPopup from './FeatureLayerPopup'
import FeaturePropertyPopup from './FeaturePropertyPopup'
import tokens from '../../config/tokens.json'
import MapMapboxGlLayerPopup from './MapMapboxGlLayerPopup'
import MapMapboxGlFeaturePropertyPopup from './MapMapboxGlFeaturePropertyPopup'
import tokens from '../config/tokens.json'
import colors from 'mapbox-gl-inspect/lib/colors'
import Color from 'color'
import ZoomControl from '../../libs/zoomcontrol'
import { colorHighlightedLayer } from '../../libs/highlight'
import ZoomControl from '../libs/zoomcontrol'
import { colorHighlightedLayer } from '../libs/highlight'
import 'mapbox-gl/dist/mapbox-gl.css'
import '../../mapboxgl.css'
import '../../libs/mapbox-rtl'
import '../mapboxgl.css'
import '../libs/mapbox-rtl'
const IS_SUPPORTED = MapboxGl.supported();
@ -52,7 +52,7 @@ function buildInspectStyle(originalMapStyle, coloredLayers, highlightedLayer) {
return inspectStyle
}
export default class MapboxGlMap extends React.Component {
export default class MapMapboxGl extends React.Component {
static propTypes = {
onDataChange: PropTypes.func,
onLayerSelect: PropTypes.func.isRequired,
@ -175,9 +175,9 @@ export default class MapboxGlMap extends React.Component {
buildInspectStyle: (originalMapStyle, coloredLayers) => buildInspectStyle(originalMapStyle, coloredLayers, this.props.highlightedLayer),
renderPopup: features => {
if(this.props.inspectModeEnabled) {
return renderPopup(<FeaturePropertyPopup features={features} />, tmpNode);
return renderPopup(<MapMapboxGlFeaturePropertyPopup features={features} />, tmpNode);
} 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 PropTypes from 'prop-types'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import Block from './Block'
import FieldString from './FieldString'
function displayValue(value) {
if (typeof value === 'undefined' || value === null) return value;
@ -15,24 +15,24 @@ function displayValue(value) {
function renderProperties(feature) {
return Object.keys(feature.properties).map(propertyName => {
const property = feature.properties[propertyName]
return <InputBlock key={propertyName} label={propertyName}>
<StringInput value={displayValue(property)} style={{backgroundColor: 'transparent'}}/>
</InputBlock>
return <Block key={propertyName} label={propertyName}>
<FieldString value={displayValue(property)} style={{backgroundColor: 'transparent'}}/>
</Block>
})
}
function renderFeatureId(feature) {
return <InputBlock key={"feature-id"} label={"feature_id"}>
<StringInput value={displayValue(feature.id)} style={{backgroundColor: 'transparent'}} />
</InputBlock>
return <Block key={"feature-id"} label={"feature_id"}>
<FieldString value={displayValue(feature.id)} style={{backgroundColor: 'transparent'}} />
</Block>
}
function renderFeature(feature, 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>
<InputBlock key={"property-type"} label={"$type"}>
<StringInput value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
</InputBlock>
<Block key={"property-type"} label={"$type"}>
<FieldString value={feature.geometry.type} style={{backgroundColor: 'transparent'}} />
</Block>
{renderFeatureId(feature)}
{renderProperties(feature)}
</div>

View file

@ -1,6 +1,6 @@
import React from 'react'
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'
function groupFeaturesBySourceLayer(features) {
@ -96,7 +96,7 @@ class FeatureLayerPopup extends React.Component {
}}
>
{feature.layer.type &&
<LayerIcon type={feature.layer.type} style={{
<IconLayer type={feature.layer.type} style={{
width: 14,
height: 14,
paddingRight: 3

View file

@ -1,9 +1,9 @@
import React from 'react'
import {throttle} from 'lodash';
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 {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 = {
onDataChange: PropTypes.func,
mapStyle: PropTypes.object.isRequired,
@ -152,7 +152,7 @@ export default class OpenLayersMap extends React.Component {
>
×
</button>
<FeatureLayerPopup
<MapMapboxGlLayerPopup
features={this.state.selectedFeatures || []}
onLayerSelect={this.props.onLayerSelect}
/>

View file

@ -5,7 +5,7 @@ import AriaModal from 'react-aria-modal'
import classnames from 'classnames';
class Modal extends React.Component {
export default class Modal extends React.Component {
static propTypes = {
"data-wd-key": PropTypes.string,
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 PropTypes from 'prop-types'
import Button from '../Button'
import Button from './Button'
import Modal from './Modal'
import LayerTypeBlock from '../layers/LayerTypeBlock'
import LayerIdBlock from '../layers/LayerIdBlock'
import LayerSourceBlock from '../layers/LayerSourceBlock'
import LayerSourceLayerBlock from '../layers/LayerSourceLayerBlock'
import BlockType from './BlockType'
import BlockId from './BlockId'
import BlockSource from './BlockSource'
import BlockSourceLayer from './BlockSourceLayer'
class AddModal extends React.Component {
export default class ModalAdd extends React.Component {
static propTypes = {
layers: PropTypes.array.isRequired,
onLayersChange: PropTypes.func.isRequired,
@ -129,20 +129,20 @@ class AddModal extends React.Component {
className="maputnik-add-modal"
>
<div className="maputnik-add-layer">
<LayerIdBlock
<BlockId
value={this.state.id}
wdKey="add-layer.layer-id"
onChange={v => {
this.setState({ id: v })
}}
/>
<LayerTypeBlock
<BlockType
value={this.state.type}
wdKey="add-layer.layer-type"
onChange={v => this.setState({ type: v })}
/>
{this.state.type !== 'background' &&
<LayerSourceBlock
<BlockSource
sourceIds={sources}
wdKey="add-layer.layer-source-block"
value={this.state.source}
@ -150,7 +150,7 @@ class AddModal extends React.Component {
/>
}
{['background', 'raster', 'hillshade', 'heatmap'].indexOf(this.state.type) < 0 &&
<LayerSourceLayerBlock
<BlockSourceLayer
isFixed={true}
sourceLayerIds={layers}
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'
class DebugModal extends React.Component {
export default class ModalDebug extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.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 {format} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import CheckboxInput from '../inputs/CheckboxInput'
import Button from '../Button'
import Block from './Block'
import FieldString from './FieldString'
import FieldCheckbox from './FieldCheckbox'
import Button from './Button'
import Modal from './Modal'
import {MdFileDownload} from 'react-icons/md'
import style from '../../libs/style'
import fieldSpecAdditional from '../../libs/field-spec-additional'
import style from '../libs/style'
import fieldSpecAdditional from '../libs/field-spec-additional'
class ExportModal extends React.Component {
export default class ModalExport extends React.Component {
static propTypes = {
mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired,
@ -75,33 +75,33 @@ class ExportModal extends React.Component {
</p>
<div>
<InputBlock
<Block
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
>
<StringInput
<FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:mapbox_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
/>
</InputBlock>
<InputBlock
</Block>
<Block
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
>
<StringInput
<FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:openmaptiles_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/>
</InputBlock>
<InputBlock
</Block>
<Block
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
>
<StringInput
<FieldString
value={(this.props.mapStyle.metadata || {})['maputnik:thunderforest_access_token']}
onChange={this.changeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
/>
</InputBlock>
</Block>
</div>
<Button
@ -117,4 +117,3 @@ class ExportModal extends React.Component {
}
}
export default ExportModal

View file

@ -1,11 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
import Button from '../Button'
import Button from './Button'
import Modal from './Modal'
class LoadingModal extends React.Component {
export default class ModalLoading extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.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 PropTypes from 'prop-types'
import LoadingModal from './LoadingModal'
import ModalLoading from './ModalLoading'
import Modal from './Modal'
import Button from '../Button'
import Button from './Button'
import FileReaderInput from 'react-file-reader-input'
import UrlInput from '../inputs/UrlInput'
import FieldUrl from './FieldUrl'
import {MdFileUpload} from 'react-icons/md'
import {MdAddCircleOutline} from 'react-icons/md'
import style from '../../libs/style.js'
import publicStyles from '../../config/styles.json'
import style from '../libs/style.js'
import publicStyles from '../config/styles.json'
class PublicStyle extends React.Component {
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 = {
isOpen: PropTypes.bool.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>.
</p>
<form onSubmit={this.onSubmitUrl}>
<UrlInput
<FieldUrl
data-wd-key="modal:open.url.input"
type="text"
className="maputnik-input"
@ -242,7 +242,7 @@ class OpenModal extends React.Component {
</section>
</Modal>
<LoadingModal
<ModalLoading
isOpen={!!this.state.activeRequest}
title={'Loading style'}
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 {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import ArrayInput from '../inputs/ArrayInput'
import NumberInput from '../inputs/NumberInput'
import StringInput from '../inputs/StringInput'
import UrlInput from '../inputs/UrlInput'
import SelectInput from '../inputs/SelectInput'
import EnumInput from '../inputs/EnumInput'
import ColorField from '../fields/ColorField'
import Block from './Block'
import FieldArray from './FieldArray'
import FieldNumber from './FieldNumber'
import FieldString from './FieldString'
import FieldUrl from './FieldUrl'
import FieldSelect from './FieldSelect'
import FieldEnum from './FieldEnum'
import FieldColor from './FieldColor'
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 = {
mapStyle: PropTypes.object.isRequired,
onStyleChanged: PropTypes.func.isRequired,
@ -87,108 +87,108 @@ class SettingsModal extends React.Component {
title={'Style Settings'}
>
<div className="modal:settings">
<InputBlock label={"Name"} fieldSpec={latest.$root.name}>
<StringInput {...inputProps}
<Block label={"Name"} fieldSpec={latest.$root.name}>
<FieldString {...inputProps}
data-wd-key="modal:settings.name"
value={this.props.mapStyle.name}
onChange={this.changeStyleProperty.bind(this, "name")}
/>
</InputBlock>
<InputBlock label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}>
<StringInput {...inputProps}
</Block>
<Block label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}>
<FieldString {...inputProps}
data-wd-key="modal:settings.owner"
value={this.props.mapStyle.owner}
onChange={this.changeStyleProperty.bind(this, "owner")}
/>
</InputBlock>
<InputBlock label={"Sprite URL"} fieldSpec={latest.$root.sprite}>
<UrlInput {...inputProps}
</Block>
<Block label={"Sprite URL"} fieldSpec={latest.$root.sprite}>
<FieldUrl {...inputProps}
data-wd-key="modal:settings.sprite"
value={this.props.mapStyle.sprite}
onChange={this.changeStyleProperty.bind(this, "sprite")}
/>
</InputBlock>
</Block>
<InputBlock label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}>
<UrlInput {...inputProps}
<Block label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}>
<FieldUrl {...inputProps}
data-wd-key="modal:settings.glyphs"
value={this.props.mapStyle.glyphs}
onChange={this.changeStyleProperty.bind(this, "glyphs")}
/>
</InputBlock>
</Block>
<InputBlock
<Block
label={fieldSpecAdditional.maputnik.mapbox_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
>
<StringInput {...inputProps}
<FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:mapbox_access_token"
value={metadata['maputnik:mapbox_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
/>
</InputBlock>
</Block>
<InputBlock
<Block
label={fieldSpecAdditional.maputnik.maptiler_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
>
<StringInput {...inputProps}
<FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
value={metadata['maputnik:openmaptiles_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
/>
</InputBlock>
</Block>
<InputBlock
<Block
label={fieldSpecAdditional.maputnik.thunderforest_access_token.label}
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
>
<StringInput {...inputProps}
<FieldString {...inputProps}
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
value={metadata['maputnik:thunderforest_access_token']}
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
/>
</InputBlock>
</Block>
<InputBlock label={"Center"} fieldSpec={latest.$root.center}>
<ArrayInput
<Block label={"Center"} fieldSpec={latest.$root.center}>
<FieldArray
length={2}
type="number"
value={mapStyle.center}
default={latest.$root.center.default || [0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")}
/>
</InputBlock>
</Block>
<InputBlock label={"Zoom"} fieldSpec={latest.$root.zoom}>
<NumberInput
<Block label={"Zoom"} fieldSpec={latest.$root.zoom}>
<FieldNumber
{...inputProps}
value={mapStyle.zoom}
default={latest.$root.zoom.default || 0}
onChange={this.changeStyleProperty.bind(this, "zoom")}
/>
</InputBlock>
</Block>
<InputBlock label={"Bearing"} fieldSpec={latest.$root.bearing}>
<NumberInput
<Block label={"Bearing"} fieldSpec={latest.$root.bearing}>
<FieldNumber
{...inputProps}
value={mapStyle.bearing}
default={latest.$root.bearing.default}
onChange={this.changeStyleProperty.bind(this, "bearing")}
/>
</InputBlock>
</Block>
<InputBlock label={"Pitch"} fieldSpec={latest.$root.pitch}>
<NumberInput
<Block label={"Pitch"} fieldSpec={latest.$root.pitch}>
<FieldNumber
{...inputProps}
value={mapStyle.pitch}
default={latest.$root.pitch.default}
onChange={this.changeStyleProperty.bind(this, "pitch")}
/>
</InputBlock>
</Block>
<InputBlock label={"Light anchor"} fieldSpec={latest.light.anchor}>
<EnumInput
<Block label={"Light anchor"} fieldSpec={latest.light.anchor}>
<FieldEnum
{...inputProps}
name="light-anchor"
value={light.anchor}
@ -196,28 +196,28 @@ class SettingsModal extends React.Component {
default={latest.light.anchor.default}
onChange={this.changeLightProperty.bind(this, "anchor")}
/>
</InputBlock>
</Block>
<InputBlock label={"Light color"} fieldSpec={latest.light.color}>
<ColorField
<Block label={"Light color"} fieldSpec={latest.light.color}>
<FieldColor
{...inputProps}
value={light.color}
default={latest.light.color.default}
onChange={this.changeLightProperty.bind(this, "color")}
/>
</InputBlock>
</Block>
<InputBlock label={"Light intensity"} fieldSpec={latest.light.intensity}>
<NumberInput
<Block label={"Light intensity"} fieldSpec={latest.light.intensity}>
<FieldNumber
{...inputProps}
value={light.intensity}
default={latest.light.intensity.default}
onChange={this.changeLightProperty.bind(this, "intensity")}
/>
</InputBlock>
</Block>
<InputBlock label={"Light position"} fieldSpec={latest.light.position}>
<ArrayInput
<Block label={"Light position"} fieldSpec={latest.light.position}>
<FieldArray
{...inputProps}
type="number"
length={latest.light.position.length}
@ -225,31 +225,31 @@ class SettingsModal extends React.Component {
default={latest.light.position.default}
onChange={this.changeLightProperty.bind(this, "position")}
/>
</InputBlock>
</Block>
<InputBlock label={"Transition delay"} fieldSpec={latest.transition.delay}>
<NumberInput
<Block label={"Transition delay"} fieldSpec={latest.transition.delay}>
<FieldNumber
{...inputProps}
value={transition.delay}
default={latest.transition.delay.default}
onChange={this.changeTransitionProperty.bind(this, "delay")}
/>
</InputBlock>
</Block>
<InputBlock label={"Transition duration"} fieldSpec={latest.transition.duration}>
<NumberInput
<Block label={"Transition duration"} fieldSpec={latest.transition.duration}>
<FieldNumber
{...inputProps}
value={transition.duration}
default={latest.transition.duration.default}
onChange={this.changeTransitionProperty.bind(this, "duration")}
/>
</InputBlock>
</Block>
<InputBlock
<Block
label={fieldSpecAdditional.maputnik.style_renderer.label}
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
>
<SelectInput {...inputProps}
<FieldSelect {...inputProps}
data-wd-key="modal:settings.maputnik:renderer"
options={[
['mbgljs', 'MapboxGL JS'],
@ -258,7 +258,7 @@ class SettingsModal extends React.Component {
value={metadata['maputnik:renderer'] || 'mbgljs'}
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'
class ShortcutsModal extends React.Component {
export default class ModalShortcuts extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.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 {latest} from '@mapbox/mapbox-gl-style-spec'
import Modal from './Modal'
import Button from '../Button'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import SelectInput from '../inputs/SelectInput'
import SourceTypeEditor from '../sources/SourceTypeEditor'
import Button from './Button'
import Block from './Block'
import FieldString from './FieldString'
import FieldSelect from './FieldSelect'
import ModalSourcesTypeEditor from './ModalSourcesTypeEditor'
import style from '../../libs/style'
import { deleteSource, addSource, changeSource } from '../../libs/source'
import publicSources from '../../config/tilesets.json'
import style from '../libs/style'
import { deleteSource, addSource, changeSource } from '../libs/source'
import publicSources from '../config/tilesets.json'
import {MdAddCircleOutline, MdDelete} from 'react-icons/md'
@ -69,7 +69,7 @@ function editorMode(source) {
return null
}
class ActiveSourceTypeEditor extends React.Component {
class ActiveModalSourcesTypeEditor extends React.Component {
static propTypes = {
sourceId: PropTypes.string.isRequired,
source: PropTypes.object.isRequired,
@ -92,7 +92,7 @@ class ActiveSourceTypeEditor extends React.Component {
</Button>
</div>
<div className="maputnik-active-source-type-editor-content">
<SourceTypeEditor
<ModalSourcesTypeEditor
onChange={this.props.onChange}
mode={editorMode(this.props.source)}
source={this.props.source}
@ -207,14 +207,14 @@ class AddSource extends React.Component {
};
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."}}>
<StringInput
<Block label={"Source ID"} fieldSpec={{doc: "Unique ID that identifies the source and is used in the layer to reference the source."}}>
<FieldString
value={this.state.sourceId}
onChange={v => this.setState({ sourceId: v})}
/>
</InputBlock>
<InputBlock label={"Source Type"} fieldSpec={sourceTypeFieldSpec}>
<SelectInput
</Block>
<Block label={"Source Type"} fieldSpec={sourceTypeFieldSpec}>
<FieldSelect
options={[
['geojson_json', 'GeoJSON (JSON)'],
['geojson_url', 'GeoJSON (URL)'],
@ -230,8 +230,8 @@ class AddSource extends React.Component {
onChange={mode => this.setState({mode: mode, source: this.defaultSource(mode)})}
value={this.state.mode}
/>
</InputBlock>
<SourceTypeEditor
</Block>
<ModalSourcesTypeEditor
onChange={this.onChangeSource}
mode={this.state.mode}
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 = {
mapStyle: PropTypes.object.isRequired,
isOpen: PropTypes.bool.isRequired,
@ -264,7 +264,7 @@ class SourcesModal extends React.Component {
const mapStyle = this.props.mapStyle
const activeSources = Object.keys(mapStyle.sources).map(sourceId => {
const source = mapStyle.sources[sourceId]
return <ActiveSourceTypeEditor
return <ActiveModalSourcesTypeEditor
key={sourceId}
sourceId={sourceId}
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 PropTypes from 'prop-types'
import {latest} from '@mapbox/mapbox-gl-style-spec'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import UrlInput from '../inputs/UrlInput'
import NumberInput from '../inputs/NumberInput'
import SelectInput from '../inputs/SelectInput'
import DynamicArrayInput from '../inputs/DynamicArrayInput'
import ArrayInput from '../inputs/ArrayInput'
import JSONEditor from '../layers/JSONEditor'
import Block from './Block'
import FieldString from './FieldString'
import FieldUrl from './FieldUrl'
import FieldNumber from './FieldNumber'
import FieldSelect from './FieldSelect'
import FieldDynamicArray from './FieldDynamicArray'
import FieldArray from './FieldArray'
import FieldJsonEditor from './FieldJsonEditor'
class TileJSONSourceEditor extends React.Component {
@ -20,15 +20,15 @@ class TileJSONSourceEditor extends React.Component {
render() {
return <div>
<InputBlock label={"TileJSON URL"} fieldSpec={latest.source_vector.url}>
<UrlInput
<Block label={"TileJSON URL"} fieldSpec={latest.source_vector.url}>
<FieldUrl
value={this.props.source.url}
onChange={url => this.props.onChange({
...this.props.source,
url: url
})}
/>
</InputBlock>
</Block>
{this.props.children}
</div>
}
@ -50,36 +50,36 @@ class TileURLSourceEditor extends React.Component {
renderTileUrls() {
const tiles = this.props.source.tiles || [];
return <InputBlock label={"Tile URL"} fieldSpec={latest.source_vector.tiles}>
<DynamicArrayInput
return <Block label={"Tile URL"} fieldSpec={latest.source_vector.tiles}>
<FieldDynamicArray
type="url"
value={tiles}
onChange={this.changeTileUrls.bind(this)}
/>
</InputBlock>
</Block>
}
render() {
return <div>
{this.renderTileUrls()}
<InputBlock label={"Min Zoom"} fieldSpec={latest.source_vector.minzoom}>
<NumberInput
<Block label={"Min Zoom"} fieldSpec={latest.source_vector.minzoom}>
<FieldNumber
value={this.props.source.minzoom || 0}
onChange={minzoom => this.props.onChange({
...this.props.source,
minzoom: minzoom
})}
/>
</InputBlock>
<InputBlock label={"Max Zoom"} fieldSpec={latest.source_vector.maxzoom}>
<NumberInput
</Block>
<Block label={"Max Zoom"} fieldSpec={latest.source_vector.maxzoom}>
<FieldNumber
value={this.props.source.maxzoom || 22}
onChange={maxzoom => this.props.onChange({
...this.props.source,
maxzoom: maxzoom
})}
/>
</InputBlock>
</Block>
{this.props.children}
</div>
@ -104,26 +104,26 @@ class ImageSourceEditor extends React.Component {
}
return <div>
<InputBlock label={"Image URL"} fieldSpec={latest.source_image.url}>
<UrlInput
<Block label={"Image URL"} fieldSpec={latest.source_image.url}>
<FieldUrl
value={this.props.source.url}
onChange={url => this.props.onChange({
...this.props.source,
url,
})}
/>
</InputBlock>
</Block>
{["top left", "top right", "bottom right", "bottom left"].map((label, idx) => {
return (
<InputBlock label={`Coord ${label}`} key={label}>
<ArrayInput
<Block label={`Coord ${label}`} key={label}>
<FieldArray
length={2}
type="number"
value={this.props.source.coordinates[idx]}
default={[0, 0]}
onChange={(val) => changeCoord(idx, val)}
/>
</InputBlock>
</Block>
);
})}
</div>
@ -155,25 +155,25 @@ class VideoSourceEditor extends React.Component {
}
return <div>
<InputBlock label={"Video URL"} fieldSpec={latest.source_video.urls}>
<DynamicArrayInput
<Block label={"Video URL"} fieldSpec={latest.source_video.urls}>
<FieldDynamicArray
type="string"
value={this.props.source.urls}
default={""}
onChange={changeUrls}
/>
</InputBlock>
</Block>
{["top left", "top right", "bottom right", "bottom left"].map((label, idx) => {
return (
<InputBlock label={`Coord ${label}`} key={label}>
<ArrayInput
<Block label={`Coord ${label}`} key={label}>
<FieldArray
length={2}
type="number"
value={this.props.source.coordinates[idx]}
default={[0, 0]}
onChange={val => changeCoord(idx, val)}
/>
</InputBlock>
</Block>
);
})}
</div>
@ -187,27 +187,27 @@ class GeoJSONSourceUrlEditor extends React.Component {
}
render() {
return <InputBlock label={"GeoJSON URL"} fieldSpec={latest.source_geojson.data}>
<UrlInput
return <Block label={"GeoJSON URL"} fieldSpec={latest.source_geojson.data}>
<FieldUrl
value={this.props.source.data}
onChange={data => this.props.onChange({
...this.props.source,
data: data
})}
/>
</InputBlock>
</Block>
}
}
class GeoJSONSourceJSONEditor extends React.Component {
class GeoJSONSourceFieldJsonEditor extends React.Component {
static propTypes = {
source: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
}
render() {
return <InputBlock label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
<JSONEditor
return <Block label={"GeoJSON"} fieldSpec={latest.source_geojson.data}>
<FieldJsonEditor
layer={this.props.source.data}
maxHeight={200}
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 = {
mode: PropTypes.string.isRequired,
source: PropTypes.object.isRequired,
@ -240,15 +240,15 @@ class SourceTypeEditor extends React.Component {
}
switch(this.props.mode) {
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 'tilexyz_vector': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster': return <TileURLSourceEditor {...commonProps} />
case 'tilejson_raster-dem': return <TileJSONSourceEditor {...commonProps} />
case 'tilexyz_raster-dem': return <TileURLSourceEditor {...commonProps}>
<InputBlock label={"Encoding"} fieldSpec={latest.source_raster_dem.encoding}>
<SelectInput
<Block label={"Encoding"} fieldSpec={latest.source_raster_dem.encoding}>
<FieldSelect
options={Object.keys(latest.source_raster_dem.encoding.values)}
onChange={encoding => this.props.onChange({
...this.props.source,
@ -256,7 +256,7 @@ class SourceTypeEditor extends React.Component {
})}
value={this.props.source.encoding || latest.source_raster_dem.encoding.default}
/>
</InputBlock>
</Block>
</TileURLSourceEditor>
case 'image': return <ImageSourceEditor {...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 PropTypes from 'prop-types'
import Button from '../Button'
import Button from './Button'
import Modal from './Modal'
import logoImage from 'maputnik-design/logos/logo-color.svg'
class SurveyModal extends React.Component {
export default class ModalSurvey extends React.Component {
static propTypes = {
isOpen: PropTypes.bool.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 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']
/** 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 fieldType = fieldName in paint ? 'paint' : 'layout';
return <FunctionSpecField
return <FieldFunction
errors={errors}
onChange={this.onPropertyChange}
key={fieldName}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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