refactor: menu
parent
ab4478c9f4
commit
994df6ff6f
|
@ -1,322 +1,22 @@
|
|||
import { defineComponent, inject, provide, toRef, App, ExtractPropTypes, Plugin } from 'vue';
|
||||
import omit from 'omit.js';
|
||||
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
|
||||
import SubMenu from './SubMenu';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import animation from '../_util/openAnimation';
|
||||
import warning from '../_util/warning';
|
||||
import Item from './MenuItem';
|
||||
import { hasProp, getOptionProps } from '../_util/props-util';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import commonPropsType from '../vc-menu/commonPropsType';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { SiderContextProps } from '../layout/Sider';
|
||||
import { tuple } from '../_util/type';
|
||||
// import raf from '../_util/raf';
|
||||
|
||||
export const MenuMode = PropTypes.oneOf([
|
||||
'vertical',
|
||||
'vertical-left',
|
||||
'vertical-right',
|
||||
'horizontal',
|
||||
'inline',
|
||||
]);
|
||||
|
||||
export const menuProps = {
|
||||
...commonPropsType,
|
||||
theme: PropTypes.oneOf(tuple('light', 'dark')).def('light'),
|
||||
mode: MenuMode.def('vertical'),
|
||||
selectable: PropTypes.looseBool,
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
openKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
defaultOpenKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
openTransitionName: PropTypes.string,
|
||||
prefixCls: PropTypes.string,
|
||||
multiple: PropTypes.looseBool,
|
||||
inlineIndent: PropTypes.number.def(24),
|
||||
inlineCollapsed: PropTypes.looseBool,
|
||||
isRootMenu: PropTypes.looseBool.def(true),
|
||||
focusable: PropTypes.looseBool.def(false),
|
||||
onOpenChange: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
onDeselect: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
onMouseenter: PropTypes.func,
|
||||
onSelectChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
|
||||
|
||||
const Menu = defineComponent({
|
||||
name: 'AMenu',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props: menuProps,
|
||||
Divider: { ...Divider, name: 'AMenuDivider' },
|
||||
Item: { ...Item, name: 'AMenuItem' },
|
||||
SubMenu: { ...SubMenu, name: 'ASubMenu' },
|
||||
ItemGroup: { ...ItemGroup, name: 'AMenuItemGroup' },
|
||||
emits: [
|
||||
'update:selectedKeys',
|
||||
'update:openKeys',
|
||||
'mouseenter',
|
||||
'openChange',
|
||||
'click',
|
||||
'selectChange',
|
||||
'select',
|
||||
'deselect',
|
||||
],
|
||||
setup() {
|
||||
const layoutSiderContext = inject<SiderContextProps>('layoutSiderContext', {});
|
||||
const layoutSiderCollapsed = toRef(layoutSiderContext, 'sCollapsed');
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
layoutSiderContext,
|
||||
layoutSiderCollapsed,
|
||||
propsUpdating: false,
|
||||
switchingModeFromInline: false,
|
||||
leaveAnimationExecutedWhenInlineCollapsed: false,
|
||||
inlineOpenKeys: [],
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const props: MenuProps = getOptionProps(this);
|
||||
warning(
|
||||
!('inlineCollapsed' in props && props.mode !== 'inline'),
|
||||
'Menu',
|
||||
"`inlineCollapsed` should only be used when Menu's `mode` is inline.",
|
||||
);
|
||||
let sOpenKeys: (number | string)[];
|
||||
|
||||
if ('openKeys' in props) {
|
||||
sOpenKeys = props.openKeys;
|
||||
} else if ('defaultOpenKeys' in props) {
|
||||
sOpenKeys = props.defaultOpenKeys;
|
||||
}
|
||||
return {
|
||||
sOpenKeys,
|
||||
};
|
||||
},
|
||||
// beforeUnmount() {
|
||||
// raf.cancel(this.mountRafId);
|
||||
// },
|
||||
watch: {
|
||||
mode(val, oldVal) {
|
||||
if (oldVal === 'inline' && val !== 'inline') {
|
||||
this.switchingModeFromInline = true;
|
||||
}
|
||||
},
|
||||
openKeys(val) {
|
||||
this.setState({ sOpenKeys: val });
|
||||
},
|
||||
inlineCollapsed(val) {
|
||||
this.collapsedChange(val);
|
||||
},
|
||||
layoutSiderCollapsed(val) {
|
||||
this.collapsedChange(val);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
provide('getInlineCollapsed', this.getInlineCollapsed);
|
||||
provide('menuPropsContext', this.$props);
|
||||
},
|
||||
updated() {
|
||||
this.propsUpdating = false;
|
||||
},
|
||||
methods: {
|
||||
collapsedChange(val: unknown) {
|
||||
if (this.propsUpdating) {
|
||||
return;
|
||||
}
|
||||
this.propsUpdating = true;
|
||||
if (!hasProp(this, 'openKeys')) {
|
||||
if (val) {
|
||||
this.switchingModeFromInline = true;
|
||||
this.inlineOpenKeys = this.sOpenKeys;
|
||||
this.setState({ sOpenKeys: [] });
|
||||
} else {
|
||||
this.setState({ sOpenKeys: this.inlineOpenKeys });
|
||||
this.inlineOpenKeys = [];
|
||||
}
|
||||
} else if (val) {
|
||||
// 缩起时,openKeys置为空的动画会闪动,react可以通过是否传递openKeys避免闪动,vue不是很方便动态传递openKeys
|
||||
this.switchingModeFromInline = true;
|
||||
}
|
||||
},
|
||||
restoreModeVerticalFromInline() {
|
||||
if (this.switchingModeFromInline) {
|
||||
this.switchingModeFromInline = false;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
// Restore vertical mode when menu is collapsed responsively when mounted
|
||||
// https://github.com/ant-design/ant-design/issues/13104
|
||||
// TODO: not a perfect solution, looking a new way to avoid setting switchingModeFromInline in this situation
|
||||
handleMouseEnter(e: Event) {
|
||||
this.restoreModeVerticalFromInline();
|
||||
this.$emit('mouseenter', e);
|
||||
},
|
||||
handleTransitionEnd(e: TransitionEvent) {
|
||||
// when inlineCollapsed menu width animation finished
|
||||
// https://github.com/ant-design/ant-design/issues/12864
|
||||
const widthCollapsed = e.propertyName === 'width' && e.target === e.currentTarget;
|
||||
|
||||
// Fix SVGElement e.target.className.indexOf is not a function
|
||||
// https://github.com/ant-design/ant-design/issues/15699
|
||||
const { className } = e.target as SVGAnimationElement | HTMLElement;
|
||||
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
|
||||
const classNameValue =
|
||||
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'
|
||||
? className.animVal
|
||||
: className;
|
||||
|
||||
// Fix for <Menu style={{ width: '100%' }} />, the width transition won't trigger when menu is collapsed
|
||||
// https://github.com/ant-design/ant-design-pro/issues/2783
|
||||
const iconScaled = e.propertyName === 'font-size' && classNameValue.indexOf('anticon') >= 0;
|
||||
|
||||
if (widthCollapsed || iconScaled) {
|
||||
this.restoreModeVerticalFromInline();
|
||||
}
|
||||
},
|
||||
handleClick(e: Event) {
|
||||
this.handleOpenChange([]);
|
||||
this.$emit('click', e);
|
||||
},
|
||||
handleSelect(info) {
|
||||
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||
this.$emit('select', info);
|
||||
this.$emit('selectChange', info.selectedKeys);
|
||||
},
|
||||
handleDeselect(info) {
|
||||
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||
this.$emit('deselect', info);
|
||||
this.$emit('selectChange', info.selectedKeys);
|
||||
},
|
||||
handleOpenChange(openKeys: (number | string)[]) {
|
||||
this.setOpenKeys(openKeys);
|
||||
this.$emit('update:openKeys', openKeys);
|
||||
this.$emit('openChange', openKeys);
|
||||
},
|
||||
setOpenKeys(openKeys: (number | string)[]) {
|
||||
if (!hasProp(this, 'openKeys')) {
|
||||
this.setState({ sOpenKeys: openKeys });
|
||||
}
|
||||
},
|
||||
getRealMenuMode() {
|
||||
const inlineCollapsed = this.getInlineCollapsed();
|
||||
if (this.switchingModeFromInline && inlineCollapsed) {
|
||||
return 'inline';
|
||||
}
|
||||
const { mode } = this.$props;
|
||||
return inlineCollapsed ? 'vertical' : mode;
|
||||
},
|
||||
getInlineCollapsed() {
|
||||
const { inlineCollapsed } = this.$props;
|
||||
if (this.layoutSiderContext.sCollapsed !== undefined) {
|
||||
return this.layoutSiderContext.sCollapsed;
|
||||
}
|
||||
return inlineCollapsed;
|
||||
},
|
||||
getMenuOpenAnimation(menuMode: string) {
|
||||
const { openAnimation, openTransitionName } = this.$props;
|
||||
let menuOpenAnimation = openAnimation || openTransitionName;
|
||||
if (openAnimation === undefined && openTransitionName === undefined) {
|
||||
if (menuMode === 'horizontal') {
|
||||
menuOpenAnimation = 'slide-up';
|
||||
} else if (menuMode === 'inline') {
|
||||
menuOpenAnimation = animation;
|
||||
} else {
|
||||
// When mode switch from inline
|
||||
// submenu should hide without animation
|
||||
if (this.switchingModeFromInline) {
|
||||
menuOpenAnimation = '';
|
||||
this.switchingModeFromInline = false;
|
||||
} else {
|
||||
menuOpenAnimation = 'zoom-big';
|
||||
}
|
||||
}
|
||||
}
|
||||
return menuOpenAnimation;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { layoutSiderContext } = this;
|
||||
const { collapsedWidth } = layoutSiderContext;
|
||||
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||
const props = getOptionProps(this);
|
||||
const { prefixCls: customizePrefixCls, theme, getPopupContainer } = props;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('menu', customizePrefixCls);
|
||||
const menuMode = this.getRealMenuMode();
|
||||
const menuOpenAnimation = this.getMenuOpenAnimation(menuMode);
|
||||
const { class: className, ...otherAttrs } = this.$attrs;
|
||||
const menuClassName = {
|
||||
[className as string]: className,
|
||||
[`${prefixCls}-${theme}`]: true,
|
||||
[`${prefixCls}-inline-collapsed`]: this.getInlineCollapsed(),
|
||||
};
|
||||
|
||||
const menuProps = {
|
||||
...omit(props, [
|
||||
'inlineCollapsed',
|
||||
'onUpdate:selectedKeys',
|
||||
'onUpdate:openKeys',
|
||||
'onSelectChange',
|
||||
]),
|
||||
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
||||
openKeys: this.sOpenKeys,
|
||||
mode: menuMode,
|
||||
prefixCls,
|
||||
...otherAttrs,
|
||||
onSelect: this.handleSelect,
|
||||
onDeselect: this.handleDeselect,
|
||||
onOpenChange: this.handleOpenChange,
|
||||
onMouseenter: this.handleMouseEnter,
|
||||
onTransitionend: this.handleTransitionEnd,
|
||||
// children: getSlot(this),
|
||||
};
|
||||
if (!hasProp(this, 'selectedKeys')) {
|
||||
delete menuProps.selectedKeys;
|
||||
}
|
||||
|
||||
if (menuMode !== 'inline') {
|
||||
// closing vertical popup submenu after click it
|
||||
menuProps.onClick = this.handleClick;
|
||||
menuProps.openTransitionName = menuOpenAnimation;
|
||||
} else {
|
||||
menuProps.onClick = (e: Event) => {
|
||||
this.$emit('click', e);
|
||||
};
|
||||
menuProps.openAnimation = menuOpenAnimation;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/8587
|
||||
const hideMenu =
|
||||
this.getInlineCollapsed() &&
|
||||
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
|
||||
if (hideMenu) {
|
||||
menuProps.openKeys = [];
|
||||
}
|
||||
|
||||
return <VcMenu {...menuProps} class={menuClassName} v-slots={this.$slots} />;
|
||||
},
|
||||
});
|
||||
|
||||
import Menu from './src/Menu';
|
||||
import MenuItem from './src/MenuItem';
|
||||
import SubMenu from './src/SubMenu';
|
||||
import ItemGroup from './src/ItemGroup';
|
||||
import Divider from './src/Divider';
|
||||
import { App } from 'vue';
|
||||
/* istanbul ignore next */
|
||||
Menu.install = function(app: App) {
|
||||
app.component(Menu.name, Menu);
|
||||
app.component(Menu.Item.name, Menu.Item);
|
||||
app.component(Menu.SubMenu.name, Menu.SubMenu);
|
||||
app.component(Menu.Divider.name, Menu.Divider);
|
||||
app.component(Menu.ItemGroup.name, Menu.ItemGroup);
|
||||
app.component(MenuItem.name, MenuItem);
|
||||
app.component(SubMenu.name, SubMenu);
|
||||
app.component(Divider.name, Divider);
|
||||
app.component(ItemGroup.name, ItemGroup);
|
||||
return app;
|
||||
};
|
||||
|
||||
export default Menu as typeof Menu &
|
||||
Plugin & {
|
||||
readonly Item: typeof Item;
|
||||
readonly Item: typeof MenuItem;
|
||||
readonly SubMenu: typeof SubMenu;
|
||||
readonly Divider: typeof Divider;
|
||||
readonly ItemGroup: typeof ItemGroup;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { getPropsSlot } from '../../_util/props-util';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import PropTypes from '../../_util/vue-types';
|
||||
import { useInjectMenu } from './hooks/useMenuContext';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenuItemGroup',
|
||||
props: {
|
||||
title: PropTypes.VNodeChild,
|
||||
},
|
||||
slots: ['title'],
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls } = useInjectMenu();
|
||||
const groupPrefixCls = computed(() => `${prefixCls.value}-item-group`);
|
||||
return () => {
|
||||
return (
|
||||
<li onClick={e => e.stopPropagation()} class={groupPrefixCls.value}>
|
||||
<div
|
||||
title={typeof props.title === 'string' ? props.title : undefined}
|
||||
class={`${groupPrefixCls.value}-title`}
|
||||
>
|
||||
{getPropsSlot(slots, props, 'title')}
|
||||
</div>
|
||||
<ul class={`${groupPrefixCls.value}-list`}>{slots.default?.()}</ul>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
import usePrefixCls from 'ant-design-vue/es/_util/hooks/usePrefixCls';
|
||||
import { defineComponent, ExtractPropTypes } from 'vue';
|
||||
import useProvideMenu from './hooks/useMenuContext';
|
||||
|
||||
export const menuProps = {
|
||||
prefixCls: String,
|
||||
};
|
||||
|
||||
export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenu',
|
||||
props: menuProps,
|
||||
setup(props, { slots }) {
|
||||
const prefixCls = usePrefixCls('menu', props);
|
||||
useProvideMenu({ prefixCls });
|
||||
return () => {
|
||||
return <div>{slots.default?.()}</div>;
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { defineComponent, getCurrentInstance } from 'vue';
|
||||
|
||||
let indexGuid = 0;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenuItem',
|
||||
setup(props, { slots }) {
|
||||
const instance = getCurrentInstance();
|
||||
const key = instance.vnode.key;
|
||||
const uniKey = `menu_item_${++indexGuid}`;
|
||||
|
||||
return () => {
|
||||
return <li>{slots.default?.()}</li>;
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,70 @@
|
|||
import { computed, ComputedRef, inject, InjectionKey, provide } from 'vue';
|
||||
|
||||
// import {
|
||||
// BuiltinPlacements,
|
||||
// MenuClickEventHandler,
|
||||
// MenuMode,
|
||||
// RenderIconType,
|
||||
// TriggerSubMenuAction,
|
||||
// } from '../interface';
|
||||
|
||||
export interface MenuContextProps {
|
||||
prefixCls: ComputedRef<string>;
|
||||
// openKeys: string[];
|
||||
// rtl?: boolean;
|
||||
|
||||
// // Mode
|
||||
// mode: MenuMode;
|
||||
|
||||
// // Disabled
|
||||
// disabled?: boolean;
|
||||
// // Used for overflow only. Prevent hidden node trigger open
|
||||
// overflowDisabled?: boolean;
|
||||
|
||||
// // Active
|
||||
// activeKey: string;
|
||||
// onActive: (key: string) => void;
|
||||
// onInactive: (key: string) => void;
|
||||
|
||||
// // Selection
|
||||
// selectedKeys: string[];
|
||||
|
||||
// // Level
|
||||
// inlineIndent: number;
|
||||
|
||||
// // Motion
|
||||
// // motion?: CSSMotionProps;
|
||||
// // defaultMotions?: Partial<{ [key in MenuMode | 'other']: CSSMotionProps }>;
|
||||
|
||||
// // Popup
|
||||
// subMenuOpenDelay: number;
|
||||
// subMenuCloseDelay: number;
|
||||
// forceSubMenuRender?: boolean;
|
||||
// builtinPlacements?: BuiltinPlacements;
|
||||
// triggerSubMenuAction?: TriggerSubMenuAction;
|
||||
|
||||
// // Icon
|
||||
// itemIcon?: RenderIconType;
|
||||
// expandIcon?: RenderIconType;
|
||||
|
||||
// // Function
|
||||
// onItemClick: MenuClickEventHandler;
|
||||
// onOpenChange: (key: string, open: boolean) => void;
|
||||
// getPopupContainer: (node: HTMLElement) => HTMLElement;
|
||||
}
|
||||
|
||||
const MenuContextKey: InjectionKey<MenuContextProps> = Symbol('menuContextKey');
|
||||
|
||||
const useProvideMenu = (props: MenuContextProps) => {
|
||||
provide(MenuContextKey, props);
|
||||
};
|
||||
|
||||
const useInjectMenu = () => {
|
||||
return inject(MenuContextKey, {
|
||||
prefixCls: computed(() => 'ant'),
|
||||
});
|
||||
};
|
||||
|
||||
export { useProvideMenu, MenuContextKey, useInjectMenu };
|
||||
|
||||
export default useProvideMenu;
|
|
@ -0,0 +1,39 @@
|
|||
// ========================== Basic ==========================
|
||||
export type MenuMode = 'horizontal' | 'vertical' | 'inline';
|
||||
|
||||
export type BuiltinPlacements = Record<string, any>;
|
||||
|
||||
export type TriggerSubMenuAction = 'click' | 'hover';
|
||||
|
||||
export interface RenderIconInfo {
|
||||
isSelected?: boolean;
|
||||
isOpen?: boolean;
|
||||
isSubMenu?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export type RenderIconType = (props: RenderIconInfo) => any;
|
||||
|
||||
export interface MenuInfo {
|
||||
key: string;
|
||||
keyPath: string[];
|
||||
domEvent: MouseEvent | KeyboardEvent;
|
||||
}
|
||||
|
||||
export interface MenuTitleInfo {
|
||||
key: string;
|
||||
domEvent: MouseEvent | KeyboardEvent;
|
||||
}
|
||||
|
||||
// ========================== Hover ==========================
|
||||
export type MenuHoverEventHandler = (info: { key: string; domEvent: MouseEvent }) => void;
|
||||
|
||||
// ======================== Selection ========================
|
||||
export interface SelectInfo extends MenuInfo {
|
||||
selectedKeys: string[];
|
||||
}
|
||||
|
||||
export type SelectEventHandler = (info: SelectInfo) => void;
|
||||
|
||||
// ========================== Click ==========================
|
||||
export type MenuClickEventHandler = (info: MenuInfo) => void;
|
|
@ -0,0 +1,52 @@
|
|||
const autoAdjustOverflow = {
|
||||
adjustX: 1,
|
||||
adjustY: 1,
|
||||
};
|
||||
|
||||
export const placements = {
|
||||
topLeft: {
|
||||
points: ['bl', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, -7],
|
||||
},
|
||||
bottomLeft: {
|
||||
points: ['tl', 'bl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, 7],
|
||||
},
|
||||
leftTop: {
|
||||
points: ['tr', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [-4, 0],
|
||||
},
|
||||
rightTop: {
|
||||
points: ['tl', 'tr'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [4, 0],
|
||||
},
|
||||
};
|
||||
|
||||
export const placementsRtl = {
|
||||
topLeft: {
|
||||
points: ['bl', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, -7],
|
||||
},
|
||||
bottomLeft: {
|
||||
points: ['tl', 'bl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [0, 7],
|
||||
},
|
||||
rightTop: {
|
||||
points: ['tr', 'tl'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [-4, 0],
|
||||
},
|
||||
leftTop: {
|
||||
points: ['tl', 'tr'],
|
||||
overflow: autoAdjustOverflow,
|
||||
offset: [4, 0],
|
||||
},
|
||||
};
|
||||
|
||||
export default placements;
|
|
@ -1,7 +1,8 @@
|
|||
.@{menu-prefix-cls} {
|
||||
// dark theme
|
||||
&-dark,
|
||||
&-dark &-sub {
|
||||
&&-dark,
|
||||
&-dark &-sub,
|
||||
&&-dark &-sub {
|
||||
color: @menu-dark-color;
|
||||
background: @menu-dark-bg;
|
||||
.@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
|
@ -19,8 +20,7 @@
|
|||
}
|
||||
|
||||
&-dark &-inline&-sub {
|
||||
background: @menu-dark-submenu-bg;
|
||||
box-shadow: 0 2px 8px fade(@black, 45%) inset;
|
||||
background: @menu-dark-inline-submenu-bg;
|
||||
}
|
||||
|
||||
&-dark&-horizontal {
|
||||
|
@ -31,17 +31,23 @@
|
|||
&-dark&-horizontal > &-submenu {
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
padding: @menu-item-padding;
|
||||
border-color: @menu-dark-bg;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&-dark&-horizontal > &-item:hover {
|
||||
background-color: @menu-dark-item-active-bg;
|
||||
}
|
||||
|
||||
&-dark&-horizontal > &-item > a::before {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&-dark &-item,
|
||||
&-dark &-item-group-title,
|
||||
&-dark &-item > a {
|
||||
&-dark &-item > a,
|
||||
&-dark &-item > span > a {
|
||||
color: @menu-dark-color;
|
||||
}
|
||||
|
||||
|
@ -77,7 +83,8 @@
|
|||
&-dark &-submenu-title:hover {
|
||||
color: @menu-dark-highlight-color;
|
||||
background-color: transparent;
|
||||
> a {
|
||||
> a,
|
||||
> span > a {
|
||||
color: @menu-dark-highlight-color;
|
||||
}
|
||||
> .@{menu-prefix-cls}-submenu-title,
|
||||
|
@ -95,6 +102,10 @@
|
|||
background-color: @menu-dark-item-hover-bg;
|
||||
}
|
||||
|
||||
&-dark&-dark:not(&-horizontal) &-item-selected {
|
||||
background-color: @menu-dark-item-active-bg;
|
||||
}
|
||||
|
||||
&-dark &-item-selected {
|
||||
color: @menu-dark-highlight-color;
|
||||
border-right: 0;
|
||||
|
@ -102,14 +113,19 @@
|
|||
border-right: 0;
|
||||
}
|
||||
> a,
|
||||
> a:hover {
|
||||
> span > a,
|
||||
> a:hover,
|
||||
> span > a:hover {
|
||||
color: @menu-dark-highlight-color;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
color: @menu-dark-selected-item-icon-color;
|
||||
}
|
||||
.@{iconfont-css-prefix} + span {
|
||||
color: @menu-dark-selected-item-text-color;
|
||||
|
||||
+ span {
|
||||
color: @menu-dark-selected-item-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +138,8 @@
|
|||
&-dark &-item-disabled,
|
||||
&-dark &-submenu-disabled {
|
||||
&,
|
||||
> a {
|
||||
> a,
|
||||
> span > a {
|
||||
color: @disabled-color-dark !important;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import './status';
|
||||
|
||||
@menu-prefix-cls: ~'@{ant-prefix}-menu';
|
||||
@menu-animation-duration-normal: 0.15s;
|
||||
|
||||
.accessibility-focus() {
|
||||
box-shadow: 0 0 0 2px fade(@primary-color, 20%);
|
||||
}
|
||||
|
||||
// TODO: Should remove icon style compatible in v5
|
||||
|
||||
// default theme
|
||||
.@{menu-prefix-cls} {
|
||||
|
@ -10,14 +18,21 @@
|
|||
margin-bottom: 0;
|
||||
padding-left: 0; // Override default ul/ol
|
||||
color: @menu-item-color;
|
||||
font-size: @menu-item-font-size;
|
||||
line-height: 0; // Fix display inline-block gap
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background: @menu-bg;
|
||||
outline: none;
|
||||
box-shadow: @box-shadow-base;
|
||||
transition: background 0.3s, width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
transition: background @animation-duration-slow,
|
||||
width @animation-duration-slow cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
.clearfix();
|
||||
|
||||
&&-root:focus-visible {
|
||||
.accessibility-focus();
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: 0;
|
||||
|
@ -25,22 +40,29 @@
|
|||
list-style: none;
|
||||
}
|
||||
|
||||
&-hidden {
|
||||
&-hidden,
|
||||
&-submenu-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-item-group-title {
|
||||
height: @menu-item-group-height;
|
||||
padding: 8px 16px;
|
||||
color: @menu-item-group-title-color;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
transition: all 0.3s;
|
||||
font-size: @menu-item-group-title-font-size;
|
||||
line-height: @menu-item-group-height;
|
||||
transition: all @animation-duration-slow;
|
||||
}
|
||||
|
||||
&-horizontal &-submenu {
|
||||
transition: border-color @animation-duration-slow @ease-in-out,
|
||||
background @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
&-submenu,
|
||||
&-submenu-inline {
|
||||
transition: border-color 0.3s @ease-in-out, background 0.3s @ease-in-out,
|
||||
padding 0.15s @ease-in-out;
|
||||
transition: border-color @animation-duration-slow @ease-in-out,
|
||||
background @animation-duration-slow @ease-in-out,
|
||||
padding @menu-animation-duration-normal @ease-in-out;
|
||||
}
|
||||
|
||||
&-submenu-selected {
|
||||
|
@ -54,11 +76,11 @@
|
|||
|
||||
&-submenu &-sub {
|
||||
cursor: initial;
|
||||
transition: background 0.3s @ease-in-out, padding 0.3s @ease-in-out;
|
||||
transition: background @animation-duration-slow @ease-in-out,
|
||||
padding @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
|
||||
&-item > a {
|
||||
display: block;
|
||||
&-item a {
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -75,7 +97,7 @@
|
|||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19809
|
||||
&-item > .@{ant-prefix}-badge > a {
|
||||
&-item > .@{ant-prefix}-badge a {
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -110,8 +132,8 @@
|
|||
|
||||
&-item-selected {
|
||||
color: @menu-highlight-color;
|
||||
> a,
|
||||
> a:hover {
|
||||
a,
|
||||
a:hover {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
@ -125,6 +147,7 @@
|
|||
&-vertical-left {
|
||||
border-right: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
||||
&-vertical-right {
|
||||
border-left: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
@ -133,9 +156,17 @@
|
|||
&-vertical-left&-sub,
|
||||
&-vertical-right&-sub {
|
||||
min-width: 160px;
|
||||
max-height: calc(100vh - 100px);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border-right: 0;
|
||||
transform-origin: 0 0;
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/22244
|
||||
// https://github.com/ant-design/ant-design/issues/26812
|
||||
&:not([class*='-active']) {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item {
|
||||
left: 0;
|
||||
|
@ -155,26 +186,48 @@
|
|||
min-width: 114px; // in case of submenu width is too big: https://codesandbox.io/s/qvpwm6mk66
|
||||
}
|
||||
|
||||
&-horizontal &-item,
|
||||
&-horizontal &-submenu-title {
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow;
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
padding: @menu-item-padding;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s @ease-in-out, border-color 0.3s @ease-in-out,
|
||||
background 0.3s @ease-in-out, padding 0.15s @ease-in-out;
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow,
|
||||
padding @animation-duration-slow @ease-in-out;
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
min-width: 14px;
|
||||
margin-right: 10px;
|
||||
font-size: @menu-icon-size;
|
||||
transition: font-size 0.15s @ease-out, margin 0.3s @ease-in-out;
|
||||
transition: font-size @menu-animation-duration-normal @ease-out,
|
||||
margin @animation-duration-slow @ease-in-out, color @animation-duration-slow;
|
||||
+ span {
|
||||
margin-left: @menu-icon-margin-right;
|
||||
opacity: 1;
|
||||
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
|
||||
// transition: opacity @animation-duration-slow @ease-in-out,
|
||||
// width @animation-duration-slow @ease-in-out, color @animation-duration-slow;
|
||||
transition: opacity @animation-duration-slow @ease-in-out, margin @animation-duration-slow,
|
||||
color @animation-duration-slow;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{menu-prefix-cls}-item-only-child {
|
||||
> .@{iconfont-css-prefix},
|
||||
> .@{menu-prefix-cls}-item-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
.accessibility-focus();
|
||||
}
|
||||
}
|
||||
|
||||
& > &-item-divider {
|
||||
|
@ -190,94 +243,105 @@
|
|||
&-popup {
|
||||
position: absolute;
|
||||
z-index: @zindex-dropdown;
|
||||
// background: @menu-popup-bg;
|
||||
background: transparent;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: none;
|
||||
transform-origin: 0 0;
|
||||
|
||||
.submenu-title-wrapper {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.0001;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
&-placement-rightTop::before {
|
||||
top: 0;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls} {
|
||||
background-color: @menu-bg;
|
||||
border-radius: @border-radius-base;
|
||||
&-submenu-title::after {
|
||||
transition: transform 0.3s @ease-in-out;
|
||||
transition: transform @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&-vertical,
|
||||
&-vertical-left,
|
||||
&-vertical-right,
|
||||
&-inline {
|
||||
> .@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
&-popup > .@{menu-prefix-cls} {
|
||||
background-color: @menu-popup-bg;
|
||||
}
|
||||
|
||||
&-expand-icon,
|
||||
&-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 10px;
|
||||
color: @menu-item-color;
|
||||
transform: translateY(-50%);
|
||||
transition: transform @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
// →
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 10px;
|
||||
transition: transform 0.3s @ease-in-out;
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 1.5px;
|
||||
// background + background-image to makes before & after cross have same color.
|
||||
// Since `linear-gradient` not work on IE9, we should hack it.
|
||||
// ref: https://github.com/ant-design/ant-design/issues/15910
|
||||
background: @menu-bg;
|
||||
background: ~'@{menu-item-color} \9';
|
||||
background-image: linear-gradient(to right, @menu-item-color, @menu-item-color);
|
||||
background-image: ~'none \9';
|
||||
border-radius: 2px;
|
||||
transition: background 0.3s @ease-in-out, transform 0.3s @ease-in-out,
|
||||
top 0.3s @ease-in-out;
|
||||
content: '';
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateY(-2px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateY(2px);
|
||||
}
|
||||
width: 6px;
|
||||
height: 1.5px;
|
||||
background-color: currentColor;
|
||||
border-radius: 2px;
|
||||
transition: background @animation-duration-slow @ease-in-out,
|
||||
transform @animation-duration-slow @ease-in-out, top @animation-duration-slow @ease-in-out,
|
||||
color @animation-duration-slow @ease-in-out;
|
||||
content: '';
|
||||
}
|
||||
> .@{menu-prefix-cls}-submenu-title:hover .@{menu-prefix-cls}-submenu-arrow {
|
||||
&::after,
|
||||
&::before {
|
||||
background: linear-gradient(to right, @menu-highlight-color, @menu-highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-inline > .@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
&::before {
|
||||
transform: rotate(-45deg) translateX(2px);
|
||||
transform: rotate(45deg) translateY(-2.5px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(45deg) translateX(-2px);
|
||||
transform: rotate(-45deg) translateY(2.5px);
|
||||
}
|
||||
}
|
||||
|
||||
&-open {
|
||||
&.@{menu-prefix-cls}-submenu-inline
|
||||
> .@{menu-prefix-cls}-submenu-title
|
||||
.@{menu-prefix-cls}-submenu-arrow {
|
||||
transform: translateY(-2px);
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateX(-2px);
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateX(2px);
|
||||
}
|
||||
&:hover > &-title > &-expand-icon,
|
||||
&:hover > &-title > &-arrow {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-inline-collapsed &-arrow,
|
||||
&-inline &-arrow {
|
||||
// ↓
|
||||
&::before {
|
||||
transform: rotate(-45deg) translateX(2.5px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(45deg) translateX(-2.5px);
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal &-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-open&-inline > &-title > &-arrow {
|
||||
// ↑
|
||||
transform: translateY(-2px);
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateX(-2.5px);
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateX(2.5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,18 +350,34 @@
|
|||
&-vertical-left &-submenu-selected,
|
||||
&-vertical-right &-submenu-selected {
|
||||
color: @menu-highlight-color;
|
||||
> a {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal {
|
||||
line-height: 46px;
|
||||
white-space: nowrap;
|
||||
line-height: @menu-horizontal-line-height;
|
||||
border: 0;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
box-shadow: none;
|
||||
|
||||
&:not(.@{menu-prefix-cls}-dark) {
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-submenu {
|
||||
margin: @menu-item-padding;
|
||||
margin-top: -1px;
|
||||
margin-bottom: 0;
|
||||
padding: @menu-item-padding;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
||||
&:hover,
|
||||
&-active,
|
||||
&-open,
|
||||
&-selected {
|
||||
color: @menu-highlight-color;
|
||||
border-bottom: 2px solid @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-submenu {
|
||||
position: relative;
|
||||
|
@ -305,19 +385,14 @@
|
|||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&-active,
|
||||
&-open,
|
||||
&-selected {
|
||||
color: @menu-highlight-color;
|
||||
border-bottom: 2px solid @menu-highlight-color;
|
||||
}
|
||||
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-item {
|
||||
> a {
|
||||
display: block;
|
||||
a {
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -326,7 +401,7 @@
|
|||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
&-selected > a {
|
||||
&-selected a {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
@ -353,7 +428,8 @@
|
|||
border-right: @menu-item-active-border-width solid @menu-highlight-color;
|
||||
transform: scaleY(0.0001);
|
||||
opacity: 0;
|
||||
transition: transform 0.15s @ease-out, opacity 0.15s @ease-out;
|
||||
transition: transform @menu-animation-duration-normal @ease-out,
|
||||
opacity @menu-animation-duration-normal @ease-out;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +441,6 @@
|
|||
margin-bottom: @menu-item-vertical-margin;
|
||||
padding: 0 16px;
|
||||
overflow: hidden;
|
||||
font-size: @menu-item-font-size;
|
||||
line-height: @menu-item-height;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@ -386,6 +461,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-vertical {
|
||||
.@{menu-prefix-cls}-item-group-list .@{menu-prefix-cls}-submenu-title,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
padding-right: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
&-inline {
|
||||
width: 100%;
|
||||
.@{menu-prefix-cls}-selected,
|
||||
|
@ -393,7 +475,8 @@
|
|||
&::after {
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
transition: transform 0.15s @ease-in-out, opacity 0.15s @ease-in-out;
|
||||
transition: transform @menu-animation-duration-normal @ease-in-out,
|
||||
opacity @menu-animation-duration-normal @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,13 +485,37 @@
|
|||
width: ~'calc(100% + 1px)';
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-group-list .@{menu-prefix-cls}-submenu-title,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
padding-right: 34px;
|
||||
}
|
||||
|
||||
// Motion enhance for first level
|
||||
&.@{menu-prefix-cls}-root {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow,
|
||||
padding 0.1s @ease-out;
|
||||
|
||||
> .@{menu-prefix-cls}-title-content {
|
||||
flex: auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-inline-collapsed {
|
||||
&&-inline-collapsed {
|
||||
width: @menu-collapsed-width;
|
||||
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-item-group
|
||||
> .@{menu-prefix-cls}-item-group-list
|
||||
|
@ -419,24 +526,34 @@
|
|||
> .@{menu-prefix-cls}-submenu-title,
|
||||
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
|
||||
left: 0;
|
||||
padding: 0 ((@menu-collapsed-width - @menu-icon-size-lg) / 2) !important;
|
||||
padding: 0 ~'calc(50% - @{menu-icon-size-lg} / 2)';
|
||||
text-overflow: clip;
|
||||
|
||||
.@{menu-prefix-cls}-submenu-arrow {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
margin: 0;
|
||||
font-size: @menu-icon-size-lg;
|
||||
line-height: @menu-item-height;
|
||||
+ span {
|
||||
display: inline-block;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-tooltip {
|
||||
pointer-events: none;
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
display: none;
|
||||
}
|
||||
|
@ -470,8 +587,19 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
&-root&-inline-collapsed {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu .@{menu-prefix-cls}-submenu-title {
|
||||
> .@{menu-prefix-cls}-inline-collapsed-noicon {
|
||||
font-size: @menu-icon-size-lg;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-sub&-inline {
|
||||
padding: 0;
|
||||
background: @menu-inline-submenu-bg;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
|
@ -495,7 +623,7 @@
|
|||
background: none;
|
||||
border-color: transparent !important;
|
||||
cursor: not-allowed;
|
||||
> a {
|
||||
a {
|
||||
color: @disabled-color !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -512,4 +640,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Integration with header element so menu items have the same height
|
||||
.@{ant-prefix}-layout-header {
|
||||
.@{menu-prefix-cls} {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@import './dark';
|
||||
@import './rtl';
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { getPropsSlot } from '../../_util/props-util';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenuItemGroup',
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return (
|
||||
<li>
|
||||
{getPropsSlot(slots, props, 'title')}
|
||||
<ul>{slots.default?.()}</ul>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenu',
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return <div>{slots.default?.()}</div>;
|
||||
};
|
||||
},
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AMenuItem',
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return <li>{slots.default?.()}</li>;
|
||||
};
|
||||
},
|
||||
});
|
|
@ -0,0 +1,323 @@
|
|||
import { defineComponent, inject, provide, toRef, App, ExtractPropTypes, Plugin } from 'vue';
|
||||
import omit from 'omit.js';
|
||||
import VcMenu, { Divider, ItemGroup } from '../vc-menu';
|
||||
import SubMenu from './SubMenu';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import animation from '../_util/openAnimation';
|
||||
import warning from '../_util/warning';
|
||||
import Item from './MenuItem';
|
||||
import { hasProp, getOptionProps } from '../_util/props-util';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import commonPropsType from '../vc-menu/commonPropsType';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { SiderContextProps } from '../layout/Sider';
|
||||
import { tuple } from '../_util/type';
|
||||
// import raf from '../_util/raf';
|
||||
|
||||
export const MenuMode = PropTypes.oneOf([
|
||||
'vertical',
|
||||
'vertical-left',
|
||||
'vertical-right',
|
||||
'horizontal',
|
||||
'inline',
|
||||
]);
|
||||
|
||||
export const menuProps = {
|
||||
...commonPropsType,
|
||||
theme: PropTypes.oneOf(tuple('light', 'dark')).def('light'),
|
||||
mode: MenuMode.def('vertical'),
|
||||
selectable: PropTypes.looseBool,
|
||||
selectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
openKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
defaultOpenKeys: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
|
||||
openAnimation: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
openTransitionName: PropTypes.string,
|
||||
prefixCls: PropTypes.string,
|
||||
multiple: PropTypes.looseBool,
|
||||
inlineIndent: PropTypes.number.def(24),
|
||||
inlineCollapsed: PropTypes.looseBool,
|
||||
isRootMenu: PropTypes.looseBool.def(true),
|
||||
focusable: PropTypes.looseBool.def(false),
|
||||
onOpenChange: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
onDeselect: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
onMouseenter: PropTypes.func,
|
||||
onSelectChange: PropTypes.func,
|
||||
};
|
||||
|
||||
export type MenuProps = Partial<ExtractPropTypes<typeof menuProps>>;
|
||||
|
||||
const Menu = defineComponent({
|
||||
name: 'AMenu',
|
||||
mixins: [BaseMixin],
|
||||
inheritAttrs: false,
|
||||
props: menuProps,
|
||||
Divider: { ...Divider, name: 'AMenuDivider' },
|
||||
Item: { ...Item, name: 'AMenuItem' },
|
||||
SubMenu: { ...SubMenu, name: 'ASubMenu' },
|
||||
ItemGroup: { ...ItemGroup, name: 'AMenuItemGroup' },
|
||||
emits: [
|
||||
'update:selectedKeys',
|
||||
'update:openKeys',
|
||||
'mouseenter',
|
||||
'openChange',
|
||||
'click',
|
||||
'selectChange',
|
||||
'select',
|
||||
'deselect',
|
||||
],
|
||||
setup() {
|
||||
const layoutSiderContext = inject<SiderContextProps>('layoutSiderContext', {});
|
||||
const layoutSiderCollapsed = toRef(layoutSiderContext, 'sCollapsed');
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
layoutSiderContext,
|
||||
layoutSiderCollapsed,
|
||||
propsUpdating: false,
|
||||
switchingModeFromInline: false,
|
||||
leaveAnimationExecutedWhenInlineCollapsed: false,
|
||||
inlineOpenKeys: [],
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const props: MenuProps = getOptionProps(this);
|
||||
warning(
|
||||
!('inlineCollapsed' in props && props.mode !== 'inline'),
|
||||
'Menu',
|
||||
"`inlineCollapsed` should only be used when Menu's `mode` is inline.",
|
||||
);
|
||||
let sOpenKeys: (number | string)[];
|
||||
|
||||
if ('openKeys' in props) {
|
||||
sOpenKeys = props.openKeys;
|
||||
} else if ('defaultOpenKeys' in props) {
|
||||
sOpenKeys = props.defaultOpenKeys;
|
||||
}
|
||||
return {
|
||||
sOpenKeys,
|
||||
};
|
||||
},
|
||||
// beforeUnmount() {
|
||||
// raf.cancel(this.mountRafId);
|
||||
// },
|
||||
watch: {
|
||||
mode(val, oldVal) {
|
||||
if (oldVal === 'inline' && val !== 'inline') {
|
||||
this.switchingModeFromInline = true;
|
||||
}
|
||||
},
|
||||
openKeys(val) {
|
||||
this.setState({ sOpenKeys: val });
|
||||
},
|
||||
inlineCollapsed(val) {
|
||||
this.collapsedChange(val);
|
||||
},
|
||||
layoutSiderCollapsed(val) {
|
||||
this.collapsedChange(val);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
provide('getInlineCollapsed', this.getInlineCollapsed);
|
||||
provide('menuPropsContext', this.$props);
|
||||
},
|
||||
updated() {
|
||||
this.propsUpdating = false;
|
||||
},
|
||||
methods: {
|
||||
collapsedChange(val: unknown) {
|
||||
if (this.propsUpdating) {
|
||||
return;
|
||||
}
|
||||
this.propsUpdating = true;
|
||||
if (!hasProp(this, 'openKeys')) {
|
||||
if (val) {
|
||||
this.switchingModeFromInline = true;
|
||||
this.inlineOpenKeys = this.sOpenKeys;
|
||||
this.setState({ sOpenKeys: [] });
|
||||
} else {
|
||||
this.setState({ sOpenKeys: this.inlineOpenKeys });
|
||||
this.inlineOpenKeys = [];
|
||||
}
|
||||
} else if (val) {
|
||||
// 缩起时,openKeys置为空的动画会闪动,react可以通过是否传递openKeys避免闪动,vue不是很方便动态传递openKeys
|
||||
this.switchingModeFromInline = true;
|
||||
}
|
||||
},
|
||||
restoreModeVerticalFromInline() {
|
||||
if (this.switchingModeFromInline) {
|
||||
this.switchingModeFromInline = false;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
// Restore vertical mode when menu is collapsed responsively when mounted
|
||||
// https://github.com/ant-design/ant-design/issues/13104
|
||||
// TODO: not a perfect solution, looking a new way to avoid setting switchingModeFromInline in this situation
|
||||
handleMouseEnter(e: Event) {
|
||||
this.restoreModeVerticalFromInline();
|
||||
this.$emit('mouseenter', e);
|
||||
},
|
||||
handleTransitionEnd(e: TransitionEvent) {
|
||||
// when inlineCollapsed menu width animation finished
|
||||
// https://github.com/ant-design/ant-design/issues/12864
|
||||
const widthCollapsed = e.propertyName === 'width' && e.target === e.currentTarget;
|
||||
|
||||
// Fix SVGElement e.target.className.indexOf is not a function
|
||||
// https://github.com/ant-design/ant-design/issues/15699
|
||||
const { className } = e.target as SVGAnimationElement | HTMLElement;
|
||||
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during an animation.
|
||||
const classNameValue =
|
||||
Object.prototype.toString.call(className) === '[object SVGAnimatedString]'
|
||||
? className.animVal
|
||||
: className;
|
||||
|
||||
// Fix for <Menu style={{ width: '100%' }} />, the width transition won't trigger when menu is collapsed
|
||||
// https://github.com/ant-design/ant-design-pro/issues/2783
|
||||
const iconScaled = e.propertyName === 'font-size' && classNameValue.indexOf('anticon') >= 0;
|
||||
|
||||
if (widthCollapsed || iconScaled) {
|
||||
this.restoreModeVerticalFromInline();
|
||||
}
|
||||
},
|
||||
handleClick(e: Event) {
|
||||
this.handleOpenChange([]);
|
||||
this.$emit('click', e);
|
||||
},
|
||||
handleSelect(info) {
|
||||
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||
this.$emit('select', info);
|
||||
this.$emit('selectChange', info.selectedKeys);
|
||||
},
|
||||
handleDeselect(info) {
|
||||
this.$emit('update:selectedKeys', info.selectedKeys);
|
||||
this.$emit('deselect', info);
|
||||
this.$emit('selectChange', info.selectedKeys);
|
||||
},
|
||||
handleOpenChange(openKeys: (number | string)[]) {
|
||||
this.setOpenKeys(openKeys);
|
||||
this.$emit('update:openKeys', openKeys);
|
||||
this.$emit('openChange', openKeys);
|
||||
},
|
||||
setOpenKeys(openKeys: (number | string)[]) {
|
||||
if (!hasProp(this, 'openKeys')) {
|
||||
this.setState({ sOpenKeys: openKeys });
|
||||
}
|
||||
},
|
||||
getRealMenuMode() {
|
||||
const inlineCollapsed = this.getInlineCollapsed();
|
||||
if (this.switchingModeFromInline && inlineCollapsed) {
|
||||
return 'inline';
|
||||
}
|
||||
const { mode } = this.$props;
|
||||
return inlineCollapsed ? 'vertical' : mode;
|
||||
},
|
||||
getInlineCollapsed() {
|
||||
const { inlineCollapsed } = this.$props;
|
||||
if (this.layoutSiderContext.sCollapsed !== undefined) {
|
||||
return this.layoutSiderContext.sCollapsed;
|
||||
}
|
||||
return inlineCollapsed;
|
||||
},
|
||||
getMenuOpenAnimation(menuMode: string) {
|
||||
const { openAnimation, openTransitionName } = this.$props;
|
||||
let menuOpenAnimation = openAnimation || openTransitionName;
|
||||
if (openAnimation === undefined && openTransitionName === undefined) {
|
||||
if (menuMode === 'horizontal') {
|
||||
menuOpenAnimation = 'slide-up';
|
||||
} else if (menuMode === 'inline') {
|
||||
menuOpenAnimation = animation;
|
||||
} else {
|
||||
// When mode switch from inline
|
||||
// submenu should hide without animation
|
||||
if (this.switchingModeFromInline) {
|
||||
menuOpenAnimation = '';
|
||||
this.switchingModeFromInline = false;
|
||||
} else {
|
||||
menuOpenAnimation = 'zoom-big';
|
||||
}
|
||||
}
|
||||
}
|
||||
return menuOpenAnimation;
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { layoutSiderContext } = this;
|
||||
const { collapsedWidth } = layoutSiderContext;
|
||||
const { getPopupContainer: getContextPopupContainer } = this.configProvider;
|
||||
const props = getOptionProps(this);
|
||||
const { prefixCls: customizePrefixCls, theme, getPopupContainer } = props;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('menu', customizePrefixCls);
|
||||
const menuMode = this.getRealMenuMode();
|
||||
const menuOpenAnimation = this.getMenuOpenAnimation(menuMode);
|
||||
const { class: className, ...otherAttrs } = this.$attrs;
|
||||
const menuClassName = {
|
||||
[className as string]: className,
|
||||
[`${prefixCls}-${theme}`]: true,
|
||||
[`${prefixCls}-inline-collapsed`]: this.getInlineCollapsed(),
|
||||
};
|
||||
|
||||
const menuProps = {
|
||||
...omit(props, [
|
||||
'inlineCollapsed',
|
||||
'onUpdate:selectedKeys',
|
||||
'onUpdate:openKeys',
|
||||
'onSelectChange',
|
||||
]),
|
||||
getPopupContainer: getPopupContainer || getContextPopupContainer,
|
||||
openKeys: this.sOpenKeys,
|
||||
mode: menuMode,
|
||||
prefixCls,
|
||||
...otherAttrs,
|
||||
onSelect: this.handleSelect,
|
||||
onDeselect: this.handleDeselect,
|
||||
onOpenChange: this.handleOpenChange,
|
||||
onMouseenter: this.handleMouseEnter,
|
||||
onTransitionend: this.handleTransitionEnd,
|
||||
// children: getSlot(this),
|
||||
};
|
||||
if (!hasProp(this, 'selectedKeys')) {
|
||||
delete menuProps.selectedKeys;
|
||||
}
|
||||
|
||||
if (menuMode !== 'inline') {
|
||||
// closing vertical popup submenu after click it
|
||||
menuProps.onClick = this.handleClick;
|
||||
menuProps.openTransitionName = menuOpenAnimation;
|
||||
} else {
|
||||
menuProps.onClick = (e: Event) => {
|
||||
this.$emit('click', e);
|
||||
};
|
||||
menuProps.openAnimation = menuOpenAnimation;
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/8587
|
||||
const hideMenu =
|
||||
this.getInlineCollapsed() &&
|
||||
(collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px');
|
||||
if (hideMenu) {
|
||||
menuProps.openKeys = [];
|
||||
}
|
||||
|
||||
return <VcMenu {...menuProps} class={menuClassName} v-slots={this.$slots} />;
|
||||
},
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
Menu.install = function(app: App) {
|
||||
app.component(Menu.name, Menu);
|
||||
app.component(Menu.Item.name, Menu.Item);
|
||||
app.component(Menu.SubMenu.name, Menu.SubMenu);
|
||||
app.component(Menu.Divider.name, Menu.Divider);
|
||||
app.component(Menu.ItemGroup.name, Menu.ItemGroup);
|
||||
return app;
|
||||
};
|
||||
|
||||
export default Menu as typeof Menu &
|
||||
Plugin & {
|
||||
readonly Item: typeof Item;
|
||||
readonly SubMenu: typeof SubMenu;
|
||||
readonly Divider: typeof Divider;
|
||||
readonly ItemGroup: typeof ItemGroup;
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
.@{menu-prefix-cls} {
|
||||
// dark theme
|
||||
&&-dark,
|
||||
&-dark &-sub,
|
||||
&&-dark &-sub {
|
||||
&-dark,
|
||||
&-dark &-sub {
|
||||
color: @menu-dark-color;
|
||||
background: @menu-dark-bg;
|
||||
.@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
|
@ -20,7 +19,8 @@
|
|||
}
|
||||
|
||||
&-dark &-inline&-sub {
|
||||
background: @menu-dark-inline-submenu-bg;
|
||||
background: @menu-dark-submenu-bg;
|
||||
box-shadow: 0 2px 8px fade(@black, 45%) inset;
|
||||
}
|
||||
|
||||
&-dark&-horizontal {
|
||||
|
@ -31,23 +31,17 @@
|
|||
&-dark&-horizontal > &-submenu {
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
padding: @menu-item-padding;
|
||||
border-color: @menu-dark-bg;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&-dark&-horizontal > &-item:hover {
|
||||
background-color: @menu-dark-item-active-bg;
|
||||
}
|
||||
|
||||
&-dark&-horizontal > &-item > a::before {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&-dark &-item,
|
||||
&-dark &-item-group-title,
|
||||
&-dark &-item > a,
|
||||
&-dark &-item > span > a {
|
||||
&-dark &-item > a {
|
||||
color: @menu-dark-color;
|
||||
}
|
||||
|
||||
|
@ -83,8 +77,7 @@
|
|||
&-dark &-submenu-title:hover {
|
||||
color: @menu-dark-highlight-color;
|
||||
background-color: transparent;
|
||||
> a,
|
||||
> span > a {
|
||||
> a {
|
||||
color: @menu-dark-highlight-color;
|
||||
}
|
||||
> .@{menu-prefix-cls}-submenu-title,
|
||||
|
@ -102,10 +95,6 @@
|
|||
background-color: @menu-dark-item-hover-bg;
|
||||
}
|
||||
|
||||
&-dark&-dark:not(&-horizontal) &-item-selected {
|
||||
background-color: @menu-dark-item-active-bg;
|
||||
}
|
||||
|
||||
&-dark &-item-selected {
|
||||
color: @menu-dark-highlight-color;
|
||||
border-right: 0;
|
||||
|
@ -113,19 +102,14 @@
|
|||
border-right: 0;
|
||||
}
|
||||
> a,
|
||||
> span > a,
|
||||
> a:hover,
|
||||
> span > a:hover {
|
||||
> a:hover {
|
||||
color: @menu-dark-highlight-color;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
color: @menu-dark-selected-item-icon-color;
|
||||
|
||||
+ span {
|
||||
color: @menu-dark-selected-item-text-color;
|
||||
}
|
||||
}
|
||||
.@{iconfont-css-prefix} + span {
|
||||
color: @menu-dark-selected-item-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,8 +122,7 @@
|
|||
&-dark &-item-disabled,
|
||||
&-dark &-submenu-disabled {
|
||||
&,
|
||||
> a,
|
||||
> span > a {
|
||||
> a {
|
||||
color: @disabled-color-dark !important;
|
||||
opacity: 0.8;
|
||||
}
|
|
@ -1,15 +1,7 @@
|
|||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import './status';
|
||||
|
||||
@menu-prefix-cls: ~'@{ant-prefix}-menu';
|
||||
@menu-animation-duration-normal: 0.15s;
|
||||
|
||||
.accessibility-focus() {
|
||||
box-shadow: 0 0 0 2px fade(@primary-color, 20%);
|
||||
}
|
||||
|
||||
// TODO: Should remove icon style compatible in v5
|
||||
|
||||
// default theme
|
||||
.@{menu-prefix-cls} {
|
||||
|
@ -18,21 +10,14 @@
|
|||
margin-bottom: 0;
|
||||
padding-left: 0; // Override default ul/ol
|
||||
color: @menu-item-color;
|
||||
font-size: @menu-item-font-size;
|
||||
line-height: 0; // Fix display inline-block gap
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
background: @menu-bg;
|
||||
outline: none;
|
||||
box-shadow: @box-shadow-base;
|
||||
transition: background @animation-duration-slow,
|
||||
width @animation-duration-slow cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
transition: background 0.3s, width 0.3s cubic-bezier(0.2, 0, 0, 1) 0s;
|
||||
.clearfix();
|
||||
|
||||
&&-root:focus-visible {
|
||||
.accessibility-focus();
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
margin: 0;
|
||||
|
@ -40,29 +25,22 @@
|
|||
list-style: none;
|
||||
}
|
||||
|
||||
&-hidden,
|
||||
&-submenu-hidden {
|
||||
&-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-item-group-title {
|
||||
height: @menu-item-group-height;
|
||||
padding: 8px 16px;
|
||||
color: @menu-item-group-title-color;
|
||||
font-size: @menu-item-group-title-font-size;
|
||||
line-height: @menu-item-group-height;
|
||||
transition: all @animation-duration-slow;
|
||||
font-size: @font-size-base;
|
||||
line-height: @line-height-base;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
&-horizontal &-submenu {
|
||||
transition: border-color @animation-duration-slow @ease-in-out,
|
||||
background @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
&-submenu,
|
||||
&-submenu-inline {
|
||||
transition: border-color @animation-duration-slow @ease-in-out,
|
||||
background @animation-duration-slow @ease-in-out,
|
||||
padding @menu-animation-duration-normal @ease-in-out;
|
||||
transition: border-color 0.3s @ease-in-out, background 0.3s @ease-in-out,
|
||||
padding 0.15s @ease-in-out;
|
||||
}
|
||||
|
||||
&-submenu-selected {
|
||||
|
@ -76,11 +54,11 @@
|
|||
|
||||
&-submenu &-sub {
|
||||
cursor: initial;
|
||||
transition: background @animation-duration-slow @ease-in-out,
|
||||
padding @animation-duration-slow @ease-in-out;
|
||||
transition: background 0.3s @ease-in-out, padding 0.3s @ease-in-out;
|
||||
}
|
||||
|
||||
&-item a {
|
||||
&-item > a {
|
||||
display: block;
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -97,7 +75,7 @@
|
|||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/19809
|
||||
&-item > .@{ant-prefix}-badge a {
|
||||
&-item > .@{ant-prefix}-badge > a {
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -132,8 +110,8 @@
|
|||
|
||||
&-item-selected {
|
||||
color: @menu-highlight-color;
|
||||
a,
|
||||
a:hover {
|
||||
> a,
|
||||
> a:hover {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +125,6 @@
|
|||
&-vertical-left {
|
||||
border-right: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
||||
&-vertical-right {
|
||||
border-left: @border-width-base @border-style-base @border-color-split;
|
||||
}
|
||||
|
@ -156,17 +133,9 @@
|
|||
&-vertical-left&-sub,
|
||||
&-vertical-right&-sub {
|
||||
min-width: 160px;
|
||||
max-height: calc(100vh - 100px);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
border-right: 0;
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/22244
|
||||
// https://github.com/ant-design/ant-design/issues/26812
|
||||
&:not([class*='-active']) {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
transform-origin: 0 0;
|
||||
|
||||
.@{menu-prefix-cls}-item {
|
||||
left: 0;
|
||||
|
@ -186,48 +155,26 @@
|
|||
min-width: 114px; // in case of submenu width is too big: https://codesandbox.io/s/qvpwm6mk66
|
||||
}
|
||||
|
||||
&-horizontal &-item,
|
||||
&-horizontal &-submenu-title {
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow;
|
||||
}
|
||||
|
||||
&-item,
|
||||
&-submenu-title {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: @menu-item-padding;
|
||||
padding: 0 20px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow,
|
||||
padding @animation-duration-slow @ease-in-out;
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
transition: color 0.3s @ease-in-out, border-color 0.3s @ease-in-out,
|
||||
background 0.3s @ease-in-out, padding 0.15s @ease-in-out;
|
||||
.@{iconfont-css-prefix} {
|
||||
min-width: 14px;
|
||||
margin-right: 10px;
|
||||
font-size: @menu-icon-size;
|
||||
transition: font-size @menu-animation-duration-normal @ease-out,
|
||||
margin @animation-duration-slow @ease-in-out, color @animation-duration-slow;
|
||||
transition: font-size 0.15s @ease-out, margin 0.3s @ease-in-out;
|
||||
+ span {
|
||||
margin-left: @menu-icon-margin-right;
|
||||
opacity: 1;
|
||||
// transition: opacity @animation-duration-slow @ease-in-out,
|
||||
// width @animation-duration-slow @ease-in-out, color @animation-duration-slow;
|
||||
transition: opacity @animation-duration-slow @ease-in-out, margin @animation-duration-slow,
|
||||
color @animation-duration-slow;
|
||||
transition: opacity 0.3s @ease-in-out, width 0.3s @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.@{menu-prefix-cls}-item-only-child {
|
||||
> .@{iconfont-css-prefix},
|
||||
> .@{menu-prefix-cls}-item-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
.accessibility-focus();
|
||||
}
|
||||
}
|
||||
|
||||
& > &-item-divider {
|
||||
|
@ -243,105 +190,94 @@
|
|||
&-popup {
|
||||
position: absolute;
|
||||
z-index: @zindex-dropdown;
|
||||
background: transparent;
|
||||
// background: @menu-popup-bg;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: none;
|
||||
transform-origin: 0 0;
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
.submenu-title-wrapper {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.0001;
|
||||
content: ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/13955
|
||||
&-placement-rightTop::before {
|
||||
top: 0;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls} {
|
||||
background-color: @menu-bg;
|
||||
border-radius: @border-radius-base;
|
||||
&-submenu-title::after {
|
||||
transition: transform @animation-duration-slow @ease-in-out;
|
||||
transition: transform 0.3s @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
&-popup > .@{menu-prefix-cls} {
|
||||
background-color: @menu-popup-bg;
|
||||
}
|
||||
|
||||
&-expand-icon,
|
||||
&-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 10px;
|
||||
color: @menu-item-color;
|
||||
transform: translateY(-50%);
|
||||
transition: transform @animation-duration-slow @ease-in-out;
|
||||
}
|
||||
|
||||
&-arrow {
|
||||
// →
|
||||
&::before,
|
||||
&::after {
|
||||
&-vertical,
|
||||
&-vertical-left,
|
||||
&-vertical-right,
|
||||
&-inline {
|
||||
> .@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 1.5px;
|
||||
background-color: currentColor;
|
||||
border-radius: 2px;
|
||||
transition: background @animation-duration-slow @ease-in-out,
|
||||
transform @animation-duration-slow @ease-in-out, top @animation-duration-slow @ease-in-out,
|
||||
color @animation-duration-slow @ease-in-out;
|
||||
content: '';
|
||||
top: 50%;
|
||||
right: 16px;
|
||||
width: 10px;
|
||||
transition: transform 0.3s @ease-in-out;
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 1.5px;
|
||||
// background + background-image to makes before & after cross have same color.
|
||||
// Since `linear-gradient` not work on IE9, we should hack it.
|
||||
// ref: https://github.com/ant-design/ant-design/issues/15910
|
||||
background: @menu-bg;
|
||||
background: ~'@{menu-item-color} \9';
|
||||
background-image: linear-gradient(to right, @menu-item-color, @menu-item-color);
|
||||
background-image: ~'none \9';
|
||||
border-radius: 2px;
|
||||
transition: background 0.3s @ease-in-out, transform 0.3s @ease-in-out,
|
||||
top 0.3s @ease-in-out;
|
||||
content: '';
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateY(-2px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateY(2px);
|
||||
}
|
||||
}
|
||||
> .@{menu-prefix-cls}-submenu-title:hover .@{menu-prefix-cls}-submenu-arrow {
|
||||
&::after,
|
||||
&::before {
|
||||
background: linear-gradient(to right, @menu-highlight-color, @menu-highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-inline > .@{menu-prefix-cls}-submenu-title .@{menu-prefix-cls}-submenu-arrow {
|
||||
&::before {
|
||||
transform: rotate(45deg) translateY(-2.5px);
|
||||
transform: rotate(-45deg) translateX(2px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateY(2.5px);
|
||||
transform: rotate(45deg) translateX(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover > &-title > &-expand-icon,
|
||||
&:hover > &-title > &-arrow {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-inline-collapsed &-arrow,
|
||||
&-inline &-arrow {
|
||||
// ↓
|
||||
&::before {
|
||||
transform: rotate(-45deg) translateX(2.5px);
|
||||
}
|
||||
&::after {
|
||||
transform: rotate(45deg) translateX(-2.5px);
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal &-arrow {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-open&-inline > &-title > &-arrow {
|
||||
// ↑
|
||||
transform: translateY(-2px);
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateX(-2.5px);
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateX(2.5px);
|
||||
&-open {
|
||||
&.@{menu-prefix-cls}-submenu-inline
|
||||
> .@{menu-prefix-cls}-submenu-title
|
||||
.@{menu-prefix-cls}-submenu-arrow {
|
||||
transform: translateY(-2px);
|
||||
&::after {
|
||||
transform: rotate(-45deg) translateX(-2px);
|
||||
}
|
||||
&::before {
|
||||
transform: rotate(45deg) translateX(2px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -350,34 +286,18 @@
|
|||
&-vertical-left &-submenu-selected,
|
||||
&-vertical-right &-submenu-selected {
|
||||
color: @menu-highlight-color;
|
||||
> a {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-horizontal {
|
||||
line-height: @menu-horizontal-line-height;
|
||||
line-height: 46px;
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
border-bottom: @border-width-base @border-style-base @border-color-split;
|
||||
box-shadow: none;
|
||||
|
||||
&:not(.@{menu-prefix-cls}-dark) {
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-submenu {
|
||||
margin: @menu-item-padding;
|
||||
margin-top: -1px;
|
||||
margin-bottom: 0;
|
||||
padding: @menu-item-padding;
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
||||
&:hover,
|
||||
&-active,
|
||||
&-open,
|
||||
&-selected {
|
||||
color: @menu-highlight-color;
|
||||
border-bottom: 2px solid @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-submenu {
|
||||
position: relative;
|
||||
|
@ -385,14 +305,19 @@
|
|||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
|
||||
padding: 0;
|
||||
&:hover,
|
||||
&-active,
|
||||
&-open,
|
||||
&-selected {
|
||||
color: @menu-highlight-color;
|
||||
border-bottom: 2px solid @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
||||
> .@{menu-prefix-cls}-item {
|
||||
a {
|
||||
> a {
|
||||
display: block;
|
||||
color: @menu-item-color;
|
||||
&:hover {
|
||||
color: @menu-highlight-color;
|
||||
|
@ -401,7 +326,7 @@
|
|||
bottom: -2px;
|
||||
}
|
||||
}
|
||||
&-selected a {
|
||||
&-selected > a {
|
||||
color: @menu-highlight-color;
|
||||
}
|
||||
}
|
||||
|
@ -428,8 +353,7 @@
|
|||
border-right: @menu-item-active-border-width solid @menu-highlight-color;
|
||||
transform: scaleY(0.0001);
|
||||
opacity: 0;
|
||||
transition: transform @menu-animation-duration-normal @ease-out,
|
||||
opacity @menu-animation-duration-normal @ease-out;
|
||||
transition: transform 0.15s @ease-out, opacity 0.15s @ease-out;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
@ -441,6 +365,7 @@
|
|||
margin-bottom: @menu-item-vertical-margin;
|
||||
padding: 0 16px;
|
||||
overflow: hidden;
|
||||
font-size: @menu-item-font-size;
|
||||
line-height: @menu-item-height;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
@ -461,13 +386,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-vertical {
|
||||
.@{menu-prefix-cls}-item-group-list .@{menu-prefix-cls}-submenu-title,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
padding-right: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
&-inline {
|
||||
width: 100%;
|
||||
.@{menu-prefix-cls}-selected,
|
||||
|
@ -475,8 +393,7 @@
|
|||
&::after {
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
transition: transform @menu-animation-duration-normal @ease-in-out,
|
||||
opacity @menu-animation-duration-normal @ease-in-out;
|
||||
transition: transform 0.15s @ease-in-out, opacity 0.15s @ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,37 +402,13 @@
|
|||
width: ~'calc(100% + 1px)';
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-group-list .@{menu-prefix-cls}-submenu-title,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
padding-right: 34px;
|
||||
}
|
||||
|
||||
// Motion enhance for first level
|
||||
&.@{menu-prefix-cls}-root {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: border-color @animation-duration-slow, background @animation-duration-slow,
|
||||
padding 0.1s @ease-out;
|
||||
|
||||
> .@{menu-prefix-cls}-title-content {
|
||||
flex: auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&&-inline-collapsed {
|
||||
&-inline-collapsed {
|
||||
width: @menu-collapsed-width;
|
||||
|
||||
> .@{menu-prefix-cls}-item,
|
||||
> .@{menu-prefix-cls}-item-group
|
||||
> .@{menu-prefix-cls}-item-group-list
|
||||
|
@ -526,34 +419,24 @@
|
|||
> .@{menu-prefix-cls}-submenu-title,
|
||||
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
|
||||
left: 0;
|
||||
padding: 0 ~'calc(50% - @{menu-icon-size-lg} / 2)';
|
||||
padding: 0 ((@menu-collapsed-width - @menu-icon-size-lg) / 2) !important;
|
||||
text-overflow: clip;
|
||||
|
||||
.@{menu-prefix-cls}-submenu-arrow {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
margin: 0;
|
||||
font-size: @menu-icon-size-lg;
|
||||
line-height: @menu-item-height;
|
||||
+ span {
|
||||
display: inline-block;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-tooltip {
|
||||
pointer-events: none;
|
||||
|
||||
.@{menu-prefix-cls}-item-icon,
|
||||
.@{iconfont-css-prefix} {
|
||||
display: none;
|
||||
}
|
||||
|
@ -587,19 +470,8 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
&-root&-inline-collapsed {
|
||||
.@{menu-prefix-cls}-item,
|
||||
.@{menu-prefix-cls}-submenu .@{menu-prefix-cls}-submenu-title {
|
||||
> .@{menu-prefix-cls}-inline-collapsed-noicon {
|
||||
font-size: @menu-icon-size-lg;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-sub&-inline {
|
||||
padding: 0;
|
||||
background: @menu-inline-submenu-bg;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
|
@ -623,7 +495,7 @@
|
|||
background: none;
|
||||
border-color: transparent !important;
|
||||
cursor: not-allowed;
|
||||
a {
|
||||
> a {
|
||||
color: @disabled-color !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
@ -640,12 +512,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Integration with header element so menu items have the same height
|
||||
.@{ant-prefix}-layout-header {
|
||||
.@{menu-prefix-cls} {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@import './dark';
|
||||
@import './rtl';
|
|
@ -490,28 +490,37 @@
|
|||
// ---
|
||||
@menu-inline-toplevel-item-height: 40px;
|
||||
@menu-item-height: 40px;
|
||||
@menu-item-group-height: @line-height-base;
|
||||
@menu-collapsed-width: 80px;
|
||||
@menu-bg: @component-background;
|
||||
@menu-popup-bg: @component-background;
|
||||
@menu-item-color: @text-color;
|
||||
@menu-inline-submenu-bg: @background-color-light;
|
||||
@menu-highlight-color: @primary-color;
|
||||
@menu-item-active-bg: @item-active-bg;
|
||||
@menu-highlight-danger-color: @error-color;
|
||||
@menu-item-active-bg: @primary-1;
|
||||
@menu-item-active-danger-bg: @red-1;
|
||||
@menu-item-active-border-width: 3px;
|
||||
@menu-item-group-title-color: @text-color-secondary;
|
||||
@menu-icon-size: @font-size-base;
|
||||
@menu-icon-size-lg: @font-size-lg;
|
||||
|
||||
@menu-item-vertical-margin: 4px;
|
||||
@menu-item-font-size: @font-size-base;
|
||||
@menu-item-boundary-margin: 8px;
|
||||
@menu-item-padding: 0 20px;
|
||||
@menu-horizontal-line-height: 46px;
|
||||
@menu-icon-margin-right: 10px;
|
||||
@menu-icon-size: @menu-item-font-size;
|
||||
@menu-icon-size-lg: @font-size-lg;
|
||||
@menu-item-group-title-font-size: @menu-item-font-size;
|
||||
|
||||
// dark theme
|
||||
@menu-dark-color: @text-color-secondary-dark;
|
||||
@menu-dark-danger-color: @error-color;
|
||||
@menu-dark-bg: @layout-header-background;
|
||||
@menu-dark-arrow-color: #fff;
|
||||
@menu-dark-submenu-bg: #000c17;
|
||||
@menu-dark-inline-submenu-bg: #000c17;
|
||||
@menu-dark-highlight-color: #fff;
|
||||
@menu-dark-item-active-bg: @primary-color;
|
||||
@menu-dark-item-active-danger-bg: @error-color;
|
||||
@menu-dark-selected-item-icon-color: @white;
|
||||
@menu-dark-selected-item-text-color: @white;
|
||||
@menu-dark-item-hover-bg: transparent;
|
||||
|
|
113
examples/App.vue
113
examples/App.vue
|
@ -1,39 +1,92 @@
|
|||
<template>
|
||||
<div>
|
||||
<demo />
|
||||
</div>
|
||||
<a-menu
|
||||
id="dddddd"
|
||||
v-model:openKeys="openKeys"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
style="width: 256px"
|
||||
mode="inline"
|
||||
@click="handleClick"
|
||||
>
|
||||
<a-sub-menu key="sub1" @titleClick="titleClick">
|
||||
<template #title>
|
||||
<span>
|
||||
<MailOutlined />
|
||||
<span>Navigation One</span>
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item-group key="g1">
|
||||
<template #title>
|
||||
<QqOutlined />
|
||||
<span>Item 1</span>
|
||||
</template>
|
||||
<a-menu-item key="1">Option 1</a-menu-item>
|
||||
<a-menu-item key="2">Option 2</a-menu-item>
|
||||
</a-menu-item-group>
|
||||
<a-menu-item-group key="g2" title="Item 2">
|
||||
<a-menu-item key="3">Option 3</a-menu-item>
|
||||
<a-menu-item key="4">Option 4</a-menu-item>
|
||||
</a-menu-item-group>
|
||||
</a-sub-menu>
|
||||
<!-- <a-sub-menu key="sub2" @titleClick="titleClick">
|
||||
<template #title>
|
||||
<span>
|
||||
<AppstoreOutlined />
|
||||
<span>Navigation Two</span>
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item key="5">Option 5</a-menu-item>
|
||||
<a-menu-item key="6">Option 6</a-menu-item>
|
||||
<a-sub-menu key="sub3" title="Submenu">
|
||||
<a-menu-item key="7">Option 7</a-menu-item>
|
||||
<a-menu-item key="8">Option 8</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-sub-menu>
|
||||
<a-sub-menu key="sub4">
|
||||
<template #title>
|
||||
<span>
|
||||
<SettingOutlined />
|
||||
<span>Navigation Three</span>
|
||||
</span>
|
||||
</template>
|
||||
<a-menu-item key="9">Option 9</a-menu-item>
|
||||
<a-menu-item key="10">Option 10</a-menu-item>
|
||||
<a-menu-item key="11">Option 11</a-menu-item>
|
||||
<a-menu-item key="12">Option 12</a-menu-item>
|
||||
</a-sub-menu> -->
|
||||
</a-menu>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import demo from '../v2-doc/src/docs/tooltip/demo/index.vue';
|
||||
// import Affix from '../components/affix';
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from 'vue';
|
||||
import { MailOutlined, QqOutlined, AppstoreOutlined, SettingOutlined } from '@ant-design/icons-vue';
|
||||
export default defineComponent({
|
||||
components: {
|
||||
demo,
|
||||
// Affix,
|
||||
MailOutlined,
|
||||
QqOutlined,
|
||||
AppstoreOutlined,
|
||||
SettingOutlined,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
pStyle: {
|
||||
fontSize: '16px',
|
||||
color: 'rgba(0,0,0,0.85)',
|
||||
lineHeight: '24px',
|
||||
display: 'block',
|
||||
marginBottom: '16px',
|
||||
},
|
||||
pStyle2: {
|
||||
marginBottom: '24px',
|
||||
},
|
||||
setup() {
|
||||
const selectedKeys = ref<string[]>(['1']);
|
||||
const openKeys = ref<string[]>(['sub1']);
|
||||
const handleClick = (e: Event) => {
|
||||
console.log('click', e);
|
||||
};
|
||||
const titleClick = (e: Event) => {
|
||||
console.log('titleClick', e);
|
||||
};
|
||||
watch(
|
||||
() => openKeys,
|
||||
val => {
|
||||
console.log('openKeys', val);
|
||||
},
|
||||
);
|
||||
return {
|
||||
selectedKeys,
|
||||
openKeys,
|
||||
|
||||
handleClick,
|
||||
titleClick,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
showDrawer() {
|
||||
this.visible = true;
|
||||
},
|
||||
onClose() {
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
|
Loading…
Reference in New Issue