Browse Source

refactor: menu

feat-new-menu
tangjinzhou 4 years ago
parent
commit
2ab77978f2
  1. 35
      components/menu/src/Menu.tsx
  2. 66
      components/menu/src/MenuItem.tsx
  3. 2
      components/menu/src/SubMenu.tsx
  4. 23
      components/menu/src/hooks/useKeyPath.ts
  5. 15
      components/menu/src/hooks/useMenuContext.ts
  6. 2
      components/menu/src/interface.ts
  7. 1
      examples/App.vue
  8. 2
      v2-doc

35
components/menu/src/Menu.tsx

@ -1,9 +1,14 @@
import usePrefixCls from 'ant-design-vue/es/_util/hooks/usePrefixCls';
import { defineComponent, ExtractPropTypes } from 'vue';
import { Key } from '../../_util/type';
import { computed, defineComponent, ExtractPropTypes, ref, PropType } from 'vue';
import useProvideMenu from './hooks/useMenuContext';
import useConfigInject from '../../_util/hooks/useConfigInject';
import { MenuTheme, MenuMode } from './interface';
export const menuProps = {
prefixCls: String,
disabled: Boolean,
theme: { type: String as PropType<MenuTheme>, default: 'light' },
mode: { type: String as PropType<MenuMode>, default: 'vertical' },
};
export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
@ -12,10 +17,30 @@ export default defineComponent({
name: 'AMenu',
props: menuProps,
setup(props, { slots }) {
const prefixCls = usePrefixCls('menu', props);
useProvideMenu({ prefixCls });
const { prefixCls, direction } = useConfigInject('menu', props);
const activeKeys = ref([]);
const openKeys = ref([]);
const selectedKeys = ref([]);
const changeActiveKeys = (keys: Key[]) => {
activeKeys.value = keys;
};
const disabled = computed(() => !!props.disabled);
useProvideMenu({ prefixCls, activeKeys, openKeys, selectedKeys, changeActiveKeys, disabled });
const isRtl = computed(() => direction.value === 'rtl');
const mergedMode = ref('vertical');
const mergedInlineCollapsed = ref(false);
const className = computed(() => {
return {
[`${prefixCls.value}`]: true,
[`${prefixCls.value}-root`]: true,
[`${prefixCls.value}-${mergedMode.value}`]: true,
[`${prefixCls.value}-inline-collapsed`]: mergedInlineCollapsed.value,
[`${prefixCls.value}-rtl`]: isRtl.value,
[`${prefixCls.value}-${props.theme}`]: true,
};
});
return () => {
return <div>{slots.default?.()}</div>;
return <ul class={className.value}>{slots.default?.()}</ul>;
};
},
});

66
components/menu/src/MenuItem.tsx

@ -1,16 +1,76 @@
import { defineComponent, getCurrentInstance } from 'vue';
import { computed, defineComponent, getCurrentInstance, ref, watch } from 'vue';
import { useInjectKeyPath } from './hooks/useKeyPath';
import { useInjectMenu } from './hooks/useMenuContext';
let indexGuid = 0;
export default defineComponent({
name: 'AMenuItem',
setup(props, { slots }) {
props: {
role: String,
disabled: Boolean,
},
emits: ['mouseenter', 'mouseleave'],
setup(props, { slots, emit }) {
const instance = getCurrentInstance();
const key = instance.vnode.key;
const uniKey = `menu_item_${++indexGuid}`;
const parentKeys = useInjectKeyPath();
console.log(parentKeys.value);
const { prefixCls, activeKeys, disabled, changeActiveKeys } = useInjectMenu();
const isActive = ref(false);
watch(
activeKeys,
() => {
isActive.value = !!activeKeys.value.find(val => val === key);
},
{ immediate: true },
);
const mergedDisabled = computed(() => disabled.value || props.disabled);
const selected = computed(() => false);
const classNames = computed(() => {
const itemCls = `${prefixCls.value}-item`;
return {
[`${itemCls}`]: true,
[`${itemCls}-active`]: isActive.value,
[`${itemCls}-selected`]: selected.value,
[`${itemCls}-disabled`]: mergedDisabled.value,
};
});
const onMouseEnter = (event: MouseEvent) => {
if (!mergedDisabled.value) {
changeActiveKeys([...parentKeys.value, key]);
emit('mouseenter', event);
}
};
const onMouseLeave = (event: MouseEvent) => {
if (!mergedDisabled.value) {
changeActiveKeys([]);
emit('mouseleave', event);
}
};
return () => {
return <li>{slots.default?.()}</li>;
// ============================ Render ============================
const optionRoleProps = {};
if (props.role === 'option') {
optionRoleProps['aria-selected'] = selected.value;
}
return (
<li
class={classNames.value}
role={props.role || 'menuitem'}
tabindex={props.disabled ? null : -1}
data-menu-id={key}
aria-disabled={props.disabled}
{...optionRoleProps}
onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave}
>
{slots.default?.()}
</li>
);
};
},
});

