refactor: menu
parent
f3db7548b5
commit
24efefe16d
|
@ -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 Transition from 'ant-design-vue/es/_util/transition';
|
||||||
import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext';
|
import { useInjectMenu, MenuContextProvider } from './hooks/useMenuContext';
|
||||||
import { MenuMode } from './interface';
|
import { MenuMode } from './interface';
|
||||||
|
@ -14,7 +14,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const fixedMode: MenuMode = 'inline';
|
const fixedMode: MenuMode = 'inline';
|
||||||
const { prefixCls, forceSubMenuRender, motion, mode, defaultMotions } = useInjectMenu();
|
const { forceSubMenuRender, motion, mode, defaultMotions } = useInjectMenu();
|
||||||
const sameModeRef = computed(() => mode.value === fixedMode);
|
const sameModeRef = computed(() => mode.value === fixedMode);
|
||||||
const destroy = ref(!sameModeRef.value);
|
const destroy = ref(!sameModeRef.value);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,14 @@ import {
|
||||||
import shallowEqual from '../../_util/shallowequal';
|
import shallowEqual from '../../_util/shallowequal';
|
||||||
import useProvideMenu, { StoreMenuInfo, useProvideFirstLevel } from './hooks/useMenuContext';
|
import useProvideMenu, { StoreMenuInfo, useProvideFirstLevel } from './hooks/useMenuContext';
|
||||||
import useConfigInject from '../../_util/hooks/useConfigInject';
|
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 devWarning from 'ant-design-vue/es/vc-util/devWarning';
|
||||||
import { collapseMotion, CSSMotionProps } from 'ant-design-vue/es/_util/transition';
|
import { collapseMotion, CSSMotionProps } from 'ant-design-vue/es/_util/transition';
|
||||||
|
|
||||||
|
@ -24,6 +31,9 @@ export const menuProps = {
|
||||||
inlineCollapsed: Boolean,
|
inlineCollapsed: Boolean,
|
||||||
overflowDisabled: Boolean,
|
overflowDisabled: Boolean,
|
||||||
openKeys: Array,
|
openKeys: Array,
|
||||||
|
selectedKeys: Array,
|
||||||
|
selectable: Boolean,
|
||||||
|
multiple: Boolean,
|
||||||
|
|
||||||
motion: Object as PropType<CSSMotionProps>,
|
motion: Object as PropType<CSSMotionProps>,
|
||||||
|
|
||||||
|
@ -46,7 +56,7 @@ export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'AMenu',
|
name: 'AMenu',
|
||||||
props: menuProps,
|
props: menuProps,
|
||||||
emits: ['update:openKeys', 'openChange'],
|
emits: ['update:openKeys', 'openChange', 'select', 'deselect', 'update:selectedKeys'],
|
||||||
setup(props, { slots, emit }) {
|
setup(props, { slots, emit }) {
|
||||||
const { prefixCls, direction } = useConfigInject('menu', props);
|
const { prefixCls, direction } = useConfigInject('menu', props);
|
||||||
const store = reactive<Record<string, StoreMenuInfo>>({});
|
const store = reactive<Record<string, StoreMenuInfo>>({});
|
||||||
|
@ -81,7 +91,48 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeKeys = ref([]);
|
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([]);
|
const mergedOpenKeys = ref([]);
|
||||||
|
|
||||||
|
@ -201,7 +252,7 @@ export default defineComponent({
|
||||||
prefixCls,
|
prefixCls,
|
||||||
activeKeys,
|
activeKeys,
|
||||||
openKeys: mergedOpenKeys,
|
openKeys: mergedOpenKeys,
|
||||||
selectedKeys,
|
selectedKeys: mergedSelectedKeys,
|
||||||
changeActiveKeys,
|
changeActiveKeys,
|
||||||
disabled,
|
disabled,
|
||||||
rtl: isRtl,
|
rtl: isRtl,
|
||||||
|
@ -219,6 +270,7 @@ export default defineComponent({
|
||||||
motion: computed(() => (isMounted.value ? props.motion : null)),
|
motion: computed(() => (isMounted.value ? props.motion : null)),
|
||||||
overflowDisabled: computed(() => props.overflowDisabled),
|
overflowDisabled: computed(() => props.overflowDisabled),
|
||||||
onOpenChange: onInternalOpenChange,
|
onOpenChange: onInternalOpenChange,
|
||||||
|
onItemClick: triggerSelection,
|
||||||
registerMenuInfo,
|
registerMenuInfo,
|
||||||
unRegisterMenuInfo,
|
unRegisterMenuInfo,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useInjectKeyPath } from './hooks/useKeyPath';
|
||||||
import { useInjectFirstLevel, useInjectMenu } from './hooks/useMenuContext';
|
import { useInjectFirstLevel, useInjectMenu } from './hooks/useMenuContext';
|
||||||
import { cloneElement } from '../../_util/vnode';
|
import { cloneElement } from '../../_util/vnode';
|
||||||
import Tooltip from '../../tooltip';
|
import Tooltip from '../../tooltip';
|
||||||
|
import { MenuInfo } from './interface';
|
||||||
|
|
||||||
let indexGuid = 0;
|
let indexGuid = 0;
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ export default defineComponent({
|
||||||
title: { type: [String, Boolean] },
|
title: { type: [String, Boolean] },
|
||||||
icon: PropTypes.VNodeChild,
|
icon: PropTypes.VNodeChild,
|
||||||
},
|
},
|
||||||
emits: ['mouseenter', 'mouseleave'],
|
emits: ['mouseenter', 'mouseleave', 'click'],
|
||||||
slots: ['icon'],
|
slots: ['icon'],
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
setup(props, { slots, emit, attrs }) {
|
setup(props, { slots, emit, attrs }) {
|
||||||
|
@ -34,6 +35,7 @@ export default defineComponent({
|
||||||
rtl,
|
rtl,
|
||||||
inlineCollapsed,
|
inlineCollapsed,
|
||||||
siderCollapsed,
|
siderCollapsed,
|
||||||
|
onItemClick,
|
||||||
} = useInjectMenu();
|
} = useInjectMenu();
|
||||||
const firstLevel = useInjectFirstLevel();
|
const firstLevel = useInjectFirstLevel();
|
||||||
const isActive = ref(false);
|
const isActive = ref(false);
|
||||||
|
@ -56,6 +58,27 @@ export default defineComponent({
|
||||||
[`${itemCls}-disabled`]: mergedDisabled.value,
|
[`${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) => {
|
const onMouseEnter = (event: MouseEvent) => {
|
||||||
if (!mergedDisabled.value) {
|
if (!mergedDisabled.value) {
|
||||||
changeActiveKeys([...parentEventKeys.value, key]);
|
changeActiveKeys([...parentEventKeys.value, key]);
|
||||||
|
@ -135,6 +158,7 @@ export default defineComponent({
|
||||||
{...optionRoleProps}
|
{...optionRoleProps}
|
||||||
onMouseenter={onMouseEnter}
|
onMouseenter={onMouseEnter}
|
||||||
onMouseleave={onMouseLeave}
|
onMouseleave={onMouseLeave}
|
||||||
|
onClick={onInternalClick}
|
||||||
title={typeof title === 'string' ? title : undefined}
|
title={typeof title === 'string' ? title : undefined}
|
||||||
>
|
>
|
||||||
{cloneElement(icon, {
|
{cloneElement(icon, {
|
||||||
|
|
|
@ -9,7 +9,13 @@ import {
|
||||||
Ref,
|
Ref,
|
||||||
UnwrapRef,
|
UnwrapRef,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { BuiltinPlacements, MenuMode, MenuTheme, TriggerSubMenuAction } from '../interface';
|
import {
|
||||||
|
BuiltinPlacements,
|
||||||
|
MenuClickEventHandler,
|
||||||
|
MenuMode,
|
||||||
|
MenuTheme,
|
||||||
|
TriggerSubMenuAction,
|
||||||
|
} from '../interface';
|
||||||
import { CSSMotionProps } from '../../../_util/transition';
|
import { CSSMotionProps } from '../../../_util/transition';
|
||||||
|
|
||||||
export interface StoreMenuInfo {
|
export interface StoreMenuInfo {
|
||||||
|
@ -77,7 +83,7 @@ export interface MenuContextProps {
|
||||||
// expandIcon?: RenderIconType;
|
// expandIcon?: RenderIconType;
|
||||||
|
|
||||||
// // Function
|
// // Function
|
||||||
// onItemClick: MenuClickEventHandler;
|
onItemClick: MenuClickEventHandler;
|
||||||
onOpenChange: (key: Key, open: boolean) => void;
|
onOpenChange: (key: Key, open: boolean) => void;
|
||||||
getPopupContainer: ComputedRef<(node: HTMLElement) => HTMLElement>;
|
getPopupContainer: ComputedRef<(node: HTMLElement) => HTMLElement>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Key } from 'ant-design-vue/es/_util/type';
|
||||||
|
|
||||||
export type MenuTheme = 'light' | 'dark';
|
export type MenuTheme = 'light' | 'dark';
|
||||||
|
|
||||||
// ========================== Basic ==========================
|
// ========================== Basic ==========================
|
||||||
|
@ -17,22 +19,24 @@ export interface RenderIconInfo {
|
||||||
export type RenderIconType = (props: RenderIconInfo) => any;
|
export type RenderIconType = (props: RenderIconInfo) => any;
|
||||||
|
|
||||||
export interface MenuInfo {
|
export interface MenuInfo {
|
||||||
key: string;
|
key: Key;
|
||||||
keyPath: string[];
|
eventKey: string;
|
||||||
|
keyPath?: string[];
|
||||||
|
eventKeyPath: Key[];
|
||||||
domEvent: MouseEvent | KeyboardEvent;
|
domEvent: MouseEvent | KeyboardEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MenuTitleInfo {
|
export interface MenuTitleInfo {
|
||||||
key: string;
|
key: Key;
|
||||||
domEvent: MouseEvent | KeyboardEvent;
|
domEvent: MouseEvent | KeyboardEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================== Hover ==========================
|
// ========================== Hover ==========================
|
||||||
export type MenuHoverEventHandler = (info: { key: string; domEvent: MouseEvent }) => void;
|
export type MenuHoverEventHandler = (info: { key: Key; domEvent: MouseEvent }) => void;
|
||||||
|
|
||||||
// ======================== Selection ========================
|
// ======================== Selection ========================
|
||||||
export interface SelectInfo extends MenuInfo {
|
export interface SelectInfo extends MenuInfo {
|
||||||
selectedKeys: string[];
|
selectedKeys: Key[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SelectEventHandler = (info: SelectInfo) => void;
|
export type SelectEventHandler = (info: SelectInfo) => void;
|
||||||
|
|
2
v2-doc
2
v2-doc
|
@ -1 +1 @@
|
||||||
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
|
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2
|
Loading…
Reference in New Issue