refactor(components): use css var to set menu global style (#3539)

* refactor(components): use css var to set menu global style

* fix(components): remove useless value & change camelCase & test file

* test: comment code that can not test final style

* test: fix unused-vars lint

* feat: add computed for useMenuCssVar

* test(components): revert test file

* fix(components): use computed value
This commit is contained in:
云游君 2021-09-22 18:41:32 +08:00 committed by GitHub
parent 2a0ebbc0b9
commit 35c90180d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 95 additions and 112 deletions

19
DEV_FAQ.md Normal file
View File

@ -0,0 +1,19 @@
# DEV FAQ
Here are the problems that are easy to encounter in development.
## If you encounter dependency related issues
```bash
yarn bootstrap
```
## Test suite failed to run (jest)
Can not run `yarn test xxx`
```bash
rm -rf dist
```
Try again.

View File

@ -37,27 +37,37 @@ describe('menu', () => {
await nextTick()
expect(item2.classes()).toContain('is-active')
})
test('background-color', async () => {
const backgroundColor = '#f00'
const textColor = '#000'
const activeTextColor = '#0f0'
const wrapper = _mount(
`<el-menu default-active="2"
background-color="#f00"
text-color="#000"
active-text-color="#0f0">
background-color="${backgroundColor}"
text-color="${textColor}"
active-text-color="${activeTextColor}">
<el-menu-item index="1" ref="item1"></el-menu-item>
<el-menu-item index="2" ref="item2"></el-menu-item>
</el-menu>`
)
const instance = wrapper.vm.$el
const item1 = await wrapper.findComponent({ ref: 'item1' })
const item2 = await wrapper.findComponent({ ref: 'item2' })
// const item2 = await wrapper.findComponent({ ref: 'item2' })
expect(instance.style.backgroundColor).toEqual('rgb(255, 0, 0)')
expect(item1.vm.$el.style.backgroundColor).toEqual('rgb(255, 0, 0)')
expect(item1.vm.$el.style.color).toEqual('rgb(0, 0, 0)')
expect(item2.vm.$el.style.color).toEqual('rgb(0, 255, 0)')
expect(
window.getComputedStyle(instance)._values['--el-menu-background-color']
).toEqual(backgroundColor)
// We can not test final style, so comment it out for now.
// expect(instance.style.backgroundColor).toEqual(backgroundColor)
// expect(item1.vm.$el.style.backgroundColor).toEqual(backgroundColor)
// expect(item1.vm.$el.style.color).toEqual(textColor)
// expect(item2.vm.$el.style.color).toEqual(activeTextColor)
await item1.trigger('mouseenter')
await nextTick()
expect(item1.vm.$el.style.backgroundColor).toEqual('rgb(204, 0, 0)')
// expect(item1.vm.$el.style.backgroundColor).toEqual('rgb(204, 0, 0)')
})
test('menu-item click', async () => {
const wrapper = _mount(
@ -150,6 +160,7 @@ describe('menu', () => {
const instance = elSubMenu.vm as any
expect(instance.opened).toBeTruthy()
})
test('hover-background-color', async () => {
const wrapper = _mount(
`<el-menu ref="menu" default-active="2"
@ -169,10 +180,10 @@ describe('menu', () => {
)
await nextTick()
const vm = wrapper.vm as any
expect(vm.$refs.menu.hoverBackground).toEqual('rgb(0, 112, 93)')
// expect(vm.$refs.menu.hoverBackground).toEqual('rgb(0, 112, 93)')
vm.background = '#F00'
await nextTick()
expect(vm.$refs.menu.hoverBackground).toEqual('rgb(204, 0, 0)')
// expect(vm.$refs.menu.hoverBackground).toEqual('rgb(204, 0, 0)')
})
test('menu-overflow', async () => {

View File

@ -15,8 +15,7 @@ import { Resize } from '@element-plus/directives'
import Menubar from '@element-plus/utils/menu/menu-bar'
import ElMenuCollapseTransition from './menu-collapse-transition.vue'
import ElSubMenu from './submenu.vue'
import useMenuColor from './useMenuColor'
import { useMenuCssVar } from './use-menu-css-var'
import type { VNode, Ref, ComputedRef } from 'vue'
import type {
IMenuProps,
@ -70,8 +69,6 @@ export default defineComponent({
const router = instance.appContext.config.globalProperties.$router
const menu = ref(null)
const hoverBackground = useMenuColor(props)
// computed
const isMenuPopup = computed(() => {
return (
@ -242,7 +239,6 @@ export default defineComponent({
openedMenus,
items,
submenus,
hoverBackground,
activeIndex,
isMenuPopup,
@ -274,7 +270,6 @@ export default defineComponent({
expose({
open,
close,
hoverBackground,
})
const flattedChildren = (children) => {
@ -350,6 +345,8 @@ export default defineComponent({
}
}
const ulStyle = useMenuCssVar(props)
const vnodeMenu = useVNodeResize(
h(
'ul',
@ -357,7 +354,7 @@ export default defineComponent({
key: String(props.collapse),
role: 'menubar',
ref: menu,
style: { backgroundColor: props.backgroundColor || '' },
style: ulStyle.value,
class: {
'el-menu': true,
'el-menu--horizontal': props.mode === 'horizontal',

View File

@ -3,16 +3,12 @@
class="el-menu-item"
role="menuitem"
tabindex="-1"
:style="[paddingStyle, itemStyle, { backgroundColor }]"
:style="paddingStyle"
:class="{
'is-active': active,
'is-disabled': disabled,
}"
@click="handleClick"
@mouseenter="onMouseEnter"
@focus="onMouseEnter"
@blur="onMouseLeave"
@mouseleave="onMouseLeave"
>
<el-tooltip
v-if="
@ -58,7 +54,7 @@ import {
} from 'vue'
import ElTooltip from '@element-plus/components/tooltip'
import { Effect } from '@element-plus/components/popper'
import useMenu from './useMenu'
import useMenu from './use-menu'
import type { RootMenuProvider, SubMenuProvider } from './menu.type'
@ -92,48 +88,7 @@ export default defineComponent({
const active = computed(() => {
return props.index === rootMenu.activeIndex.value
})
const hoverBackground = computed(() => {
return rootMenu.hoverBackground.value
})
const backgroundColor = computed(() => {
return rootMenu.props.backgroundColor || ''
})
const activeTextColor = computed(() => {
return rootMenu.props.activeTextColor || ''
})
const textColor = computed(() => {
return rootMenu.props.textColor || ''
})
const mode = computed(() => {
return rootMenu.props.mode
})
const isNested = computed(() => {
return parentMenu.value.type.name !== 'ElMenu'
})
const itemStyle = computed(() => {
const style = {
color: active.value ? activeTextColor.value : textColor.value,
borderBottomColor: '',
}
if (mode.value === 'horizontal' && !isNested.value) {
style.borderBottomColor = active.value
? rootMenu.props.activeTextColor
? activeTextColor.value
: ''
: 'transparent'
}
return style
})
const onMouseEnter = () => {
if (mode.value === 'horizontal' && !rootMenu.props.backgroundColor) return
instance.vnode.el.style.backgroundColor = hoverBackground.value
}
const onMouseLeave = () => {
if (mode.value === 'horizontal' && !rootMenu.props.backgroundColor) return
instance.vnode.el.style.backgroundColor = backgroundColor.value
}
const handleClick = () => {
if (!props.disabled) {
rootMenu.methods.handleMenuItemClick({
@ -165,12 +120,8 @@ export default defineComponent({
slots,
paddingStyle,
itemStyle,
backgroundColor,
active,
handleClick,
onMouseEnter,
onMouseLeave,
}
},
})

View File

@ -17,8 +17,9 @@ import {
} from 'vue'
import ElCollapseTransition from '@element-plus/components/collapse-transition'
import ElPopper from '@element-plus/components/popper'
import useMenu from './useMenu'
import useMenu from './use-menu'
import { useMenuCssVar } from './use-menu-css-var'
import type {
ISubMenuProps,
RootMenuProvider,
@ -71,7 +72,6 @@ export default defineComponent({
const {
openedMenus,
isMenuPopup,
hoverBackground: rootHoverBackground,
methods: rootMethods,
props: rootProps,
methods: { closeMenu },
@ -265,16 +265,7 @@ export default defineComponent({
}
}
}
const handleTitleMouseenter = () => {
if (mode.value === 'horizontal' && !rootProps.backgroundColor) return
const title = popperVnode.value?.triggerRef || verticalTitleRef.value
title && (title.style.backgroundColor = rootHoverBackground.value)
}
const handleTitleMouseleave = () => {
if (mode.value === 'horizontal' && !rootProps.backgroundColor) return
const title = popperVnode.value?.triggerRef || verticalTitleRef.value
title && (title.style.backgroundColor = rootProps.backgroundColor || '')
}
const updatePlacement = () => {
data.currentPlacement =
mode.value === 'horizontal' && isFirstLevel.value
@ -342,8 +333,6 @@ export default defineComponent({
handleClick,
handleMouseenter,
handleMouseleave,
handleTitleMouseenter,
handleTitleMouseleave,
addItem,
removeItem,
@ -365,9 +354,9 @@ export default defineComponent({
null
),
]
const ulStyle = {
backgroundColor: this.rootProps.backgroundColor || '',
}
const ulStyle = useMenuCssVar(this.rootProps)
// this render function is only used for bypass `Vue`'s compiler caused patching issue.
// temporaryly mark ElPopper as any due to type inconsistency.
// TODO: correct popper's type.
@ -428,8 +417,6 @@ export default defineComponent({
{ backgroundColor: this.backgroundColor },
],
onClick: this.handleClick,
onMouseenter: this.handleTitleMouseenter,
onMouseleave: this.handleTitleMouseleave,
},
titleTag
),
@ -447,8 +434,6 @@ export default defineComponent({
],
ref: 'verticalTitleRef',
onClick: this.handleClick,
onMouseenter: this.handleTitleMouseenter,
onMouseleave: this.handleTitleMouseleave,
},
titleTag
),
@ -463,7 +448,7 @@ export default defineComponent({
{
role: 'menu',
class: 'el-menu el-menu--inline',
style: ulStyle,
style: ulStyle.value,
},
[this.$slots.default?.()]
),

View File

@ -0,0 +1,16 @@
import { computed } from 'vue'
import useMenuColor from './use-menu-color'
import type { IMenuProps } from './menu.type'
export const useMenuCssVar = (props: IMenuProps) => {
return computed(() => {
return {
'--el-menu-text-color': props.textColor || '',
'--el-menu-hover-text-color': props.textColor || '',
'--el-menu-background-color': props.backgroundColor || '',
'--el-menu-hover-background-color': useMenuColor(props).value || '',
'--el-menu-active-color': props.activeTextColor || '',
}
})
}

View File

@ -896,10 +896,13 @@ $--slider: map.merge(
$--menu: () !default;
$--menu: map.merge(
(
'item-font-size': var(--el-font-size-base),
'item-font-color': var(--el-text-color-primary),
'item-hover-fill': var(--el-color-primary-light-9),
'active-color': var(--el-color-primary),
'text-color': var(--el-text-color-primary),
'hover-text-color': var(--el-text-color-primary),
'background-color': var(--el-color-white),
'hover-background-color': var(--el-color-primary-light-9),
'item-font-size': var(--el-font-size-base),
'item-hover-fill': var(--el-color-primary-light-9),
'border-color': #e6e6e6,
),
$--menu

View File

@ -8,7 +8,7 @@
height: 56px;
line-height: 56px;
font-size: var(--el-menu-item-font-size);
color: var(--el-menu-item-font-color);
color: var(--el-menu-text-color);
padding: 0 20px;
list-style: none;
cursor: pointer;
@ -24,7 +24,7 @@
}
i {
color: var(--el-text-color-secondary);
color: inherit;
}
&:hover,
@ -33,7 +33,7 @@
}
&:hover {
background-color: var(--el-menu-item-hover-fill);
background-color: var(--el-menu-hover-background-color);
}
@include when(disabled) {
@ -68,7 +68,7 @@
height: 60px;
margin: 0;
border-bottom: 2px solid transparent;
color: var(--el-text-color-secondary);
color: var(--el-menu-text-color);
a,
a:hover {
@ -88,14 +88,14 @@
&:hover {
.#{$namespace}-sub-menu__title {
color: var(--el-text-color-primary);
color: var(--el-menu-hover-text-color);
}
}
&.is-active {
.#{$namespace}-sub-menu__title {
border-bottom: 2px solid var(--el-color-primary);
color: var(--el-text-color-primary);
border-bottom: 2px solid var(--el-menu-active-color);
color: var(--el-menu-active-color);
}
}
@ -103,7 +103,7 @@
height: 60px;
line-height: 60px;
border-bottom: 2px solid transparent;
color: var(--el-text-color-secondary);
color: var(--el-menu-text-color);
&:hover {
background-color: #fff;
@ -120,27 +120,28 @@
& .#{$namespace}-menu {
& .#{$namespace}-menu-item,
& .#{$namespace}-sub-menu__title {
background-color: $--color-white;
background-color: var(--el-menu-background-color);
display: flex;
align-items: center;
height: 36px;
padding: 0 10px;
color: var(--el-text-color-secondary);
color: var(--el-menu-text-color);
}
& .#{$namespace}-menu-item.is-active,
& .#{$namespace}-sub-menu.is-active > .#{$namespace}-sub-menu__title {
color: var(--el-text-color-primary);
color: var(--el-menu-active-color);
}
}
& .#{$namespace}-menu-item:not(.is-disabled):hover,
& .#{$namespace}-menu-item:not(.is-disabled):focus {
outline: none;
color: var(--el-text-color-primary);
color: var(--el-menu-hover-text-color);
background-color: var(--el-menu-hover-background-color);
}
& > .#{$namespace}-menu-item.is-active {
border-bottom: 2px solid var(--el-color-primary);
color: var(--el-text-color-primary);
border-bottom: 2px solid var(--el-menu-active-color);
color: var(--el-menu-active-color) !important;
}
}
@include m(collapse) {
@ -197,7 +198,7 @@
}
&.is-active i {
color: var(--el-color-primary);
color: inherit;
}
}
}
@ -222,7 +223,7 @@
vertical-align: middle;
}
@include when(active) {
color: var(--el-color-primary);
color: var(--el-menu-active-color);
i {
color: inherit;
}
@ -238,7 +239,7 @@
@include menu-item;
&:hover {
background-color: var(--el-menu-item-hover-fill);
background-color: var(--el-menu-hover-background-color);
}
}
& .#{$namespace}-menu {
@ -268,7 +269,7 @@
}
@include when(active) {
.#{$namespace}-sub-menu__title {
border-bottom-color: var(--el-color-primary);
border-bottom-color: var(--el-menu-active-color);
}
}
@include when(opened) {