mirror of https://github.com/ElemeFE/element
Menu: move menu popup to body when collapse (#9263)
* change menu popup to body
* add menu-list
* Revert "add menu-list"
This reverts commit 5799df9bf2
.
* fix menu popup
* Update yarn.lock
* Update submenu.vue
pull/9437/head
parent
68e0573db7
commit
f0f75fb561
|
@ -1,4 +1,5 @@
|
|||
export default {
|
||||
inject: ['rootMenu'],
|
||||
computed: {
|
||||
indexPath() {
|
||||
const path = [this.index];
|
||||
|
@ -11,16 +12,6 @@ export default {
|
|||
}
|
||||
return path;
|
||||
},
|
||||
rootMenu() {
|
||||
let parent = this.$parent;
|
||||
while (
|
||||
parent &&
|
||||
parent.$options.componentName !== 'ElMenu'
|
||||
) {
|
||||
parent = parent.$parent;
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
parentMenu() {
|
||||
let parent = this.$parent;
|
||||
while (
|
||||
|
|
|
@ -127,6 +127,9 @@
|
|||
computed: {
|
||||
hoverBackground() {
|
||||
return this.backgroundColor ? this.mixColor(this.backgroundColor, 0.2) : '';
|
||||
},
|
||||
isMenuPopup() {
|
||||
return this.mode === 'horizontal' || (this.mode === 'vertical' && this.collapse);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -140,6 +143,7 @@
|
|||
|
||||
collapse(value) {
|
||||
if (value) this.openedMenus = [];
|
||||
this.broadcast('ElSubmenu', 'toggle-collapse', value);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -1,53 +1,31 @@
|
|||
<template>
|
||||
<li
|
||||
:class="{
|
||||
'el-submenu': true,
|
||||
'is-active': active,
|
||||
'is-opened': opened
|
||||
}"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
@focus="handleMouseenter"
|
||||
role="menuitem"
|
||||
aria-haspopup="true"
|
||||
:aria-expanded="opened"
|
||||
>
|
||||
<div
|
||||
class="el-submenu__title"
|
||||
ref="submenu-title"
|
||||
@click="handleClick"
|
||||
@mouseenter="handleTitleMouseenter"
|
||||
@mouseleave="handleTitleMouseleave"
|
||||
:style="[paddingStyle, titleStyle, { backgroundColor }]">
|
||||
<slot name="title"></slot>
|
||||
<i :class="{
|
||||
'el-submenu__icon-arrow': true,
|
||||
'el-icon-arrow-down': rootMenu.mode === 'horizontal' || rootMenu.mode === 'vertical' && !rootMenu.collapse,
|
||||
'el-icon-arrow-right': rootMenu.mode === 'vertical' && rootMenu.collapse
|
||||
}">
|
||||
</i>
|
||||
</div>
|
||||
<template v-if="rootMenu.mode === 'horizontal' || (rootMenu.mode === 'vertical' && rootMenu.collapse)">
|
||||
<transition :name="menuTransitionName">
|
||||
<ul class="el-menu" v-show="opened" :style="{ backgroundColor: rootMenu.backgroundColor || '' }" role="menu"><slot></slot></ul>
|
||||
</transition>
|
||||
</template>
|
||||
<el-collapse-transition v-else>
|
||||
<ul class="el-menu" v-show="opened" :style="{ backgroundColor: rootMenu.backgroundColor || '' }" role="menu"><slot></slot></ul>
|
||||
</el-collapse-transition>
|
||||
</li>
|
||||
</template>
|
||||
<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],
|
||||
mixins: [menuMixin, Emitter, poperMixins],
|
||||
|
||||
components: { ElCollapseTransition },
|
||||
|
||||
|
@ -68,12 +46,26 @@
|
|||
|
||||
data() {
|
||||
return {
|
||||
popperJS: null,
|
||||
timeout: null,
|
||||
items: {},
|
||||
submenus: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
opened(val) {
|
||||
if (this.isMenuPopup) {
|
||||
this.$nextTick(_ => {
|
||||
this.updatePopper();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// popper option
|
||||
appendToBody() {
|
||||
return this.rootMenu === this.$parent;
|
||||
},
|
||||
menuTransitionName() {
|
||||
return this.rootMenu.collapse ? 'el-zoom-in-left' : 'el-zoom-in-top';
|
||||
},
|
||||
|
@ -114,6 +106,9 @@
|
|||
mode() {
|
||||
return this.rootMenu.mode;
|
||||
},
|
||||
isMenuPopup() {
|
||||
return this.rootMenu.isMenuPopup;
|
||||
},
|
||||
titleStyle() {
|
||||
if (this.mode !== 'horizontal') {
|
||||
return {
|
||||
|
@ -131,6 +126,13 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
handleCollapseToggle(value) {
|
||||
if (value) {
|
||||
this.initPopper();
|
||||
} else {
|
||||
this.doDestroy();
|
||||
}
|
||||
},
|
||||
addItem(item) {
|
||||
this.$set(this.items, item.index, item);
|
||||
},
|
||||
|
@ -188,15 +190,106 @@
|
|||
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' ? 'bottom-start' : 'right-start';
|
||||
},
|
||||
initPopper() {
|
||||
this.referenceElm = this.$el;
|
||||
this.popperElm = this.$refs.menu;
|
||||
this.updatePlacement();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.parentMenu.addSubmenu(this);
|
||||
this.rootMenu.addSubmenu(this);
|
||||
this.$on('toggle-collapse', this.handleCollapseToggle);
|
||||
},
|
||||
mounted() {
|
||||
this.initPopper();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.parentMenu.removeSubmenu(this);
|
||||
this.rootMenu.removeSubmenu(this);
|
||||
},
|
||||
render(h) {
|
||||
const {
|
||||
active,
|
||||
opened,
|
||||
paddingStyle,
|
||||
titleStyle,
|
||||
backgroundColor,
|
||||
$slots,
|
||||
rootMenu,
|
||||
currentPlacement,
|
||||
menuTransitionName,
|
||||
mode
|
||||
} = this;
|
||||
|
||||
const popupMenu = (
|
||||
<transition name={menuTransitionName}>
|
||||
<div
|
||||
ref="menu"
|
||||
v-show={opened}
|
||||
class={[`el-menu--${mode}`]}
|
||||
on-mouseenter={this.handleMouseenter}
|
||||
on-mouseleave={this.handleMouseleave}
|
||||
on-focus={this.handleMouseenter}>
|
||||
<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>
|
||||
);
|
||||
|
||||
return (
|
||||
<li
|
||||
class={{
|
||||
'el-submenu': true,
|
||||
'is-active': active,
|
||||
'is-opened': opened
|
||||
}}
|
||||
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': true,
|
||||
'el-icon-arrow-down': rootMenu.mode === 'horizontal' || rootMenu.mode === 'vertical' && !rootMenu.collapse,
|
||||
'el-icon-arrow-right': rootMenu.mode === 'vertical' && rootMenu.collapse
|
||||
}}>
|
||||
</i>
|
||||
</div>
|
||||
{this.isMenuPopup ? popupMenu : inlineMenu}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
font-size: 14px;
|
||||
color: $--menu-item-color;
|
||||
padding: 0 20px;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: border-color .3s, background-color .3s, color .3s;
|
||||
|
@ -24,21 +25,14 @@
|
|||
background-color: $--menu-item-fill;
|
||||
@include utils-clearfix;
|
||||
|
||||
& li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@include m(horizontal) {
|
||||
border-right: none;
|
||||
border-bottom: solid 1px #e6e6e6;
|
||||
& .el-menu-item {
|
||||
& > .el-menu-item {
|
||||
float: left;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: $--color-text-secondary;
|
||||
|
||||
|
@ -51,26 +45,22 @@
|
|||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
& .el-submenu {
|
||||
& > .el-submenu {
|
||||
float: left;
|
||||
position: relative;
|
||||
&:focus {
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
outline: none;
|
||||
> .el-submenu__title {
|
||||
.el-submenu__title {
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
}
|
||||
> .el-menu {
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 0;
|
||||
border: none;
|
||||
padding: 5px 0;
|
||||
background-color: $--color-white;
|
||||
z-index: 100;
|
||||
min-width: 100%;
|
||||
box-shadow: $--box-shadow-light;
|
||||
border-radius: $--border-radius-small;
|
||||
|
||||
&.is-active {
|
||||
.el-submenu__title {
|
||||
border-bottom: 2px solid $--color-primary;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
}
|
||||
|
||||
& .el-submenu__title {
|
||||
|
@ -78,20 +68,11 @@
|
|||
line-height: 60px;
|
||||
border-bottom: 2px solid transparent;
|
||||
color: $--color-text-secondary;
|
||||
}
|
||||
|
||||
.el-submenu__title:hover {
|
||||
background-color: #fff;
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
& .el-menu-item {
|
||||
background-color: $--color-white;
|
||||
float: none;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
& .el-submenu__icon-arrow {
|
||||
position: static;
|
||||
vertical-align: middle;
|
||||
|
@ -99,14 +80,26 @@
|
|||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
& .el-menu {
|
||||
& .el-menu-item {
|
||||
background-color: $--color-white;
|
||||
float: none;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 10px;
|
||||
color: $--color-text-secondary;
|
||||
|
||||
&.is-active {
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
& .el-menu-item:hover,
|
||||
& .el-submenu__title:hover,
|
||||
& .el-menu-item:focus {
|
||||
outline: none;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
& > .el-menu-item.is-active,
|
||||
& > .el-submenu.is-active .el-submenu__title {
|
||||
& > .el-menu-item.is-active {
|
||||
border-bottom: 2px solid $--color-primary;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
|
@ -162,6 +155,21 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
@include m(popup) {
|
||||
z-index: 100;
|
||||
min-width: 200px;
|
||||
border: none;
|
||||
padding: 5px 0;
|
||||
border-radius: $--border-radius-small;
|
||||
box-shadow: $--box-shadow-light;
|
||||
|
||||
&-bottom-start {
|
||||
margin-top: 5px;
|
||||
}
|
||||
&-right-start {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include b(menu-item) {
|
||||
@include menu-item;
|
||||
|
@ -198,6 +206,10 @@
|
|||
}
|
||||
|
||||
@include b(submenu) {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
|
||||
@include e(title) {
|
||||
position: relative;
|
||||
@include menu-item;
|
||||
|
|
|
@ -16,6 +16,10 @@ const stop = e => e.stopPropagation();
|
|||
*/
|
||||
export default {
|
||||
props: {
|
||||
transformOrigin: {
|
||||
type: [Boolean, String],
|
||||
default: true
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
|
@ -139,6 +143,7 @@ export default {
|
|||
},
|
||||
|
||||
resetTransformOrigin() {
|
||||
if (!this.transformOrigin) return;
|
||||
let placementMap = {
|
||||
top: 'bottom',
|
||||
bottom: 'top',
|
||||
|
@ -147,7 +152,9 @@ export default {
|
|||
};
|
||||
let placement = this.popperJS._popper.getAttribute('x-placement').split('-')[0];
|
||||
let origin = placementMap[placement];
|
||||
this.popperJS._popper.style.transformOrigin = ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`;
|
||||
this.popperJS._popper.style.transformOrigin = typeof this.transformOrigin === 'string'
|
||||
? this.transformOrigin
|
||||
: ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`;
|
||||
},
|
||||
|
||||
appendArrow(element) {
|
||||
|
|
|
@ -270,7 +270,7 @@ describe('Menu', () => {
|
|||
var submenu = vm.$refs.submenu;
|
||||
triggerEvent(submenu.$el, 'mouseenter');
|
||||
setTimeout(_ => {
|
||||
expect(submenu.$el.querySelector('.el-menu').style.display).to.not.ok;
|
||||
expect(document.body.querySelector('.el-menu--popup').parentElement.style.display).to.not.ok;
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
|
@ -301,10 +301,10 @@ describe('Menu', () => {
|
|||
triggerElm.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(submenu.$el.querySelector('.el-menu').style.display).to.not.ok;
|
||||
expect(document.body.querySelector('.el-menu--popup').parentElement.style.display).to.not.ok;
|
||||
triggerElm.click();
|
||||
setTimeout(_ => {
|
||||
expect(submenu.$el.querySelector('.el-menu').style.display).to.be.equal('none');
|
||||
expect(document.body.querySelector('.el-menu--popup').parentElement.style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 1000);
|
||||
}, 500);
|
||||
|
|
Loading…
Reference in New Issue