首页 > 其他 > 详细

element dropdown源码

时间:2019-05-30 18:11:26      阅读:184      评论:0      收藏:0      [点我收藏+]

dropdown.vue

<script>
  import Clickoutside from element-ui/src/utils/clickoutside;
  import Emitter from element-ui/src/mixins/emitter;
  import Migrating from element-ui/src/mixins/migrating;
  import ElButton from element-ui/packages/button;
  import ElButtonGroup from element-ui/packages/button-group;
  import { generateId } from element-ui/src/utils/util;

  export default {
    name: ElDropdown,

    componentName: ElDropdown,

    mixins: [Emitter, Migrating],

    directives: { Clickoutside },

    components: {
      ElButton,
      ElButtonGroup
    },

    provide() {
      return {
        dropdown: this
      };
    },

    props: {
      // trigger    触发下拉的行为    string    hover, click    hover
      trigger: {
        type: String,
        default: hover
      },
      // 菜单按钮类型,同 Button 组件(只在split-button为 true 的情况下有效)
      type: String,
      // 菜单尺寸,在split-button为 true 的情况下也对触发按钮生效
      size: {
        type: String,
        default: ‘‘
      },
      // 下拉触发元素呈现为按钮组
      splitButton: Boolean,
      // hide-on-click    是否在点击菜单项后隐藏菜单
      hideOnClick: {
        type: Boolean,
        default: true
      },
      // placement    菜单弹出位置    string    top/top-start/top-end/bottom/bottom-start/bottom-end
      placement: {
        type: String,
        default: bottom-end
      },
      visibleArrow: {
        default: true
      },
      // 展开下拉菜单的延时(仅在 trigger 为 hover 时有效)
      showTimeout: {
        type: Number,
        default: 250
      },
      // 收起下拉菜单的延时(仅在 trigger 为 hover 时有效)
      hideTimeout: {
        type: Number,
        default: 150
      },
      // Dropdown 组件的 tabindex    number    —    0
      tabindex: {
        type: Number,
        default: 0
      }
    },

    data() {
      return {
        timeout: null,
        visible: false,
        triggerElm: null,
        menuItems: null,
        menuItemsArray: null,
        dropdownElm: null,
        focusing: false,
        listId: `dropdown-menu-${generateId()}`
      };
    },

    computed: {
      dropdownSize() {
        return this.size || (this.$ELEMENT || {}).size;
      }
    },

    mounted() {
      // 接收下拉的li点击事件
      this.$on(menu-item-click, this.handleMenuItemClick);
    },

    watch: {
      visible(val) {
        this.broadcast(ElDropdownMenu, visible, val);
        this.$emit(visible-change, val);
      },
      focusing(val) {
        const selfDefine = this.$el.querySelector(.el-dropdown-selfdefine);
        if (selfDefine) { // 自定义
          if (val) {
            selfDefine.className +=  focusing;
          } else {
            selfDefine.className = selfDefine.className.replace(focusing, ‘‘);
          }
        }
      }
    },

    methods: {
      getMigratingConfig() {
        return {
          props: {
            menu-align: menu-align is renamed to placement.
          }
        };
      },
      // 展开下拉框框
      show() {
        if (this.triggerElm.disabled) return;
        clearTimeout(this.timeout);
        this.timeout = setTimeout(() => {
          this.visible = true;
          // 如果是点击直接触发,反之延迟传入的额数值触发
        }, this.trigger === click ? 0 : this.showTimeout);
      },
      // 隐藏下拉框
      hide() {
        if (this.triggerElm.disabled) return;
        this.removeTabindex();
        if (this.tabindex >= 0) {
          this.resetTabindex(this.triggerElm);
        }
        clearTimeout(this.timeout);
        this.timeout = setTimeout(() => {
          this.visible = false;
        }, this.trigger === click ? 0 : this.hideTimeout);
      },
      // 点击展开关闭
      handleClick() {
        if (this.triggerElm.disabled) return;
        if (this.visible) {
          this.hide();
        } else {
          this.show();
        }
      },

      handleTriggerKeyDown(ev) {
        const keyCode = ev.keyCode;
        if ([38, 40].indexOf(keyCode) > -1) { // up/down
          this.removeTabindex();
          this.resetTabindex(this.menuItems[0]);
          this.menuItems[0].focus();
          ev.preventDefault();
          ev.stopPropagation();
        } else if (keyCode === 13) { // space enter选中
          this.handleClick();
        } else if ([9, 27].indexOf(keyCode) > -1) { // tab || esc
          this.hide();
        }
      },
      handleItemKeyDown(ev) {
        const keyCode = ev.keyCode;
        const target = ev.target;
        const currentIndex = this.menuItemsArray.indexOf(target);
        const max = this.menuItemsArray.length - 1;
        let nextIndex;
        if ([38, 40].indexOf(keyCode) > -1) { // up/down
          if (keyCode === 38) { // up
            nextIndex = currentIndex !== 0 ? currentIndex - 1 : 0;
          } else { // down
            nextIndex = currentIndex < max ? currentIndex + 1 : max;
          }
          this.removeTabindex();
          this.resetTabindex(this.menuItems[nextIndex]);
          this.menuItems[nextIndex].focus();
          ev.preventDefault();
          ev.stopPropagation();
        } else if (keyCode === 13) { // enter选中
          this.triggerElmFocus();
          target.click();
          if (this.hideOnClick) { // click关闭
            this.visible = false;
          }
        } else if ([9, 27].indexOf(keyCode) > -1) { // tab // esc
          this.hide();
          this.triggerElmFocus();
        }
      },
      resetTabindex(ele) { // 下次tab时组件聚焦元素
        this.removeTabindex();
        ele.setAttribute(tabindex, 0); // 下次期望的聚焦元素
      },
      removeTabindex() {
        this.triggerElm.setAttribute(tabindex, -1);
        this.menuItemsArray.forEach((item) => {
          item.setAttribute(tabindex, -1);
        });
      },
      initAria() {
        this.dropdownElm.setAttribute(id, this.listId);
        this.triggerElm.setAttribute(aria-haspopup, list);
        this.triggerElm.setAttribute(aria-controls, this.listId);

        if (!this.splitButton) { // 自定义
          this.triggerElm.setAttribute(role, button);
          this.triggerElm.setAttribute(tabindex, this.tabindex);
          this.triggerElm.setAttribute(class, (this.triggerElm.getAttribute(class) || ‘‘) +  el-dropdown-selfdefine); // 控制
        }
      },
      // 初始化事件
      initEvent() {
        let { trigger, show, hide, handleClick, splitButton, handleTriggerKeyDown, handleItemKeyDown } = this;
        this.triggerElm = splitButton
          ? this.$refs.trigger.$el
          : this.$slots.default[0].elm;

        let dropdownElm = this.dropdownElm;

        this.triggerElm.addEventListener(keydown, handleTriggerKeyDown); // triggerElm keydown
        dropdownElm.addEventListener(keydown, handleItemKeyDown, true); // item keydown
        // 控制自定义元素的样式
        if (!splitButton) {
          this.triggerElm.addEventListener(focus, () => {
            this.focusing = true;
          });
          this.triggerElm.addEventListener(blur, () => {
            this.focusing = false;
          });
          this.triggerElm.addEventListener(click, () => {
            this.focusing = false;
          });
        }
        if (trigger === hover) {
          this.triggerElm.addEventListener(mouseenter, show);
          this.triggerElm.addEventListener(mouseleave, hide);
          dropdownElm.addEventListener(mouseenter, show);
          dropdownElm.addEventListener(mouseleave, hide);
        } else if (trigger === click) {
          this.triggerElm.addEventListener(click, handleClick);
        }
      },
      // 点击菜单触发的回调
      handleMenuItemClick(command, instance) {
        // 如果设置了点击菜单后隐藏菜单
        if (this.hideOnClick) {
          // 隐藏下拉框
          this.visible = false;
        }
        // 否则触发command事件
        this.$emit(command, command, instance);
      },
      // 触发获取焦点事件
      triggerElmFocus() {
        this.triggerElm.focus && this.triggerElm.focus();
      },
      // 初始化dom
      initDomOperation() {
        this.dropdownElm = this.popperElm;
        // 获取所有tab键选中的元素
        this.menuItems = this.dropdownElm.querySelectorAll("[tabindex=‘-1‘]");
        this.menuItemsArray = [].slice.call(this.menuItems);
        // 初始化事件
        this.initEvent();
        this.initAria();
      }
    },

    render(h) {
      let { hide, splitButton, type, dropdownSize } = this;

      const handleMainButtonClick = (event) => {
        this.$emit(click, event);
        hide();
      };

      let triggerElm = !splitButton
        ? this.$slots.default
        : (<el-button-group>
          <el-button type={type} size={dropdownSize} nativeOn-click={handleMainButtonClick}>
            {this.$slots.default}
          </el-button>
          <el-button ref="trigger" type={type} size={dropdownSize} class="el-dropdown__caret-button">
            <i class="el-dropdown__icon el-icon-arrow-down"></i>
          </el-button>
        </el-button-group>);

      return (
        <div class="el-dropdown" v-clickoutside={hide}>
          {triggerElm}
          {this.$slots.dropdown}
        </div>
      );
    }
  };
