UNPKG

chowa

Version:

UI component library based on React

292 lines (291 loc) 11.8 kB
/** * @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;