You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
250 lines
7.7 KiB
250 lines
7.7 KiB
<script type="text/jsx">
|
|
import MenuMixin from 'element-ui/packages/menu/src/menu-mixin'
|
|
import Emitter from 'element-ui/lib/mixins/emitter'
|
|
import Popper from 'element-ui/lib/utils/vue-popper'
|
|
|
|
const PopperMixin = {
|
|
props: {
|
|
transformOrigin: {type: [Boolean, String], default: false},
|
|
offset: Popper.props.offset,
|
|
boundariesPadding: Popper.props.boundariesPadding,
|
|
popperOptions: Popper.props.popperOptions
|
|
},
|
|
data: Popper.data,
|
|
methods: Popper.methods,
|
|
beforeDestroy: Popper.beforeDestroy,
|
|
deactivated: Popper.deactivated
|
|
}
|
|
|
|
export default {
|
|
name: 'ElSubmenu',
|
|
|
|
componentName: 'ElSubmenu',
|
|
|
|
mixins: [MenuMixin, Emitter, PopperMixin],
|
|
|
|
props: {
|
|
index: String,
|
|
showTimeout: {type: Number, default: 300},
|
|
hideTimeout: {type: Number, default: 300},
|
|
popperClass: String,
|
|
disabled: Boolean,
|
|
popperAppendToBody: {type: Boolean, default: true}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
popperJS: null,
|
|
timeout: null,
|
|
items: {},
|
|
submenus: {},
|
|
mouseInChild: false
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
appendToBody() {
|
|
return this.popperAppendToBody === undefined
|
|
? this.isFirstLevel
|
|
: this.popperAppendToBody
|
|
},
|
|
menuTransitionName() {
|
|
return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top'
|
|
},
|
|
opened() {
|
|
return this.rootMenu.openedMenus.includes(this.index)
|
|
},
|
|
active() {
|
|
const active = Object.values(this.items).some(v => v.active)
|
|
return active || Object.values(this.submenus).some(v => v.active)
|
|
},
|
|
mode() {
|
|
return this.rootMenu.mode
|
|
},
|
|
isMenuPopup() {
|
|
return this.rootMenu.isMenuPopup
|
|
},
|
|
isFirstLevel() {
|
|
const arr = ['ElSubmenu', 'ElMenuItemGroup']
|
|
let isFirstLevel = true
|
|
let parent = this.$parent
|
|
|
|
while (parent && parent !== this.rootMenu) {
|
|
if (arr.includes(parent.$options.componentName)) {
|
|
isFirstLevel = false
|
|
break
|
|
}
|
|
else parent = parent.$parent
|
|
}
|
|
return isFirstLevel
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
opened() {
|
|
this.isMenuPopup && this.$nextTick(this.updatePopper)
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
handleCollapseToggle(value) {
|
|
value ? this.initPopper() : this.doDestroy()
|
|
},
|
|
|
|
addItem(item) {
|
|
this.$set(this.items, item.index, item)
|
|
},
|
|
removeItem(item) {
|
|
delete this.items[item.index]
|
|
},
|
|
addSubmenu(item) {
|
|
this.$set(this.submenus, item.index, item)
|
|
},
|
|
removeSubmenu(item) {
|
|
delete this.submenus[item.index]
|
|
},
|
|
|
|
handleClick() {
|
|
const {rootMenu, disabled} = this
|
|
if (
|
|
(rootMenu.menuTrigger === 'hover' && rootMenu.mode === 'horizontal') ||
|
|
(rootMenu.collapse && rootMenu.mode === 'vertical') ||
|
|
disabled
|
|
) {
|
|
return
|
|
}
|
|
this.dispatch('ElMenu', 'submenu-click', this)
|
|
},
|
|
|
|
handleMouseenter(event, showTimeout = this.showTimeout) {
|
|
if (!('ActiveXObject' in window) && event.type === 'focus' && !event.relatedTarget) {
|
|
return
|
|
}
|
|
|
|
const {rootMenu, disabled} = this
|
|
|
|
if (rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal'
|
|
|| !rootMenu.collapse && rootMenu.mode === 'vertical'
|
|
|| disabled) return
|
|
|
|
this.dispatch('ElSubmenu', 'mouse-enter-child')
|
|
|
|
window.clearTimeout(this.timeout)
|
|
this.timeout = window.setTimeout(() => this.rootMenu.openMenu(this.index, this.indexPath), showTimeout)
|
|
|
|
this.appendToBody && this.$parent.$el.dispatchEvent(new MouseEvent('mouseenter'))
|
|
},
|
|
|
|
handleMouseleave(deepDispatch = false) {
|
|
const {rootMenu} = this
|
|
|
|
if (rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal'
|
|
|| !rootMenu.collapse && rootMenu.mode === 'vertical') return
|
|
|
|
this.dispatch('ElSubmenu', 'mouse-leave-child')
|
|
|
|
window.clearTimeout(this.timeout)
|
|
this.timeout = window.setTimeout(() => !this.mouseInChild && this.rootMenu.closeMenu(this.index), this.hideTimeout)
|
|
|
|
if (this.appendToBody && deepDispatch) {
|
|
if (this.$parent.$options.name === 'ElSubmenu') {
|
|
this.$parent.handleMouseleave(true)
|
|
}
|
|
}
|
|
},
|
|
|
|
updatePlacement() {
|
|
this.placement = this.mode === 'horizontal' && this.isFirstLevel
|
|
? 'bottom-start'
|
|
: 'right-start'
|
|
},
|
|
|
|
initPopper() {
|
|
this.referenceElm = this.$el
|
|
this.popperElm = this.$refs.menu
|
|
this.updatePlacement()
|
|
}
|
|
},
|
|
|
|
created() {
|
|
this.$on('toggle-collapse', this.handleCollapseToggle)
|
|
this.$on('mouse-enter-child', () => {
|
|
this.mouseInChild = true
|
|
window.clearTimeout(this.timeout)
|
|
})
|
|
this.$on('mouse-leave-child', () => {
|
|
this.mouseInChild = false
|
|
window.clearTimeout(this.timeout)
|
|
})
|
|
},
|
|
|
|
mounted() {
|
|
this.parentMenu.addSubmenu(this)
|
|
this.rootMenu.addSubmenu(this)
|
|
this.initPopper()
|
|
},
|
|
|
|
beforeDestroy() {
|
|
this.parentMenu.removeSubmenu(this)
|
|
this.rootMenu.removeSubmenu(this)
|
|
},
|
|
|
|
render() {
|
|
const {rootMenu, $slots} = this
|
|
|
|
const popupMenu = (
|
|
<transition name={this.menuTransitionName}>
|
|
<div
|
|
ref="menu"
|
|
v-show={this.opened}
|
|
class={[`el-menu--${this.mode}`, this.popperClass]}
|
|
on-mouseenter={e => this.handleMouseenter(e, 100)}
|
|
on-mouseleave={() => this.handleMouseleave(true)}
|
|
on-focus={e => this.handleMouseenter(e, 100)}>
|
|
<ul class={['el-menu el-menu--popup', `el-menu--popup-${this.currentPlacement}`]}>
|
|
{$slots.default}
|
|
</ul>
|
|
</div>
|
|
</transition>
|
|
)
|
|
|
|
const inlineMenu = (
|
|
<el-collapse-transition>
|
|
<ul v-show={this.opened} class="el-menu el-menu--inline">
|
|
{$slots.default}
|
|
</ul>
|
|
</el-collapse-transition>
|
|
)
|
|
|
|
const submenuTitleIcon = (
|
|
rootMenu.mode === 'horizontal' && this.isFirstLevel ||
|
|
rootMenu.mode === 'vertical' && !rootMenu.collapse
|
|
) ? 'el-icon-arrow-down' : 'el-icon-arrow-right'
|
|
|
|
return (
|
|
<li
|
|
class={{
|
|
'el-submenu': true,
|
|
'is-active': this.active,
|
|
'is-opened': this.opened,
|
|
'is-disabled': this.disabled
|
|
}}
|
|
on-mouseenter={this.handleMouseenter}
|
|
on-mouseleave={() => this.handleMouseleave(false)}
|
|
on-focus={this.handleMouseenter}
|
|
>
|
|
<div
|
|
ref="submenu-title"
|
|
class="el-submenu__title"
|
|
style={this.paddingStyle}
|
|
on-click={this.handleClick}
|
|
>
|
|
{$slots.title}
|
|
<i class={['el-submenu__icon-arrow', submenuTitleIcon]}/>
|
|
</div>
|
|
{this.isMenuPopup ? popupMenu : inlineMenu}
|
|
</li>
|
|
)
|
|
}
|
|
}
|
|
</script>
|
|
|