Dropdown 组件实现思路

概述:Dropdown 下拉菜单与 Select 的区别在于它是包含了一部分操作,所以它的 Option 下拉项要对其进行支持。

publish:2019-03-31

自从我写了关于中后台系统组件的第一篇文章以来,已经三个月了。由于业务场景的较为简单,所以它没有其他组件库的功能那么强大,但是整体结构较为类似,代码结构清晰,容易扩展。

将 Dropdown 分为两个模块,父组件 Dropdown,子组件 Dropdown-option。其中Dropdown 负责控制整体的显示,Drop-option 负责下拉菜单的每一个选项。

Dropdown - 组件结构

├── Dropdown

├── Dropdown-option

Dropdown

父组件主要负责的是选中项的显示以及下拉菜单状态(开、合)的控制。

具体的代码如下

<template>
  <div
    :class="['dropdown', { 'is-hover': trigger === 'hover' && isOpen }]"
    tabindex="0"
    @click.stop="isOpen = !isOpen"
    @blur="trigger !== 'hover' && (isOpen = false)"
  >
    <div :class="['dropdown__label']">
      <slot name="label">
        <span class="c-color-success">{{ placeholder }}</span>
      </slot>
      <fat-icon name="expand_more" class="c-color-success" />
    </div>

    <div class="dropdown__menu" v-if="trigger === 'hover' || isOpen">
      <slot name="menu"></slot>
    </div>
  </div>
</template>

<script>
export default {
  name: "dropdown",
  provide() {
    return {
      Dropdown: this
    };
  },
  props: {
    placeholder: { type: String, default: "下拉菜单" },
    trigger: { type: String, default: "hover" },
    selectValue: { type: [String, Number] },
    optionKey: { type: String, default: "value" }
  },
  data() {
    return {
      isOpen: this.trigger === "hover",
      selectItem: {}
    };
  },
  model: {
    prop: "selectValue",
    event: "select"
  }
};
复制代码

首先对处理下拉菜单开关状态的控制,依据 trigger 也就是触发方式的不同,可以分为两类 hover || click

  • trigger = 'hover' 时,对最外层的 div 添加 is-hover 的类名,它主要是负责添加 :hover 伪类来显示下拉菜单
    &.is-hover {
        &:hover {
            .dropdown__menu {
                display: block;
            }
        }
        .dropdown__menu {
            display: none;
        }
    }
复制代码

​ 同时依据 trigger 初始化 isOpen 状态为 true

  • trigger = 'click' 时,利用 isOpen 的状态来控制下拉菜单的开、合。主要是依据事件来触发,利用 @click.stop="isOpen = !isOpen",来完成下来菜单的展开操作。之后,对最外层的 div 添加 tabindex="0" 属性使得它能够触发失焦事件 blur,同时添加 @blur="trigger !== 'hover' && (isOpen = false)",意味着当它失效的时候,自动关闭下拉菜单。

以上完成了 Dropdown 对下拉菜单控制的功能,利用 provide ,完成它与 Dropdown-option 的通讯,传递 selectValueselectItemoptionKey

Dropdown-option

Dropdown-option 是下拉菜单的每个选项,其基本结构

<template>
  <div
    :class="[
      'dorpdown-option',
      { 'is-disabled': disabled },
      { 'is-selected': isSelected }
    ]"
    @mousedown="handleClick"
  >
    <slot>
      {{ label }}
    </slot>
  </div>
</template>
复制代码

主要是利用默认插槽,和 label 属性来构建每一项,并且包含着两种状态,是否 disabled 或 selected。disabled 状态是依据 props: disabled 来改变的,而 selected 则是由 computed 来完成的

<script>
export default {
  inject: {
    Dropdown: { default: "Dropdown" }
  },
  computed: {
    isSelected() {
      const {
        Dropdown: { optionKey, selectValue }
      } = this;
      const key = this[optionKey] || this.$attrs[optionKey];

      return key === selectValue;
    }
  },
  ...
};
</script>
复制代码

首先利用 inject 将父组件 Dropdown 注入,这样可以通过 this.Dropdown 来访问它的状态、属性。

然后在 isSelected() 中获取 selectValue,与当前 Dropdown-option 的 key 值进行比对,查看是否为选中项。

为了要引入 optionKey,是因为在实际的业务中,有的场景会以 label 作为去区分项,有的则是以 value,故引入,方便自定义。

每个 Dropdown-option 具备选中功能,但是从 @mousedown="handleClick" 可以看出,利用 mousedown 来代替 click 事件

由于我们利用 Dropdown 的 blur 事件来控制下拉列表的展开与关闭,此时如果利用 click 事件,则会在 blur 之后触发,所以无法选中。故采用 mousedown 来完成该功能。

methods: {
    handleSelect(key) {
        let {
            Dropdown: { multiple, trigger },
            value,
            label
        } = this;

        this.Dropdown.$emit("change", key);
        this.Dropdown.$emit("select", key);
        if (trigger !== "hover") {
            this.Dropdown.isOpen = false;
        }
    },
        handleClick() {
            let {
                Dropdown: { optionKey },
                disabled
            } = this;
            const key = this[optionKey] || this.$attrs[optionKey];

            if (!disabled) {
                this.$slots.default[0].elm.click && this.$slots.default[0].elm.click();
                key && this.handleSelect(key);
            }
        }
}
复制代码

这一份部分的逻辑就比较简单了,只有一处需要解释下

this.$slots.default[0].elm.click && this.$slots.default[0].elm.click();
复制代码

由于我们利用 mousedown 来代替原来的 click 事件,但我们利用 slot 插槽来完成下拉菜单的开发时,就无法触发 slot 的点击时间,所以利用上述代码来手动触发。

由于 Dropdown 组件中,使用了 v-model 来完成数据的双向绑定

model: {
    prop: "selectValue",
    event: "select"
}
复制代码

所以在 Dropdown-option 中则需要利用 this.Dropdown.$emit("select", key); 来完成双向绑定。

总结

代码地址:Dropdown Github

实例:Fat-UI lib

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值