2020-09-07 17:48:39 +08:00

217 lines
4.8 KiB
JavaScript

import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import { polyfill } from 'react-lifecycles-compat';
import { KEYCODE } from '../util';
import ConfigProvider from '../config-provider';
import zhCN from '../locale/zh-cn';
/** Switch */
class Switch extends React.Component {
static contextTypes = {
prefix: PropTypes.string,
};
static propTypes = {
prefix: PropTypes.string,
rtl: PropTypes.bool,
pure: PropTypes.bool,
/**
* 自定义类名
*/
className: PropTypes.string,
/**
* 自定义内敛样式
*/
style: PropTypes.object,
/**
* 打开时的内容
*/
checkedChildren: PropTypes.any,
/**
* 关闭时的内容
*/
unCheckedChildren: PropTypes.any,
/**
* 开关状态改变是触发此事件
* @param {Boolean} checked 是否为打开状态
* @param {Event} e DOM事件对象
*/
onChange: PropTypes.func,
/**
* 开关当前的值(针对受控组件)
*/
checked: PropTypes.bool,
/**
* 开关默认值 (针对非受控组件)
*/
defaultChecked: PropTypes.bool,
/**
* 表示开关被禁用
*/
disabled: PropTypes.bool,
/**
* switch的尺寸
* @enumdesc 正常大小, 缩小版大小
*/
size: PropTypes.oneOf(['medium', 'small']),
/**
* 鼠标点击事件
* @param {Event} e DOM事件对象
*/
onClick: PropTypes.func,
/**
* 键盘按键事件
* @param {Event} e DOM事件对象
*/
onKeyDown: PropTypes.func,
/**
* 是否为预览态
*/
isPreview: PropTypes.bool,
/**
* 预览态模式下渲染的内容
* @param {number} value 评分值
*/
renderPreview: PropTypes.func,
/**
* 国际化配置
*/
locale: PropTypes.object,
};
static defaultProps = {
prefix: 'next-',
size: 'medium',
disabled: false,
defaultChecked: false,
isPreview: false,
readOnly: false,
onChange: () => {},
locale: zhCN.Switch,
};
constructor(props, context) {
super(props, context);
const checked = props.checked || props.defaultChecked;
this.onChange = this.onChange.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.state = {
checked,
};
}
static getDerivedStateFromProps(props, state) {
if ('checked' in props && props.checked !== state.checked) {
return {
checked: !!props.checked,
};
}
return null;
}
onChange(ev) {
const checked = !this.state.checked;
if (!('checked' in this.props)) {
this.setState({
checked,
});
}
this.props.onChange(checked, ev);
this.props.onClick && this.props.onClick(ev);
}
onKeyDown(e) {
if (e.keyCode === KEYCODE.ENTER || e.keyCode === KEYCODE.SPACE) {
this.onChange(e);
}
this.props.onKeyDown && this.props.onKeyDown(e);
}
render() {
const {
prefix,
className,
disabled,
readOnly,
size,
checkedChildren,
unCheckedChildren,
rtl,
isPreview,
renderPreview,
locale,
...others
} = this.props;
const { checked } = this.state;
const status = checked ? 'on' : 'off';
const children = checked ? checkedChildren : unCheckedChildren;
let _size = size;
if (_size !== 'small' && _size !== 'medium') {
_size = 'medium';
}
const classes = classNames({
[`${prefix}switch`]: true,
[`${prefix}switch-${status}`]: true,
[`${prefix}switch-${_size}`]: true,
[className]: className,
});
let attrs;
const isDisabled = disabled || readOnly;
if (!isDisabled) {
attrs = {
onClick: this.onChange,
tabIndex: 0,
onKeyDown: this.onKeyDown,
disabled: false,
};
} else {
attrs = {
disabled: true,
};
}
if (isPreview) {
const previewCls = classNames(className, {
[`${prefix}form-preview`]: true,
});
if ('renderPreview' in this.props) {
return (
<div className={previewCls} {...others}>
{renderPreview(checked, this.props)}
</div>
);
}
return (
<p className={previewCls} {...others}>
{locale[status]}
</p>
);
}
return (
<div
role="switch"
dir={rtl ? 'rtl' : undefined}
tabIndex="0"
{...others}
className={classes}
{...attrs}
aria-checked={checked}
>
<div className={`${prefix}switch-children`}>{children}</div>
</div>
);
}
}
export default ConfigProvider.config(polyfill(Switch));