element dropdown源码

摘要:
下拉列表。vue<script>importClickouterdefrom'元素-ui/src/utils/clickouter';importEmitterfrom'element-ui/src/mixins/emitter';importMigratingfrom'element-ui/src/mixins/imigrating';从“”导入ElButton

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源码》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇EggJS 设置代理实现跨域 egg-http-proxyiOS开发——OC篇&amp;amp;常用问题解答(一)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

HashMap源码和并发异常问题分析

要点源码分析 HashMap允许键值对为null;HashTable则不允许,会报空指针异常; HashMap<String, String> map= new HashMap<>(2); map.put(null,null); map.put("1",null); Hash...

AI Accord.NET入门

Accord.NET官网:http://accord-framework.net/index.html Accord.NET的Github页面:https://github.com/accord-net/framework 入门用到的资料全部来源以上两个链接,源码可以在Github上下载,介绍Accord.NET的可以在官网上看看,总之Accord.NE...

Python+Selenium笔记(十五)调用JS

(一)方法 方法 简单说明 execute_async_script(script, args) 异步执行JS代码 script:被执行的JS代码 args:js代码中的任意参数 execute_script(script, args) 同步执行JS代码 script:被执行的JS代码 args:js代码中的任意参数 (二)示例 f...

appium的元素定位总结

appium的元素定位也是继承selenium的方法并有APP独有的定位方法,包含单个元素定位、元素列表定位: 单个元素定位: driver.find_element_by_accessibility_id(id)driver.find_element_by_android_uiautomator(uia_string)driver.find_elemen...

extremeComponents(ec)源码分析

eXtremeComponents(简称ec)是一系列提供高级显示的开源JSP定制标签,当前的包含的组件为eXtremeTable,用于以表形式显示数据。 其本质是jsp的自定义标签,抓住这一点就抓住了ec的本源。 1. Table定义 我们先看一下标签的定义:extremComponents.tld,其中table的标签定义如下: <t...

Android自动化学习5--对uiautomator2常用操作进行封装

前言 本次我们将会对 uiautomator2 的一些基本操作进行简单的封装,以便更好的应用到UI自动化中。 重复多次滑动 在 uiautomator2 中,给我们提供了一些滑动的操作 swipe(),以及滑动扩展的操作 swipe_ext(),基于此我们可以对重复多次的滑动操作进行简单封装。 def up(self, scale=0.9, tim...