This commit is contained in:
Ivan
2021-06-07 11:56:04 +08:00
commit c3c9fee2fb
1071 changed files with 195655 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
import ElDropdown from './src/dropdown';
/* istanbul ignore next */
ElDropdown.install = function(Vue) {
Vue.component(ElDropdown.name, ElDropdown);
};
export default ElDropdown;

View File

@@ -0,0 +1,37 @@
<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) {
this.dispatch('ElDropdown', 'menu-item-click', [this.command, this]);
}
}
};
</script>

View File

@@ -0,0 +1,63 @@
<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();
});
this.$on('visible', val => {
this.showPopper = val;
});
},
mounted() {
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>

View File

@@ -0,0 +1,279 @@
<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: {
type: String,
default: 'hover'
},
type: String,
size: {
type: String,
default: ''
},
splitButton: Boolean,
hideOnClick: {
type: Boolean,
default: true
},
placement: {
type: String,
default: 'bottom-end'
},
visibleArrow: {
default: true
},
showTimeout: {
type: Number,
default: 250
},
hideTimeout: {
type: Number,
default: 150
},
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() {
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;
}
this.$emit('command', command, instance);
},
triggerElmFocus() {
this.triggerElm.focus && this.triggerElm.focus();
},
initDomOperation() {
this.dropdownElm = this.popperElm;
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>