Skip to content

Commit

Permalink
feat(Slider): add ratio, step to marks
Browse files Browse the repository at this point in the history
add ratio to custom mark range width
add step to custom step in diff mark range

fix #58
close #59
  • Loading branch information
ZxBing0066 committed Oct 30, 2018
1 parent 2fffced commit 717970f
Show file tree
Hide file tree
Showing 6 changed files with 536 additions and 144 deletions.
221 changes: 195 additions & 26 deletions src/components/Slider/Slider.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import RcSlider from 'rce-slider';

import Tooltip from 'components/Tooltip';
import NumberInput from 'components/NumberInput';
import uncontrolledDecorator from 'src/decorators/uncontrolled';

import { prefixCls, SliderWrap } from './style';

const Handle = RcSlider.Handle;
Expand All @@ -20,7 +22,7 @@ const handle = props => {
visible={dragging && tipFormatter !== null}
placement="top"
key={index}
tooltipStyle="black"
theme="dark"
getPopupContainer={triggerNode => triggerNode}
forceAlignWhenUpdate
>
Expand All @@ -47,6 +49,9 @@ const getPrecision = n => {
return precision;
};

const sliderSplit = 300;

@uncontrolledDecorator({})
class Slider extends Component {
static propTypes = {
/** 值,受控 */
Expand Down Expand Up @@ -85,31 +90,187 @@ class Slider extends Component {
};
static defaultProps = {
onChange: () => {},
defaultValue: 0,
step: 1,
size: 'md',
min: 0,
max: 100
};
state = {
value: 'value' in this.props ? this.props.value : this.props.defaultValue
};
componentWillReceiveProps = nextProps => {
if ('value' in nextProps) {
constructor(props) {
super(props);
this.state = {};
this.state.cacheMarks = { ...props.marks };
this.state.marks = this.computeMarks(props.marks);
this.state.marksForSlider = this.computeMarksForSlider(this.state.marks);
this.cache = {
valueToSliderValueMap: {},
sliderValueToValueMap: {}
};
}
componentWillReceiveProps(nextProps) {
if (!_.isEqual(nextProps.marks, this.props.marks)) {
const marks = this.computeMarks(nextProps.marks);
this.setState({
value: nextProps.value
cacheMarks: { ...nextProps.marks },
marks,
marksForSlider: this.computeMarksForSlider(marks)
});
this.cache = {
valueToSliderValueMap: {},
sliderValueToValueMap: {}
};
}
}
computeMarks = _marks => {
if (_.isEmpty(_marks)) {
return null;
}
const { min, max, step } = this.props;
const values = _.sortBy(_.keys(_marks), v => +v);
const l = values.length;
const marks = [];
const indexWithRatio = [];
const indexWithoutRatio = [];
let usedRatio = 0;
let usedRange = 0;
if (+values[l - 1] !== +max) {
values.push(max);
}
_.each(values, (v, i) => {
let info = _marks[v];
v = +v;
if (!_.isObject(info)) {
info = {
label: info
};
}
const range = [];
if (i === 0) {
range[0] = min;
} else {
range[0] = +values[i - 1];
}
range[1] = v;
info.range = range;
info.value = v;
marks.push(info);
if (info.ratio) {
indexWithRatio.push(i);
usedRatio += info.ratio;
usedRange += range[1] - range[0];
} else {
indexWithoutRatio.push(i);
}
if (!('step' in info)) {
info.step = step;
}
});
const remainRange = max - min - usedRange;
_.each(indexWithoutRatio, i => {
const info = marks[i];
info.ratio = (info.range[1] - info.range[0]) / remainRange * (100 - usedRatio);
});
let ratioBefore = 0;
_.each(marks, mark => {
mark.ratioBefore = ratioBefore;
ratioBefore += mark.ratio;
});
return marks;
};
computeMarksForSlider = _marks => {
if (_.isEmpty(_marks)) {
return null;
}
const { max } = this.props;
const marks = {};
let baseRatio = 0;
_.each(_marks, _mark => {
if (_mark.label == null) {
return;
}
const value = (baseRatio += _mark.ratio) / 100 * sliderSplit;
const mark = {
label: _mark.label,
style: _mark.style
};
if (_mark.value == max) {
mark.style = { ..._mark.style, borderRight: 'none' };
}
marks[value] = mark;
});
return marks;
};
onChange = value => {
onNumberChange = v => {
const { onChange } = this.props;
value = this.computeValidNumber(value);
this.setState({
value
});
const value = this.computeValidNumber(v);
onChange(value);
};
onSliderChange = v => {
const { onChange } = this.props;
const value = this.translateSliderValueToValue(v);
onChange(value);
};
computeValidNumber = number => {
const { step, min, max } = this.props;
translateSliderValueToValue = v => {
if (v in this.cache.sliderValueToValueMap) {
return this.cache.sliderValueToValueMap[v];
}
const { marks } = this.state;
const { min, max, step } = this.props;
let value;
if (_.isEmpty(marks)) {
value = this.computeValidNumber(min + (max - min) * v / sliderSplit, {
step,
min,
max
});
} else if (v == sliderSplit) {
value = max;
} else {
const vRatio = v / sliderSplit * 100;
let mark = _.find(marks, mark => {
const { ratioBefore, ratio } = mark;
if (vRatio >= ratioBefore && vRatio < ratioBefore + ratio) {
return true;
}
});
if (!mark) mark = marks[0];
const { ratioBefore, range, ratio } = mark;
value = this.computeValidNumber((vRatio - ratioBefore) / ratio * (range[1] - range[0]) + range[0], {
min: range[0],
max: range[1],
step: mark.step
});
}
this.cache.sliderValueToValueMap[v] = value;
return value;
};
translateValueToSliderValue = v => {
if (v in this.cache.valueToSliderValueMap) {
return this.cache.valueToSliderValueMap[v];
}
const { marks } = this.state;
const { min, max } = this.props;
let value;
if (_.isEmpty(marks)) {
value = (v - min) / (max - min) * sliderSplit;
} else if (v == max) {
value = sliderSplit;
} else {
let mark = _.find(marks, mark => {
const { range } = mark;
if (v >= range[0] && v < range[1]) {
return true;
}
});
if (!mark) mark = marks[0];
const { range, ratio, ratioBefore } = mark;
value = ((v - range[0]) / (range[1] - range[0]) * ratio + ratioBefore) / 100 * sliderSplit;
}
this.cache.valueToSliderValueMap[v] = value;
return value;
};
computeValidNumber = (number, options) => {
let { step, min, max } = options || this.props;
if (number < min) {
number = min;
}
Expand All @@ -133,7 +294,7 @@ class Slider extends Component {
render() {
/* eslint-disable no-unused-vars */
const {
value: _value,
value,
defaultValue,
onChange,
className,
Expand All @@ -147,53 +308,61 @@ class Slider extends Component {
numberInput: _inputProps,
tipFormatter,
size,
marks: _marks,
...rest
} = this.props;
/* eslint-enable no-unused-vars */
const { value } = this.state;
const { marksForSlider } = this.state;
const sharingProps = {
disabled,
min,
max,
step,
value
disabled
};
const sliderProps = {
...rest,
className: sliderClassName,
style: sliderStyle,
onChange: this.onChange
onChange: this.onSliderChange
};
const inputProps = {
..._inputProps,
onNumberChange: this.onChange,
min,
max,
step,
value,
onNumberChange: this.onNumberChange,
size,
computeValidNumber: this.computeValidNumber
};
return (
<SliderWrap style={style} size={size}>
<RcSlider
min={0}
max={sliderSplit}
step={1}
value={this.translateValueToSliderValue(value)}
prefixCls={prefixCls}
handle={handleProps =>
handle({
...handleProps,
value,
tipFormatter
})
}
computeMarkStyle={(info, vertical) => {
const { point, min, range, markWidth } = info;
const { point, min, range } = info;
const bottomStyle = {
marginBottom: '-50%',
bottom: `${(point - min) / range * 100}%`
};
const leftStyle = {
width: `${markWidth / 2}%`,
marginLeft: `${-markWidth / 2}%`,
width: `30%`,
marginLeft: `-30%`,
overflow: 'hidden',
left: `${(point - min) / range * 100}%`
};
const style = vertical ? bottomStyle : leftStyle;
return style;
}}
marks={marksForSlider || {}}
{...sliderProps}
{...sharingProps}
/>
Expand Down
32 changes: 32 additions & 0 deletions src/components/Slider/__demo__/marks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ class Demo extends React.Component {
}}
/>
</div>
<div className="demo-wrap">
<Slider
min={10}
max={1000}
defaultValue={12}
marks={{
50: {
label: '50',
step: 2,
ratio: 10
},
100: {
label: '100',
step: 5,
ratio: 10
},
200: {
label: '200',
step: 10,
ratio: 15
},
400: {
label: '400',
step: 20
},
1000: {
label: '1000',
step: 50
}
}}
/>
</div>
</div>
);
}
Expand Down
Loading

0 comments on commit 717970f

Please sign in to comment.