<script> import ElCollapseTransition from 'element-ui/src/transitions/collapse-transition'; import menuMixin from './menu-mixin'; import Emitter from 'element-ui/src/mixins/emitter'; import Popper from 'element-ui/src/utils/vue-popper'; const poperMixins = { 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, poperMixins], components: { ElCollapseTransition }, props: { index: { type: String, required: true }, showTimeout: { type: Number, default: 300 }, hideTimeout: { type: Number, default: 300 }, popperClass: String, disabled: Boolean, popperAppendToBody: { type: Boolean, default: undefined } }, data() { return { popperJS: null, timeout: null, items: {}, submenus: {}, mouseInChild: false }; }, watch: { opened(val) { if (this.isMenuPopup) { this.$nextTick(_ => { this.updatePopper(); }); } } }, computed: { // popper option 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.indexOf(this.index) > -1; }, active() { let isActive = false; const submenus = this.submenus; const items = this.items; Object.keys(items).forEach(index => { if (items[index].active) { isActive = true; } }); Object.keys(submenus).forEach(index => { if (submenus[index].active) { isActive = true; } }); return isActive; }, hoverBackground() { return this.rootMenu.hoverBackground; }, backgroundColor() { return this.rootMenu.backgroundColor || ''; }, activeTextColor() { return this.rootMenu.activeTextColor || ''; }, textColor() { return this.rootMenu.textColor || ''; }, mode() { return this.rootMenu.mode; }, isMenuPopup() { return this.rootMenu.isMenuPopup; }, titleStyle() { if (this.mode !== 'horizontal') { return { color: this.textColor }; } return { borderBottomColor: this.active ? (this.rootMenu.activeTextColor ? this.activeTextColor : '') : 'transparent', color: this.active ? this.activeTextColor : this.textColor }; }, isFirstLevel() { let isFirstLevel = true; let parent = this.$parent; while (parent && parent !== this.rootMenu) { if (['ElSubmenu', 'ElMenuItemGroup'].indexOf(parent.$options.componentName) > -1) { isFirstLevel = false; break; } else { parent = parent.$parent; } } return isFirstLevel; } }, methods: { handleCollapseToggle(value) { if (value) { this.initPopper(); } else { 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'); clearTimeout(this.timeout); this.timeout = setTimeout(() => { this.rootMenu.openMenu(this.index, this.indexPath); }, showTimeout); }, handleMouseleave() { const {rootMenu} = this; if ( (rootMenu.menuTrigger === 'click' && rootMenu.mode === 'horizontal') || (!rootMenu.collapse && rootMenu.mode === 'vertical') ) { return; } this.dispatch('ElSubmenu', 'mouse-leave-child'); clearTimeout(this.timeout); this.timeout = setTimeout(() => { !this.mouseInChild && this.rootMenu.closeMenu(this.index); }, this.hideTimeout); }, handleTitleMouseenter() { if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return; const title = this.$refs['submenu-title']; title && (title.style.backgroundColor = this.rootMenu.hoverBackground); }, handleTitleMouseleave() { if (this.mode === 'horizontal' && !this.rootMenu.backgroundColor) return; const title = this.$refs['submenu-title']; title && (title.style.backgroundColor = this.rootMenu.backgroundColor || ''); }, updatePlacement() { this.currentPlacement = 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; clearTimeout(this.timeout); }); this.$on('mouse-leave-child', () => { this.mouseInChild = false; clearTimeout(this.timeout); }); }, mounted() { this.parentMenu.addSubmenu(this); this.rootMenu.addSubmenu(this); this.initPopper(); }, beforeDestroy() { this.parentMenu.removeSubmenu(this); this.rootMenu.removeSubmenu(this); }, render(h) { const { active, opened, paddingStyle, titleStyle, backgroundColor, rootMenu, currentPlacement, menuTransitionName, mode, disabled, popperClass, $slots, isFirstLevel } = this; const popupMenu = ( <transition name={menuTransitionName}> <div ref="menu" v-show={opened} class={[`el-menu--${mode}`, popperClass]} on-mouseenter={($event) => this.handleMouseenter($event, 100)} on-mouseleave={this.handleMouseleave} on-focus={($event) => this.handleMouseenter($event, 100)}> <ul role="menu" class={['el-menu el-menu--popup', `el-menu--popup-${currentPlacement}`]} style={{ backgroundColor: rootMenu.backgroundColor || '' }}> {$slots.default} </ul> </div> </transition> ); const inlineMenu = ( <el-collapse-transition> <ul role="menu" class="el-menu el-menu--inline" v-show={opened} style={{ backgroundColor: rootMenu.backgroundColor || '' }}> {$slots.default} </ul> </el-collapse-transition> ); const submenuTitleIcon = ( rootMenu.mode === 'horizontal' && isFirstLevel || rootMenu.mode === 'vertical' && !rootMenu.collapse ) ? 'el-icon-arrow-down' : 'el-icon-arrow-right'; return ( <li class={{ 'el-submenu': true, 'is-active': active, 'is-opened': opened, 'is-disabled': disabled }} role="menuitem" aria-haspopup="true" aria-expanded={opened} on-mouseenter={this.handleMouseenter} on-mouseleave={this.handleMouseleave} on-focus={this.handleMouseenter} > <div class="el-submenu__title" ref="submenu-title" on-click={this.handleClick} on-mouseenter={this.handleTitleMouseenter} on-mouseleave={this.handleTitleMouseleave} style={[paddingStyle, titleStyle, { backgroundColor }]} > {$slots.title} <i class={[ 'el-submenu__icon-arrow', submenuTitleIcon ]}></i> </div> {this.isMenuPopup ? popupMenu : inlineMenu} </li> ); } }; </script>