Button組件html
import * as React from 'react'; import { findDOMNode } from 'react-dom'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import omit from 'omit.js'; import Icon from '../icon'; import Group from './button-group'; //2箇中文字符 const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); function isString(str: any) { return typeof str === 'string'; } // Insert one space between two chinese characters automatically. function insertSpace(child: React.ReactChild, needInserted: boolean) { // Check the child if is undefined or null. if (child == null) { return; } const SPACE = needInserted ? ' ' : ''; // strictNullChecks oops. // child是react的組件而且組件的子元素只有2箇中文字符 // 若是是<span>點擊</span>,child是一個obj,裏面type爲"span" if (typeof child !== 'string' && typeof child !== 'number' && isString(child.type) && isTwoCNChar(child.props.children)) { // 克隆組件(element,[props],[...children]) // 也能夠寫成 // <child.type {...child.props}>{child.props.children.split('').join(SPACE)}</child.type> return React.cloneElement(child, {}, child.props.children.split('').join(SPACE)); } // child是字符串 if (typeof child === 'string') { // child只有2箇中文字符 if (isTwoCNChar(child)) { child = child.split('').join(SPACE); } return <span>{child}</span>; } return child; } export type ButtonType = 'default' | 'primary' | 'ghost' | 'dashed' | 'danger'; export type ButtonShape = 'circle' | 'circle-outline'; export type ButtonSize = 'small' | 'default' | 'large'; export interface BaseButtonProps { type?: ButtonType; htmlType?: string; icon?: string; shape?: ButtonShape; size?: ButtonSize; loading?: boolean | { delay?: number }; prefixCls?: string; className?: string; ghost?: boolean; } export type AnchorButtonProps = BaseButtonProps & React.AnchorHTMLAttributes<HTMLAnchorElement>; export type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>; export type ButtonProps = AnchorButtonProps | NativeButtonProps; export default class Button extends React.Component<ButtonProps, any> { static Group: typeof Group; static __ANT_BUTTON = true; static defaultProps = { prefixCls: 'ant-btn', loading: false, ghost: false, }; static propTypes = { type: PropTypes.string, shape: PropTypes.oneOf(['circle', 'circle-outline']), size: PropTypes.oneOf(['large', 'default', 'small']), htmlType: PropTypes.oneOf(['submit', 'button', 'reset']), onClick: PropTypes.func, loading: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), className: PropTypes.string, icon: PropTypes.string, }; timeout: number; delayTimeout: number; constructor(props: ButtonProps) { super(props); this.state = { loading: props.loading, clicked: false, hasTwoCNChar: false, }; } //每次建立時 componentDidMount() { //判斷子元素是否只有2個字符,而且更改state this.fixTwoCNChar(); } //每次prop改變 componentWillReceiveProps(nextProps: ButtonProps) { const currentLoading = this.props.loading; const loading = nextProps.loading; //若是傳了loading if (currentLoading) { //先清空以前loading的計時器 clearTimeout(this.delayTimeout); } //loading不爲布爾值,而且存在delay屬性(自定義loadidng延遲) if (typeof loading !== 'boolean' && loading && loading.delay) { this.delayTimeout = window.setTimeout(() => this.setState({ loading }), loading.delay); } else { //未自定義loading延遲 this.setState({ loading }); } } //每次更新render後,檢查是否2中文字符 componentDidUpdate() { this.fixTwoCNChar(); } componentWillUnmount() { if (this.timeout) { clearTimeout(this.timeout); } if (this.delayTimeout) { clearTimeout(this.delayTimeout); } } //判斷子元素是否只有2個字符,而且將判斷結果給state.hasTwoCNChar fixTwoCNChar() { // Fix for HOC usage like <FormatMessage /> //返回已經裝在的DOM(這裏多是<a>或者<button>) const node = (findDOMNode(this) as HTMLElement); //返回button上的值(例如:點擊) const buttonText = node.textContent || node.innerText; //只有1個子元素,而且這個子元素是2箇中文字符 if (this.isNeedInserted() && isTwoCNChar(buttonText)) { //將state的hasTowCNChar改變爲true if (!this.state.hasTwoCNChar) { this.setState({ hasTwoCNChar: true, }); } } else if (this.state.hasTwoCNChar) { //若是不符合條件,而且hasTowCNChar爲true(說明這個組件以前是2箇中文字,後來update就不是了),變爲false this.setState({ hasTwoCNChar: false, }); } } /** * 每次click都經過改變state.clicked來變動class,從而改變視覺效果 */ handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => { // Add click effect this.setState({ clicked: true }); clearTimeout(this.timeout); this.timeout = window.setTimeout(() => this.setState({ clicked: false }), 500); const onClick = this.props.onClick; //onClick存在,執行 if (onClick) { (onClick as (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void)(e); } } //只有1個子元素而且沒有icon isNeedInserted() { const { icon, children } = this.props; return React.Children.count(children) === 1 && !icon; } render() { const { type, shape, size, className, htmlType, children, icon, prefixCls, ghost, ...others, } = this.props; const { loading, clicked, hasTwoCNChar } = this.state; // large => lg // small => sm let sizeCls = ''; switch (size) { case 'large': sizeCls = 'lg'; break; case 'small': sizeCls = 'sm'; default: break; } const ComponentProp = (others as AnchorButtonProps).href ? 'a' : 'button'; //classNames 能夠將如下class變爲"xx1 xx2 xx3..."的格式 //其中值爲true,key成立,值爲false,key不合並 const classes = classNames(prefixCls, className, { [`${prefixCls}-${type}`]: type, [`${prefixCls}-${shape}`]: shape, [`${prefixCls}-${sizeCls}`]: sizeCls, [`${prefixCls}-icon-only`]: !children && icon, [`${prefixCls}-loading`]: loading, [`${prefixCls}-clicked`]: clicked, [`${prefixCls}-background-ghost`]: ghost, [`${prefixCls}-two-chinese-chars`]: hasTwoCNChar, }); const iconType = loading ? 'loading' : icon; const iconNode = iconType ? <Icon type={iconType} /> : null; //this.props.children存在時,對每個children類型執行判斷(null,react組件,字符串)而且根據判斷執行添加操做 const kids = (children || children === 0) ? React.Children.map(children, child => insertSpace(child, this.isNeedInserted())) : null; /** * omit:從others裏刪除loading這個屬性 * type:若是有href屬性(a標籤)設爲undefined,若是是button則設置爲傳入值或者'button' * */ return ( <ComponentProp {...omit(others, ['loading'])} type={(others as AnchorButtonProps).href ? undefined : (htmlType || 'button')} className={classes} onClick={this.handleClick} > {iconNode}{kids} </ComponentProp> ); } }