2
components/menu/src/SubMenu.tsx

@ -1,8 +1,10 @@
import { defineComponent } from 'vue';
import useProvideKeyPath from './hooks/useKeyPath';
export default defineComponent({
name: 'ASubMenu',
setup(props, { slots }) {
useProvideKeyPath();
return () => {
return <ul>{slots.default?.()}</ul>;
};

23
components/menu/src/hooks/useKeyPath.ts

@ -0,0 +1,23 @@
import { Key } from '../../../_util/type';
import { computed, ComputedRef, getCurrentInstance, inject, InjectionKey, provide } from 'vue';
const KeyPathContext: InjectionKey<ComputedRef<Key[]>> = Symbol('KeyPathContext');
const useInjectKeyPath = () => {
return inject(
KeyPathContext,
computed(() => []),
);
};
const useProvideKeyPath = () => {
const parentKeys = useInjectKeyPath();
const key = getCurrentInstance().vnode.key;
const keys = computed(() => [...parentKeys.value, key]);
provide(KeyPathContext, keys);
return keys;
};
export { useProvideKeyPath, useInjectKeyPath, KeyPathContext };
export default useProvideKeyPath;

15
components/menu/src/hooks/useMenuContext.ts

@ -1,4 +1,5 @@
import { computed, ComputedRef, inject, InjectionKey, provide } from 'vue';
import { Key } from '../../../_util/type';
import { ComputedRef, inject, InjectionKey, provide, Ref } from 'vue';
// import {
// BuiltinPlacements,
@ -10,19 +11,21 @@ import { computed, ComputedRef, inject, InjectionKey, provide } from 'vue';
export interface MenuContextProps {
prefixCls: ComputedRef<string>;
// openKeys: string[];
openKeys: Ref<Key[]>;
selectedKeys: Ref<Key[]>;
// rtl?: boolean;
// // Mode
// mode: MenuMode;
// // Disabled
// disabled?: boolean;
disabled?: ComputedRef<boolean>;
// // Used for overflow only. Prevent hidden node trigger open
// overflowDisabled?: boolean;
// // Active
// activeKey: string;
activeKeys: Ref<Key[]>;
changeActiveKeys: (keys: Key[]) => void;
// onActive: (key: string) => void;
// onInactive: (key: string) => void;
@ -60,9 +63,7 @@ const useProvideMenu = (props: MenuContextProps) => {
};
const useInjectMenu = () => {
return inject(MenuContextKey, {
prefixCls: computed(() => 'ant'),
});
return inject(MenuContextKey);
};
export { useProvideMenu, MenuContextKey, useInjectMenu };

2
components/menu/src/interface.ts

@ -1,3 +1,5 @@
export type MenuTheme = 'light' | 'dark';
// ========================== Basic ==========================
export type MenuMode = 'horizontal' | 'vertical' | 'inline';

1
examples/App.vue

@ -7,6 +7,7 @@
mode="inline"
@click="handleClick"
>
<a-menu-item key="0">Option 0</a-menu-item>
<a-sub-menu key="sub1" @titleClick="titleClick">
<template #title>
<span>

2
v2-doc

@ -1 +1 @@
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2
Loading…
Cancel
Save