refactor(tree-select): rewrite with hook (#27593)

This commit is contained in:
Tom Xu 2020-11-08 15:27:30 +08:00 committed by GitHub
parent 535400f7cd
commit cd15ba4c1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 128 additions and 147 deletions

View File

@ -6,7 +6,7 @@ import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest'; import rtlTest from '../../../tests/shared/rtlTest';
describe('TreeSelect', () => { describe('TreeSelect', () => {
focusTest(TreeSelect); focusTest(TreeSelect, { refFocus: true });
mountTest(TreeSelect); mountTest(TreeSelect);
rtlTest(TreeSelect); rtlTest(TreeSelect);

View File

@ -8,7 +8,8 @@ import RcTreeSelect, {
} from 'rc-tree-select'; } from 'rc-tree-select';
import classNames from 'classnames'; import classNames from 'classnames';
import omit from 'omit.js'; import omit from 'omit.js';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { DefaultValueType } from 'rc-tree-select/lib/interface';
import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning'; import devWarning from '../_util/devWarning';
import { AntTreeNodeProps } from '../tree'; import { AntTreeNodeProps } from '../tree';
import getIcons from '../select/utils/iconUtil'; import getIcons from '../select/utils/iconUtil';
@ -35,167 +36,147 @@ export interface TreeSelectProps<T>
bordered?: boolean; bordered?: boolean;
} }
class TreeSelect<T> extends React.Component<TreeSelectProps<T>, {}> { export interface RefTreeSelectProps {
static TreeNode = TreeNode; focus: () => void;
blur: () => void;
}
static SHOW_ALL: typeof SHOW_ALL = SHOW_ALL; const InternalTreeSelect = <T extends DefaultValueType>(
{
static SHOW_PARENT: typeof SHOW_PARENT = SHOW_PARENT; prefixCls: customizePrefixCls,
size: customizeSize,
static SHOW_CHILD: typeof SHOW_CHILD = SHOW_CHILD; bordered = true,
className,
static defaultProps = { treeCheckable,
transitionName: 'slide-up', multiple,
choiceTransitionName: '', listHeight = 256,
bordered: true, listItemHeight = 26,
}; notFoundContent,
switcherIcon,
selectRef = React.createRef<RcTreeSelect>(); treeLine,
getPopupContainer,
constructor(props: TreeSelectProps<T>) { dropdownClassName,
super(props); treeIcon = false,
transitionName = 'slide-up',
devWarning( choiceTransitionName = '',
props.multiple !== false || !props.treeCheckable, ...props
'TreeSelect', }: TreeSelectProps<T>,
'`multiple` will alway be `true` when `treeCheckable` is true', ref: React.Ref<RefTreeSelectProps>,
); ) => {
} const {
focus() {
if (this.selectRef.current) {
this.selectRef.current.focus();
}
}
blur() {
if (this.selectRef.current) {
this.selectRef.current.blur();
}
}
renderTreeSelect = ({
getPopupContainer: getContextPopupContainer, getPopupContainer: getContextPopupContainer,
getPrefixCls, getPrefixCls,
renderEmpty, renderEmpty,
direction, direction,
virtual, virtual,
dropdownMatchSelectWidth, dropdownMatchSelectWidth,
}: ConfigConsumerProps) => { } = React.useContext(ConfigContext);
const { const size = React.useContext(SizeContext);
prefixCls: customizePrefixCls,
size: customizeSize,
className,
treeCheckable,
multiple,
listHeight = 256,
listItemHeight = 26,
notFoundContent,
switcherIcon,
treeLine,
getPopupContainer,
dropdownClassName,
bordered,
treeIcon = false,
} = this.props;
const prefixCls = getPrefixCls('select', customizePrefixCls); devWarning(
const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls); multiple !== false || !treeCheckable,
const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls); 'TreeSelect',
'`multiple` will alway be `true` when `treeCheckable` is true',
);
const mergedDropdownClassName = classNames( const prefixCls = getPrefixCls('select', customizePrefixCls);
dropdownClassName, const treePrefixCls = getPrefixCls('select-tree', customizePrefixCls);
`${treeSelectPrefixCls}-dropdown`, const treeSelectPrefixCls = getPrefixCls('tree-select', customizePrefixCls);
{
[`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl',
},
);
const isMultiple = !!(treeCheckable || multiple); const mergedDropdownClassName = classNames(dropdownClassName, `${treeSelectPrefixCls}-dropdown`, {
[`${treeSelectPrefixCls}-dropdown-rtl`]: direction === 'rtl',
});
// ===================== Icons ===================== const isMultiple = !!(treeCheckable || multiple);
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
...this.props,
multiple: isMultiple,
prefixCls,
});
// ===================== Empty ===================== // ===================== Icons =====================
let mergedNotFound: React.ReactNode; const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons({
if (notFoundContent !== undefined) { ...props,
mergedNotFound = notFoundContent; multiple: isMultiple,
} else { prefixCls,
mergedNotFound = renderEmpty('Select'); });
}
// ==================== Render ===================== // ===================== Empty =====================
const selectProps = omit(this.props, [ let mergedNotFound: React.ReactNode;
'prefixCls', if (notFoundContent !== undefined) {
'suffixIcon', mergedNotFound = notFoundContent;
'itemIcon', } else {
'removeIcon', mergedNotFound = renderEmpty('Select');
'clearIcon',
'switcherIcon',
'size',
'bordered',
]);
return (
<SizeContext.Consumer>
{size => {
const mergedSize = customizeSize || size;
const mergedClassName = classNames(
!customizePrefixCls && treeSelectPrefixCls,
{
[`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-borderless`]: !bordered,
},
className,
);
return (
<RcTreeSelect
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps}
ref={this.selectRef}
prefixCls={prefixCls}
className={mergedClassName}
listHeight={listHeight}
listItemHeight={listItemHeight}
treeCheckable={
treeCheckable ? (
<span className={`${prefixCls}-tree-checkbox-inner`} />
) : (
treeCheckable
)
}
inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon}
removeIcon={removeIcon}
clearIcon={clearIcon}
switcherIcon={(nodeProps: AntTreeNodeProps) =>
renderSwitcherIcon(treePrefixCls, switcherIcon, treeLine, nodeProps)
}
showTreeIcon={treeIcon}
notFoundContent={mergedNotFound}
getPopupContainer={getPopupContainer || getContextPopupContainer}
treeMotion={null}
dropdownClassName={mergedDropdownClassName}
/>
);
}}
</SizeContext.Consumer>
);
};
render() {
return <ConfigConsumer>{this.renderTreeSelect}</ConfigConsumer>;
} }
// ==================== Render =====================
const selectProps = omit(props, [
'suffixIcon',
'itemIcon',
'removeIcon',
'clearIcon',
'switcherIcon',
]);
const mergedSize = customizeSize || size;
const mergedClassName = classNames(
!customizePrefixCls && treeSelectPrefixCls,
{
[`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-borderless`]: !bordered,
},
className,
);
return (
<RcTreeSelect
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps}
ref={ref}
prefixCls={prefixCls}
className={mergedClassName}
listHeight={listHeight}
listItemHeight={listItemHeight}
treeCheckable={
treeCheckable ? <span className={`${prefixCls}-tree-checkbox-inner`} /> : treeCheckable
}
inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon}
multiple={multiple}
removeIcon={removeIcon}
clearIcon={clearIcon}
switcherIcon={(nodeProps: AntTreeNodeProps) =>
renderSwitcherIcon(treePrefixCls, switcherIcon, treeLine, nodeProps)
}
showTreeIcon={treeIcon}
notFoundContent={mergedNotFound}
getPopupContainer={getPopupContainer || getContextPopupContainer}
treeMotion={null}
dropdownClassName={mergedDropdownClassName}
choiceTransitionName={choiceTransitionName}
transitionName={transitionName}
/>
);
};
const TreeSelectRef = React.forwardRef(InternalTreeSelect) as <T extends DefaultValueType>(
props: TreeSelectProps<T> & { ref?: React.Ref<RefTreeSelectProps> },
) => React.ReactElement;
type InternalTreeSelectType = typeof TreeSelectRef;
interface TreeSelectInterface extends InternalTreeSelectType {
TreeNode: typeof TreeNode;
SHOW_ALL: typeof SHOW_ALL;
SHOW_PARENT: typeof SHOW_PARENT;
SHOW_CHILD: typeof SHOW_CHILD;
} }
const TreeSelect = TreeSelectRef as TreeSelectInterface;
TreeSelect.TreeNode = TreeNode;
TreeSelect.SHOW_ALL = SHOW_ALL;
TreeSelect.SHOW_PARENT = SHOW_PARENT;
TreeSelect.SHOW_CHILD = SHOW_CHILD;
export { TreeNode }; export { TreeNode };
export default TreeSelect; export default TreeSelect;