fix(components): [el-menu] fix warn & listen item (#3225)

* feat(directives): resize add element argument

* fix(components): [el-menu] fix warn & listen item

* chore: move import order

* fix(components): remove debounce
This commit is contained in:
三咲智子 2021-09-04 19:44:11 +08:00 committed by GitHub
parent 6f23947920
commit eb932c18c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 122 deletions

View File

@ -3,7 +3,7 @@ import { nextTick } from 'vue'
import { sleep } from '@element-plus/test-utils'
import { rAF } from '@element-plus/test-utils/tick'
import Menu from '../src/menu.vue'
import Menu from '../src/menu'
import MenuGroup from '../src/menuItemGroup.vue'
import MenuItem from '../src/menuItem.vue'
import SubMenu from '../src/submenu.vue'

View File

@ -1,4 +1,4 @@
import Menu from './src/menu.vue'
import Menu from './src/menu'
import MenuItem from './src/menuItem.vue'
import MenuItemGroup from './src/menuItemGroup.vue'
import SubMenu from './src/submenu.vue'

View File

@ -1,4 +1,3 @@
<script lang="ts">
import {
defineComponent,
getCurrentInstance,
@ -21,6 +20,7 @@ import ElMenuCollapseTransition from './menu-collapse-transition.vue'
import ElSubMenu from './submenu.vue'
import useMenuColor from './useMenuColor'
import type { VNode } from 'vue'
import type {
IMenuProps,
RootMenuProvider,
@ -30,13 +30,7 @@ import type {
export default defineComponent({
name: 'ElMenu',
directives: {
Resize,
},
components: {
ElMenuCollapseTransition,
ElSubMenu,
},
props: {
mode: {
type: String,
@ -63,7 +57,8 @@ export default defineComponent({
},
},
emits: ['close', 'open', 'select'],
setup(props: IMenuProps, { emit, slots }) {
setup(props: IMenuProps, { emit, slots, expose }) {
// data
const openedMenus = ref(
props.defaultOpeneds && !props.collapse
@ -78,7 +73,6 @@ export default defineComponent({
const rootMenuEmitter = mitt()
const router = instance.appContext.config.globalProperties.$router
const menu = ref(null)
const filteredSlot = ref(slots.default?.())
const hoverBackground = useMenuColor(props)
@ -220,79 +214,7 @@ export default defineComponent({
}
}
}
const flattedChildren = (children) => {
const temp = Array.isArray(children) ? children : [children]
const res = []
temp.forEach((child) => {
if (Array.isArray(child.children)) {
res.push(...flattedChildren(child.children))
} else {
res.push(child)
}
})
return res
}
const updateFilteredSlot = async () => {
filteredSlot.value = slots.default?.()
await nextTick()
if (props.mode === 'horizontal') {
const items = Array.from(menu.value.childNodes).filter(
(item: HTMLElement) => item.nodeName !== '#text' || item.nodeValue
) as [HTMLElement]
const originalSlot = flattedChildren(slots.default?.()) || []
if (items.length === originalSlot.length) {
const moreItemWidth = 64
const paddingLeft = parseInt(getComputedStyle(menu.value).paddingLeft)
const paddingRight = parseInt(
getComputedStyle(menu.value).paddingRight
)
const menuWidth = menu.value.clientWidth - paddingLeft - paddingRight
let calcWidth = 0
let sliceIndex = 0
items.forEach((item, index) => {
calcWidth += item.offsetWidth || 0
if (calcWidth <= menuWidth - moreItemWidth) {
sliceIndex = index + 1
}
})
const defaultSlot = originalSlot.slice(0, sliceIndex)
const moreSlot = originalSlot.slice(sliceIndex)
if (moreSlot?.length) {
filteredSlot.value = [
...defaultSlot,
h(
ElSubMenu,
{
index: 'sub-menu-more',
class: 'el-sub-menu__hide-arrow',
},
{
title: () =>
h('i', {
class: ['el-icon-more', 'el-sub-menu__icon-more'],
}),
default: () => moreSlot,
}
),
]
}
}
}
}
const handleResize = () => {
updateFilteredSlot()
}
// watch
watch(
() => slots.default?.(),
() => {
updateFilteredSlot()
}
)
const handleResize = () => instance.proxy.$forceUpdate()
watch(
() => props.defaultActive,
@ -359,45 +281,95 @@ export default defineComponent({
}
})
return {
hoverBackground,
isMenuPopup,
menu,
filteredSlot,
props,
expose({
open,
close,
handleResize,
}
},
render() {
const directives =
this.mode === 'horizontal' ? [[Resize, this.handleResize]] : []
const menu = withDirectives(
h(
'ul',
{
key: String(this.collapse),
role: 'menubar',
ref: 'menu',
style: { backgroundColor: this.backgroundColor || '' },
class: {
'el-menu': true,
'el-menu--horizontal': this.mode === 'horizontal',
'el-menu--collapse': this.collapse,
},
},
[this.filteredSlot]
),
directives
)
hoverBackground,
})
if (this.collapseTransition && this.mode === 'vertical') {
return h(ElMenuCollapseTransition, () => menu)
const useVNodeResize = (vnode: VNode) =>
props.mode === 'horizontal'
? withDirectives(vnode, [[Resize, handleResize]])
: vnode
return () => {
let slot = slots.default?.() ?? []
const showMore = []
if (props.mode === 'horizontal') {
const items = Array.from(
(menu.value as Node | undefined)?.childNodes ?? []
).filter(
(item) => item.nodeName !== '#text' || item.nodeValue
) as HTMLElement[]
const originalSlot = slot.flat(Infinity)
if (items.length === originalSlot.length) {
const moreItemWidth = 64
const paddingLeft = parseInt(
getComputedStyle(menu.value).paddingLeft,
10
)
const paddingRight = parseInt(
getComputedStyle(menu.value).paddingRight,
10
)
const menuWidth = menu.value.clientWidth - paddingLeft - paddingRight
let calcWidth = 0
let sliceIndex = 0
items.forEach((item, index) => {
calcWidth += item.offsetWidth || 0
if (calcWidth <= menuWidth - moreItemWidth) {
sliceIndex = index + 1
}
})
const defaultSlot = originalSlot.slice(0, sliceIndex)
const moreSlot = originalSlot.slice(sliceIndex)
if (moreSlot?.length) {
slot = defaultSlot
showMore.push(
h(
ElSubMenu,
{
index: 'sub-menu-more',
class: 'el-sub-menu__hide-arrow',
},
{
title: () =>
h('i', {
class: ['el-icon-more', 'el-sub-menu__icon-more'],
}),
default: () => moreSlot,
}
)
)
}
} else {
nextTick(() => instance.proxy.$forceUpdate())
}
}
const vnodeMenu = useVNodeResize(
h(
'ul',
{
key: String(props.collapse),
role: 'menubar',
ref: menu,
style: { backgroundColor: props.backgroundColor || '' },
class: {
'el-menu': true,
'el-menu--horizontal': props.mode === 'horizontal',
'el-menu--collapse': props.collapse,
},
},
[...slot.map((vnode) => useVNodeResize(vnode)), ...showMore]
)
)
if (props.collapseTransition && props.mode === 'vertical') {
return h(ElMenuCollapseTransition, () => vnodeMenu)
}
return vnodeMenu
}
return menu
},
})
</script>

View File

@ -8,7 +8,7 @@ import type { ObjectDirective } from 'vue'
const Resize: ObjectDirective = {
beforeMount(el, binding) {
el._handleResize = () => {
el && binding.value?.()
el && binding.value?.(el)
}
addResizeListener(el, el._handleResize)
},