@douyinfe/semi-ui
Version:
A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.
273 lines • 8.95 kB
JavaScript
import _noop from "lodash/noop";
import _isBoolean from "lodash/isBoolean";
import _isUndefined from "lodash/isUndefined";
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { checkboxClasses as css, strings } from '@douyinfe/semi-foundation/lib/es/checkbox/constants';
import CheckboxFoundation from '@douyinfe/semi-foundation/lib/es/checkbox/checkboxFoundation';
import CheckboxInner from './checkboxInner';
import BaseComponent from '../_base/baseComponent';
import '@douyinfe/semi-foundation/lib/es/checkbox/checkbox.css';
import { Context } from './context';
import { getUuidShort } from '@douyinfe/semi-foundation/lib/es/utils/uuid';
class Checkbox extends BaseComponent {
get adapter() {
return Object.assign(Object.assign({}, super.adapter), {
setNativeControlChecked: checked => {
this.setState({
checked
});
},
notifyChange: cbContent => {
const {
onChange
} = this.props;
onChange && onChange(cbContent);
},
generateEvent: (checked, e) => {
const {
props
} = this;
const cbValue = {
target: Object.assign(Object.assign({}, props), {
checked
}),
stopPropagation: () => {
e.stopPropagation();
},
preventDefault: () => {
e.preventDefault();
},
nativeEvent: {
stopImmediatePropagation: () => {
if (e.nativeEvent && typeof e.nativeEvent.stopImmediatePropagation === 'function') {
e.nativeEvent.stopImmediatePropagation();
}
}
}
};
return cbValue;
},
getIsInGroup: () => this.isInGroup(),
getGroupValue: () => this.context && this.context.checkboxGroup.value || [],
notifyGroupChange: cbContent => {
this.context.checkboxGroup.onChange(cbContent);
},
getGroupDisabled: () => this.context && this.context.checkboxGroup.disabled,
setAddonId: () => {
this.setState({
addonId: getUuidShort({
prefix: 'addon'
})
});
},
setExtraId: () => {
this.setState({
extraId: getUuidShort({
prefix: 'extra'
})
});
},
setFocusVisible: focusVisible => {
this.setState({
focusVisible
});
},
focusCheckboxEntity: () => {
this.focus();
}
});
}
constructor(props) {
super(props);
this.handleChange = e => this.foundation.handleChange(e);
this.handleEnterPress = e => this.foundation.handleEnterPress(e);
this.handleFocusVisible = event => {
this.foundation.handleFocusVisible(event);
};
this.handleBlur = event => {
this.foundation.handleBlur();
};
const checked = false;
this.state = {
checked: props.checked || props.defaultChecked || checked,
addonId: props.addonId,
extraId: props.extraId,
focusVisible: false
};
this.checkboxEntity = null;
this.foundation = new CheckboxFoundation(this.adapter);
}
componentDidUpdate(prevProps) {
if (this.props.checked !== prevProps.checked) {
if (_isUndefined(this.props.checked)) {
this.foundation.setChecked(false);
} else if (_isBoolean(this.props.checked)) {
this.foundation.setChecked(this.props.checked);
}
}
}
isInGroup() {
// Why do we need to determine whether there is a value in props?
// If there is no value in the props of the checkbox in the context of the checkboxGroup,
// it will be considered not to belong to the checkboxGroup。
return Boolean(this.context && this.context.checkboxGroup && 'value' in this.props);
}
focus() {
this.checkboxEntity && this.checkboxEntity.focus();
}
blur() {
this.checkboxEntity && this.checkboxEntity.blur();
}
render() {
const {
disabled,
style,
prefixCls,
className,
indeterminate,
children,
onMouseEnter,
onMouseLeave,
extra,
value,
role,
tabIndex,
id,
type
} = this.props;
const {
checked,
addonId,
extraId,
focusVisible
} = this.state;
const props = {
checked,
disabled
};
const inGroup = this.isInGroup();
if (inGroup) {
if (this.context.checkboxGroup.value) {
const realChecked = (this.context.checkboxGroup.value || []).includes(value);
props.checked = realChecked;
}
if (this.context.checkboxGroup.disabled) {
props.disabled = this.context.checkboxGroup.disabled || this.props.disabled;
}
const {
isCardType,
isPureCardType
} = this.context.checkboxGroup;
props.isCardType = isCardType;
props.isPureCardType = isPureCardType;
props['name'] = this.context.checkboxGroup.name;
} else {
props.isPureCardType = type === strings.TYPE_PURECARD;
props.isCardType = type === strings.TYPE_CARD || props.isPureCardType;
}
const prefix = prefixCls || css.PREFIX;
const focusOuter = props.isCardType || props.isPureCardType;
const wrapper = classnames(prefix, {
[`${prefix}-disabled`]: props.disabled,
[`${prefix}-indeterminate`]: indeterminate,
[`${prefix}-checked`]: props.checked,
[`${prefix}-unChecked`]: !props.checked,
[`${prefix}-cardType`]: props.isCardType,
[`${prefix}-cardType_disabled`]: props.disabled && props.isCardType,
[`${prefix}-cardType_enable`]: !(props.disabled && props.isCardType),
[`${prefix}-cardType_checked`]: props.isCardType && props.checked && !props.disabled,
[`${prefix}-cardType_checked_disabled`]: props.isCardType && props.checked && props.disabled,
[className]: Boolean(className),
[`${prefix}-focus`]: focusVisible && focusOuter
});
const extraCls = classnames(`${prefix}-extra`, {
[`${prefix}-cardType_extra_noChildren`]: props.isCardType && !children
});
const name = inGroup && this.context.checkboxGroup.name;
const xSemiPropChildren = this.props['x-semi-children-alias'] || 'children';
const renderContent = () => {
if (!children && !extra) {
return null;
}
return /*#__PURE__*/React.createElement("div", {
className: `${prefix}-content`
}, children ? (/*#__PURE__*/React.createElement("span", {
id: addonId,
className: `${prefix}-addon`,
"x-semi-prop": xSemiPropChildren
}, children)) : null, extra ? (/*#__PURE__*/React.createElement("div", {
id: extraId,
className: extraCls,
"x-semi-prop": "extra"
}, extra)) : null);
};
return (
/*#__PURE__*/
// label is better than span, however span is here which is to solve gitlab issue #364
React.createElement("span", Object.assign({
role: role,
tabIndex: tabIndex,
style: style,
className: wrapper,
id: id,
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave,
onClick: this.handleChange,
onKeyPress: this.handleEnterPress,
"aria-labelledby": this.props['aria-labelledby']
}, this.getDataAttr(this.props)), /*#__PURE__*/React.createElement(CheckboxInner, Object.assign({}, this.props, props, {
addonId: children && addonId,
extraId: extra && extraId,
isPureCardType: props.isPureCardType,
ref: ref => {
this.checkboxEntity = ref;
},
focusInner: focusVisible && !focusOuter,
onInputFocus: this.handleFocusVisible,
onInputBlur: this.handleBlur
})), renderContent())
);
}
}
Checkbox.contextType = Context;
Checkbox.propTypes = {
'aria-describedby': PropTypes.string,
'aria-errormessage': PropTypes.string,
'aria-invalid': PropTypes.bool,
'aria-labelledby': PropTypes.string,
'aria-required': PropTypes.bool,
// Specifies whether it is currently selected
checked: PropTypes.bool,
// Initial check
defaultChecked: PropTypes.bool,
// Failure state
disabled: PropTypes.bool,
// Set indeterminate state, only responsible for style control
indeterminate: PropTypes.bool,
// Callback function when changing
onChange: PropTypes.func,
value: PropTypes.any,
style: PropTypes.object,
className: PropTypes.string,
prefixCls: PropTypes.string,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
extra: PropTypes.node,
index: PropTypes.number,
'aria-label': PropTypes.string,
tabIndex: PropTypes.number,
preventScroll: PropTypes.bool,
type: PropTypes.string
};
Checkbox.defaultProps = {
defaultChecked: false,
indeterminate: false,
onChange: _noop,
onMouseEnter: _noop,
onMouseLeave: _noop,
type: 'default'
};
Checkbox.elementType = 'Checkbox';
export default Checkbox;