Merge pull request #563 from orangemug/feature/more-root-property-support

Added support for more root level properties
This commit is contained in:
Orange Mug 2019-10-21 10:18:37 +01:00 committed by GitHub
commit c45cf2f0c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 236 additions and 21 deletions

View file

@ -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}
/>
})

View file

@ -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 <NumberInput
key={i}
value={v}
default={containsValues ? undefined : this.props.default[i]}
value={value[i]}
required={containsValues ? true : false}
onChange={this.changeValue.bind(this, i)}
/>
} else {
return <StringInput
key={i}
value={v}
default={containsValues ? undefined : this.props.default[i]}
value={value[i]}
required={containsValues ? true : false}
onChange={this.changeValue.bind(this, i)}
/>
}

View file

@ -29,13 +29,13 @@ class EnumInput extends React.Component {
if(options.length <= 3 && optionsLabelLength(options) <= 20) {
return <MultiButtonInput
options={options}
value={value}
value={value || this.props.default}
onChange={onChange}
/>
} else {
return <SelectInput
options={options}
value={value}
value={value || this.props.default}
onChange={onChange}
/>
}

View file

@ -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}
/>
}
}

View file

@ -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,
});
}
}

View file

@ -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 <Modal
data-wd-key="modal-settings"
isOpen={this.props.isOpen}
@ -89,6 +139,100 @@ class SettingsModal extends React.Component {
/>
</InputBlock>
<InputBlock label={"Center"} doc={latest.$root.center.doc}>
<ArrayInput
length={2}
type="number"
value={mapStyle.center}
default={latest.$root.center.default || [0, 0]}
onChange={this.changeStyleProperty.bind(this, "center")}
/>
</InputBlock>
<InputBlock label={"Zoom"} doc={latest.$root.zoom.doc}>
<NumberInput
{...inputProps}
value={mapStyle.zoom}
default={latest.$root.zoom.default || 0}
onChange={this.changeStyleProperty.bind(this, "zoom")}
/>
</InputBlock>
<InputBlock label={"Bearing"} doc={latest.$root.bearing.doc}>
<NumberInput
{...inputProps}
value={mapStyle.bearing}
default={latest.$root.bearing.default}
onChange={this.changeStyleProperty.bind(this, "bearing")}
/>
</InputBlock>
<InputBlock label={"Pitch"} doc={latest.$root.pitch.doc}>
<NumberInput
{...inputProps}
value={mapStyle.pitch}
default={latest.$root.pitch.default}
onChange={this.changeStyleProperty.bind(this, "pitch")}
/>
</InputBlock>
<InputBlock label={"Light anchor"} doc={latest.light.anchor.doc}>
<EnumInput
{...inputProps}
value={light.anchor}
options={Object.keys(latest.light.anchor.values)}
default={latest.light.anchor.default}
onChange={this.changeLightProperty.bind(this, "anchor")}
/>
</InputBlock>
<InputBlock label={"Light color"} doc={latest.light.color.doc}>
<ColorField
{...inputProps}
value={light.color}
default={latest.light.color.default}
onChange={this.changeLightProperty.bind(this, "color")}
/>
</InputBlock>
<InputBlock label={"Light intensity"} doc={latest.light.intensity.doc}>
<NumberInput
{...inputProps}
value={light.intensity}
default={latest.light.intensity.default}
onChange={this.changeLightProperty.bind(this, "intensity")}
/>
</InputBlock>
<InputBlock label={"Light position"} doc={latest.light.position.doc}>
<ArrayInput
{...inputProps}
type="number"
length={latest.light.position.length}
value={light.position}
default={latest.light.position.default}
onChange={this.changeLightProperty.bind(this, "position")}
/>
</InputBlock>
<InputBlock label={"Transition delay"} doc={latest.transition.delay.doc}>
<NumberInput
{...inputProps}
value={transition.delay}
default={latest.transition.delay.default}
onChange={this.changeTransitionProperty.bind(this, "delay")}
/>
</InputBlock>
<InputBlock label={"Transition duration"} doc={latest.transition.duration.doc}>
<NumberInput
{...inputProps}
value={transition.duration}
default={latest.transition.duration.default}
onChange={this.changeTransitionProperty.bind(this, "duration")}
/>
</InputBlock>
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
<SelectInput {...inputProps}
data-wd-key="modal-settings.maputnik:renderer"
@ -101,6 +245,8 @@ class SettingsModal extends React.Component {
/>
</InputBlock>
</div>
</Modal>
}

View file

@ -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 {