</script>

dropdown-menu.vue

<template>
  <transition name="el-zoom-in-top" @after-leave="doDestroy">
    <ul class="el-dropdown-menu el-popper" :class="[size && `el-dropdown-menu--${size}`]" v-show="showPopper">
      <slot></slot>
    </ul>
  </transition>
</template>
<script>
  import Popper from element-ui/src/utils/vue-popper;

  export default {
    name: ElDropdownMenu,

    componentName: ElDropdownMenu,

    mixins: [Popper],

    props: {
      visibleArrow: {
        type: Boolean,
        default: true
      },
      arrowOffset: {
        type: Number,
        default: 0
      }
    },

    data() {
      return {
        size: this.dropdown.dropdownSize
      };
    },
    // 注入父组件传进的组件
    inject: [dropdown],

    created() {
      this.$on(updatePopper, () => {
        if (this.showPopper) this.updatePopper();
      });
      // 监听visible事件
      this.$on(visible, val => {
        // 改变showPoper
        this.showPopper = val;
      });
    },

    mounted() {
      // 获取到popperElm元素
      this.dropdown.popperElm = this.popperElm = this.$el;
      this.referenceElm = this.dropdown.$el;
      // compatible with 2.6 new v-slot syntax
      // issue link https://github.com/ElemeFE/element/issues/14345
      // 在此初始化调用
      this.dropdown.initDomOperation();
    },

    watch: {
      dropdown.placement: {
        immediate: true,
        handler(val) {
          this.currentPlacement = val;
        }
      }
    }
  };
</script>

dropdown-item.vue

<template>
  <li
    class="el-dropdown-menu__item"
    :class="{
      ‘is-disabled‘: disabled,
      ‘el-dropdown-menu__item--divided‘: divided
    }"
    @click="handleClick"
    :aria-disabled="disabled"
    :tabindex="disabled ? null : -1"
  >
    <i :class="icon" v-if="icon"></i>
    <slot></slot>
  </li>
</template>
<script>
  import Emitter from element-ui/src/mixins/emitter;

  export default {
    name: ElDropdownItem,

    mixins: [Emitter],

    props: {
      command: {},
      disabled: Boolean,
      divided: Boolean,
      icon: String
    },

    methods: {
      handleClick(e) {
        // 触发showPopper的menu-item-click事件
        this.dispatch(ElDropdown, menu-item-click, [this.command, this]);
      }
    }
  };
</script>

 

element dropdown源码

原文:https://www.cnblogs.com/wsk1576025821/p/10950880.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!