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

323 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import UIState from '../mixin-ui-state';
import ConfigProvider from '../config-provider';
import Icon from '../icon';
import { obj, func } from '../util';
const { noop } = func;
function isChecked(selectedValue, value) {
return selectedValue.indexOf(value) > -1;
}
/**
* Checkbox
* @order 1
*/
class Checkbox extends UIState {
static displayName = 'Checkbox';
static propTypes = {
...ConfigProvider.propTypes,
prefix: PropTypes.string,
rtl: PropTypes.bool,
/**
* 自定义类名
*/
className: PropTypes.string,
/**
* checkbox id, 挂载在input上
*/
id: PropTypes.string,
/**
* 自定义内敛样式
*/
style: PropTypes.object,
/**
* 选中状态
*/
checked: PropTypes.bool,
/**
* 默认选中状态
*/
defaultChecked: PropTypes.bool,
/**
* 禁用
*/
disabled: PropTypes.bool,
/**
* 通过属性配置label
*/
label: PropTypes.node,
/**
* Checkbox 的中间状态,只会影响到 Checkbox 的样式,并不影响其 checked 属性
*/
indeterminate: PropTypes.bool,
/**
* Checkbox 的默认中间态,只会影响到 Checkbox 的样式,并不影响其 checked 属性
*/
defaultIndeterminate: PropTypes.bool,
/**
* 状态变化时触发的事件
* @param {Boolean} checked 是否选中
* @param {Event} e Dom 事件对象
*/
onChange: PropTypes.func,
/**
* 鼠标进入enter事件
* @param {Event} e Dom 事件对象
*/
onMouseEnter: PropTypes.func,
/**
* 鼠标离开Leave事件
* @param {Event} e Dom 事件对象
*/
onMouseLeave: PropTypes.func,
/**
* checkbox 的value
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/**
* name
*/
name: PropTypes.string,
/**
* 是否为预览态
*/
isPreview: PropTypes.bool,
/**
* 预览态模式下渲染的内容
* @param {number} value 评分值
*/
renderPreview: PropTypes.func,
};
static defaultProps = {
defaultChecked: false,
defaultIndeterminate: false,
onChange: noop,
onMouseEnter: noop,
onMouseLeave: noop,
prefix: 'next-',
isPreview: false,
};
static contextTypes = {
onChange: PropTypes.func,
__group__: PropTypes.bool,
selectedValue: PropTypes.array,
disabled: PropTypes.bool,
prefix: PropTypes.string,
};
constructor(props, context) {
super(props);
let checked; let
indeterminate;
if ('checked' in props) {
checked = props.checked;
} else {
checked = props.defaultChecked;
}
if ('indeterminate' in props) {
indeterminate = props.indeterminate;
} else {
indeterminate = props.defaultIndeterminate;
}
if (context.__group__) {
checked = isChecked(context.selectedValue, props.value);
}
this.state = {
checked,
indeterminate,
};
this.disabled =
props.disabled ||
(context.__group__ && 'disabled' in context && context.disabled);
this.onChange = this.onChange.bind(this);
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextContext.__group__) {
if ('selectedValue' in nextContext) {
this.setState({
checked: isChecked(
nextContext.selectedValue,
nextProps.value,
),
});
}
this.disabled =
nextProps.disabled ||
('disabled' in nextContext && nextContext.disabled);
} else {
if ('checked' in nextProps) {
this.setState({
checked: nextProps.checked,
});
}
this.disabled = nextProps.disabled;
}
if ('indeterminate' in nextProps) {
this.setState({
indeterminate: nextProps.indeterminate,
});
}
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
const { shallowEqual } = obj;
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState) ||
!shallowEqual(this.context, nextContext)
);
}
onChange(e) {
const { checked } = e.target;
const { value } = this.props;
if (this.disabled) {
return;
}
if (this.context.__group__) {
this.context.onChange(value, e);
} else {
if (!('checked' in this.props)) {
this.setState({
checked,
});
}
if (!('indeterminate' in this.props)) {
this.setState({
indeterminate: false,
});
}
this.props.onChange(checked, e);
}
}
render() {
/* eslint-disable no-unused-vars */
const {
id,
className,
children,
style,
label,
onMouseEnter,
onMouseLeave,
rtl,
isPreview,
renderPreview,
value,
name,
...otherProps
} = this.props;
const checked = !!this.state.checked;
const { disabled } = this;
const indeterminate = !!this.state.indeterminate;
const prefix = this.context.prefix || this.props.prefix;
const others = obj.pickOthers(Checkbox.propTypes, otherProps);
const othersData = obj.pickAttrsWith(others, 'data-');
if (otherProps.title) {
othersData.title = otherProps.title;
}
let childInput = (
<input
{...obj.pickOthers(Checkbox.propTypes, otherProps)}
id={id}
value={value}
name={name}
disabled={disabled}
checked={checked}
type="checkbox"
onChange={this.onChange}
aria-checked={indeterminate ? 'mixed' : checked}
className={`${prefix}checkbox-input`}
/>
);
// disable 无状态操作
if (!disabled) {
childInput = this.getStateElement(childInput);
}
const cls = classnames({
[`${prefix}checkbox-wrapper`]: true,
[className]: !!className,
checked,
disabled,
indeterminate,
[this.getStateClassName()]: true,
});
const labelCls = `${prefix}checkbox-label`;
const type = indeterminate ? 'semi-select' : 'select';
if (isPreview) {
const previewCls = classnames(className, `${prefix}form-preview`);
if ('renderPreview' in this.props) {
return (
<div
id={id}
dir={rtl ? 'rtl' : undefined}
{...othersData}
className={previewCls}
>
{renderPreview(checked, this.props)}
</div>
);
}
return (
<p
id={id}
dir={rtl ? 'rtl' : undefined}
{...othersData}
className={previewCls}
>
{checked && (children || label || this.state.value)}
</p>
);
}
return (
<label
{...othersData}
className={cls}
style={style}
dir={rtl ? 'rtl' : undefined}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<span className={`${prefix}checkbox`}>
<span className={`${prefix}checkbox-inner`}>
<Icon
type={type}
size="xs"
className={indeterminate ? 'zoomIn' : ''}
/>
</span>
{childInput}
</span>
{[label, children].map((item, i) => ([undefined, null].indexOf(item) === -1 ? (
<span key={i} className={labelCls}>
{item}
</span>
) : null))}
</label>
);
}
}
export default ConfigProvider.config(Checkbox);