refactor: menu
parent
994df6ff6f
commit
2ab77978f2
|
@ -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>;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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;
|
|
@ -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 };
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export type MenuTheme = 'light' | 'dark';
|
||||
|
||||
// ========================== Basic ==========================
|
||||
export type MenuMode = 'horizontal' | 'vertical' | 'inline';
|
||||
|
||||
|
|
|
@ -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
2
v2-doc
|
@ -1 +1 @@
|
|||
Subproject commit a7013ae87f69dcbcf547f4b023255b8a7a775557
|
||||
Subproject commit d197053285b81e77718621c0b5b94cb3b21831a2
|
Loading…
Reference in New Issue