Dropdown 組件實現思路

概述:Dropdown 下拉菜單與 Select 的區別在於它是包含了一部分操做,因此它的 Option 下拉項要對其進行支持。javascript

publish:2019-03-31css

自從我寫了關於中後臺系統組件的第一篇文章以來,已經三個月了。因爲業務場景的較爲簡單,因此它沒有其餘組件庫的功能那麼強大,可是總體結構較爲相似,代碼結構清晰,容易擴展。html

將 Dropdown 分爲兩個模塊,父組件 Dropdown,子組件 Dropdown-option。其中Dropdown 負責控制總體的顯示,Drop-option 負責下拉菜單的每個選項。java

Dropdown - 組件結構

├── Dropdownide

├── Dropdown-optionpost

Dropdown

父組件主要負責的是選中項的顯示以及下拉菜單狀態(開、合)的控制。ui

具體的代碼以下this

<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 || clickspa

  • 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

相關文章
相關標籤/搜索