mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2025-01-29 22:54:39 +01:00
Merge pull request #522 from orangemug/feature/add-range-slider
Add range input for minZoom/maxZoom
This commit is contained in:
commit
8911f83ef3
5 changed files with 146 additions and 16 deletions
|
@ -1,6 +1,8 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
let IDX = 0;
|
||||
|
||||
class NumberInput extends React.Component {
|
||||
static propTypes = {
|
||||
value: PropTypes.number,
|
||||
|
@ -8,37 +10,52 @@ class NumberInput extends React.Component {
|
|||
min: PropTypes.number,
|
||||
max: PropTypes.number,
|
||||
onChange: PropTypes.func,
|
||||
allowRange: PropTypes.bool,
|
||||
rangeStep: PropTypes.number,
|
||||
wdKey: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
rangeStep: 1
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
uuid: IDX++,
|
||||
editing: false,
|
||||
value: props.value,
|
||||
dirtyValue: props.value,
|
||||
}
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (!state.editing) {
|
||||
return {
|
||||
value: props.value
|
||||
value: props.value,
|
||||
dirtyValue: props.value,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
return null;
|
||||
}
|
||||
|
||||
changeValue(newValue) {
|
||||
this.setState({editing: true});
|
||||
const value = (newValue === "" || newValue === undefined) ?
|
||||
undefined :
|
||||
parseFloat(newValue);
|
||||
|
||||
const hasChanged = this.state.value !== value
|
||||
const hasChanged = this.props.value !== value;
|
||||
if(this.isValid(value) && hasChanged) {
|
||||
this.props.onChange(value)
|
||||
this.setState({
|
||||
dirtyValue: newValue,
|
||||
});
|
||||
}
|
||||
this.setState({ value: newValue })
|
||||
|
||||
this.setState({
|
||||
value: newValue,
|
||||
})
|
||||
}
|
||||
|
||||
isValid(v) {
|
||||
|
@ -79,16 +96,112 @@ class NumberInput extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
onChangeRange = (e) => {
|
||||
let value = parseFloat(e.target.value, 10);
|
||||
const step = this.props.rangeStep;
|
||||
let dirtyValue = value;
|
||||
|
||||
if(step) {
|
||||
// Can't do this with the <input/> range step attribute else we won't be able to set a high precision value via the text input.
|
||||
const snap = value % step;
|
||||
|
||||
// Round up/down to step
|
||||
if (this._keyboardEvent) {
|
||||
// If it's keyboard event we might get a low positive/negative value,
|
||||
// for example we might go from 13 to 13.23, however because we know
|
||||
// that came from a keyboard event we always want to increase by a
|
||||
// single step value.
|
||||
if (value < this.state.dirtyValue) {
|
||||
value = this.state.value - step;
|
||||
}
|
||||
else {
|
||||
value = this.state.value + step
|
||||
}
|
||||
dirtyValue = value;
|
||||
}
|
||||
else {
|
||||
if (snap < step/2) {
|
||||
value = value - snap;
|
||||
}
|
||||
else {
|
||||
value = value + (step - snap);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this._keyboardEvent = false;
|
||||
|
||||
// Clamp between min/max
|
||||
value = Math.max(this.props.min, Math.min(this.props.max, value));
|
||||
|
||||
this.setState({value, dirtyValue});
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <input
|
||||
spellCheck="false"
|
||||
className="maputnik-number"
|
||||
placeholder={this.props.default}
|
||||
value={this.state.value === undefined ? "" : this.state.value}
|
||||
onChange={e => this.changeValue(e.target.value)}
|
||||
onBlur={this.resetValue}
|
||||
required={this.props.required}
|
||||
/>
|
||||
if(
|
||||
this.props.hasOwnProperty("min") && this.props.hasOwnProperty("max") &&
|
||||
this.props.min !== undefined && this.props.max !== undefined &&
|
||||
this.props.allowRange
|
||||
) {
|
||||
const dirtyValue = this.state.dirtyValue === undefined ? this.props.default : this.state.dirtyValue
|
||||
const value = this.state.value === undefined ? "" : this.state.value;
|
||||
|
||||
return <div className="maputnik-number-container">
|
||||
<input
|
||||
className="maputnik-number-range"
|
||||
key="range"
|
||||
type="range"
|
||||
max={this.props.max}
|
||||
min={this.props.min}
|
||||
step="any"
|
||||
spellCheck="false"
|
||||
value={dirtyValue}
|
||||
aria-hidden="true"
|
||||
onChange={this.onChangeRange}
|
||||
onKeyDown={() => {
|
||||
this._keyboardEvent = true;
|
||||
}}
|
||||
onPointerDown={() => {
|
||||
this.setState({editing: true});
|
||||
}}
|
||||
onPointerUp={() => {
|
||||
// Safari doesn't get onBlur event
|
||||
this.setState({editing: false});
|
||||
}}
|
||||
onBlur={() => {
|
||||
this.setState({editing: false});
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
key="text"
|
||||
type="text"
|
||||
spellCheck="false"
|
||||
className="maputnik-number"
|
||||
placeholder={this.props.default}
|
||||
value={value}
|
||||
onChange={e => {
|
||||
if (!this.state.editing) {
|
||||
this.changeValue(e.target.value);
|
||||
}
|
||||
}}
|
||||
onBlur={this.resetValue}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
const value = this.state.value === undefined ? "" : this.state.value;
|
||||
|
||||
return <input
|
||||
spellCheck="false"
|
||||
className="maputnik-number"
|
||||
placeholder={this.props.default}
|
||||
value={value}
|
||||
onChange={e => this.changeValue(e.target.value)}
|
||||
onBlur={this.resetValue}
|
||||
required={this.props.required}
|
||||
/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ class MaxZoomBlock extends React.Component {
|
|||
data-wd-key="max-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
allowRange={true}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
min={latest.layer.maxzoom.minimum}
|
||||
|
|
|
@ -16,6 +16,7 @@ class MinZoomBlock extends React.Component {
|
|||
data-wd-key="min-zoom"
|
||||
>
|
||||
<NumberInput
|
||||
allowRange={true}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
min={latest.layer.minzoom.minimum}
|
||||
|
|
|
@ -27,6 +27,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
.maputnik-number-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.maputnik-number-range {
|
||||
width: calc(100% - 4.5em);
|
||||
margin-right: 0.5em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.maputnik-number {
|
||||
@extend .maputnik-input;
|
||||
}
|
||||
|
@ -178,3 +188,8 @@
|
|||
margin-bottom: $margin-3;
|
||||
}
|
||||
}
|
||||
|
||||
.maputnik-input-block-content {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ describe("layers", function() {
|
|||
|
||||
const elem = $(wd.$("layer-list-item:background:"+bgId));
|
||||
elem.click();
|
||||
browser.setValueSafe(wd.$("min-zoom", "input"), 1)
|
||||
browser.setValueSafe(wd.$("min-zoom", 'input[type="text"]'), 1)
|
||||
const elem2 = $(wd.$("layer-editor.layer-id", "input"));
|
||||
elem2.click();
|
||||
|
||||
|
@ -232,7 +232,7 @@ describe("layers", function() {
|
|||
|
||||
const elem = $(wd.$("layer-list-item:background:"+bgId));
|
||||
elem.click();
|
||||
browser.setValueSafe(wd.$("max-zoom", "input"), 1)
|
||||
browser.setValueSafe(wd.$("max-zoom", 'input[type="text"]'), 1)
|
||||
const elem2 = $(wd.$("layer-editor.layer-id", "input"));
|
||||
elem2.click();
|
||||
|
||||
|
|
Loading…
Reference in a new issue