tuya-panel-kit
Version:
a functional component library for developing tuya device panels!
382 lines (364 loc) • 12.5 kB
JavaScript
/* eslint-disable prefer-destructuring */
/* eslint-disable prettier/prettier */
import React from 'react';
import { I18nManager, ColorPropType, Text, ViewPropTypes, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import Picker from '../picker-view';
import { CoreUtils, NumberUtils, RatioUtils, ThemeUtils } from '../../utils';
import withSkeleton from './withSkeleton';
import {
StyledCountdownContainer,
StyledOverview,
StyledPickerUnitText,
StyledCountdownContent,
StyledCountdownOnePickerContent
} from './styled';
const { toFixed } = CoreUtils;
const { range, inMaxMin } = NumberUtils;
const { isIos, width, convertX: cx } = RatioUtils;
const { getTheme, ThemeConsumer } = ThemeUtils;
class CountdownPopup extends React.Component {
static propTypes = {
...Picker.PropTypes,
/**
* 外部样式
*/
countdownWrapperStyle: ViewPropTypes.style,
/**
* 按钮开关值
*/
switchValue: PropTypes.bool.isRequired,
/**
* 倒计时具体值
*/
value: PropTypes.number.isRequired,
/**
* 是否只显示分钟
*/
onlyone: PropTypes.bool,
/**
* 最大值
*/
max: PropTypes.number,
/**
* 最小值
*/
min: PropTypes.number,
/**
* 步长
*/
step: PropTypes.number,
/**
* picker字体颜色
*/
pickerFontColor: ColorPropType,
/**
* picker单位颜色
*/
pickerUnitColor: ColorPropType,
/**
* 小时文案
*/
hourText: PropTypes.string,
/**
* 分钟文案
*/
minuteText: PropTypes.string,
/**
* 倒计时更改值回调
*/
onValueChange: PropTypes.func,
/**
* 数据更改值回调
*/
_onDataChange: PropTypes.func,
/**
* 小时picker样式
*/
hourPickerStyle: ViewPropTypes.style,
/**
* 小时单位样式
*/
hourUnitStyle: Text.propTypes.style,
/**
* 分钟picker样式
*/
minutePickerStyle: ViewPropTypes.style,
/**
* 分钟单位样式
*/
minuteUnitStyle: Text.propTypes.style,
};
static defaultProps = {
countdownWrapperStyle: null,
onlyone: false,
max: 1440,
min: 0,
step: 1,
pickerFontColor: null,
pickerUnitColor: null,
hourText: 'Hour',
minuteText: 'Minute',
onValueChange: () => {},
_onDataChange: () => {},
hourPickerStyle: null,
hourUnitStyle: null,
minutePickerStyle: null,
minuteUnitStyle: null,
};
constructor(props) {
super(props);
if (props.onlyone) {
this.Hours = [];
this.Minutes = range(props.min, props.max + 1, props.step);
} else {
const remainMinutes = props.max % 60;
const shiftMinutes = props.min % 60;
this.Hours = range(parseInt(props.min / 60, 10), parseInt(props.max / 60, 10) + 1);
this.Minutes = range(0, 60, props.step);
this.EqualMinutes = range(shiftMinutes, remainMinutes + 1, props.step); // 当最大小时与最小小时相等的分钟
this.RemainMinutes = range(0, remainMinutes + 1, props.step); // 选中最大小时时剩余的分钟
this.shiftMinutes = range(shiftMinutes, 60, props.step); // 选中最小小时时剩余的分钟
}
this.state = {
hour: this.getHour(props),
minute: this.getMinute(props),
};
props._onDataChange({
...this.state,
value: this.state.hour * 60 + this.state.minute,
});
}
componentWillReceiveProps(nextProps) {
if (this.props.value !== nextProps.value) {
this.setState({
hour: this.getHour(nextProps),
minute: this.getMinute(nextProps),
});
}
}
componentDidUpdate(prevProps) {
if (prevProps.switchValue !== this.props.switchValue) {
const data = this.props.switchValue
? { ...this.state, value: this.state.hour * 60 + this.state.minute }
: { hour: 0, minute: 0, value: 0 };
this.props._onDataChange(data);
}
}
getHour(props) {
const { onlyone, value, max } = props;
const v = inMaxMin(0, max, value);
return onlyone ? 0 : parseInt(v / 60, 10);
}
getMinute(props) {
const { onlyone, value, max } = props;
const v = inMaxMin(0, max, value);
return onlyone ? parseInt(v, 10) : parseInt(v - this.getHour(props) * 60, 10);
}
handleHourChange = hour => {
const { min, max, onValueChange, _onDataChange } = this.props;
const isMaxHour = hour === parseInt(max / 60, 10);
const isMinHour = hour === parseInt(min / 60, 10);
// const isMinHour = hour === parseInt(min / 60, 10);
let minute = this.state.minute;
// 如果此前选择的分钟在选中最大小时情况下的剩余分钟中不存在,则把分钟置为0
if (isMaxHour && this.RemainMinutes.indexOf(minute) === -1) {
minute = this.RemainMinutes[0];
}
// 如果此前选择的分钟在选中最小时情况下的剩余分钟中不存在,则把分钟置为最初值中的最小值
if (isMinHour && this.shiftMinutes.indexOf(minute) === -1) {
minute = this.shiftMinutes[0];
}
this.setState({ hour, minute });
const data = { hour, minute, value: hour * 60 + minute };
onValueChange && onValueChange(data);
_onDataChange && _onDataChange(data);
};
handleMinuteChange = minute => {
const { onValueChange, _onDataChange } = this.props;
this.setState({ minute });
const data = {
...this.state,
minute,
value: this.state.hour * 60 + minute,
};
onValueChange && onValueChange(data);
_onDataChange && _onDataChange(data);
};
renderOnePicker() {
const {
switchValue,
countdownWrapperStyle,
pickerFontColor,
pickerUnitColor,
minuteText,
minutePickerStyle,
minuteUnitStyle,
...rest
} = this.props;
return (
<ThemeConsumer>
{globalTheme => {
const countdownTheme = { ...this.props, theme: globalTheme };
const countFontColor = pickerFontColor || getTheme(countdownTheme, 'popup.cellFontColor');
const countUnitColor = pickerUnitColor || getTheme(countdownTheme, 'popup.cellFontColor');
return (
<StyledCountdownContainer
style={[countdownWrapperStyle, !switchValue && { opacity: 0.6 }]}
pointerEvents={!switchValue ? 'none' : 'auto'}
>
<StyledCountdownOnePickerContent>
<StyledOverview style={{ flex: 1 }}>
<Picker
{...rest}
theme={{ fontColor: countFontColor }}
accessibilityLabel="Popup_CountdownPicker_Minutes"
style={StyleSheet.flatten([{ width, height: 220, backgroundColor: getTheme(countdownTheme, 'popup.cellBg', '#FFF') }, minutePickerStyle])}
selectedValue={this.state.minute}
onValueChange={this.handleMinuteChange}
>
{this.Minutes.map(k => (
<Picker.Item key={k} label={`${k}`} value={k} />
))}
</Picker>
<StyledPickerUnitText
style={StyleSheet.flatten([{ marginLeft: -(width / 2) + 20 }, minuteUnitStyle])}
pointerEvents="none"
pickerUnitColor={countUnitColor}
text={minuteText}
/>
</StyledOverview>
</StyledCountdownOnePickerContent>
</StyledCountdownContainer>
);
}}
</ThemeConsumer>
);
}
renderTwoPicker() {
const {
min,
max,
switchValue,
countdownWrapperStyle,
pickerFontColor,
pickerUnitColor,
hourText,
minuteText,
hourPickerStyle,
hourUnitStyle,
minutePickerStyle,
minuteUnitStyle,
...rest
} = this.props;
const isMaxHour = this.state.hour === parseInt(max / 60, 10);
const isMinHour = this.state.hour === parseInt(min / 60, 10);
let minuteValues;
if (isMaxHour && isMinHour) {
minuteValues = this.EqualMinutes;
} else if (isMaxHour && !isMinHour) {
minuteValues = this.RemainMinutes;
} else if (isMinHour && !isMaxHour) {
minuteValues = this.shiftMinutes;
} else {
minuteValues = this.Minutes;
}
return (
<ThemeConsumer>
{globalTheme => {
const countdownTheme = { ...this.props, theme: globalTheme };
const countFontColor = pickerFontColor || getTheme(countdownTheme, 'popup.cellFontColor');
const countUnitColor = pickerUnitColor || getTheme(countdownTheme, 'popup.cellFontColor');
return (
<StyledCountdownContainer
style={StyleSheet.flatten([countdownWrapperStyle, !switchValue && { opacity: 0.6 }])}
pointerEvents={!switchValue ? 'none' : 'auto'}
>
<StyledCountdownContent>
<StyledOverview
style={{
flex: isIos ? 1.1 : 1.2,
height: 240,
}}
>
<Picker
{...rest}
theme={{ fontColor: countFontColor }}
accessibilityLabel="Popup_CountdownPicker_Hours"
style={StyleSheet.flatten([
{
width: isIos ? width * (7 / 10) - 60 : width * (1 / 2),
marginRight: isIos ? 0 : cx(20),
backgroundColor: getTheme(countdownTheme, 'popup.cellBg', '#FFF')
},
hourPickerStyle,
])}
selectedValue={this.state.hour}
onValueChange={this.handleHourChange}
>
{this.Hours.map(k => (
<Picker.Item key={k} label={toFixed(k, 2)} value={k} />
))}
</Picker>
<StyledPickerUnitText
style={StyleSheet.flatten([
{ marginLeft: isIos ? -(width * (7 / 10 / 2)) + 45 : -width * (1 / 4) },
hourUnitStyle,
])}
pointerEvents="none"
pickerUnitColor={countUnitColor}
text={hourText}
/>
</StyledOverview>
<StyledOverview style={{ flex: 1 }}>
<Picker
{...rest}
theme={{ fontColor: countFontColor }}
accessibilityLabel="Popup_CountdownPicker_Minutes"
selectedValue={this.state.minute}
onValueChange={this.handleMinuteChange}
itemAlign={isIos ? 'center' : I18nManager.isRTL ? 'flex-end' : 'flex-start'}
style={StyleSheet.flatten([
isIos
? {
width: width * (8 / 10),
marginLeft: -(width * 3) / 10,
}
: {
width: width * (1 / 2),
marginRight: cx(40),
},
{ backgroundColor: getTheme(countdownTheme, 'popup.cellBg', '#FFF') },
minutePickerStyle,
])}
>
{minuteValues.map(k => (
<Picker.Item key={k} label={toFixed(k, 2)} value={k} />
))}
</Picker>
<StyledPickerUnitText
style={StyleSheet.flatten([
{
marginLeft: isIos ? -(width * (8 / 10 / 2)) + 15 : -width * (1 / 2),
},
minuteUnitStyle,
])}
pointerEvents="none"
pickerUnitColor={countUnitColor}
text={minuteText}
/>
</StyledOverview>
</StyledCountdownContent>
</StyledCountdownContainer>
);
}}
</ThemeConsumer>
);
}
render() {
const { onlyone } = this.props;
return onlyone ? this.renderOnePicker() : this.renderTwoPicker();
}
}
export const CountdownModal = withSkeleton(CountdownPopup, true);
export default withSkeleton(CountdownPopup);