/** * @file stepper.tsx * @author lihuanji * * 步进器 */ import React, { PureComponent } from 'react'; import classnames from 'classnames'; const styles = require('./style.module.styl'); const addIcon = require('./add.svg'); const cutIcon = require('./cut.svg'); interface StepperProps { // 默认显示数量 value?: number; // 步进数 step?: number; // 最小数 min?: number; // 最大数 max?: number | null; // 获取焦点时 onFocus?: Function; // 数值改变时 onChange?: Function; // 离开焦点时 onBlur?: Function; // 增/减 按钮点击时 onButtonClick?: Function; // 是否禁用 disabled?: boolean; } interface StepperState { num: number; disableAddButton: boolean; disableCutButton: boolean; } class Stepper extends PureComponent { static defaultProps = { value: null, step: 1, min: 1, max: null, onFocus: () => {}, onChange: () => {}, onBlur: () => {}, onButtonClick: () => {}, disabled: false }; stepperRef: any; // 存储高度,用于判读是否需要滑动视图 scroll = document.documentElement.clientHeight; constructor(props: StepperProps) { super(props); const value = props.value || props.min || 1; this.state = { num: value, disableAddButton: Boolean(props.max && value === props.max), disableCutButton: value === props.min }; this.add = this.add.bind(this); this.cut = this.cut.bind(this); this.handleChange = this.handleChange.bind(this); this.handleFocus = this.handleFocus.bind(this); this.handleBlur = this.handleBlur.bind(this); } componentWillReceiveProps(nextProps: StepperProps) { if (this.props !== nextProps && nextProps.value) { this.setState({ num: nextProps.value, disableAddButton: nextProps.value === nextProps.max, disableCutButton: nextProps.value === nextProps.min }); } } /** * 增加数量 */ add(e: React.MouseEvent) { e.stopPropagation(); const { step = 1, max, value, onButtonClick } = this.props; const { num } = this.state; if (max && max <= num) { this.setState({ disableAddButton: true }); return; } const showValue = num + step; if (!value) { this.setState({ num: showValue, disableAddButton: showValue === max, disableCutButton: false }); } onButtonClick && onButtonClick(showValue); } /** * 删除数量 */ cut(e: React.MouseEvent) { e.stopPropagation(); const { min = 1, step = 1, value, onButtonClick } = this.props; const { num } = this.state; if (min >= num) { this.setState({ disableCutButton: true }); return; } const showValue = num - step; if (!value) { this.setState({ num: showValue, disableAddButton: false, disableCutButton: showValue === min }); } onButtonClick && onButtonClick(showValue); } /** * input变化 * * @param {object} e 事件对象 */ handleChange(e: React.SyntheticEvent) { const { onChange } = this.props; const val = parseInt(e.currentTarget.value, 10); this.setState({ num: val }); onChange && onChange(val); } handleFocus(e: React.SyntheticEvent) { e.stopPropagation(); e.preventDefault(); const u = navigator.userAgent; const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; // 解决安卓输入页面未上滑 if (isAndroid) { const interval = setInterval(() => { if (document.documentElement.clientHeight !== this.scroll) { this.stepperRef.scrollIntoViewIfNeeded(); clearInterval(interval); } }, 100); } this.props.onFocus && this.props.onFocus(); } /** * input失去焦点 * * @param {object} e 事件对象 */ handleBlur(e: React.SyntheticEvent) { const { onBlur, max, min = 1 } = this.props; const val = parseInt(e.currentTarget.value, 10); let showValue = val; if (max && val >= max) { showValue = max; } if (val < min || !showValue) { showValue = min; } if (!this.props.value) { this.setState({ num: showValue, disableAddButton: showValue === this.props.max, disableCutButton: showValue === this.props.min }); } onBlur && onBlur(showValue); } render() { const { num, disableAddButton, disableCutButton } = this.state; const { disabled } = this.props; return (
{ this.stepperRef = ref; }} >
); } } export default Stepper;