下拉菜单组件是一个能够将页面上比较冗杂的操做收纳在一个点,以便节省页面空间,达到整洁美观的目的。react
Antd的下拉菜单组件中有一个点,就是他的内部元素必须是Antd的Menu组件,感受有点捆绑的意思。git
这里留一个小问题,为何触发方式是一个数组,而不是单个的github
export interface DropDownProps { trigger?: ('click' | 'hover')[]; // 触发方式 overlay: React.ReactNode; // 下拉菜单所承载的内容元素,要求为Antd的Menu组件 style?: React.CSSProperties; // 行内样式 onVisibleChange?: (visible?: boolean) => void; // 监听下拉菜单出现/消失 visible?: boolean; // 菜单是否显示 disabled?: boolean; // 菜单是否能够用 align?: Object; // 这个参数目前没有被使用 getPopupContainer?: (triggerNode: Element) => HTMLElement; // 渲染的挂载点,默认为body prefixCls?: string; // 样式类的命名前缀 className?: string; // 样式 placement?: 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight'; // 弹出框与触发点的对齐方式 }复制代码
由于这个组件主要使用的是rc-dropdown组件
因此这里只是对其参数作了一些封装,比较简单。typescript
export default class Dropdown extends React.Component<DropDownProps, any> {
static Button: typeof DropdownButton;
static defaultProps = {
prefixCls: 'ant-dropdown',
mouseEnterDelay: 0.15,
mouseLeaveDelay: 0.1,
placement: 'bottomLeft',
};
// 设定一个动画效果名称
getTransitionName() {
const { placement = '' } = this.props;
// js的indexOf()可使用在Array上也可使用在String上
// 使用方法同样,第一个参数是匹配的对象,第二个参数是从哪里开始匹配
if (placement.indexOf('top') >= 0) {
return 'slide-down';
}
return 'slide-up';
}
componentDidMount() {
// 这里就在检测菜单内容是不是antd的menu组件,而且检测menu组件的样式
const { overlay } = this.props;
const overlayProps = (overlay as any).props as any;
// warning函数仍是和以前学习的同样的用法
warning(
!overlayProps.mode || overlayProps.mode === 'vertical',
`mode="${overlayProps.mode}" is not supported for Dropdown\'s Menu.`,
);
}
render() {
const { children, prefixCls, overlay, trigger, disabled } = this.props;
// 将dropdown包裹的触发器加以封装,再渲染
const dropdownTrigger = cloneElement(children as any, {
className: classNames((children as any).props.className, `${prefixCls}-trigger`),
disabled,
});
// menu cannot be selectable in dropdown defaultly
const overlayProps = overlay && (overlay as any).props;
const selectable = (overlayProps && 'selectable' in overlayProps)
? overlayProps.selectable : false;
// 一样的将dropdown包裹的内容加以封装,再渲染
const fixedModeOverlay = cloneElement(overlay as any, {
mode: 'vertical',
selectable,
});
return (
// 最后将全部参数传入rc-dropdown组件
<RcDropdown
{...this.props}
transitionName={this.getTransitionName()}
trigger={disabled ? [] : trigger}
overlay={fixedModeOverlay}
>
{dropdownTrigger}
</RcDropdown>
);
}
}复制代码
这个组件是一个带按钮的下拉菜单组件,其实其原理就是使用的以前所讲的ButtonGroup来进行组合的一个组件。数组
想要了解ButtonGroup组件的能够点击这里markdown
这个组件的props继承了两个其余组件的props,这是typescript的interface的一个特性,能够继承多个,来造成一个新的。antd
export interface DropdownButtonProps extends ButtonGroupProps, DropDownProps { type?: 'primary' | 'ghost' | 'dashed'; // 按钮类型 disabled?: boolean; // 是否禁用 onClick?: React.MouseEventHandler<any>; // 点击事件 children?: any; // 子节点 }复制代码
从render函数就能够看出这个组件就是antd为了方便,为你们封装好了的一个带下拉菜单的按钮组件ide
render() { const { type, disabled, onClick, children, prefixCls, className, overlay, trigger, align, visible, onVisibleChange, placement, getPopupContainer, ...restProps, } = this.props; const dropdownProps = { align, overlay, trigger: disabled ? [] : trigger, onVisibleChange, placement, getPopupContainer, }; if ('visible' in this.props) { (dropdownProps as any).visible = visible; } return ( <ButtonGroup {...restProps} className={classNames(prefixCls, className)} > <Button type={type} disabled={disabled} onClick={onClick} > {children} </Button> <Dropdown {...dropdownProps}> <Button type={type} disabled={disabled}> <Icon type="down" /> </Button> </Dropdown> </ButtonGroup> ); }复制代码
写到这里就完了么?是滴,这一节就完了,由于查看了一下rc-dropdown的实现,而后根据日常看代码的习惯函数
从render()函数入口发现这一段代码oop
render() { const { prefixCls, children, transitionName, animation, align, placement, getPopupContainer, showAction, hideAction, overlayClassName, overlayStyle, trigger, ...otherProps, } = this.props; return ( <Trigger {...otherProps} prefixCls={prefixCls} ref={this.saveTrigger} popupClassName={overlayClassName} popupStyle={overlayStyle} builtinPlacements={placements} action={trigger} showAction={showAction} hideAction={hideAction} popupPlacement={placement} popupAlign={align} popupTransitionName={transitionName} popupAnimation={animation} popupVisible={this.state.visible} afterPopupVisibleChange={this.afterVisibleChange} popup={this.getMenuElement()} onPopupVisibleChange={this.onVisibleChange} getPopupContainer={getPopupContainer} > {children} </Trigger> ); }复制代码
而后再看到Trigger
这个组件,竟然是另一个库rc-trigger
而后在看了rc-trigger
的实现以后,才知道原来tigger组件才是下拉菜单的核心。
因此还没完呢,只是须要讲解的太多,不适合在一篇文章中讲全面,因此敬请期待下一篇文章,
咱们将会学习rc-trigger组件的实现,以后会再写一篇rc-dropdown的组件实现解读,
最后看完这三篇文章,再倒过来重温一遍,你将会学到怎么样一层层的包装组件,将一个基础组件包装成为一个高级组件的过程。