diff --git a/src/components/fields/PropertyGroup.jsx b/src/components/fields/PropertyGroup.jsx index efb567b..3557724 100644 --- a/src/components/fields/PropertyGroup.jsx +++ b/src/components/fields/PropertyGroup.jsx @@ -59,7 +59,7 @@ export default class PropertyGroup extends React.Component { onChange={this.onPropertyChange} key={fieldName} fieldName={fieldName} - value={fieldValue === undefined ? fieldSpec.default : fieldValue} + value={fieldValue} fieldSpec={fieldSpec} /> }) diff --git a/src/components/inputs/ArrayInput.jsx b/src/components/inputs/ArrayInput.jsx index 69a01d5..bb406d8 100644 --- a/src/components/inputs/ArrayInput.jsx +++ b/src/components/inputs/ArrayInput.jsx @@ -12,29 +12,89 @@ class ArrayInput extends React.Component { onChange: PropTypes.func, } - changeValue(idx, newValue) { - console.log(idx, newValue) - const values = this.values.slice(0) - values[idx] = newValue - this.props.onChange(values) + static defaultProps = { + value: [], + default: [], } - get values() { - return this.props.value || this.props.default || [] + constructor (props) { + super(props); + this.state = { + value: this.props.value.slice(0), + // This is so we can compare changes in getDerivedStateFromProps + initialPropsValue: this.props.value.slice(0), + }; + } + + static getDerivedStateFromProps(props, state) { + const value = []; + const initialPropsValue = state.initialPropsValue.slice(0); + + Array(props.length).fill(null).map((_, i) => { + if (props.value[i] === state.initialPropsValue[i]) { + value[i] = state.value[i]; + } + else { + value[i] = state.value[i]; + initialPropsValue[i] = state.value[i]; + } + }) + + return { + value, + initialPropsValue, + }; + } + + isComplete (value) { + return Array(this.props.length).fill(null).every((_, i) => { + const val = value[i] + return !(val === undefined || val === ""); + }); + } + + changeValue(idx, newValue) { + const value = this.state.value.slice(0); + value[idx] = newValue; + + this.setState({ + value, + }, () => { + if (this.isComplete(value)) { + this.props.onChange(value); + } + else { + // Unset until complete + this.props.onChange(undefined); + } + }); } render() { - const inputs = this.values.map((v, i) => { + const {value} = this.state; + + const containsValues = ( + value.length > 0 && + !value.every(val => { + return (val === "" || val === undefined) + }) + ); + + const inputs = Array(this.props.length).fill(null).map((_, i) => { if(this.props.type === 'number') { return } else { return } diff --git a/src/components/inputs/EnumInput.jsx b/src/components/inputs/EnumInput.jsx index e704110..9472a2f 100644 --- a/src/components/inputs/EnumInput.jsx +++ b/src/components/inputs/EnumInput.jsx @@ -29,13 +29,13 @@ class EnumInput extends React.Component { if(options.length <= 3 && optionsLabelLength(options) <= 20) { return } else { return } diff --git a/src/components/inputs/NumberInput.jsx b/src/components/inputs/NumberInput.jsx index 0de1fa4..b2d7dec 100644 --- a/src/components/inputs/NumberInput.jsx +++ b/src/components/inputs/NumberInput.jsx @@ -8,6 +8,7 @@ class NumberInput extends React.Component { min: PropTypes.number, max: PropTypes.number, onChange: PropTypes.func, + required: PropTypes.bool, } constructor(props) { @@ -65,7 +66,7 @@ class NumberInput extends React.Component { this.setState({editing: false}); // Reset explicitly to default value if value has been cleared if(this.state.value === "") { - return this.changeValue(this.props.default) + return; } // If set value is invalid fall back to the last valid value from props or at last resort the default value @@ -73,7 +74,7 @@ class NumberInput extends React.Component { if(this.isValid(this.props.value)) { this.changeValue(this.props.value) } else { - this.changeValue(this.props.default) + this.changeValue(undefined); } } } @@ -83,9 +84,10 @@ class NumberInput extends React.Component { spellCheck="false" className="maputnik-number" placeholder={this.props.default} - value={this.state.value || ""} + value={this.state.value === undefined ? "" : this.state.value} onChange={e => this.changeValue(e.target.value)} onBlur={this.resetValue} + required={this.props.required} /> } } diff --git a/src/components/inputs/StringInput.jsx b/src/components/inputs/StringInput.jsx index 875a454..e160d69 100644 --- a/src/components/inputs/StringInput.jsx +++ b/src/components/inputs/StringInput.jsx @@ -9,6 +9,7 @@ class StringInput extends React.Component { default: PropTypes.string, onChange: PropTypes.func, multi: PropTypes.bool, + required: PropTypes.bool, } constructor(props) { @@ -50,7 +51,7 @@ class StringInput extends React.Component { spellCheck: !(tag === "input"), className: classes.join(" "), style: this.props.style, - value: this.state.value, + value: this.state.value === undefined ? "" : this.state.value, placeholder: this.props.default, onChange: e => { this.setState({ @@ -63,7 +64,8 @@ class StringInput extends React.Component { this.setState({editing: false}); this.props.onChange(this.state.value); } - } + }, + required: this.props.required, }); } } diff --git a/src/components/modals/SettingsModal.jsx b/src/components/modals/SettingsModal.jsx index 04ca51d..de4ced5 100644 --- a/src/components/modals/SettingsModal.jsx +++ b/src/components/modals/SettingsModal.jsx @@ -3,8 +3,12 @@ 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 SelectInput from '../inputs/SelectInput' +import EnumInput from '../inputs/EnumInput' +import ColorField from '../fields/ColorField' import Modal from './Modal' class SettingsModal extends React.Component { @@ -16,18 +20,64 @@ class SettingsModal extends React.Component { onOpenToggle: PropTypes.func.isRequired, } + changeTransitionProperty(property, value) { + const transition = { + ...this.props.mapStyle.transition, + } + + if (value === undefined) { + delete transition[property]; + } + else { + transition[property] = value; + } + + this.props.onStyleChanged({ + ...this.props.mapStyle, + transition, + }); + } + + changeLightProperty(property, value) { + const light = { + ...this.props.mapStyle.light, + } + + if (value === undefined) { + delete light[property]; + } + else { + light[property] = value; + } + + this.props.onStyleChanged({ + ...this.props.mapStyle, + light, + }); + } + changeStyleProperty(property, value) { const changedStyle = { ...this.props.mapStyle, - [property]: value + }; + + if (value === undefined) { + delete changedStyle[property]; } - this.props.onStyleChanged(changedStyle) + else { + changedStyle[property] = value; + } + this.props.onStyleChanged(changedStyle); } render() { const metadata = this.props.mapStyle.metadata || {} - const {onChangeMetadataProperty} = this.props; + const {onChangeMetadataProperty, mapStyle} = this.props; const inputProps = { } + + const light = this.props.mapStyle.light || {}; + const transition = this.props.mapStyle.transition || {}; + return + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } diff --git a/src/styles/_input.scss b/src/styles/_input.scss index 90e84ed..7caaa77 100644 --- a/src/styles/_input.scss +++ b/src/styles/_input.scss @@ -11,6 +11,11 @@ border: none; background-color: $color-gray; color: lighten($color-lowgray, 12); + + &:invalid { + border: solid 1px #B71C1C; + border-radius: 2px; + } } .maputnik-string {