refactor: menu

feat-new-menu
tangjinzhou 2021-05-19 23:38:56 +08:00
parent f3db7548b5
commit 24efefe16d
6 changed files with 101 additions and 15 deletions

View File

@ -1,4 +1,4 @@
import { computed, defineComponent, reactive, ref, watch } from '@vue/runtime-core';
import { computed, defineComponent, ref, watch } from '@vue/runtime-core';
import Transition from 'ant-design-vue/es/_util/transition';
import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext';
import { MenuMode } from './interface';
@ -14,7 +14,7 @@ export default defineComponent({
},
setup(props, { slots }) {
const fixedMode: MenuMode = 'inline';
const { prefixCls, forceSubMenuRender, motion, mode, defaultMotions } = useInjectMenu();
const { forceSubMenuRender, motion, mode, defaultMotions } = useInjectMenu();
const sameModeRef = computed(() => mode.value === fixedMode);
const destroy = ref(!sameModeRef.value);

View File

@ -14,7 +14,14 @@ import {
import shallowEqual from '../../_util/shallowequal';
import useProvideMenu, { StoreMenuInfo, useProvideFirstLevel } from './hooks/useMenuContext';
import useConfigInject from '../../_util/hooks/useConfigInject';
import { MenuTheme, MenuMode, BuiltinPlacements, TriggerSubMenuAction } from './interface';
import {
MenuTheme,
MenuMode,
BuiltinPlacements,
TriggerSubMenuAction,
MenuInfo,
SelectInfo,
} from './interface';
import devWarning from 'ant-design-vue/es/vc-util/devWarning';
import { collapseMotion, CSSMotionProps } from 'ant-design-vue/es/_util/transition';
@ -24,6 +31,9 @@ export const menuProps = {
inlineCollapsed: Boolean,
overflowDisabled: Boolean,
openKeys: Array,
selectedKeys: Array,
selectable: Boolean,
multiple: Boolean,
motion: Object as PropType<CSSMotionProps>,
@ -46,7 +56,7 @@ export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
export default defineComponent({
name: 'AMenu',
props: menuProps,
emits: ['update:openKeys', 'openChange'],
emits: ['update:openKeys', 'openChange', 'select', 'deselect', 'update:selectedKeys'],
setup(props, { slots, emit }) {
const { prefixCls, direction } = useConfigInject('menu', props);
const store = reactive<Record<string, StoreMenuInfo>>({});
@ -81,7 +91,48 @@ export default defineComponent({
});
const activeKeys = ref([]);
const selectedKeys = ref([]);
const mergedSelectedKeys = ref([]);
watch(
() => props.selectedKeys,
(selectedKeys = mergedSelectedKeys.value) => {
mergedSelectedKeys.value = selectedKeys;
},
{ immediate: true },
);
// >>>>> Trigger select
const triggerSelection = (info: MenuInfo) => {
if (!props.selectable) {
return;
}
// Insert or Remove
const { key: targetKey } = info;
const exist = mergedSelectedKeys.value.includes(targetKey);
let newSelectedKeys: Key[];
if (exist) {
newSelectedKeys = mergedSelectedKeys.value.filter(key => key !== targetKey);
} else if (props.multiple) {
newSelectedKeys = [...mergedSelectedKeys.value, targetKey];
} else {
newSelectedKeys = [targetKey];
}
mergedSelectedKeys.value = newSelectedKeys;
// Trigger event
const selectInfo: SelectInfo = {
...info,
selectedKeys: newSelectedKeys,
};
if (exist) {
emit('deselect', selectInfo);
} else {
emit('select', selectInfo);
}
};
const mergedOpenKeys = ref([]);
@ -201,7 +252,7 @@ export default defineComponent({
prefixCls,
activeKeys,
openKeys: mergedOpenKeys,
selectedKeys,
selectedKeys: mergedSelectedKeys,
changeActiveKeys,
disabled,
rtl: isRtl,
@ -219,6 +270,7 @@ export default defineComponent({
motion: computed(() => (isMounted.value ? props.motion : null)),
overflowDisabled: computed(() => props.overflowDisabled),
onOpenChange: onInternalOpenChange,
onItemClick: triggerSelection,
registerMenuInfo,
unRegisterMenuInfo,
});

View File

@ -5,6 +5,7 @@ import { useInjectKeyPath } from './hooks/useKeyPath';
import { useInjectFirstLevel, useInjectMenu } from './hooks/useMenuContext';
import { cloneElement } from '../../_util/vnode';
import Tooltip from '../../tooltip';
import { MenuInfo } from './interface';
let indexGuid = 0;
@ -17,7 +18,7 @@ export default defineComponent({
title: { type: [String, Boolean] },
icon: PropTypes.VNodeChild,
},
emits: ['mouseenter', 'mouseleave'],
emits: ['mouseenter', 'mouseleave', 'click'],
slots: ['icon'],
inheritAttrs: false,
setup(props, { slots, emit, attrs }) {
@ -34,6 +35,7 @@ export default defineComponent({
rtl,
inlineCollapsed,
siderCollapsed,
onItemClick,
} = useInjectMenu();
const firstLevel = useInjectFirstLevel();
const isActive = ref(false);
@ -56,6 +58,27 @@ export default defineComponent({
[`${itemCls}-disabled`]: mergedDisabled.value,
};
});
const getEventInfo = (e: MouseEvent): MenuInfo => {
return {
key: key,
eventKey: eventKey,
eventKeyPath: [...parentEventKeys.value, key],
domEvent: e,
};
};
// ============================ Events ============================
const onInternalClick = (e: MouseEvent) => {
if (mergedDisabled.value) {
return;
}
const info = getEventInfo(e);
emit('click', e);
onItemClick(info);
};
const onMouseEnter = (event: MouseEvent) => {
if (!mergedDisabled.value) {
changeActiveKeys([...parentEventKeys.value, key]);
@ -135,6 +158,7 @@ export default defineComponent({
{...optionRoleProps}
onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave}
onClick={onInternalClick}
title={typeof title === 'string' ? title : undefined}
>
{cloneElement(icon, {

View File

@ -9,7 +9,13 @@ import {
Ref,
UnwrapRef,
} from 'vue';
import { BuiltinPlacements, MenuMode, MenuTheme, TriggerSubMenuAction } from '../interface';
import {
BuiltinPlacements,
MenuClickEventHandler,
MenuMode,
MenuTheme,
TriggerSubMenuAction,
} from '../interface';
import { CSSMotionProps } from '../../../_util/transition';
export interface StoreMenuInfo {
@ -77,7 +83,7 @@ export interface MenuContextProps {
// expandIcon?: RenderIconType;
// // Function
// onItemClick: MenuClickEventHandler;
onItemClick: MenuClickEventHandler;
onOpenChange: (key: Key, open: boolean) => void;
getPopupContainer: ComputedRef<(node: HTMLElement) => HTMLElement>;
}

View File

@ -1,3 +1,5 @@
import { Key } from 'ant-design-vue/es/_util/type';
export type MenuTheme = 'light' | 'dark';
// ========================== Basic ==========================
@ -17,22 +19,24 @@ export interface RenderIconInfo {
export type RenderIconType = (props: RenderIconInfo) => any;
export interface MenuInfo {
key: string;
keyPath: string[];
key: Key;
eventKey: string;
keyPath?: string[];
eventKeyPath: Key[];
domEvent: MouseEvent | KeyboardEvent;
}
export interface MenuTitleInfo {
key: string;
key: Key;
domEvent: MouseEvent | KeyboardEvent;
}
// ========================== Hover ==========================
export type MenuHoverEventHandler = (info: { key: string; domEvent: MouseEvent }) => void;
export type MenuHoverEventHandler = (info: { key: Key; domEvent: MouseEvent }) => void;
// ======================== Selection ========================
export interface SelectInfo extends MenuInfo {
selectedKeys: string[];
selectedKeys: Key[];
}
export type SelectEventHandler = (info: SelectInfo) => void;

2
v2-doc

@ -1 +1 @@
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2