From 04ea1429a841f9ec3c62984b45cf6751152cf7d7 Mon Sep 17 00:00:00 2001 From: GaoNeng-wWw Date: Sun, 11 Sep 2022 21:41:29 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(menu):=20=E4=BF=AE=E5=A4=8D=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E9=83=A8=E5=88=86=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../devui/menu/__tests__/menu.spec.ts | 31 +++- .../src/components/menu-item/menu-item.tsx | 6 +- .../src/components/menu-item/use-menu-item.ts | 12 ++ .../menu/src/components/sub-menu/sub-menu.tsx | 110 ++++++------ .../composables/use-nearest-menu-element.ts | 6 + .../devui/menu/src/composables/use-store.ts | 4 +- packages/devui-vue/devui/menu/src/menu.scss | 1 + packages/devui-vue/devui/menu/src/menu.tsx | 75 +++++---- .../devui/menu/src/styles/clear.scss | 5 +- .../devui/menu/src/styles/horizontal.scss | 16 ++ .../devui/menu/src/styles/public.scss | 16 +- .../devui/menu/src/styles/vertical.scss | 156 +++++++++--------- .../devui-vue/docs/components/menu/index.md | 53 ++++-- .../docs/en-US/components/menu/index.md | 51 ++++-- 14 files changed, 346 insertions(+), 196 deletions(-) create mode 100644 packages/devui-vue/devui/menu/src/composables/use-nearest-menu-element.ts diff --git a/packages/devui-vue/devui/menu/__tests__/menu.spec.ts b/packages/devui-vue/devui/menu/__tests__/menu.spec.ts index 06b95399ab..e7796d0693 100644 --- a/packages/devui-vue/devui/menu/__tests__/menu.spec.ts +++ b/packages/devui-vue/devui/menu/__tests__/menu.spec.ts @@ -4,6 +4,7 @@ import { Menu, SubMenu, MenuItem } from '../index'; import { useNamespace } from '../../shared/hooks/use-namespace'; const ns = useNamespace('menu'); +const SubNs = useNamespace('submenu'); const dotNs = useNamespace('menu', true); const dotSubNs = useNamespace('submenu', true); @@ -12,6 +13,9 @@ const menuHorizontal = ns.b() + '-horizontal'; const dotMenuItem = dotNs.b() + '-item'; const dotMenuItemVerticalWrapper = dotNs.b() + '-item-vertical-wrapper'; const dotSubMenu = dotSubNs.b(); +const submenuDisabled = SubNs.b() + '-disabled'; +const menuitemDisabled = ns.b() + '-item-disabled'; + describe('menu test', () => { let wrapper: VueWrapper; @@ -136,7 +140,6 @@ describe('menu test', () => { expect(wrapper.findAll('i')[0].classes().includes('is-opened')).toBe(true); expect(wrapper.findAll('i')[1].classes().includes('is-opened')).toBe(false); }); - it.todo('props mode(vertical/horizontal) work well.'); it.todo('props multiple work well.'); @@ -148,4 +151,30 @@ describe('menu test', () => { it.todo('props router work well.'); it.todo('slot icon work well.'); + it('menu - disabled', async ()=>{ + wrapper = wrapper = mount({ + components: { + 'd-menu': Menu, + 'd-sub-menu': SubMenu, + 'd-menu-item': MenuItem, + }, + template: ` + + 首页 + + C + + 基础 + 进阶 + + + 个人 + Link To Baidu + + `, + }); + await nextTick(); + expect(wrapper.findAll(dotMenuItem).at(-1)?.classes().includes(menuitemDisabled)).toBe(true); + expect(wrapper.find('.course').classes().includes(submenuDisabled)).toBe(true); + }); }); diff --git a/packages/devui-vue/devui/menu/src/components/menu-item/menu-item.tsx b/packages/devui-vue/devui/menu/src/components/menu-item/menu-item.tsx index d84cc87856..8de5d3bd80 100644 --- a/packages/devui-vue/devui/menu/src/components/menu-item/menu-item.tsx +++ b/packages/devui-vue/devui/menu/src/components/menu-item/menu-item.tsx @@ -34,7 +34,6 @@ export default defineComponent({ const rootMenuEmit = inject('rootMenuEmit') as (eventName: string, ...args: unknown[]) => void; const useRouter = inject('useRouter') as boolean; const router = instance?.appContext.config.globalProperties.$router as Router; - const classObject = computed(()=>({ [`${ns.b()}-item`]: true, [`${ns.b()}-item-isCollapsed`]: isCollapsed.value, @@ -48,6 +47,7 @@ export default defineComponent({ e.stopPropagation(); const ele = e.currentTarget as HTMLElement; let changeRouteResult = undefined; + props.disabled && e.preventDefault(); if (!props.disabled) { if (!multiple) { menuStore.emit('menuItem:clear:select'); @@ -85,7 +85,9 @@ export default defineComponent({ const icons = {ctx.slots.icon?.()}; const menuItems = ref(null); watch(disabled, () => { - classObject.value[menuItemSelect] = false; + if (!multiple){ + classObject.value[menuItemSelect] = false; + } }); watch( () => defaultSelectKey, diff --git a/packages/devui-vue/devui/menu/src/components/menu-item/use-menu-item.ts b/packages/devui-vue/devui/menu/src/components/menu-item/use-menu-item.ts index cc643ec428..44669c63ee 100644 --- a/packages/devui-vue/devui/menu/src/components/menu-item/use-menu-item.ts +++ b/packages/devui-vue/devui/menu/src/components/menu-item/use-menu-item.ts @@ -44,3 +44,15 @@ export function changeRoute(props: MenuItemProps, router: Router, useRouter: boo } return undefined; } + +export function changeSelect(isMultiple: boolean, defaultSelectKeys: string[], key: string): string[]{ + if (isMultiple){ + if (!defaultSelectKeys.indexOf(key)){ + defaultSelectKeys.push(key); + } + } else{ + defaultSelectKeys = []; + defaultSelectKeys.push(key); + } + return defaultSelectKeys; +} diff --git a/packages/devui-vue/devui/menu/src/components/sub-menu/sub-menu.tsx b/packages/devui-vue/devui/menu/src/components/sub-menu/sub-menu.tsx index 6c11acf03c..b4b8480f29 100644 --- a/packages/devui-vue/devui/menu/src/components/sub-menu/sub-menu.tsx +++ b/packages/devui-vue/devui/menu/src/components/sub-menu/sub-menu.tsx @@ -1,12 +1,21 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { defineComponent, getCurrentInstance, inject, onMounted, ref, watchEffect, watch } from 'vue'; +import { randomId } from '../../../../shared/utils/random-id'; import type { ComponentInternalInstance, Ref } from 'vue'; -import { addLayer, pushElement, clearSelect, getLayer } from '../../composables/use-layer-operate'; +import { + defineComponent, + getCurrentInstance, + inject, + onMounted, + ref, + watch, + watchEffect +} from 'vue'; +import { useNamespace } from '../../../../shared/hooks/use-namespace'; import { useClick } from '../../composables/use-click'; -import { useShowSubMenu } from './use-sub-menu'; -import { SubMenuProps, subMenuProps } from './sub-menu-types'; +import { addLayer, clearSelect, getLayer, pushElement } from '../../composables/use-layer-operate'; +import { useNearestMenuElement } from '../../composables/use-nearest-menu-element'; import MenuTransition from '../menu-transition/menu-transition'; -import { useNamespace } from '../../../../shared/hooks/use-namespace'; +import { SubMenuProps, subMenuProps } from './sub-menu-types'; +import { useShowSubMenu } from './use-sub-menu'; const ns = useNamespace('menu'); const subNs = useNamespace('submenu'); @@ -24,29 +33,23 @@ export default defineComponent({ const { vnode: { key } } = getCurrentInstance() as ComponentInternalInstance; - const key_ = String(key); - const isOpen = ref(false); + let key_ = String(key); const defaultOpenKeys = inject('openKeys') as Ref; + const isOpen = ref(defaultOpenKeys.value.includes(key_)); const indent = inject('defaultIndent'); const isCollapsed = inject('isCollapsed') as Ref; const mode = inject('mode') as Ref; const subMenuItemContainer = ref(null) as Ref; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const parentEmit = inject('rootMenuEmit') as (eventName: 'submenu-change', ...args: any[]) => void; const isHorizontal = mode.value === 'horizontal'; if (key_ === 'null') { console.warn(`[devui][menu]: Key can not be null`); - } else { - if (defaultOpenKeys.value.includes(key_)) { - isOpen.value = true; - } else { - isOpen.value = false; - } + key_ = `randomKey-${randomId(16)}`; } const clickHandle = (e: MouseEvent) => { - e.preventDefault(); e.stopPropagation(); - const ele = e.currentTarget as HTMLElement; - + const ele = useNearestMenuElement(e.target as HTMLElement); if (ele.classList.contains(subMenuClass) && isHorizontal) { return; } @@ -55,26 +58,22 @@ export default defineComponent({ useClick(e as clickEvent); } if (!props.disabled && mode.value !== 'horizontal') { - const target = e.target as HTMLElement; - let cur = e.target as HTMLElement; - if (target.tagName === 'UL') { - if (target.classList.contains(`${subMenuClass}-open`)) { - isOpen.value = !isOpen.value; - } else { - isOpen.value = isOpen.value; - } + const cur = useNearestMenuElement(e.target as HTMLElement); + const idx = defaultOpenKeys.value.indexOf(key_); + if (idx >= 0 && cur.tagName === 'UL') { + defaultOpenKeys.value.splice(idx, 1); } else { - while (cur && cur.tagName !== 'UL') { - if (cur.tagName === 'LI') { - break; - } - cur = cur.parentElement as HTMLElement; - } - if (cur.tagName === 'UL') { - isOpen.value = !isOpen.value; + if (cur.tagName === 'UL'){ + defaultOpenKeys.value.push(key_); } } - parentEmit('submenu-change', { type: 'submenu-change', state: isOpen.value, key: key_, el: cur }); + isOpen.value = defaultOpenKeys.value.indexOf(key_) >= 0; + parentEmit('submenu-change', { + type: 'submenu-change', + state: isOpen.value, + key: key_, + el: ele + }); } }; const wrapper = ref(null); @@ -86,6 +85,7 @@ export default defineComponent({ watchEffect( () => { wrapperDom = wrapper.value as unknown as HTMLElement; + // eslint-disable-next-line @typescript-eslint/no-explicit-any pushElement({ el: subMenu.value } as any); }, { flush: 'post' } @@ -93,7 +93,7 @@ export default defineComponent({ watch( () => defaultOpenKeys, (n) => { - if (n.value.includes(key_)) { + if (n.value.includes(key_)){ isOpen.value = true; } else { isOpen.value = false; @@ -101,11 +101,11 @@ export default defineComponent({ },{deep: true} ); onMounted(() => { - const el = title.value as unknown as HTMLElement; - const e = subMenu.value as unknown as HTMLElement; + const subMenuTitle = title.value as unknown as HTMLElement; + const subMenuWrapper = subMenu.value as unknown as HTMLElement; addLayer(); - class_layer.value = `layer_${Array.from(e.classList).at(-1)?.replace('layer_', '')}`; - if (isHorizontal) { + class_layer.value = `layer_${Array.from(subMenuWrapper.classList).at(-1)?.replace('layer_', '')}`; + if (isHorizontal && !props.disabled) { (subMenu.value as unknown as Element as HTMLElement).addEventListener('mouseenter', (ev: MouseEvent) => { ev.stopPropagation(); useShowSubMenu('mouseenter', ev, wrapperDom); @@ -116,30 +116,34 @@ export default defineComponent({ }); } watch(isCollapsed, (newValue) => { - const layer = Number(getLayer(e)); + const layer = Number(getLayer(subMenuWrapper)); if (!Number.isNaN(layer)) { layer > 2 && (isShow.value = !isCollapsed.value); } if (newValue) { - el.style.padding !== '0' && (oldPadding = el.style.padding); + subMenuTitle.style.padding !== '0' && (oldPadding = subMenuTitle.style.padding); setTimeout(() => { - el.style.padding = '0'; - el.style.width = ''; - el.style.textAlign = `center`; + subMenuTitle.style.padding = '0'; + subMenuTitle.style.width = ''; + subMenuTitle.style.textAlign = `center`; }, 300); - el.style.display = `block`; + subMenuTitle.style.display = `block`; } else { - el.style.padding = `${oldPadding}`; - el.style.textAlign = ``; - el.style.display = `flex`; + subMenuTitle.style.padding = `${oldPadding}`; + subMenuTitle.style.textAlign = ``; + subMenuTitle.style.display = `flex`; } }); }); return () => { return ( -
    +
      {ctx.slots?.icon?.()} @@ -155,7 +159,11 @@ export default defineComponent({ }}>
      {isHorizontal ? ( -
      +
      {ctx.slots.default?.()}
      ) : ( diff --git a/packages/devui-vue/devui/menu/src/composables/use-nearest-menu-element.ts b/packages/devui-vue/devui/menu/src/composables/use-nearest-menu-element.ts new file mode 100644 index 0000000000..622f91a58f --- /dev/null +++ b/packages/devui-vue/devui/menu/src/composables/use-nearest-menu-element.ts @@ -0,0 +1,6 @@ +export function useNearestMenuElement(ele: HTMLElement): HTMLElement{ + while (ele && ele.tagName !== 'LI' && ele.tagName !== 'UL'){ + ele = ele.parentElement as HTMLElement; + } + return ele; +} diff --git a/packages/devui-vue/devui/menu/src/composables/use-store.ts b/packages/devui-vue/devui/menu/src/composables/use-store.ts index 4502b01b92..9fcca009d1 100644 --- a/packages/devui-vue/devui/menu/src/composables/use-store.ts +++ b/packages/devui-vue/devui/menu/src/composables/use-store.ts @@ -22,7 +22,7 @@ export class Store{ emit(eventName: string, ...args: any[]): void{ recordTable[this.rootMenuName][eventName].forEach((fn)=>fn(...args)); } - off(eventName: string, fn: (...args: []) => void): void { + off(eventName: string, fn: (...args: []) => void): void{ const idx = recordTable[this.rootMenuName][eventName].indexOf(fn); if (idx >= 0) { recordTable[this.rootMenuName][eventName].splice(idx, 1); @@ -30,7 +30,7 @@ export class Store{ } } -export function useStore(rootName: string): Store { +export function useStore(rootName: string): Store{ if (!recordTable[rootName]){ Reflect.set(recordTable, rootName, {}); } diff --git a/packages/devui-vue/devui/menu/src/menu.scss b/packages/devui-vue/devui/menu/src/menu.scss index 79bc8e84b0..40ec54647d 100644 --- a/packages/devui-vue/devui/menu/src/menu.scss +++ b/packages/devui-vue/devui/menu/src/menu.scss @@ -8,6 +8,7 @@ $devui-menu-item: var(--devui-menu-item); $devui-menu-item-sub: var(--devui-menu-item-sub); $devui-menu-item-disabled: var(--devui-menu-disabled); $devui-menu-item-select: var(--devui-menu-item-hover); +$devui-menu-item-hover: var(--devui-menu-item-hover); $devui-menu-item-selectBar: var(--devui-primary-hover, #5e7ce0); $devui-menu-active-parent: var(--devui-icon-fill-active); diff --git a/packages/devui-vue/devui/menu/src/menu.tsx b/packages/devui-vue/devui/menu/src/menu.tsx index 60e95a189a..cbd5390a29 100644 --- a/packages/devui-vue/devui/menu/src/menu.tsx +++ b/packages/devui-vue/devui/menu/src/menu.tsx @@ -1,4 +1,4 @@ -import { defineComponent, provide, ref, computed, onMounted, toRefs } from 'vue'; +import { defineComponent, provide, ref, computed, onMounted, toRefs, reactive } from 'vue'; import type { ComponentPublicInstance } from 'vue'; import { menuProps, MenuProps } from './menu-types'; import './menu.scss'; @@ -32,52 +32,69 @@ export default defineComponent({ provide('useRouter', props.router); setDefaultIndent(props['indentSize']); const menuRoot = ref(null); - const overflow_container = ref(null); const overflowItemLength = ref(0); + const overflowContainer = ref(null); + const selectClassName = `${ns.b()}-item-select`; const rootClassName = computed(()=>({ [`${ns.b()}`]: true, [`${ns.b()}-vertical`]: mode.value === 'vertical', [`${ns.b()}-horizontal`]: mode.value === 'horizontal', [`${ns.b()}-collapsed`]: collapsed.value })); + const overflowContainerClassName = reactive({ + [selectClassName]: false, + [`${ns.b()}-overflow-container`]: true + }); + // 如果一个或多个菜单元素被选中,当宽度发生变化时。如果溢出容易中有被选中的元素,那么溢出容器也应当被选中 + const resetOverflowContainerSelectState = (e: Element) => { + const children = Array.from(e.children); + for (const item of children){ + if (item.classList.contains(selectClassName)){ + overflowContainerClassName[selectClassName] = true; + break; + } else { + overflowContainerClassName[selectClassName] = false; + } + } + }; onMounted(() => { if (props['mode'] === 'horizontal') { let flag = false; - const overflowContainer = overflow_container.value?.$el as unknown as HTMLElement; + const overflowContainerElement = overflowContainer.value?.$el as unknown as HTMLElement; const root = menuRoot.value as unknown as HTMLElement; const children = root.children; - const container = overflowContainer.children[1]; + const container = overflowContainerElement.children[1]; const ob = new IntersectionObserver( (entries: IntersectionObserverEntry[]) => { - entries.forEach((v: IntersectionObserverEntry) => { - if (!v.isIntersecting) { - const cloneNode = v.target.cloneNode(true) as Element as HTMLElement; - if (v.target.classList.contains(`${ns.b()}-overflow-container`)){ - if (flag && v.target.previousElementSibling && container.children.length){ - root.appendChild(v.target.previousElementSibling); + entries.forEach((entry: IntersectionObserverEntry) => { + if (!entry.isIntersecting) { + const cloneNode = entry.target.cloneNode(true) as Element as HTMLElement; + if (entry.target.classList.contains(`${ns.b()}-overflow-container`)){ + if (flag && entry.target.previousElementSibling && container.children.length){ + root.appendChild(entry.target.previousElementSibling); } else {flag = true;} } else { overflowItemLength.value += 1; - (v.target as Element as HTMLElement).style.visibility = 'hidden'; - if (overflowContainer.nextSibling) { - root.insertBefore(v.target, overflowContainer.nextSibling); + (entry.target as Element as HTMLElement).style.visibility = 'hidden'; + if (overflowContainerElement.nextSibling) { + root.insertBefore(entry.target, overflowContainerElement.nextSibling); } else { - root.appendChild(v.target); + root.appendChild(entry.target); } container.appendChild(cloneNode); + resetOverflowContainerSelectState(container); } } else { if ( - !v.target.classList.contains(`${ns.b()}-overflow-container`) && - (v.target as HTMLElement).style.visibility === 'hidden' + !entry.target.classList.contains(`${ns.b()}-overflow-container`) && + (entry.target as HTMLElement).style.visibility === 'hidden' ) { - ob.unobserve(v.target); - const el = container.lastChild; - if (el){ - root.insertBefore(el, overflowContainer); - } - const obItem = overflowContainer.previousElementSibling; - if (obItem) { + ob.unobserve(entry.target); + root.insertBefore(entry.target, overflowContainerElement); + (entry.target as HTMLElement).style.visibility = ''; + const obItem = overflowContainerElement.previousElementSibling; + const canObAgin = obItem && (entry.boundingClientRect.width % entry.target.getBoundingClientRect().width === 0); + if (canObAgin) { ob.observe(obItem); } if (obItem?.classList.contains('devui-submenu')){ @@ -92,9 +109,12 @@ export default defineComponent({ useShowSubMenu('mouseleave', ev, wrapper); }); } - (v.target as HTMLElement).style.visibility = ''; - v.target.remove(); overflowItemLength.value -= 1; + ob.observe(entry.target); + if (container.lastChild){ + container.removeChild(container.lastChild); + } + resetOverflowContainerSelectState(container); } } }); @@ -117,14 +137,13 @@ export default defineComponent({ class={rootClassName.value} style={[ props['collapsed'] ? `width:${props['collapsedIndent'] * 2}px` : `width: ${props['width']}`, - 'white-space: nowrap', ]}> {ctx.slots.default?.()} 0 && mode.value === 'horizontal'}>
    diff --git a/packages/devui-vue/devui/menu/src/styles/clear.scss b/packages/devui-vue/devui/menu/src/styles/clear.scss index ede506aaf6..6b76b4c387 100644 --- a/packages/devui-vue/devui/menu/src/styles/clear.scss +++ b/packages/devui-vue/devui/menu/src/styles/clear.scss @@ -1,6 +1,9 @@ .#{$devui-prefix}-menu-vertical, .#{$devui-prefix}-menu-horizontal { - a { + a, + a:hover, + a:active, + a:visited { text-decoration: none; } diff --git a/packages/devui-vue/devui/menu/src/styles/horizontal.scss b/packages/devui-vue/devui/menu/src/styles/horizontal.scss index c020c924c8..36419bac56 100644 --- a/packages/devui-vue/devui/menu/src/styles/horizontal.scss +++ b/packages/devui-vue/devui/menu/src/styles/horizontal.scss @@ -170,4 +170,20 @@ } } } + + .#{$devui-prefix}-menu-item-disabled, + .#{$devui-prefix}-submenu-disabled { + span, + a { + color: $devui-menu-item-disabled !important; + cursor: not-allowed; + } + + &::after { + content: unset !important; + } + &+.#{$devui-prefix}-menu-item-horizontal-wrapper { + display: none; + } + } } diff --git a/packages/devui-vue/devui/menu/src/styles/public.scss b/packages/devui-vue/devui/menu/src/styles/public.scss index 8b2959ebf1..732c32b3de 100644 --- a/packages/devui-vue/devui/menu/src/styles/public.scss +++ b/packages/devui-vue/devui/menu/src/styles/public.scss @@ -3,12 +3,6 @@ margin-left: $devui-menu-item-margin; } -.#{$devui-prefix}-menu-item-disabled, -.#{$devui-prefix}-submenu-disabled { - color: $devui-menu-item-disabled !important; - cursor: not-allowed !important; -} - .#{$devui-prefix}-menu-item-disabled:hover, .#{$devui-prefix}-submenu-disabled:hover { color: $devui-menu-item-disabled !important; @@ -34,3 +28,13 @@ .fade-leave-to { opacity: 0; } + +.#{$devui-prefix}-menu-item-disabled, +.#{$devui-prefix}-menu-item-disabled.devui-menu-vertical +.#{$devui-prefix}-menu-item-disabled.devui-menu-item-select +.#{$devui-prefix}-submenu-disabled, +.#{$devui-prefix}-submenu-disabled.devui-menu-vertical, +.#{$devui-prefix}-submenu-disabled.devui-menu-item-select { + color: $devui-menu-item-disabled !important; + cursor: not-allowed !important; +} diff --git a/packages/devui-vue/devui/menu/src/styles/vertical.scss b/packages/devui-vue/devui/menu/src/styles/vertical.scss index 5ec1d3d33b..4077c0e002 100644 --- a/packages/devui-vue/devui/menu/src/styles/vertical.scss +++ b/packages/devui-vue/devui/menu/src/styles/vertical.scss @@ -1,7 +1,9 @@ .#{$devui-prefix}-submenu-menu-item-vertical-wrapper { overflow: hidden; } - +.#{$devui-prefix}-submenu-menu-item { + color: $devui-menu-item; +} .#{$devui-prefix}-menu-vertical { padding: 0; transition: @@ -80,69 +82,23 @@ opacity: 1; background: $devui-menu-item-selectBar; } - - .#{$devui-prefix}-menu-item-select { - background: $devui-primary-bg !important; - position: relative; - - span, - a { - color: $devui-menu-item-select; - } - - &::after { - display: block; - position: absolute; - right: 0; - top: 0; - height: 100%; - width: 4px; - content: ''; - opacity: 1; - background: var(--devui-brand, #5e7ce0); - transform: scaleX(1); - } - } - - .#{$devui-prefix}-submenu > div:hover { - span.#{$devui-prefix}-submenu-title-content { - color: $devui-menu-item-select; - } - } - - .#{$devui-prefix}-menu-item:hover { - color: $devui-menu-item-select; - } - - li.#{$devui-prefix}-menu-item, - div.#{$devui-prefix}-submenu-title { - white-space: nowrap; - overflow: hidden; - - span:nth-child(2) { - overflow: hidden; - text-overflow: ellipsis; - - span { - overflow: hidden; - text-overflow: ellipsis; - } - } - } - - .#{$devui-prefix}-menu-item-isCollapsed { - width: fit-content; - - .#{$devui-prefix}-menu-icon { - margin: auto; - } - } - // sub menu ul.#{$devui-prefix}-submenu { margin: 0; padding: 0; + .#{$devui-prefix}-menu-item { + display: flex; + background: $devui-area; + & > span { + flex: auto; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + transition: all $devui-animation-duration-fast $devui-animation-ease-in-smooth; + color: $devui-menu-item; + } + } div.#{$devui-prefix}-submenu-title { display: flex; cursor: pointer; @@ -161,6 +117,7 @@ span.#{$devui-prefix}-submenu-title-content { font-size: $devui-font-size-lg; flex: auto; + color: $devui-menu-item; } span.#{$devui-prefix}-menu-icon { @@ -175,22 +132,14 @@ transform: rotate(180deg); } } - - .#{$devui-prefix}-menu-item { - display: flex; - - & > span { - flex: auto; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - transition: all $devui-animation-duration-fast $devui-animation-ease-in-smooth; - color: $devui-menu-item-sub; + .#{$devui-prefix}-submenu-title:hover { + span { + color: $devui-menu-item-hover !important; } } .#{$devui-prefix}-menu-item:hover { - & > span { + span { color: $devui-menu-item-select; } } @@ -202,11 +151,70 @@ } } + .#{$devui-prefix}-menu-item-select { + background: $devui-primary-bg !important; + position: relative; + + span, + a { + color: $devui-menu-item-select; + } + + &::after { + display: block; + position: absolute; + right: 0; + top: 0; + height: 100%; + width: 4px; + content: ''; + opacity: 1; + background: var(--devui-brand, #5e7ce0); + transform: scaleX(1); + } + } + + .#{$devui-prefix}-menu-item:hover { + color: $devui-menu-item-select; + } + + li.#{$devui-prefix}-menu-item, + div.#{$devui-prefix}-submenu-title { + white-space: nowrap; + overflow: hidden; + + span:nth-child(2) { + overflow: hidden; + text-overflow: ellipsis; + + span { + overflow: hidden; + text-overflow: ellipsis; + } + } + } + + .#{$devui-prefix}-menu-item-isCollapsed { + width: fit-content; + + .#{$devui-prefix}-menu-icon { + margin: auto; + } + } + ul li ~ ul > div { margin-top: 0 !important; } - - ul li { - background: $devui-area !important; + .#{$devui-prefix}-menu-item-disabled, + .#{$devui-prefix}-submenu-disabled { + * { + color: $devui-menu-item-disabled !important; + cursor: not-allowed !important; + background: $devui-block !important; + } + } + .#{$devui-prefix}-menu-item-disabled::after, + .#{$devui-prefix}-submenu-disabled::after { + content: unset; } } diff --git a/packages/devui-vue/docs/components/menu/index.md b/packages/devui-vue/docs/components/menu/index.md index e317b81e95..53ab9e046f 100644 --- a/packages/devui-vue/docs/components/menu/index.md +++ b/packages/devui-vue/docs/components/menu/index.md @@ -21,13 +21,13 @@ Menu 组件通常用于导航. C - + 基础 进阶 个人 - Link To Baidu + Link To Baidu @@ -82,14 +82,14 @@ let width = ref(480); - + System item - + Setting item @@ -127,13 +127,14 @@ let width = ref(480); ::: -### 仅一项展开 +### 仅展开一项根子菜单 :::demo 通过子菜单状态改变事件修改```open-keys```数组达到效果 ``` vue