<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
|
},
|
disabled: {
|
type: Boolean,
|
default: false
|
}
|
},
|
|
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.disabled) return;
|
clearTimeout(this.timeout);
|
this.timeout = setTimeout(() => {
|
this.visible = true;
|
}, this.trigger === 'click' ? 0 : this.showTimeout);
|
},
|
hide() {
|
if (this.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.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, disabled } = this;
|
|
const handleMainButtonClick = (event) => {
|
this.$emit('click', event);
|
hide();
|
};
|
|
let triggerElm = null;
|
if (splitButton) {
|
triggerElm = <el-button-group>
|
<el-button type={type} size={dropdownSize} nativeOn-click={handleMainButtonClick} disabled={disabled}>
|
{this.$slots.default}
|
</el-button>
|
<el-button ref="trigger" type={type} size={dropdownSize} class="el-dropdown__caret-button" disabled={disabled}>
|
<i class="el-dropdown__icon el-icon-arrow-down"></i>
|
</el-button>
|
</el-button-group>;
|
} else {
|
triggerElm = this.$slots.default;
|
const vnodeData = triggerElm[0].data || {};
|
let { attrs = {} } = vnodeData;
|
if (disabled && !attrs.disabled) {
|
attrs.disabled = true;
|
vnodeData.attrs = attrs;
|
}
|
}
|
const menuElm = disabled ? null : this.$slots.dropdown;
|
|
return (
|
<div class="el-dropdown" v-clickoutside={hide} aria-disabled={disabled}>
|
{triggerElm}
|
{menuElm}
|
</div>
|
);
|
}
|
};
|
</script>
|