Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/devui-theme/src/theme/theme-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,13 @@ export const devuiLightTheme: Theme = new Theme({
'devui-z-index-dropdown': '1052',
'devui-z-index-modal': '1050',
'devui-z-index-drawer': '1040',
'devui-z-index-framework': '1000'
'devui-z-index-framework': '1000',

// Menu
'devui-menu-item': '#252b3a',
'devui-menu-item-sub':'#6C6C6C',
'devui-menu-item-hover': '#0f0f0f',
'devui-menu-disabled': '#919191'
},
isDark: false,
});
Expand Down Expand Up @@ -281,6 +287,12 @@ export const devuiDarkTheme: Theme = new Theme({
'devui-primary-bg': '#383D4F',
'devui-default-line': '#5e7ce0',
'devui-default-bg': '#383838',

// Menu
'devui-menu-item': '#dcdcdc',
'devui-menu-item-sub':'#c6c6c6',
'devui-menu-item-hover': '#fff',
'devui-menu-disabled': '#919191'
},
extends: 'devui-light-theme',
isDark: true,
Expand Down
101 changes: 101 additions & 0 deletions packages/devui-vue/devui/menu/__tests__/menu.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { mount, shallowMount } from '@vue/test-utils';
import { reactive, ref } from 'vue';
import { Menu,SubMenu,MenuItem } from '../index';

const factory_Menu = (value: Record<string,unknown>) => shallowMount(Menu, value);

describe('menu test', () => {
it('menu - defaultMode test', async ()=>{
//
const menu = factory_Menu({});
expect(menu.classes().indexOf('vertical')).toBe(1);
});
it('menu - dynamic - mode', async ()=>{
const menu = factory_Menu({
props: {
'mode': 'horizontal'
}
});
expect(menu.classes().indexOf('horizontal')).not.toBe(-1);
});

// 参数动态测试 - defaultSelectKeys
it('menu - dynamic attr - defaultSelectKeys ', async ()=>{
//
const wrapper = mount({
components: {
Menu,
SubMenu,
MenuItem
},
template: `
<Menu :default-select-keys="selectKeys">
<MenuItem key='test'>
Test
</MenuItem>
</Menu>
<button @click="clickHandle">Change key</button>
`,
setup(){
const selectKeys = ref(['test']);
const clickHandle = () => {
selectKeys.value.pop();
};
return {
selectKeys,
clickHandle
};
}
});
expect(wrapper.find('li').classes().indexOf('devui-menu-item-select')).not.toBe(-1);
await wrapper.find('button').trigger('click');
expect(wrapper.find('li').classes().indexOf('devui-menu-item-select')).toBe(-1);
});
// 参数动态测试 - openKeys
it('menu - dynamic attr - openKeys', async () => {
//
const wrapper = mount({
components: {Menu,SubMenu,MenuItem},
template: `
<Menu :open-keys="defaultOpenKey">
<SubMenu key="1">
<MenuItem>
SubMenu > Item 1
</MenuItem>
<MenuItem>
SubMenu > Item 2
</MenuItem>
</SubMenu>
<SubMenu key="2">
<MenuItem>
SubMenu2 > Item 1
</MenuItem>
<MenuItem>
SubMenu2 > Item 2
</MenuItem>
</SubMenu>
</Menu>
<button @click=change>Click to Change openKeys</button>
`,
setup(){
const defaultOpenKey = ref(['1']);
const change = ()=>{
if (defaultOpenKey.value.length < 2){
defaultOpenKey.value.push('2');
} else {
defaultOpenKey.value.pop();
}
};
return {
defaultOpenKey,
change
};
}
});
console.log(wrapper.findAll('ul')[2].classes());
await wrapper.find('button').trigger('click');
console.log(wrapper.findAll('ul')[2].classes());
await wrapper.find('button').trigger('click');
console.log(wrapper.findAll('ul')[2].classes());
});
});
17 changes: 17 additions & 0 deletions packages/devui-vue/devui/menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { App } from 'vue';
import MenuItem from './src/components/menu-item';
import SubMenu from './src/components/sub-menu';
import Menu from './src/menu';

export { Menu,SubMenu,MenuItem };

export default {
title: 'Menu 菜单',
category: '布局',
status: "90%", // TODO: 组件若开发完成则填入"100%",并删除该注释
install(app: App): void {
app.component(Menu.name, Menu);
app.component(MenuItem.name, MenuItem);
app.component(SubMenu.name, SubMenu);
}
};
175 changes: 175 additions & 0 deletions packages/devui-vue/devui/menu/src/components/menu-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { changeKey } from '../composables/layer-composables';
import { defineComponent,
getCurrentInstance,
onMounted,
ref,
Transition,
watch,
inject,
Ref,
reactive,
toRefs,
} from "vue";
import { MenuItemProps, menuItemProps } from "../types/menu-item-types";
import { useInitSelect } from '../composables/init-select';
import { addActiveParent } from '../composables/add-active-parent';


export default defineComponent({
name: 'DMenuItem',
props: menuItemProps,
setup(props: MenuItemProps, ctx){
const instance = getCurrentInstance();
const key = String(instance?.vnode.key);
const mode = inject('mode') as Ref<('vertical' | 'horizontal')>;
const multiple = inject('multiple') as boolean;
const indent = inject('defaultIndent');
const isCollapsed = inject('isCollapsed') as Ref<boolean>;
const defaultSelectKey = (inject('defaultSelectKey') as string[]);
const {disabled} = toRefs(props);
const isSelect = ref(useInitSelect(defaultSelectKey, key, multiple, disabled));
const isLayer1 = ref(true);
const rootMenuEmit = inject('rootMenuEmit') as (eventName: string, ...args: unknown[]) => void;
const classObject: Record<string,boolean> = reactive({
'devui-menu-item': true,
'devui-menu-item-isCollapsed': isCollapsed.value,
'devui-isCollapsed-item': isCollapsed.value,
'devui-menu-item-select': isSelect.value,
'devui-menu-item-disabled': disabled.value
});
const onClick = (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
const ele = e.currentTarget as HTMLElement;
if (!props.disabled){
if (!multiple){
changeKey(ele,e);
ele.classList.add('devui-menu-item-select');
} else {
if (ele.classList.contains('devui-menu-item-select')){
ele.classList.remove('devui-menu-item-select');
rootMenuEmit('deselect', {type: 'deselect', el: ele, e});
return;
} else {
ele.classList.add('devui-menu-item-select');
}
}
rootMenuEmit('select', {type: 'select', el: ele, e});
}
if (mode.value === 'vertical'){
const target = (e.currentTarget as HTMLElement);
addActiveParent(target);
}
if (mode.value === 'horizontal') {
const ul = ele.parentElement?.parentElement;
ul?.classList.add('devui-menu-active-parent');
}
};
const icons = <span class="devui-menu-icon">{ctx.slots.icon?.()}</span>;
const menuItems = ref(null);
// watchEffect(()=>{
// pushItem({el: menuItems.value as HTMLElement | null});
// },{flush:'post'});
watch(disabled, ()=> {classObject['devui-menu-item-select'] = false;});
watch(defaultSelectKey, (n)=>{
isSelect.value = useInitSelect(n,key,multiple,disabled);
classObject['devui-menu-item-select'] = isSelect.value;
});
onMounted(()=>{
let oldPadding = '';
const ele = (menuItems.value) as unknown as HTMLElement;
if (mode.value === 'vertical'){
if (ele.parentElement?.parentElement?.classList.contains('devui-menu')){
isLayer1.value = true;
if (isLayer1.value){
ele.style.paddingRight = ``;
ele.style.paddingLeft = `${indent}px`;
}
watch(isCollapsed, (val)=>{
if (val){
if (ele.style.padding !== '0'){
oldPadding = ele.style.padding;
}
setTimeout(() => {
ele.style.padding = '0';
ele.style.width = '';
ele.style.textAlign = `center`;
}, 300);
ele.style.display=`block`;
} else {
ele.style.padding = `${oldPadding}`;
ele.style.textAlign = ``;
ele.style.display=`flex`;
}
});
} else {
isLayer1.value = false;
}
}
});
return () => {
return (
mode.value === 'vertical' ?
<div class='devui-menu-item-vertical-wrapper'>
<li
class = {
[
classObject,
props['disabled'] ? 'devui-menu-item-disabled' : '',
isLayer1.value ? 'layer_1' : '',
]
}
onClick={onClick}
ref={menuItems}
>
{ctx.slots.icon !== undefined &&
icons
}
{ props.href === '' ?
<Transition name='fade'>
<span v-show={!isCollapsed.value}>
{ctx.slots.default?.()}
</span>
</Transition>
:
<a href={props.href}>
<Transition name='fade'>
{ctx.slots.default?.()}
</Transition>
</a>
}
</li>
</div>
:
<li
class = {
[
classObject,
props['disabled'] ? 'devui-menu-item-disabled' : '',
isLayer1.value ? 'layer_1' : '',
]
}
onClick={onClick}
ref={menuItems}
>
{ctx.slots.icon !== undefined &&
icons
}
{ props.href === '' ?
<Transition name='fade'>
<span v-show={!isCollapsed.value}>
{ctx.slots.default?.()}
</span>
</Transition>
:
<a href={props.href}>
<Transition name='fade'>
{ctx.slots.default?.()}
</Transition>
</a>
}
</li>
);
};
},
});
Loading