tuya-panel-kit
Version:
a functional component library for developing tuya device panels!
294 lines (277 loc) • 8.65 kB
JavaScript
/* eslint-disable max-len */
import React from 'react';
import { FlatList, View, StyleSheet, ViewPropTypes } from 'react-native';
import PropTypes from 'prop-types';
import TYFlatList from '../TYLists/list';
import withSkeleton from './withSkeleton';
import { ThemeUtils, RatioUtils } from '../../utils';
import { StyledIconFont, StyledFlatList, StyledCheckout } from './styled';
const selectedPath =
'M788.053333 276.053333a32 32 0 0 1 48.341334 41.642667l-3.114667 3.584-384 384a32 32 0 0 1-41.642667 3.114667l-3.584-3.114667-170.666666-170.666667a32 32 0 0 1 41.642666-48.341333l3.584 3.114667L426.666667 637.397333l361.386666-361.386666z';
const { getTheme, ThemeConsumer } = ThemeUtils;
const { viewWidth, convertX: cx } = RatioUtils;
let itemHeight = 56;
class ListPopup extends React.Component {
static propTypes = {
...FlatList.propTypes,
/**
* 按钮开关值
*/
switchValue: PropTypes.bool.isRequired,
/**
* 列表弹窗样式
*/
listWrapperStyle: ViewPropTypes.style,
/**
* 数据源
*/
dataSource: PropTypes.arrayOf(
PropTypes.shape({
styles: PropTypes.object,
title: PropTypes.string,
Icon: PropTypes.any,
value: PropTypes.any.isRequired,
})
),
/**
* 最大列表数量
*/
maxItemNum: PropTypes.number,
/**
* 设置type为radio时选中的图标
*/
selectedIcon: PropTypes.element,
/**
* 列表选择弹出层的类型
*/
type: PropTypes.oneOf(['switch', 'radio', 'arrow']),
/**
* 设置type为radio时选中图标的颜色
*/
iconTintColor: PropTypes.string,
/**
* 内容是否居中
*/
contentCenter: PropTypes.bool,
/**
* 选中的值
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]),
/**
* 设置每个listItem的样式
*/
listItemStyle: ViewPropTypes.style,
/**
* 选中事件的回调
* @param {(value: string | number | string[] | number[], switchValue?: boolean) => void}
*/
onSelect: PropTypes.func,
/**
* 数据更改回调
*/
_onDataChange: PropTypes.func,
/**
* 多选框的样式
*/
switchStyle: ViewPropTypes.style,
/**
* 多选框的选中☑️的颜色
*/
checkIconColor: PropTypes.string,
};
static defaultProps = {
listWrapperStyle: null,
dataSource: [],
maxItemNum: 6,
selectedIcon: null,
type: 'radio',
iconTintColor: '',
checkIconColor: '#e5e5e5',
contentCenter: true,
value: -1,
listItemStyle: null,
onSelect: (value, switchValue) => {},
_onDataChange: () => {},
switchStyle: {},
};
constructor(props) {
super(props);
const { selected, selectedArr } = this.calcSelected(props);
this.state = {
selected,
selectedArr,
itemHeight: StyleSheet.flatten([props.listItemStyle]).height || itemHeight,
};
props._onDataChange(props.value);
}
componentWillReceiveProps(nextProps) {
if (this.state.selected === nextProps.value || this.props.value === nextProps.value) return;
const { selected, selectedArr } = this.calcSelected(nextProps);
this.setState({ selected, selectedArr });
}
calcSelected = props => {
const { type, value } = props;
const isRadio = type === 'radio' && (typeof value === 'string' || typeof value === 'number');
if (isRadio) return { selected: value, selectedArr: [] };
const isSwitch =
type === 'switch' && Object.prototype.toString.call(value) === '[object Array]';
if (isSwitch) return { selected: -1, selectedArr: value };
return { selected: -1, selectedArr: [] };
};
selectRow = value => {
const { onSelect, type, _onDataChange } = this.props;
const { selectedArr } = this.state;
const newSelectedArr = selectedArr;
if (type === 'switch') {
if (selectedArr.indexOf(value) === -1) {
newSelectedArr.push(value);
} else {
const index = selectedArr.indexOf(value);
newSelectedArr.splice(index, 1);
}
this.setState({
selectedArr: newSelectedArr,
});
onSelect && onSelect(value);
_onDataChange && _onDataChange(newSelectedArr);
} else {
this.setState({ selected: value });
onSelect && onSelect(value);
_onDataChange && _onDataChange(value);
}
};
renderSwitch = value => {
const { selectedArr } = this.state;
const { switchStyle, checkIconColor } = this.props;
const isActive = selectedArr.indexOf(value.toString()) !== -1;
return (
<StyledCheckout active={isActive} style={switchStyle}>
{isActive && <StyledIconFont d={selectedPath} color={checkIconColor} size={cx(16)} />}
</StyledCheckout>
);
};
renderSelectIcon = value => {
const { selectedIcon, iconTintColor } = this.props;
if (this.state.selected === value) {
return (
selectedIcon || <StyledIconFont d={selectedPath} color={iconTintColor} size={cx(26)} />
);
}
return null;
};
renderActions = value => {
const { type } = this.props;
if (type === 'switch') {
return this.renderSwitch(value);
}
if (type === 'radio') {
return this.renderSelectIcon(value);
}
return null;
};
renderItem = ({ item, index }) => {
const { styles = {}, type, contentCenter, listItemStyle, dataSource } = this.props;
const containerStyle = {
alignSelf: 'stretch',
minHeight: this.state.itemHeight,
backgroundColor: '#fff',
};
let titleAlign;
if (contentCenter) {
titleAlign = 'center';
} else {
titleAlign = 'left';
}
return (
<ThemeConsumer>
{globalTheme => {
const popupTheme = { ...this.props, theme: globalTheme };
const cellFontColor = getTheme(popupTheme, 'popup.list.cellFontColor');
const cellFontSize = getTheme(popupTheme, 'popup.cellFontSize');
const tintColor = getTheme(popupTheme, 'popup.tintColor');
let flatItemStyle;
if (listItemStyle !== null && listItemStyle.backgroundColor) {
flatItemStyle = listItemStyle;
} else {
flatItemStyle = {
...listItemStyle,
backgroundColor: getTheme(popupTheme, 'popup.cellBg'),
};
}
const itemStyle = {
...styles,
container: [{ ...containerStyle, ...flatItemStyle }, styles.container],
content: [
{
flex: 1,
alignItems: 'center',
paddingLeft: cx(24),
paddingRight: cx(24),
},
styles.content,
],
title: [
{
textAlign: titleAlign,
fontSize: cellFontSize,
color: cellFontColor,
},
!!contentCenter && { width: viewWidth - cx(48) },
styles.title,
],
contentRight: [
!!contentCenter && { position: 'absolute', right: cx(24) },
styles.contentRight,
],
contentLeft: [{ marginRight: 8 }, styles.contentLeft],
};
return (
<TYFlatList.Item
key={`list_${index}`}
activeOpacity={type === 'switch' ? 1 : 0.8}
styles={itemStyle}
tintColor={tintColor}
Action={this.renderActions(dataSource[index].value)}
onPress={() => this.selectRow(dataSource[index].value)}
{...item}
/>
);
}}
</ThemeConsumer>
);
};
render() {
const {
switchValue,
maxItemNum,
listWrapperStyle,
dataSource,
selectedIcon,
type,
iconTintColor,
contentCenter,
value,
listItemStyle,
onSelect,
_onDataChange,
...FlatListProps
} = this.props;
const dataCount = dataSource.length > maxItemNum ? maxItemNum : dataSource.length;
const totalHeight = this.state.itemHeight * dataCount;
return (
<View
style={[listWrapperStyle, !switchValue && { opacity: 0.6 }, { height: totalHeight }]}
pointerEvents={!switchValue ? 'none' : 'auto'}
>
<StyledFlatList
data={dataSource}
renderItem={this.renderItem}
{...FlatListProps}
scrollEnabled={dataSource.length > maxItemNum}
/>
</View>
);
}
}
export const ListModal = withSkeleton(ListPopup, true);
export default withSkeleton(ListPopup);