fix(components): [menu] resize event infinite invoke (#9342)

* fix(components): [menu] resize event infinite invoke

* chore: update

* Update menu.ts

* Update menu.ts

* chore: update

* chore: update

* chore: update test case

* chore: optimize

* Update menu.ts

Co-authored-by: RealityBoy <1923740402@qq.com>
This commit is contained in:
zz
2022-08-19 18:33:50 +08:00
committed by GitHub
parent 3d13c14ae7
commit b7fe5df6f9
2 changed files with 57 additions and 49 deletions

View File

@@ -191,7 +191,7 @@ describe('menu', () => {
})
test('menu-overflow', async () => {
// TODO: jsdom not support `offsetWidth`.
// TODO: jsdom not support `offsetWidth` and `ResizeObserver`.
})
})
@@ -423,30 +423,6 @@ describe('other', () => {
expect(onOpen).toHaveBeenCalled()
})
test('ellipsis', async () => {
const wrapper = _mount(
`<div :style="{ width: width + 'px' }">
<el-menu mode="horizontal" ellipsis>
<el-menu-item index="1">Item1</el-menu-item>
<el-menu-item index="2">Item2</el-menu-item>
<el-menu-item index="3">Item3</el-menu-item>
<el-menu-item index="4">Item4</el-menu-item>
</el-menu>
</div>`,
{
data() {
return {
width: 1000,
}
},
}
)
expect(wrapper.find('.el-sub-menu__icon-more').exists()).toBe(false)
wrapper.vm.width = 200
await nextTick()
expect(wrapper.find('.el-sub-menu__icon-more').exists()).toBe(true)
})
test('menu group', async () => {
const wrapper = _mount(
`<el-menu mode="vertical" default-active="1">

View File

@@ -105,6 +105,8 @@ export default defineComponent({
const nsSubMenu = useNamespace('sub-menu')
// data
const sliceIndex = ref(-1)
const openedMenus = ref<MenuProvider['openedMenus']>(
props.defaultOpeneds && !props.collapse
? props.defaultOpeneds.slice(0)
@@ -214,8 +216,53 @@ export default defineComponent({
}
}
const calcSliceIndex = () => {
const items = Array.from(menu.value!.childNodes ?? []).filter(
(item) => item.nodeName !== '#text' || item.nodeValue
) as HTMLElement[]
const moreItemWidth = 64
const paddingLeft = Number.parseInt(
getComputedStyle(menu.value!).paddingLeft,
10
)
const paddingRight = Number.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
}
})
return sliceIndex === items.length ? -1 : sliceIndex
}
// Common computer monitor FPS is 60Hz, which means 60 redraws per second. Calculation formula: 1000ms/60 ≈ 16.67ms, In order to avoid a certain chance of repeated triggering when `resize`, set wait to 16.67 * 2 = 33.34
const debounce = (fn: () => void, wait = 33.34) => {
let timmer: ReturnType<typeof setTimeout> | null
return () => {
timmer && clearTimeout(timmer)
timmer = setTimeout(() => {
fn()
}, wait)
}
}
let isFirstTimeRender = true
const handleResize = () => {
nextTick(() => instance.proxy!.$forceUpdate())
const callback = () => {
sliceIndex.value = -1
nextTick(() => {
sliceIndex.value = calcSliceIndex()
})
}
// execute callback directly when first time resize to avoid shaking
isFirstTimeRender ? callback() : debounce(callback)()
isFirstTimeRender = false
}
watch(
@@ -326,30 +373,15 @@ export default defineComponent({
const vShowMore: VNode[] = []
if (props.mode === 'horizontal' && menu.value) {
const items = Array.from(menu.value?.childNodes ?? []).filter(
(item) => item.nodeName !== '#text' || item.nodeValue
) as HTMLElement[]
const originalSlot = flattedChildren(slot)
const moreItemWidth = 64
const paddingLeft = Number.parseInt(
getComputedStyle(menu.value).paddingLeft,
10
)
const paddingRight = Number.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 slotDefault = originalSlot.slice(0, sliceIndex)
const slotMore = originalSlot.slice(sliceIndex)
const slotDefault =
sliceIndex.value === -1
? originalSlot
: originalSlot.slice(0, sliceIndex.value)
const slotMore =
sliceIndex.value === -1 ? [] : originalSlot.slice(sliceIndex.value)
if (slotMore?.length && props.ellipsis) {
slot = slotDefault
vShowMore.push(