Ant-design dropdown 源码学习

UI组件学习过程中的一些思考,同步于:  zhuanlan.zhihu.com/p/40811867


原生dom可以很容易的实现简单的dropdown,却很难满足我们的各种需求,因此各式各样的dropdown第三方实现就出现了。ant-design是基于react实现的一组UI组件,我们选择对其中的dropdown进行分析。

Dropdown 的主要组成:

  • 一个弹出的下拉列表
  • 一个当前的选中项
实现时需要注意的几个问题:什么时候弹出下拉选项, 下拉选项挂放在哪个位置。直观上下拉选项应该是一个绝对定位的Popup div。区别用户事件是发生在下拉选项内部还是外部,毕竟外部可能是期望收起这个下拉列表。
带着这几个问题我们开始看 react-component/dropdown
对外的接口:
onOverlayClick: func
onVisibleChange: func
animation: any
align: object
placement: string
overlay: node
trigger: array
alignPoint: bool
showAction:
hideAction
getPopupContainer: func复制代码
通过这几个接口可以发现我们的那些问题都有对应的答案
dropdown react节点图
dropdown 的源码主要是调用了Trigger这个抽象组件实现大部分的逻辑。
Trigger组件是一个抽象化组件,用来指定popup类型的UI。涉及到的包括popup, alignment。

Trigger创建、关闭popup

创建Popup在react 16之后提供了 Portal可以实现将内容指定到非当前节点所在层级的div上,方便了popup,而为兼容之前的版本使用的ReactDOM.unstable_renderSubtreeIntoContainer 等方法,实现较为复杂,具体可看util中的 ContainerRender
在Trigger component的componentDidUpdate生命周期里面监听事件。
if (state.popupVisible) {
      let currentDocument;
      if (!this.clickOutsideHandler && (this.isClickToHide() || this.isContextMenuToShow())) {
        currentDocument = props.getDocument();
        this.clickOutsideHandler = addEventListener(currentDocument,
          'mousedown', this.onDocumentClick);
      }
      // always hide on mobile
      if (!this.touchOutsideHandler) {
        currentDocument = currentDocument || props.getDocument();
        this.touchOutsideHandler = addEventListener(currentDocument,
          'touchstart', this.onDocumentClick);
      }
      // close popup when trigger type contains 'onContextMenu' and document is scrolling.
      if (!this.contextMenuOutsideHandler1 && this.isContextMenuToShow()) {
        currentDocument = currentDocument || props.getDocument();
        this.contextMenuOutsideHandler1 = addEventListener(currentDocument,
          'scroll', this.onContextMenuClose);
      }
      // close popup when trigger type contains 'onContextMenu' and window is blur.
      if (!this.contextMenuOutsideHandler2 && this.isContextMenuToShow()) {
        this.contextMenuOutsideHandler2 = addEventListener(window,
          'blur', this.onContextMenuClose);
      }
      return;
    }复制代码
判断鼠标是否离开popup
onPopupMouseLeave = (e) => {
    // https://github.com/react-component/trigger/pull/13
    // react bug?
    if (e.relatedTarget && !e.relatedTarget.setTimeout &&
      this._component &&
      this._component.getPopupDomNode &&
      contains(this._component.getPopupDomNode(), e.relatedTarget)) {
      return;
    }
    this.delaySetPopupVisible(false, this.props.mouseLeaveDelay);
  }复制代码
操作在popup上还是其他区域
onDocumentClick = (event) => {
    if (this.props.mask && !this.props.maskClosable) {
      return;
    }
    const target = event.target;
    const root = findDOMNode(this);
    const popupNode = this.getPopupDomNode();
    if (!contains(root, target) && !contains(popupNode, target)) {
      this.close();
    }
  }复制代码

Trigger Alignment

Trigger 做的另一件事情就是对popup的位置进行定位。介绍下dom-align库,用于指定dom节点对齐位置。API主要接口定义source、target、offset、overflow。


转载于:https://juejin.im/post/5b5d8008f265da0fb0187d46

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值