UNPKG

tuya-panel-kit

Version:

a functional component library for developing tuya device panels!

347 lines (330 loc) 8.69 kB
import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { ViewPropTypes } from 'react-native'; import { NumberUtils } from '../../utils'; import { BigButton, TouchableOpacityView, StyledIconFont, StyledInput, RightView, TouchableThreeView, dPlus, dMinus, } from './styled'; const { inMaxMin, add, subtract } = NumberUtils; export default class Stepper extends PureComponent { static propTypes = { /** * 步进器内容样式 */ style: ViewPropTypes.style, /** * 输入框样式 */ inputStyle: ViewPropTypes.style, /** * 加减按钮样式 */ buttonStyle: ViewPropTypes.style, /** * 是否支持手动编辑 */ editable: PropTypes.bool, /** * 按钮类型 */ buttonType: PropTypes.oneOf(['ellipse', 'triangle']), /** * 最小值 */ min: PropTypes.number, /** * 最大值 */ max: PropTypes.number, /** * 具体值 */ value: PropTypes.number, /** * 步长 */ stepValue: PropTypes.number, /** * 按钮类型为 ellipse 时按钮激活状态下的颜色 */ ellipseIconColor: PropTypes.string, /** * 按钮类型为 triangle 时激活状态下的颜色 */ triangleIconColor: PropTypes.string, /** * 文本输入的高亮和光标颜色 */ selectionColor: PropTypes.string, /** * 按钮类型为 triangle 时的减法按钮路径 */ iconMinusPath: PropTypes.string, /** * 按钮类型为 triangle 时的加法按钮路径 */ iconPlusPath: PropTypes.string, /** * 短按值回调 * @param {number} value - 具体值 */ onValueChange: PropTypes.func, /** * 是否禁用 * @version 2.0.0-rc.7 */ disabled: PropTypes.bool, /** * 获取TextInput 实例ref * @version 2.0.0-rc.7 */ getTextInputRef: PropTypes.func, }; static defaultProps = { style: {}, buttonStyle: {}, inputStyle: {}, editable: true, min: 0, value: 20, max: 99, stepValue: 1, ellipseIconColor: '#333', selectionColor: '#FF4800', buttonType: 'ellipse', iconMinusPath: dMinus, iconPlusPath: dPlus, triangleIconColor: '#FF4800', onValueChange: () => {}, disabled: false, getTextInputRef: () => {}, }; constructor(props) { super(props); this.state = { value: inMaxMin(props.min, props.max, props.value), }; } componentDidMount() { const { getTextInputRef } = this.props; getTextInputRef && getTextInputRef(this.TextInputRef); } componentWillReceiveProps(nextProps) { if (this.props.value !== nextProps.value) { this.setState({ value: nextProps.value, }); } } componentWillUnmount() { this.clearInterval(); } clearInterval() { if (this.timer) { clearInterval(this.timer); this.timer = null; } } _handleMath = isMinus => { const { min, max, onValueChange, stepValue } = this.props; const { value } = this.state; if (isMinus) { if (value > min) { const step = Math.min(stepValue, subtract(value, min)); onValueChange && onValueChange(subtract(value, step)); this.setState({ value: subtract(value, step), }); } } else if (value <= max) { const step = Math.min(stepValue, subtract(max, value)); onValueChange && onValueChange(add(value, step)); this.setState({ value: add(value, step), }); } }; _handlePressOut = () => { this.clearInterval(); }; _handlePressIn = isMinus => () => { this._handleMath(isMinus); this.clearInterval(); this.timer = setInterval(() => { this._handleMath(isMinus); }, 250); }; _handleChangeText = newValue => { const { max, min } = this.props; const idx = newValue.indexOf('.'); if (!idx) return; if (idx === -1) { if ( Number(newValue) > max || Number(newValue) < min || (newValue.length === 2 && !newValue.indexOf('0') && newValue[1] !== '.') ) { return; } this.setState({ value: newValue, }); } else { if ( Number(newValue.substr(0, idx)) >= max || Number(newValue.substr(0, idx)) < min || newValue.length > idx + 2 ) { return; } this.setState({ value: newValue, }); } }; _handleEndText = () => { const { min, onValueChange } = this.props; const { value } = this.state; const newValue = Number(value); if (typeof value === 'string' && !value.length) { onValueChange && onValueChange(min); this.setState({ value: min, }); } else { this.setState({ value: newValue, }); onValueChange && onValueChange(newValue); } }; renderEllipse = () => { const { min, max, style, buttonStyle, ellipseIconColor, selectionColor, inputStyle, editable, disabled, ...textInputProps } = this.props; const { value } = this.state; return ( <BigButton style={style}> <TouchableOpacityView style={[buttonStyle, (disabled || value === min) && { opacity: 0.4 }]} disabled={disabled || value === min} onPressOut={this._handlePressOut} onPressIn={this._handlePressIn(true)} > <StyledIconFont fill={ellipseIconColor} fillOpacity={disabled || value === min ? 0.4 : 1} name="minus" /> </TouchableOpacityView> <StyledInput ref={ref => { this.TextInputRef = ref; }} maxLength={4} {...textInputProps} style={[disabled && { color: 'rgba(51,51,51,.4)' }, inputStyle]} onEndEditing={this._handleEndText} value={value.toString()} onChangeText={this._handleChangeText} keyboardType="numeric" selectionColor={selectionColor} enablesReturnKeyAutomatically={true} editable={!disabled && editable} /> <TouchableOpacityView style={[buttonStyle, (disabled || value === max) && { opacity: 0.4 }]} disabled={disabled || value === max} onPressOut={this._handlePressOut} onPressIn={this._handlePressIn(false)} > <StyledIconFont fill={ellipseIconColor} fillOpacity={disabled || value === max ? 0.4 : 1} name="plus" /> </TouchableOpacityView> </BigButton> ); }; renderTriangle = () => { const { min, max, style, buttonStyle, triangleIconColor, selectionColor, iconMinusPath, iconPlusPath, inputStyle, editable, disabled, ...textInputProps } = this.props; const { value } = this.state; return ( <RightView style={style}> <TouchableThreeView style={[buttonStyle, (disabled || value === min) && { opacity: 0.4 }]} disabled={disabled || value === min} onPressOut={this._handlePressOut} onPressIn={this._handlePressIn(true)} > <StyledIconFont fill={triangleIconColor} fillOpacity={disabled || value === min ? 0.4 : 1} d={iconMinusPath} /> </TouchableThreeView> <StyledInput ref={ref => { this.TextInputRef = ref; }} maxLength={4} {...textInputProps} editable={!disabled && editable} style={[disabled && { color: 'rgba(51,51,51,.4)' }, inputStyle]} onEndEditing={this._handleEndText} value={value.toString()} onChangeText={this._handleChangeText} keyboardType="numeric" selectionColor={selectionColor} enablesReturnKeyAutomatically={true} /> <TouchableThreeView style={[buttonStyle, (disabled || value === max) && { opacity: 0.4 }]} disabled={disabled || value === max} onPressOut={this._handlePressOut} onPressIn={this._handlePressIn(false)} > <StyledIconFont fill={triangleIconColor} fillOpacity={disabled || value === max ? 0.4 : 1} d={iconPlusPath} /> </TouchableThreeView> </RightView> ); }; render() { const { buttonType } = this.props; return buttonType === 'ellipse' ? this.renderEllipse() : this.renderTriangle(); } }