diff --git a/packages/components/menu/__tests__/menu.test.ts b/packages/components/menu/__tests__/menu.test.ts
index 15a42dc5d7..f45aaafd66 100644
--- a/packages/components/menu/__tests__/menu.test.ts
+++ b/packages/components/menu/__tests__/menu.test.ts
@@ -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(
- `
-
- Item1
- Item2
- Item3
- Item4
-
-
`,
- {
- 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(
`
diff --git a/packages/components/menu/src/menu.ts b/packages/components/menu/src/menu.ts
index 1b472d38ae..604a1e99ef 100644
--- a/packages/components/menu/src/menu.ts
+++ b/packages/components/menu/src/menu.ts
@@ -105,6 +105,8 @@ export default defineComponent({
const nsSubMenu = useNamespace('sub-menu')
// data
+ const sliceIndex = ref(-1)
+
const openedMenus = ref(
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 | 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(