chowa
Version:
UI component library based on React
292 lines (291 loc) • 11.8 kB
JavaScript
/**
* @license chowa v1.1.3
*
* Copyright (c) Chowa Techonlogies Co.,Ltd.(http://www.chowa.cn).
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const PropTypes = require("prop-types");
const classnames_1 = require("classnames");
const utils_1 = require("../utils");
const tooltip_1 = require("../tooltip");
class Slider extends React.PureComponent {
constructor(props) {
super(props);
const { range, defaultValue, min, max, value } = props;
const initValue = utils_1.isExist(value) ? value : defaultValue;
this.state = Object.assign(Object.assign({}, this.compileRenderParams(initValue, range, min)), { isDraging: false, dragBtn: undefined, distance: max - min });
[
'onRailClickHandler',
'onDragStart',
'onDraging',
'onDragEnd',
'onMarkStepClick',
'onKeyDownHandler'
].forEach((fn) => {
this[fn] = this[fn].bind(this);
});
}
componentDidUpdate(preProps) {
if (!utils_1.isEqual(preProps.value, this.props.value)) {
this.setState(Object.assign({}, this.compileRenderParams(this.props.value, this.props.range, this.props.min)));
}
}
compileRenderParams(value, range, min) {
const begin = range && Array.isArray(value)
? utils_1.isExist(value[0]) ? value[0] - min : 0
: 0;
const end = range && Array.isArray(value)
? utils_1.isExist(value[1]) ? value[1] - min : 0
: utils_1.isExist(value) ? value - min : 0;
return { begin, end };
}
onMarkStepClick(step, e) {
const { begin, end } = this.state;
const { min, range } = this.props;
step -= min;
if (range && Math.abs(step - begin) < Math.abs(step - end)) {
this.setState({
begin: step
}, () => {
this.triggerChange();
});
}
else {
this.setState({
end: step
}, () => {
this.triggerChange();
});
}
utils_1.stopReactPropagation(e);
}
onDragStart(dragBtn, e) {
this.setState({
isDraging: true,
dragBtn
}, () => {
utils_1.doms.on(document.body, 'mousemove', this.onDraging);
utils_1.doms.on(document.body, 'mouseup', this.onDragEnd);
});
e.preventDefault();
e.stopPropagation();
}
onDraging(e) {
const { isDraging } = this.state;
if (!isDraging) {
return;
}
this.normalize(e.pageX, e.pageY);
e.preventDefault();
e.stopPropagation();
}
onDragEnd(e) {
this.setState({
isDraging: false,
dragBtn: undefined
});
utils_1.doms.off(document.body, 'mousemove', this.onDraging);
utils_1.doms.off(document.body, 'mouseup', this.onDragEnd);
e.preventDefault();
e.stopPropagation();
}
onRailClickHandler(e) {
this.normalize(e.pageX, e.pageY);
utils_1.stopReactPropagation(e);
}
onKeyDownHandler(dragBtn, e) {
const { min, max, step } = this.props;
let value = this.state[dragBtn];
switch (e.keyCode) {
case 9:
return;
case 38:
case 39:
value += step;
if (value <= max - min) {
this.setState(dragBtn === 'begin' ? { begin: value } : { end: value }, () => {
this.triggerChange();
});
}
break;
case 37:
case 40:
value -= step;
if (value >= 0) {
this.setState(dragBtn === 'begin' ? { begin: value } : { end: value }, () => {
this.triggerChange();
});
}
break;
}
utils_1.stopReactPropagation(e);
e.preventDefault();
}
normalize(pageX, pageY) {
const { top, left, width, height } = utils_1.doms.offset(this.railEle);
const { mode, step, range } = this.props;
const { dragBtn, distance, begin, end } = this.state;
const movement = mode === 'vertical' ? pageY - top : pageX - left;
const percent = mode === 'vertical' ? 1 - movement / height : movement / width;
let result = distance * percent;
if (step !== 1 && result % step !== 0) {
const remainder = result % step;
if ((range && Math.abs(result - begin) < Math.abs(result - end)) ||
(!range && remainder < step / 2)) {
result -= remainder;
}
else {
result += step - remainder;
}
}
if (result > distance) {
result = distance;
}
if (result < 0) {
result = 0;
}
this.setState(dragBtn === 'begin' ? { begin: result } : { end: result }, () => {
this.triggerChange();
});
}
getValues() {
const { begin, end } = this.state;
const { min } = this.props;
return {
begin: Math.floor(begin) + min,
end: Math.floor(end) + min
};
}
triggerChange() {
const { onChange, range } = this.props;
if (onChange) {
const { begin, end } = this.getValues();
if (range) {
onChange([begin, end].sort());
}
else {
onChange(end);
}
}
}
renderMarks() {
const { marks, min, max, mode, disabled } = this.props;
const stepNodes = [];
const labelNodes = [];
const renderNodes = [];
const len = max - min;
const marksCount = Object.keys(marks).length;
let stepLabelCenterStyle = {};
if (mode !== 'vertical') {
stepLabelCenterStyle = {
width: `${100 / (marksCount - 1)}%`,
marginLeft: `-${100 / 2 / (marksCount - 1)}%`
};
}
for (const index of Object.keys(marks)) {
const step = parseInt(index, 10);
if (Number.isNaN(step)) {
return;
}
let itemStyle = {};
if (mode === 'vertical') {
itemStyle = {
bottom: `${(step - min) / len * 100}%`
};
}
else {
itemStyle = {
left: `${(step - min) / len * 100}%`
};
}
stepNodes.push(React.createElement("span", { key: index, onClick: disabled ? null : this.onMarkStepClick.bind(this, step), className: utils_1.preClass('slider-step'), style: itemStyle }));
const customStyle = typeof marks[index] === 'string' ? {} : marks[index].style;
const label = typeof marks[index] === 'string' ? marks[index] : marks[index].label;
labelNodes.push(React.createElement("div", { key: index, className: utils_1.preClass('slider-step-label'), style: Object.assign({}, customStyle, itemStyle, stepLabelCenterStyle) },
React.createElement("span", { className: utils_1.preClass('slider-step-detail'), onClick: disabled ? null : this.onMarkStepClick.bind(this, step) }, label)));
}
renderNodes.push(React.createElement("div", { key: 'slider-step', className: utils_1.preClass('slider-step-wrapper') }, stepNodes));
renderNodes.push(React.createElement("div", { key: 'slider-step-lablel', className: utils_1.preClass('slider-step-label-wrapper') }, labelNodes));
return renderNodes;
}
render() {
const { className, style, range, marks, disabled, formatter, mode, tabIndex } = this.props;
const { begin, end, isDraging, dragBtn, distance } = this.state;
const componentClass = classnames_1.default({
[utils_1.preClass('slider')]: true,
[utils_1.preClass(`slider-${mode}`)]: true,
[utils_1.preClass('slider-disabled')]: disabled,
[className]: utils_1.isExist(className)
});
let trackStyle = {};
let thumbStartStyle = {};
let thumbEndStyle = {};
if (mode === 'vertical') {
trackStyle = {
bottom: `${(begin < end ? begin : end) / distance * 100}%`,
height: `${Math.abs(end - begin) / distance * 100}%`
};
thumbStartStyle = {
bottom: `${begin / distance * 100}%`
};
thumbEndStyle = {
bottom: `${end / distance * 100}%`
};
}
else {
trackStyle = {
left: `${(begin < end ? begin : end) / distance * 100}%`,
width: `${Math.abs(end - begin) / distance * 100}%`
};
thumbStartStyle = {
left: `${begin / distance * 100}%`
};
thumbEndStyle = {
left: `${end / distance * 100}%`
};
}
return (React.createElement("section", { style: style, className: componentClass },
React.createElement("div", { className: utils_1.preClass('slider-drag-wrapper'), onClick: disabled ? null : this.onRailClickHandler },
React.createElement("div", { className: utils_1.preClass('slider-rail'), ref: (ele) => {
this.railEle = ele;
} }),
React.createElement("div", { className: utils_1.preClass('slider-track'), style: trackStyle }),
range &&
React.createElement(tooltip_1.default, { disabled: disabled || dragBtn === 'end', title: utils_1.isExist(formatter) ? formatter(this.getValues().begin) : this.getValues().begin },
React.createElement("button", { tabIndex: disabled ? -1 : tabIndex, type: 'button', disabled: disabled, onKeyDown: disabled ? null : this.onKeyDownHandler.bind(this, 'begin'), onMouseDown: disabled || isDraging ? null : this.onDragStart.bind(this, 'begin'), style: thumbStartStyle, className: utils_1.preClass('slider-thumb') })),
React.createElement(tooltip_1.default, { disabled: disabled || dragBtn === 'begin', title: utils_1.isExist(formatter) ? formatter(this.getValues().end) : this.getValues().end },
React.createElement("button", { type: 'button', tabIndex: disabled ? -1 : tabIndex, disabled: disabled, onKeyDown: disabled ? null : this.onKeyDownHandler.bind(this, 'end'), onMouseDown: disabled || isDraging ? null : this.onDragStart.bind(this, 'end'), style: thumbEndStyle, className: utils_1.preClass('slider-thumb') }))),
marks && this.renderMarks()));
}
}
Slider.propTypes = {
className: PropTypes.string,
style: PropTypes.object,
tabIndex: PropTypes.number,
range: PropTypes.bool,
mode: PropTypes.oneOf(['horizontal', 'vertical']),
defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.array]),
value: PropTypes.oneOfType([PropTypes.number, PropTypes.array]),
marks: PropTypes.object,
min: PropTypes.number,
max: PropTypes.number,
onChange: PropTypes.func,
step: PropTypes.number,
disabled: PropTypes.bool,
formatter: PropTypes.func
};
Slider.defaultProps = {
tabIndex: 0,
range: false,
mode: 'horizontal',
min: 0,
max: 100,
step: 1,
disabled: false
};
exports.default = Slider;