UNPKG

tuya-panel-kit

Version:

a functional component library for developing tuya device panels!

294 lines (280 loc) 8.98 kB
import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { View, ViewPropTypes, ColorPropType } from 'react-native'; import Motion from '../motion'; import { RatioUtils } from '../../utils'; import { StyledViewChildren, StyledIconFont } from './styled'; const { convertX: cx } = RatioUtils; const MOTION_TYPES = Object.keys(Motion).filter(v => { return v !== 'Toast'; }); const path = { top: 'M1023.977245 1023.948801c-37.318282 0-74.60812 0.170663-111.926402-0.22755-35.696985-0.398213-75.660541-0.96709-110.418879-14.108131-37.545832-14.221906-62.377281-39.110242-87.379392-68.378925-18.004933-21.048421-53.360592-65.961201-70.597542-87.635386-14.079687-17.777383-41.670185-52.905491-56.745406-69.943335C567.966045 762.294171 544.528344 739.539121 511.988622 739.539121c-32.568165 0-55.977423 22.75505-74.921001 44.08791-15.075221 17.0094-42.665719 52.165952-56.77385 69.943334-17.180063 21.674185-52.535721 66.586965-70.540654 87.635386-25.058999 29.297127-49.862003 54.157019-87.407836 68.378925-34.758339 13.112597-74.721895 13.709918-110.418879 14.108131-37.318282 0.398213-74.60812 0.22755-111.926402 0.22755h1023.977245z', bottom: 'M1023.977245 0.051199c-37.318282 0-74.60812-0.170663-111.926402 0.22755-35.696985 0.398213-75.660541 0.96709-110.418879 14.108131-37.545832 14.221906-62.377281 39.110242-87.379392 68.378925-18.004933 21.048421-53.360592 65.961201-70.597542 87.635386-14.079687 17.777383-41.670185 52.905491-56.745406 69.943335C567.966045 261.705829 544.528344 284.460879 511.988622 284.460879c-32.568165 0-55.977423-22.75505-74.921001-44.08791-15.075221-17.0094-42.665719-52.165952-56.77385-69.943334-17.180063-21.674185-52.535721-66.586965-70.540654-87.635386-25.058999-29.297127-49.862003-54.157019-87.407836-68.378925C187.586943 1.302727 147.623386 0.705407 111.926402 0.307193 74.60812-0.09102 37.318282 0.079643 0 0.079643h1023.977245z', }; const Center = 'Center'; const Right = 'Right'; const Left = 'Left'; const Top = 'top'; const bottomLeft = 'bottomLeft'; const bottomRight = 'bottomRight'; const topLeft = 'topLeft'; const topRight = 'topRight'; class Tips extends PureComponent { static propTypes = { /** * 气泡的样式 */ contentStyle: ViewPropTypes.style, /** * 气泡位置 */ tipStyle: ViewPropTypes.style, // 气泡的位置 /** * 气泡背景颜色 */ bgColor: ColorPropType, /** * 是否显示气泡 */ show: PropTypes.bool, /** * 嵌套子元素 */ children: PropTypes.element, /** * 是否显示角标 */ showCorner: PropTypes.bool, /** * 角标位置 */ cornerPosition: PropTypes.oneOf([ 'topLeft', 'topCenter', 'topRight', 'bottomLeft', 'bottomCenter', 'bottomRight', ]), /** * 气泡动画类型 */ motionType: PropTypes.oneOf(MOTION_TYPES), /** * 动画配置 */ motionConfig: PropTypes.object, /** * 是否应用于Popup上伴有遮罩 */ withModal: PropTypes.bool, }; static defaultProps = { children: null, show: false, bgColor: '#fff', showCorner: true, contentStyle: undefined, tipStyle: null, motionType: 'ScaleFadeIn', cornerPosition: 'topCenter', motionConfig: {}, withModal: false, }; constructor(props) { super(props); this.state = { visible: props.show, width: 0, height: 0, }; } componentWillReceiveProps(nextProps) { if (nextProps.show !== this.props.show) { this.setState({ visible: nextProps.show, }); } } /* eslint-disable getter-return */ get cornerPosition() { const { cornerPosition } = this.props; /* eslint-disable indent */ const position = cornerPosition.match(Left) ? Left : cornerPosition.match(Right) ? Right : Center; switch (position) { case Left: return { alignSelf: 'flex-start', left: cx(16) }; case Center: return { alignSelf: 'center' }; case Right: return { alignSelf: 'flex-end', right: cx(16) }; default: break; } } /* eslint-disable getter-return */ get tipPosition() { const { tipStyle, cornerPosition } = this.props; const { width, height } = this.state; let top, left, right, bottom; if (!tipStyle || !tipStyle.position) return; if (tipStyle && tipStyle.position === 'absolute') { if (cornerPosition.match(Center)) return; const tipTop = this._handlePosition(tipStyle.top); const tipLeft = this._handlePosition(tipStyle.left); const tipBottom = this._handlePosition(tipStyle.bottom); const tipRight = this._handlePosition(tipStyle.right); switch (cornerPosition) { case bottomLeft: if (tipTop) { top = tipStyle.top + height; } if (tipLeft) { left = tipStyle.left - width + cx(16); } if (tipRight) { right = tipStyle.right + width - cx(16); } if (tipBottom) { bottom = tipStyle.bottom - height; } return { top, left, right, bottom }; case bottomRight: if (tipTop) { top = tipStyle.top + height; } if (tipLeft) { left = tipStyle.left + width - cx(16); } if (tipRight) { right = tipStyle.right - width + cx(16); } if (tipBottom) { bottom = tipStyle.bottom - height; } return { top, left, right, bottom }; case topRight: if (tipTop) { top = tipStyle.top - height; } if (tipLeft) { left = tipStyle.left + width - cx(16); } if (tipRight) { right = tipStyle.right - width + cx(16); } if (tipBottom) { bottom = tipStyle.bottom + height; } return { top, left, right, bottom }; case topLeft: if (tipTop) { top = tipStyle.top - height; } if (tipLeft) { left = tipStyle.left - width + cx(16); } if (tipRight) { right = tipStyle.right + width - cx(16); } if (tipBottom) { bottom = tipStyle.bottom + height; } return { top, left, right, bottom }; default: break; } } } /* eslint-disable getter-return */ get translate() { const { cornerPosition } = this.props; const { width, height } = this.state; if (cornerPosition.match(Center)) { return { width: 0, height: 0, }; } switch (cornerPosition) { case topLeft: return { width: width - cx(16), height }; case topRight: return { width: -width + cx(16), height }; case bottomLeft: return { width: width - cx(16), height: -height }; case bottomRight: return { width: -width + cx(16), height: -height }; default: break; } } _handlePosition = value => { if (value !== 'undefined' && typeof value === 'number') return true; return false; }; _handleLayout = layout => { const { withModal } = this.props; if (layout && typeof layout.width === 'number' && typeof layout.height === 'number') { this.setState({ width: withModal ? 0 : layout.width / 2, height: withModal ? 0 : layout.height / 2, }); } }; render() { const { visible } = this.state; const { contentStyle, children, cornerPosition, motionType, bgColor, showCorner, motionConfig, tipStyle, } = this.props; const { width, height } = this.translate; const iconPosition = this.cornerPosition; const newTipStyle = this.tipPosition; const isTop = cornerPosition.match(Top); const MotionComp = Motion[motionType]; return ( <MotionComp {...motionConfig} show={visible} style={[{ alignItems: 'center', justifyContent: 'center' }, tipStyle, newTipStyle]} width={width} height={height} > <View onLayout={({ nativeEvent: { layout } }) => this._handleLayout(layout)} style={{ borderRadius: cx(5) }} > {showCorner && isTop && ( <StyledIconFont style={iconPosition} color={bgColor} d={path.top} /> )} <StyledViewChildren style={[ contentStyle, { backgroundColor: bgColor, alignItems: 'center', justifyContent: 'center' }, ]} > {children} </StyledViewChildren> {!isTop && showCorner && ( <StyledIconFont style={iconPosition} color={bgColor} d={path.bottom} /> )} </View> </MotionComp> ); } } export default Tips;