Open Kikobeats opened 7 years ago
@Kikobeats you can set this up with a custom algorithm - here's a component I made (has some redux-form wrapper cruft) that lets you pass a float precision integer and will render a label and set values accordingly (note the labels must use the raw string output of .toFixed so that the trailing zeros aren't decimated and the value must wrap the toFixed in parseFloat so that they remain a number type). The algorithm is essentially the same as the linear algorithm but takes the float precision as a curried value and returns a setValue param fn in accordance with the desired precision:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Rheostat from 'rheostat';
import 'rheostat/initialize';
import { Field } from 'redux-form';
import glamorous from 'glamorous';
import { defaultStyle } from './RangeSlider.style';
import Handle from './Handle';
export const algorithm = (precision) => ({
getPosition: (value, min, max) => ((value - min) / (max - min)) * 100,
getValue: (pos, min, max) => {
const decimal = pos / 100;
if (pos === 0) {
return min;
}
if (pos === 100) {
return max;
}
return parseFloat((((max - min) * decimal) + min).toFixed(precision));
}
});
export class _RangeSlider extends Component {
static propTypes = {
className: PropTypes.string,
input: PropTypes.object,
meta: PropTypes.object,
min: PropTypes.number.isRequired,
max: PropTypes.number.isRequired,
floatPrecision: PropTypes.number.isRequired
};
static defaultProps = {
meta: {},
input: {}
};
handleUpdate = (onChange) => ({ values }) => (
onChange(values)
);
render() {
const {
className,
input: {
onChange,
value,
name
},
meta: {
error,
initial,
dirty,
},
min,
max,
floatPrecision
} = this.props;
const [lowValue, highValue] = value;
const renderValueLabel = (val) => parseFloat(val).toFixed(floatPrecision);
return (
<div className={classNames(className, { dirty, initial, error })}>
<div className="title">
<p>{name}</p>
</div>
<Rheostat
min={min}
max={max}
values={value || [min, max]}
onValuesUpdated={this.handleUpdate(onChange)}
handle={Handle}
algorithm={algorithm(floatPrecision)}
/>
<div className="value-display">
<p className="value-low">{renderValueLabel(lowValue || min)}</p>
<p className="value-high">{renderValueLabel(highValue || max)}</p>
</div>
</div>
);
}
}
export const StyledRangeSlider = glamorous(_RangeSlider)(defaultStyle);
export const _RangeSliderField = ({
min,
max,
name,
floatPrecision
}) => (
<Field name={name} defaultValue={[min, max]} component={StyledRangeSlider} props={{ min, max, floatPrecision }} />
);
_RangeSliderField.propTypes = {
name: PropTypes.string.isRequired,
min: PropTypes.number,
max: PropTypes.number,
floatPrecision: PropTypes.number
};
_RangeSliderField.defaultProps = {
min: 0,
max: 100,
floatPrecision: 0
};
export default _RangeSliderField;
Another workaround would be to use proportions:
const [value, setValue] = useState(7.5);
const totalPoints = 20; // there are 20 + 1 points in [0, 0.5, 1, 1.5, ... 9.5, 10]
const maxValue = 10;
return (
<Rheostat
min={0}
max={totalPoints}
values={[(value * totalPoints) / maxValue]}
onValuesUpdated={({ values }) => {
let [value] = values;
value = (value * maxValue) / totalPoints;
setValue(value);
}}
snap
/>
);
Currently I have something like:
and yeah, I can move the slider from
{0, 10}
values, but only for integer values.Could be possible setup for
.5
values? like:Ideally, something similar to lodash#range