20260720172840
This commit is contained in:
1 parent
cda7f16d3a
commit
94207c77ff
3117 files changed
+201407
-1236
No files matched your search
@@ -0,0 +1,116 @@
|
||||
# _semantic
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { MailOutlined } from '@antdv-next/icons'
|
||||
import { computed, h, ref } from 'vue'
|
||||
import { SemanticPreview } from '@/components/semantic'
|
||||
import { useComponentLocale } from '@/composables/use-locale'
|
||||
import { locales } from '../locales'
|
||||
|
||||
const { t } = useComponentLocale(locales)
|
||||
|
||||
const mode = ref<'horizontal' | 'vertical' | 'inline'>('horizontal')
|
||||
const current = ref('mail')
|
||||
const divRef = ref<HTMLDivElement | null>(null)
|
||||
|
||||
const semantics = computed(() => {
|
||||
const baseLocale = [
|
||||
{ name: 'root', desc: t('root') },
|
||||
{ name: 'item', desc: t('item') },
|
||||
{ name: 'itemIcon', desc: t('itemIcon') },
|
||||
{ name: 'itemContent', desc: t('itemContent') },
|
||||
]
|
||||
const subMenuLocale = [
|
||||
{ name: 'subMenu.itemTitle', desc: t('subMenu.itemTitle') },
|
||||
{ name: 'subMenu.list', desc: t('subMenu.list') },
|
||||
{ name: 'subMenu.item', desc: t('subMenu.item') },
|
||||
{ name: 'subMenu.itemIcon', desc: t('subMenu.itemIcon') },
|
||||
{ name: 'subMenu.itemContent', desc: t('subMenu.itemContent') },
|
||||
]
|
||||
const groupLocale = [
|
||||
{ name: 'itemTitle', desc: t('itemTitle') },
|
||||
{ name: 'list', desc: t('list') },
|
||||
]
|
||||
|
||||
const additionalPopupLocale = mode.value !== 'inline' ? [{ name: 'popup', desc: t('popup') }] : []
|
||||
const additionalGroupLocale = mode.value !== 'horizontal' ? groupLocale : []
|
||||
|
||||
return [...baseLocale, ...additionalGroupLocale, ...additionalPopupLocale, ...subMenuLocale]
|
||||
})
|
||||
|
||||
const items = [
|
||||
{ label: 'Navigation One', key: 'mail', icon: h(MailOutlined) },
|
||||
{
|
||||
key: 'SubMenu',
|
||||
label: 'Navigation One',
|
||||
icon: h(MailOutlined),
|
||||
children: [
|
||||
{
|
||||
key: 'g1',
|
||||
label: 'Item 1',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1', icon: h(MailOutlined) },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const groupItem = [
|
||||
{
|
||||
key: 'grp',
|
||||
label: 'Group',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '13', label: 'Option 13' },
|
||||
{ key: '14', label: 'Option 14' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const itemList = computed(() => {
|
||||
return mode.value === 'horizontal' ? items : [...items, ...groupItem]
|
||||
})
|
||||
|
||||
function onClick(e: { key: string }) {
|
||||
current.value = e.key
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SemanticPreview
|
||||
component-name="Menu"
|
||||
:semantics="semantics"
|
||||
>
|
||||
<template #default="{ classes }">
|
||||
<a-flex ref="divRef" vertical gap="middle" align="center">
|
||||
<a-segmented
|
||||
:options="['horizontal', 'vertical', 'inline']"
|
||||
:value="mode"
|
||||
@change="(val: 'horizontal' | 'vertical' | 'inline') => mode = val"
|
||||
/>
|
||||
<div :style="{ height: '360px' }">
|
||||
<a-menu
|
||||
:selected-keys="[current]"
|
||||
:mode="mode"
|
||||
:items="itemList"
|
||||
:styles="{
|
||||
root: { width: mode === 'horizontal' ? '310px' : '230px' },
|
||||
popup: { root: { zIndex: 1 } },
|
||||
}"
|
||||
:open-keys="['SubMenu']"
|
||||
:get-popup-container="() => divRef!"
|
||||
:classes="classes"
|
||||
@click="onClick"
|
||||
/>
|
||||
</div>
|
||||
</a-flex>
|
||||
</template>
|
||||
</SemanticPreview>
|
||||
</template>
|
||||
```
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
# Custom Submenu Render
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Use the `popupRender` prop to customize submenu popup rendering.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType, MenuProps } from 'antdv-next'
|
||||
import { theme } from 'antdv-next'
|
||||
import { computed, h } from 'vue'
|
||||
|
||||
const { token } = theme.useToken()
|
||||
|
||||
const popupStyle = computed(() => ({
|
||||
padding: `${token.value.padding}px`,
|
||||
minWidth: '480px',
|
||||
background: token.value.colorBgElevated,
|
||||
borderRadius: `${token.value.borderRadiusLG}px`,
|
||||
boxShadow: token.value.boxShadowSecondary,
|
||||
}))
|
||||
|
||||
const headerStyle = computed(() => ({
|
||||
margin: 0,
|
||||
paddingBottom: `${token.value.paddingXS}px`,
|
||||
borderBottom: `1px solid ${token.value.colorSplit}`,
|
||||
}))
|
||||
|
||||
const cardStyle = computed(() => ({
|
||||
padding: `${token.value.paddingSM}px`,
|
||||
}))
|
||||
|
||||
const cardItemStyle = computed(() => ({
|
||||
borderRadius: `${token.value.borderRadius}px`,
|
||||
transition: `all ${token.value.motionDurationSlow}`,
|
||||
cursor: 'pointer',
|
||||
}))
|
||||
|
||||
function renderMenuCard(title: string, description: string) {
|
||||
return h('div', { class: 'menu-card', style: cardStyle.value }, [
|
||||
h('div', { class: 'menu-card-title' }, title),
|
||||
h('div', { class: 'menu-card-desc' }, description),
|
||||
])
|
||||
}
|
||||
|
||||
const menuItems = computed<MenuItemType[]>(() => [
|
||||
{
|
||||
key: 'home',
|
||||
label: 'Home',
|
||||
},
|
||||
{
|
||||
key: 'features',
|
||||
label: 'Features',
|
||||
children: [
|
||||
{
|
||||
key: 'getting-started',
|
||||
label: renderMenuCard('Getting Started', 'Quick start guide and learn the basics.'),
|
||||
class: 'menu-card-item',
|
||||
style: cardItemStyle.value,
|
||||
},
|
||||
{
|
||||
key: 'components',
|
||||
label: renderMenuCard('Components', 'Explore our component library.'),
|
||||
class: 'menu-card-item',
|
||||
style: cardItemStyle.value,
|
||||
},
|
||||
{
|
||||
key: 'templates',
|
||||
label: renderMenuCard('Templates', 'Ready-to-use template designs.'),
|
||||
class: 'menu-card-item',
|
||||
style: cardItemStyle.value,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'resources',
|
||||
label: 'Resources',
|
||||
children: [
|
||||
{
|
||||
key: 'blog',
|
||||
label: renderMenuCard('Blog', 'Latest updates and articles.'),
|
||||
class: 'menu-card-item',
|
||||
style: cardItemStyle.value,
|
||||
},
|
||||
{
|
||||
key: 'community',
|
||||
label: renderMenuCard('Community', 'Join our developer community.'),
|
||||
class: 'menu-card-item',
|
||||
style: cardItemStyle.value,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const popupRender: MenuProps['popupRender'] = (node, info) => {
|
||||
const title = info.item.title || info.item.key
|
||||
return h('div', { class: 'menu-popup', style: popupStyle.value }, [
|
||||
h('div', { class: 'menu-popup-title', style: headerStyle.value }, title),
|
||||
node,
|
||||
])
|
||||
}
|
||||
|
||||
const themeConfig = {
|
||||
components: {
|
||||
Menu: {
|
||||
popupBg: '#fff',
|
||||
horizontalItemSelectedColor: '#1677ff',
|
||||
horizontalItemHoverColor: '#1677ff',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-config-provider :theme="themeConfig">
|
||||
<a-menu mode="horizontal" :items="menuItems" :popup-render="popupRender" />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.menu-popup :deep(.ant-menu) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
column-gap: 16px;
|
||||
row-gap: 8px;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-popup :deep(.ant-menu-item) {
|
||||
height: auto;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.menu-popup :deep(.menu-card-item) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-popup :deep(.menu-card-item:hover) {
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.menu-popup :deep(.ant-menu-title-content) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu-card-title {
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.menu-card-desc {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -0,0 +1,80 @@
|
||||
# Top Navigation
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Horizontal top navigation menu.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@antdv-next/icons'
|
||||
import { h, ref } from 'vue'
|
||||
|
||||
const current = ref('mail')
|
||||
|
||||
function handleClick(e: any) {
|
||||
console.log('click ', e)
|
||||
current.value = e.key
|
||||
}
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
label: 'Navigation One',
|
||||
key: 'mail',
|
||||
icon: MailOutlined,
|
||||
},
|
||||
{
|
||||
label: 'Navigation Two',
|
||||
key: 'app',
|
||||
icon: AppstoreOutlined,
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
label: 'Navigation Three - Submenu',
|
||||
key: 'SubMenu',
|
||||
icon: SettingOutlined,
|
||||
children: [
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Item 1',
|
||||
children: [
|
||||
{ label: 'Option 1', key: 'setting:1' },
|
||||
{ label: 'Option 2', key: 'setting:2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Item 2',
|
||||
children: [
|
||||
{ label: 'Option 3', key: 'setting:3' },
|
||||
{ label: 'Option 4', key: 'setting:4' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'alipay',
|
||||
label: h(
|
||||
'a',
|
||||
{
|
||||
href: 'https://ant.design',
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer',
|
||||
},
|
||||
'Navigation Four - Link',
|
||||
),
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
:selected-keys="[current]"
|
||||
mode="horizontal"
|
||||
:items="items"
|
||||
@click="handleClick"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
# Collapsed inline menu
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Inline menu could be collapsed.
|
||||
|
||||
Here is [a complete demo](../../layout/docs.md/#layout-demo-side) with sider layout.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
ContainerOutlined,
|
||||
DesktopOutlined,
|
||||
MailOutlined,
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
PieChartOutlined,
|
||||
} from '@antdv-next/icons'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const collapsed = ref(false)
|
||||
|
||||
function toggleCollapsed() {
|
||||
collapsed.value = !collapsed.value
|
||||
}
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{ key: '1', icon: PieChartOutlined, label: 'Option 1' },
|
||||
{ key: '2', icon: DesktopOutlined, label: 'Option 2' },
|
||||
{ key: '3', icon: ContainerOutlined, label: 'Option 3' },
|
||||
{
|
||||
key: 'sub1',
|
||||
label: 'Navigation One',
|
||||
icon: MailOutlined,
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
label: 'Navigation Two',
|
||||
icon: AppstoreOutlined,
|
||||
children: [
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
{
|
||||
key: 'sub3',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '11', label: 'Option 11' },
|
||||
{ key: '12', label: 'Option 12' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="width: 256px">
|
||||
<a-button
|
||||
type="primary"
|
||||
style="margin-bottom: 16px"
|
||||
@click="toggleCollapsed"
|
||||
>
|
||||
<MenuUnfoldOutlined v-if="collapsed" />
|
||||
<MenuFoldOutlined v-else />
|
||||
</a-button>
|
||||
<a-menu
|
||||
:default-selected-keys="['1']"
|
||||
:default-open-keys="['sub1']"
|
||||
mode="inline"
|
||||
theme="dark"
|
||||
:inline-collapsed="collapsed"
|
||||
:items="items"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,100 @@
|
||||
# Inline menu
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Vertical menu with inline submenus.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@antdv-next/icons'
|
||||
import { ref } from 'vue'
|
||||
|
||||
function handleClick(e: any) {
|
||||
console.log('click ', e)
|
||||
}
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: 'sub1',
|
||||
label: 'Navigation One',
|
||||
icon: MailOutlined,
|
||||
children: [
|
||||
{
|
||||
key: 'g1',
|
||||
label: 'Item 1',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'g2',
|
||||
label: 'Item 2',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '3', label: 'Option 3' },
|
||||
{ key: '4', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
label: 'Navigation Two',
|
||||
icon: AppstoreOutlined,
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
{
|
||||
key: 'sub3',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'sub4',
|
||||
label: 'Navigation Three',
|
||||
icon: SettingOutlined,
|
||||
children: [
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
{ key: '11', label: 'Option 11' },
|
||||
{ key: '12', label: 'Option 12' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'grp',
|
||||
label: 'Group',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '13', label: 'Option 13' },
|
||||
{ key: '14', label: 'Option 14' },
|
||||
],
|
||||
},
|
||||
]
|
||||
const openKeys = ref(['sub1'])
|
||||
const selectedKeys = ref(['1'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
v-model:open-keys="openKeys"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
style="width: 256px"
|
||||
mode="inline"
|
||||
:items="items"
|
||||
@click="handleClick"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
# SFC Mode (Nested)
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Build an SFC menu with declarative `a-menu-item` and `a-sub-menu`, equivalent to the `items` structure.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
CalendarOutlined,
|
||||
LinkOutlined,
|
||||
MailOutlined,
|
||||
SettingOutlined,
|
||||
} from '@antdv-next/icons'
|
||||
import { computed, ref, shallowRef, watch } from 'vue'
|
||||
|
||||
const mode = ref<'vertical' | 'inline'>('inline')
|
||||
const theme = ref<'dark' | 'light'>('light')
|
||||
const collapsed = ref(false)
|
||||
const openKeys = ref<string[]>(['sub1'])
|
||||
const cachedOpenKeys = ref<string[]>(['sub1'])
|
||||
const inlineCollapsed = computed(() => mode.value === 'inline' && collapsed.value)
|
||||
const expandAllChecked = computed(() => !collapsed.value && openKeys.value.length > 0)
|
||||
|
||||
watch(openKeys, (keys) => {
|
||||
if (!collapsed.value) {
|
||||
cachedOpenKeys.value = [...keys]
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
function changeExpanded(value: boolean) {
|
||||
if (collapsed.value) {
|
||||
return
|
||||
}
|
||||
|
||||
openKeys.value = value ? ['sub1', 'sub1-2', 'sub2'] : []
|
||||
}
|
||||
|
||||
function changeCollapsed(value: boolean) {
|
||||
collapsed.value = value
|
||||
if (value) {
|
||||
openKeys.value = []
|
||||
}
|
||||
else {
|
||||
openKeys.value = [...cachedOpenKeys.value]
|
||||
}
|
||||
}
|
||||
|
||||
function changeMode(value: boolean) {
|
||||
mode.value = value ? 'vertical' : 'inline'
|
||||
}
|
||||
|
||||
function changeTheme(value: boolean) {
|
||||
theme.value = value ? 'dark' : 'light'
|
||||
}
|
||||
const selectedKeys = shallowRef(['1'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-switch :checked="mode === 'vertical'" @change="changeMode" />
|
||||
Change Mode
|
||||
<a-divider type="vertical" />
|
||||
<a-switch :checked="collapsed" @change="changeCollapsed" />
|
||||
Inline Collapsed
|
||||
<a-divider type="vertical" />
|
||||
<a-switch :checked="expandAllChecked" :disabled="collapsed" @change="changeExpanded" />
|
||||
Expand All
|
||||
<a-divider type="vertical" />
|
||||
<a-switch :checked="theme === 'dark'" @change="changeTheme" />
|
||||
Change Style
|
||||
<br>
|
||||
<br>
|
||||
<div style="width: 256px">
|
||||
<a-menu
|
||||
v-model:selected-keys="selectedKeys"
|
||||
v-model:open-keys="openKeys"
|
||||
:inline-collapsed="inlineCollapsed"
|
||||
:mode="mode"
|
||||
:theme="theme"
|
||||
>
|
||||
<a-menu-item key="1">
|
||||
<template #icon>
|
||||
<MailOutlined />
|
||||
</template>
|
||||
Navigation One
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item key="2">
|
||||
<template #icon>
|
||||
<CalendarOutlined />
|
||||
</template>
|
||||
Navigation Two
|
||||
</a-menu-item>
|
||||
|
||||
<a-sub-menu key="sub1">
|
||||
<template #icon>
|
||||
<AppstoreOutlined />
|
||||
</template>
|
||||
<template #title>
|
||||
Navigation Two
|
||||
</template>
|
||||
|
||||
<a-menu-item key="3">
|
||||
Option 3
|
||||
</a-menu-item>
|
||||
<a-menu-item key="4">
|
||||
Option 4
|
||||
</a-menu-item>
|
||||
|
||||
<a-sub-menu key="sub1-2">
|
||||
<template #title>
|
||||
Submenu
|
||||
</template>
|
||||
|
||||
<a-menu-item key="5">
|
||||
Option 5
|
||||
</a-menu-item>
|
||||
<a-menu-item key="6">
|
||||
Option 6
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
</a-sub-menu>
|
||||
|
||||
<a-sub-menu key="sub2">
|
||||
<template #icon>
|
||||
<SettingOutlined />
|
||||
</template>
|
||||
<template #title>
|
||||
Navigation Three
|
||||
</template>
|
||||
|
||||
<a-menu-item key="7">
|
||||
Option 7
|
||||
</a-menu-item>
|
||||
<a-menu-item key="8">
|
||||
Option 8
|
||||
</a-menu-item>
|
||||
<a-menu-item key="9">
|
||||
Option 9
|
||||
</a-menu-item>
|
||||
<a-menu-item key="10">
|
||||
Option 10
|
||||
</a-menu-item>
|
||||
</a-sub-menu>
|
||||
|
||||
<a-menu-item key="link">
|
||||
<template #icon>
|
||||
<LinkOutlined />
|
||||
</template>
|
||||
<a
|
||||
href="https://antdv-next.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Antdv Next
|
||||
</a>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,118 @@
|
||||
# Open current submenu only
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Click the menu and you will see that all the other menus gets collapsed to keep the entire menu compact.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@antdv-next/icons'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: '1',
|
||||
icon: MailOutlined,
|
||||
label: 'Navigation One',
|
||||
children: [
|
||||
{ key: '11', label: 'Option 1' },
|
||||
{ key: '12', label: 'Option 2' },
|
||||
{ key: '13', label: 'Option 3' },
|
||||
{ key: '14', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
icon: AppstoreOutlined,
|
||||
label: 'Navigation Two',
|
||||
children: [
|
||||
{ key: '21', label: 'Option 1' },
|
||||
{ key: '22', label: 'Option 2' },
|
||||
{
|
||||
key: '23',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '231', label: 'Option 1' },
|
||||
{ key: '232', label: 'Option 2' },
|
||||
{ key: '233', label: 'Option 3' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '24',
|
||||
label: 'Submenu 2',
|
||||
children: [
|
||||
{ key: '241', label: 'Option 1' },
|
||||
{ key: '242', label: 'Option 2' },
|
||||
{ key: '243', label: 'Option 3' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
icon: SettingOutlined,
|
||||
label: 'Navigation Three',
|
||||
children: [
|
||||
{ key: '31', label: 'Option 1' },
|
||||
{ key: '32', label: 'Option 2' },
|
||||
{ key: '33', label: 'Option 3' },
|
||||
{ key: '34', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
interface LevelKeysProps {
|
||||
key?: string
|
||||
children?: LevelKeysProps[]
|
||||
}
|
||||
|
||||
function getLevelKeys(itemList: LevelKeysProps[]) {
|
||||
const keyLevelMap: Record<string, number> = {}
|
||||
const func = (list: LevelKeysProps[], level = 1) => {
|
||||
list.forEach((item) => {
|
||||
if (item.key) {
|
||||
keyLevelMap[item.key] = level
|
||||
}
|
||||
if (item.children) {
|
||||
func(item.children, level + 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
func(itemList)
|
||||
return keyLevelMap
|
||||
}
|
||||
|
||||
const levelKeys = getLevelKeys(items as LevelKeysProps[])
|
||||
const stateOpenKeys = ref<string[]>(['2', '23'])
|
||||
|
||||
function onOpenChange(openKeys: string[]) {
|
||||
const currentOpenKey = openKeys.find(key => !stateOpenKeys.value.includes(key))
|
||||
if (currentOpenKey !== undefined) {
|
||||
const repeatIndex = openKeys
|
||||
.filter(key => key !== currentOpenKey)
|
||||
.findIndex(key => levelKeys[key] === levelKeys[currentOpenKey])
|
||||
|
||||
stateOpenKeys.value = openKeys
|
||||
.filter((_, index) => index !== repeatIndex)
|
||||
.filter(key => levelKeys[key]! <= levelKeys[currentOpenKey]!)
|
||||
}
|
||||
else {
|
||||
stateOpenKeys.value = openKeys
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
mode="inline"
|
||||
style="width: 256px"
|
||||
:default-selected-keys="['231']"
|
||||
:open-keys="stateOpenKeys"
|
||||
:items="items"
|
||||
@open-change="onOpenChange"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
# Slot Render (items mode)
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Customize menu item rendering with `labelRender`, `extraRender`, and `iconRender` slots.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { ClockCircleFilled } from '@antdv-next/icons'
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: 'sub1',
|
||||
label: 'Navigation One',
|
||||
children: [
|
||||
{
|
||||
key: 'g1',
|
||||
label: 'Item 1',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'g2',
|
||||
label: 'Item 2',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '3', label: 'Option 3' },
|
||||
{ key: '4', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
label: 'Navigation Two',
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
{
|
||||
key: 'sub3',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
key: 'sub4',
|
||||
label: 'Navigation Three',
|
||||
children: [
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
{ key: '11', label: 'Option 11' },
|
||||
{ key: '12', label: 'Option 12' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'grp',
|
||||
label: 'Group',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '13', label: 'Option 13' },
|
||||
{ key: '14', label: 'Option 14' },
|
||||
],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
style="width: 320px"
|
||||
:default-open-keys="['sub1']"
|
||||
:default-selected-keys="['1']"
|
||||
mode="inline"
|
||||
:items="items"
|
||||
>
|
||||
<template #labelRender="item">
|
||||
<span class="menu-slot-label">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #extraRender="item">
|
||||
<span
|
||||
v-if="item?.key && !item?.children && item?.type !== 'group'"
|
||||
class="menu-slot-extra"
|
||||
>
|
||||
#{{ item.key }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #iconRender="item">
|
||||
<span v-if="item?.key" class="menu-slot-icon">
|
||||
<ClockCircleFilled />
|
||||
</span>
|
||||
</template>
|
||||
</a-menu>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.menu-slot-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.menu-slot-extra {
|
||||
color: #1677ff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.menu-slot-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
background: #1677ff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# Custom semantic dom styling
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType, MenuProps } from 'antdv-next'
|
||||
import { useCssModule } from 'vue'
|
||||
|
||||
const moduleStyles = useCssModule()
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: 'SubMenu',
|
||||
label: 'Navigation One',
|
||||
children: [
|
||||
{
|
||||
key: 'g1',
|
||||
label: 'Item 1',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ key: 'mail', label: 'Navigation Two' },
|
||||
]
|
||||
|
||||
const classes: MenuProps['classes'] = {
|
||||
root: moduleStyles.root,
|
||||
item: moduleStyles.item,
|
||||
}
|
||||
|
||||
const styles: MenuProps['styles'] = {
|
||||
root: { border: '1px solid #f0f0f0', padding: '8px', borderRadius: '4px' },
|
||||
item: { color: '#1677ff' },
|
||||
subMenu: { list: { color: '#fa541c' } },
|
||||
}
|
||||
|
||||
const stylesFn: MenuProps['styles'] = (info) => {
|
||||
const hasSub = info.props.items?.[0]
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: hasSub ? 'rgba(240,249,255, 0.6)' : '#fff',
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-flex vertical gap="middle">
|
||||
<a-menu :classes="classes" :items="items" :styles="styles" />
|
||||
<a-menu mode="inline" :classes="classes" :items="items" :styles="stylesFn" />
|
||||
</a-flex>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
.root {
|
||||
border: 1px solid #f0f0f0;
|
||||
max-width: 600px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.item {
|
||||
color: #1677ff;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
# Sub-menu theme
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
You can config SubMenu theme with `theme` prop to enable different theme color effect. This sample is dark for root and light for SubMenu.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { MailOutlined } from '@antdv-next/icons'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const menuTheme = ref<'dark' | 'light'>('light')
|
||||
const current = ref('1')
|
||||
|
||||
function changeTheme(value: boolean) {
|
||||
menuTheme.value = value ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
function onClick(e: any) {
|
||||
current.value = e.key
|
||||
}
|
||||
|
||||
const items = computed<MenuItemType[]>(() => [
|
||||
{
|
||||
key: 'sub1',
|
||||
icon: MailOutlined,
|
||||
label: 'Navigation One',
|
||||
theme: menuTheme.value,
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
{ key: '3', label: 'Option 3' },
|
||||
],
|
||||
},
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<a-switch
|
||||
:checked="menuTheme === 'dark'"
|
||||
checked-children="Dark"
|
||||
un-checked-children="Light"
|
||||
@change="changeTheme"
|
||||
/>
|
||||
<br>
|
||||
<br>
|
||||
<a-menu
|
||||
style="width: 256px"
|
||||
:open-keys="['sub1']"
|
||||
:selected-keys="[current]"
|
||||
mode="vertical"
|
||||
theme="dark"
|
||||
:items="items"
|
||||
:get-popup-container="(node: any) => node.parentNode"
|
||||
@click="onClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,104 @@
|
||||
# Switch the menu type
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Show the dynamic switching mode (between `inline` and `vertical`).
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import {
|
||||
AppstoreOutlined,
|
||||
CalendarOutlined,
|
||||
LinkOutlined,
|
||||
MailOutlined,
|
||||
SettingOutlined,
|
||||
} from '@antdv-next/icons'
|
||||
import { h, ref } from 'vue'
|
||||
|
||||
const mode = ref<'vertical' | 'inline'>('inline')
|
||||
const theme = ref<'dark' | 'light'>('light')
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: '1',
|
||||
icon: MailOutlined,
|
||||
label: 'Navigation One',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
icon: CalendarOutlined,
|
||||
label: 'Navigation Two',
|
||||
},
|
||||
{
|
||||
key: 'sub1',
|
||||
label: 'Navigation Two',
|
||||
icon: AppstoreOutlined,
|
||||
children: [
|
||||
{ key: '3', label: 'Option 3' },
|
||||
{ key: '4', label: 'Option 4' },
|
||||
{
|
||||
key: 'sub1-2',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
label: 'Navigation Three',
|
||||
icon: SettingOutlined,
|
||||
children: [
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'link',
|
||||
icon: LinkOutlined,
|
||||
label: h(
|
||||
'a',
|
||||
{
|
||||
href: 'https://ant.design',
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer',
|
||||
},
|
||||
'Antdv Next',
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
function changeMode(value: boolean) {
|
||||
mode.value = value ? 'vertical' : 'inline'
|
||||
}
|
||||
|
||||
function changeTheme(value: boolean) {
|
||||
theme.value = value ? 'dark' : 'light'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-switch :checked="mode === 'vertical'" @change="changeMode" />
|
||||
Change Mode
|
||||
<a-divider type="vertical" />
|
||||
<a-switch :checked="theme === 'dark'" @change="changeTheme" />
|
||||
Change Style
|
||||
<br>
|
||||
<br>
|
||||
<a-menu
|
||||
style="width: 256px"
|
||||
:default-selected-keys="['1']"
|
||||
:default-open-keys="['sub1']"
|
||||
:mode="mode"
|
||||
:theme="theme"
|
||||
:items="items"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
# Menu Themes
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
There are two built-in themes: `light` and `dark`. The default value is `light`.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@antdv-next/icons'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const theme = ref<'dark' | 'light'>('dark')
|
||||
const current = ref('1')
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: 'sub1',
|
||||
label: 'Navigation One',
|
||||
icon: MailOutlined,
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
{ key: '3', label: 'Option 3' },
|
||||
{ key: '4', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
label: 'Navigation Two',
|
||||
icon: AppstoreOutlined,
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
{
|
||||
key: 'sub3',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub4',
|
||||
label: 'Navigation Three',
|
||||
icon: SettingOutlined,
|
||||
children: [
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
{ key: '11', label: 'Option 11' },
|
||||
{ key: '12', label: 'Option 12' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function changeTheme(value: boolean) {
|
||||
theme.value = value ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
function onClick(e: any) {
|
||||
console.log('click ', e)
|
||||
current.value = e.key
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-switch
|
||||
:checked="theme === 'dark'"
|
||||
checked-children="Dark"
|
||||
un-checked-children="Light"
|
||||
@change="changeTheme"
|
||||
/>
|
||||
<br>
|
||||
<br>
|
||||
<a-menu
|
||||
:theme="theme"
|
||||
:selected-keys="[current]"
|
||||
style="width: 256px"
|
||||
:default-open-keys="['sub1']"
|
||||
mode="inline"
|
||||
:items="items"
|
||||
@click="onClick"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
# Vertical menu
|
||||
|
||||
## Description (en-US)
|
||||
|
||||
Submenus open as pop-ups.
|
||||
|
||||
## Source
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { MenuItemType } from 'antdv-next'
|
||||
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@antdv-next/icons'
|
||||
|
||||
function onClick(e: any) {
|
||||
console.log('click', e)
|
||||
}
|
||||
|
||||
const items: MenuItemType[] = [
|
||||
{
|
||||
key: 'sub1',
|
||||
icon: MailOutlined,
|
||||
label: 'Navigation One',
|
||||
children: [
|
||||
{
|
||||
key: '1-1',
|
||||
label: 'Item 1',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '1', label: 'Option 1' },
|
||||
{ key: '2', label: 'Option 2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: '1-2',
|
||||
label: 'Item 2',
|
||||
type: 'group',
|
||||
children: [
|
||||
{ key: '3', label: 'Option 3' },
|
||||
{ key: '4', label: 'Option 4' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub2',
|
||||
icon: AppstoreOutlined,
|
||||
label: 'Navigation Two',
|
||||
children: [
|
||||
{ key: '5', label: 'Option 5' },
|
||||
{ key: '6', label: 'Option 6' },
|
||||
{
|
||||
key: 'sub3',
|
||||
label: 'Submenu',
|
||||
children: [
|
||||
{ key: '7', label: 'Option 7' },
|
||||
{ key: '8', label: 'Option 8' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sub4',
|
||||
label: 'Navigation Three',
|
||||
icon: SettingOutlined,
|
||||
children: [
|
||||
{ key: '9', label: 'Option 9' },
|
||||
{ key: '10', label: 'Option 10' },
|
||||
{ key: '11', label: 'Option 11' },
|
||||
{ key: '12', label: 'Option 12' },
|
||||
],
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-menu
|
||||
style="width: 256px"
|
||||
mode="vertical"
|
||||
:items="items"
|
||||
@click="onClick"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Menu
|
||||
description: A versatile menu for navigation.
|
||||
---
|
||||
|
||||
## When To Use
|
||||
Navigation is an important part of any website, as a good navigation setup allows users to move around the site quickly and efficiently. Antdv Next offers two navigation options: top and side. Top navigation provides all the categories and functions of the website. Side navigation provides the multi-level structure of the website.
|
||||
|
||||
More layouts with navigation: [Layout](../layout/docs.md).
|
||||
|
||||
## Notes for developers
|
||||
- Menu is rendered as a `ul` element, so it only supports [`li` and `script-supporting` elements](https://html.spec.whatwg.org/multipage/grouping-content.html#the-ul-element) as children nodes. Your customized node should be wrapped by `Menu.Item`.
|
||||
- Menu needs to collect its node structure, so its children should be `Menu.*` or encapsulated components.
|
||||
|
||||
## Demos
|
||||
|
||||
| Demo | Path |
|
||||
| --- | --- |
|
||||
| Top Navigation | demo/horizontal.md |
|
||||
| Inline menu | demo/inline.md |
|
||||
| Collapsed inline menu | demo/inline-collapsed.md |
|
||||
| Open current submenu only | demo/sider-current.md |
|
||||
| Vertical menu | demo/vertical.md |
|
||||
| Menu Themes | demo/theme.md |
|
||||
| Sub-menu theme | demo/submenu-theme.md |
|
||||
| Switch the menu type | demo/switch-mode.md |
|
||||
| SFC Mode (Nested) | demo/sfc.md |
|
||||
| Slot Render (items mode) | demo/slot-render.md |
|
||||
| Custom semantic dom styling | demo/style-class.md |
|
||||
| Custom Submenu Render | demo/custom-popup-render.md |
|
||||
|
||||
## API
|
||||
|
||||
Common props ref:[Common props](../../docs/vue/common-props.md)
|
||||
|
||||
### Props
|
||||
|
||||
| Property | Description | Type | Default | Version | [Global Config](../config-provider/docs.md#component-config) |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| classes | Customize class for each semantic structure inside the component. Supports object or function | MenuClassNamesType | - | - | ✓ |
|
||||
| styles | Customize inline style for each semantic structure inside the component. Supports object or function | MenuStylesType | - | - | ✓ |
|
||||
| rootClass | Root container class | string | - | - | × |
|
||||
| defaultOpenKeys | Array with the keys of default opened sub menus | string[] | - | - | × |
|
||||
| defaultSelectedKeys | Array with the keys of default selected menu items | string[] | - | - | × |
|
||||
| expandIcon | Custom expand icon of submenu | VueNode \| ((props: SubMenuProps & { isSubMenu: boolean }) => VueNode) | - | - | ✓ |
|
||||
| forceSubMenuRender | Render submenu into DOM before it becomes visible | boolean | false | - | × |
|
||||
| inlineCollapsed | Specifies the collapsed status when menu is inline mode | boolean | - | - | × |
|
||||
| inlineIndent | Indent (in pixels) of inline menu items on each level | number | 24 | - | × |
|
||||
| items | Menu item content | ItemType[] | - | - | × |
|
||||
| mode | Type of menu | `vertical` \| `horizontal` \| `inline` | `vertical` | - | × |
|
||||
| multiple | Allows selection of multiple items | boolean | false | - | × |
|
||||
| openKeys | Array with the keys of currently opened sub-menus | string[] | - | - | × |
|
||||
| overflowedIndicator | Customized the ellipsis icon when menu is collapsed horizontally | VueNode | `<EllipsisOutlined />` | - | × |
|
||||
| selectable | Allows selecting menu items | boolean | true | - | × |
|
||||
| selectedKeys | Array with the keys of currently selected menu items | string[] | - | - | × |
|
||||
| subMenuCloseDelay | Delay time to hide submenu when mouse leaves (in seconds) | number | 0.1 | - | × |
|
||||
| subMenuOpenDelay | Delay time to show submenu when mouse enters, (in seconds) | number | 0 | - | × |
|
||||
| theme | Color theme of the menu | `light` \| `dark` | `light` | - | × |
|
||||
| triggerSubMenuAction | Which action can trigger submenu open/close | `hover` \| `click` | `hover` | - | × |
|
||||
| getPopupContainer | To set the container of the submenu popup. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition | (triggerNode: HTMLElement) => HTMLElement | () => document.body | - | × |
|
||||
| itemIcon | Custom menu item icon render | (props: MenuItemProps & RenderIconInfo) => any | - | - | × |
|
||||
| labelRender | Custom label render | (item: RenderItem) => any | - | - | × |
|
||||
| iconRender | Custom icon render (items mode) | (item: RenderItem) => any | - | - | × |
|
||||
| extraRender | Custom extra render | (item: RenderItem) => any | - | - | × |
|
||||
| popupRender | Custom popup renderer for submenu | (node: VueNode, info: { item: SubMenuProps; keys: string[] }) => VueNode | - | - | × |
|
||||
|
||||
### Events
|
||||
|
||||
| Event | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| click | Called when a menu item is clicked | (info: MenuInfo) => void | - |
|
||||
| select | Called when a menu item is selected | (info: SelectInfo) => void | - |
|
||||
| deselect | Called when a menu item is deselected (multiple mode only) | (info: SelectInfo) => void | - |
|
||||
| openChange | Called when sub-menus are opened or closed | (openKeys: string[]) => void | - |
|
||||
|
||||
### Slots
|
||||
|
||||
| Slot | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| expandIcon | Custom expand icon | () => any | - |
|
||||
| labelRender | Customize label content | (item: RenderItem) => any | - |
|
||||
| iconRender | Customize item icon (items mode) | (item: RenderItem) => any | - |
|
||||
| extraRender | Customize extra content | (item: RenderItem) => any | - |
|
||||
| itemIcon | Custom item icon | (props: MenuItemProps & RenderIconInfo) => any | - |
|
||||
|
||||
### Methods
|
||||
|
||||
| Method | Description | Type | Version |
|
||||
| --- | --- | --- | --- |
|
||||
| menu | Menu ref | VcMenuRef \| null | - |
|
||||
| focus | Focus menu | (options?: FocusOptions) => void | - |
|
||||
|
||||
## Types
|
||||
|
||||
### ItemType
|
||||
|
||||
> type ItemType = MenuItemType | SubMenuType | MenuItemGroupType | MenuDividerType | null;
|
||||
|
||||
#### MenuItemType
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| danger | Display the danger style | boolean | false | - |
|
||||
| disabled | Whether menu item is disabled | boolean | false | - |
|
||||
| extra | The extra of the menu item | VueNode | - | - |
|
||||
| icon | The icon of the menu item | VueNode | - | - |
|
||||
| key | Unique ID of the menu item | string | - | - |
|
||||
| label | Menu label | VueNode | - | - |
|
||||
| title | Set display title for collapsed item | string | - | - |
|
||||
|
||||
#### SubMenuType
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| children | Sub-menus or sub-menu items | ItemType[] | - | - |
|
||||
| disabled | Whether sub-menu is disabled | boolean | false | - |
|
||||
| icon | Icon of sub menu | VueNode | - | - |
|
||||
| key | Unique ID of the sub-menu | string | - | - |
|
||||
| label | Menu label | VueNode | - | - |
|
||||
| popupClassName | Sub-menu class name, not working when `mode="inline"` | string | - | - |
|
||||
| popupOffset | Sub-menu offset, not working when `mode="inline"` | [number, number] | - | - |
|
||||
| theme | Color theme of the SubMenu (inherits from Menu by default) | `light` \| `dark` | - | - |
|
||||
| onTitleClick | Callback executed when the sub-menu title is clicked | (info: { key: string; domEvent: MouseEvent }) => void | - | - |
|
||||
| popupRender | Custom popup renderer for current sub-menu | (node: VueNode, info: { item: SubMenuProps; keys: string[] }) => VueNode | - | - |
|
||||
|
||||
#### MenuItemGroupType
|
||||
|
||||
Define `type` as `group` to make as group:
|
||||
|
||||
```ts
|
||||
const groupItem = {
|
||||
type: 'group', // Must have
|
||||
label: 'My Group',
|
||||
children: [],
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| children | Sub-menu items | MenuItemType[] | - | - |
|
||||
| label | The title of the group | VueNode | - | - |
|
||||
|
||||
#### MenuDividerType
|
||||
|
||||
Divider line in between menu items, only used in vertical popup Menu or Dropdown Menu. Need define the `type` as `divider`:
|
||||
|
||||
```ts
|
||||
const dividerItem = {
|
||||
type: 'divider', // Must have
|
||||
}
|
||||
```
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| dashed | Whether line is dashed | boolean | false | - |
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why will Menu's children be rendered twice?
|
||||
Menu collects structure info with [twice-render](https://github.com/react-component/menu/blob/f4684514096d6b7123339cbe72e7b0f68db0bce2/src/Menu.tsx#L543) to support HOC usage. Merging into one render may cause the logic to become much more complex. Contributions to help improve the collection logic are welcomed.
|
||||
|
||||
### Why Menu do not responsive collapse in Flex layout?
|
||||
Menu will render fully item in flex layout and then collapse it. You need tell flex not consider Menu width to enable responsive ([online demo](https://codesandbox.io/s/ding-bu-dao-hang-antd-4-21-7-forked-5e3imy?file=/demo.js)):
|
||||
|
||||
```html
|
||||
<div style="display: flex;">
|
||||
<div style="flex: none;">Some Content</div>
|
||||
<a-menu style="min-width: 0; flex: auto;" />
|
||||
</div>
|
||||
```
|
||||
|
||||
## Semantic DOM
|
||||
|
||||
| _semantic | demo/_semantic.md |
|
||||
@@ -0,0 +1,80 @@
|
||||
# menu Token
|
||||
|
||||
Use these variables through `theme.components.Menu`. This document lists definitions only and does not include values.
|
||||
|
||||
```vue
|
||||
<a-config-provider
|
||||
:theme="{
|
||||
token: {
|
||||
// Global token configuration
|
||||
},
|
||||
components: {
|
||||
Menu: {
|
||||
// Token name: value
|
||||
},
|
||||
},
|
||||
}"
|
||||
>
|
||||
...
|
||||
</a-config-provider>
|
||||
```
|
||||
|
||||
## Token List
|
||||
|
||||
| Token | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `dropdownWidth` | `string \| number` | Width of popup menu |
|
||||
| `zIndexPopup` | `number` | z-index of popup menu |
|
||||
| `groupTitleColor` | `string` | Color of group title text |
|
||||
| `groupTitleLineHeight` | `string \| number` | line-height of group title |
|
||||
| `groupTitleFontSize` | `number` | font-size of group title |
|
||||
| `itemBorderRadius` | `number` | Radius of menu item |
|
||||
| `subMenuItemBorderRadius` | `number` | Radius of sub-menu item |
|
||||
| `itemColor` | `string` | Color of menu item text |
|
||||
| `itemHoverColor` | `string` | Hover color of menu item text |
|
||||
| `horizontalItemHoverColor` | `string` | Hover color of horizontal menu item text |
|
||||
| `itemSelectedColor` | `string` | Color of selected menu item text |
|
||||
| `subMenuItemSelectedColor` | `string` | Color of submenu title when submenu has selected item |
|
||||
| `horizontalItemSelectedColor` | `string` | Color of selected horizontal menu item text |
|
||||
| `itemDisabledColor` | `string` | Color of disabled menu item text |
|
||||
| `dangerItemColor` | `string` | Color of danger menu item text |
|
||||
| `dangerItemHoverColor` | `string` | Hover color of danger menu item text |
|
||||
| `dangerItemSelectedColor` | `string` | Color of selected danger menu item text |
|
||||
| `dangerItemActiveBg` | `string` | Background color of danger menu item when active |
|
||||
| `dangerItemSelectedBg` | `string` | Background color of selected danger menu item |
|
||||
| `itemBg` | `string` | - |
|
||||
| `itemHoverBg` | `string` | Background color of menu item when hover |
|
||||
| `subMenuItemBg` | `string` | Background color of sub-menu item |
|
||||
| `itemActiveBg` | `string` | Background color of menu item when active |
|
||||
| `itemSelectedBg` | `string` | Background color of menu item when selected |
|
||||
| `horizontalItemSelectedBg` | `string` | Background color of horizontal menu item when selected |
|
||||
| `activeBarWidth` | `string \| number` | Width of menu item active bar |
|
||||
| `activeBarHeight` | `number` | Height of menu item active bar |
|
||||
| `activeBarBorderWidth` | `string \| number` | Border width of menu item active bar |
|
||||
| `itemMarginInline` | `number` | Horizontal margin of menu item |
|
||||
| `horizontalItemHoverBg` | `string` | Background color of horizontal menu item when hover |
|
||||
| `horizontalItemBorderRadius` | `number` | Border radius of horizontal menu item |
|
||||
| `itemHeight` | `string \| number` | Height of menu item |
|
||||
| `collapsedWidth` | `string \| number` | Width when collapsed |
|
||||
| `popupBg` | `string` | Background color of popup |
|
||||
| `itemMarginBlock` | `CSSProperties` | margin-block of menu item |
|
||||
| `itemPaddingInline` | `CSSProperties` | padding-inline of menu item |
|
||||
| `horizontalLineHeight` | `CSSProperties` | LineHeight of horizontal menu item |
|
||||
| `iconMarginInlineEnd` | `CSSProperties` | Spacing between icon and text |
|
||||
| `iconSize` | `number` | Size of icon |
|
||||
| `collapsedIconSize` | `number` | Size of icon when collapsed |
|
||||
| `darkPopupBg` | `string` | The background color of the overlay menu in dark mode. |
|
||||
| `darkItemColor` | `string` | Color of menu item text in dark mode |
|
||||
| `darkDangerItemColor` | `string` | Color of danger menu item text in dark mode |
|
||||
| `darkItemBg` | `string` | Background of menu item in dark mode |
|
||||
| `darkSubMenuItemBg` | `string` | Background of submenu item in dark mode |
|
||||
| `darkItemSelectedColor` | `string` | Color of selected menu item in dark mode |
|
||||
| `darkItemSelectedBg` | `string` | Background of active menu item in dark mode |
|
||||
| `darkItemHoverBg` | `string` | Background of hovered menu item in dark mode |
|
||||
| `darkGroupTitleColor` | `string` | Color of group title text in dark mode |
|
||||
| `darkItemHoverColor` | `string` | Color of hovered menu item in dark mode |
|
||||
| `darkItemDisabledColor` | `string` | Color of disabled menu item in dark mode |
|
||||
| `darkDangerItemSelectedBg` | `string` | Background of active danger menu item in dark mode |
|
||||
| `darkDangerItemHoverColor` | `string` | Background of hovered danger menu item in dark mode |
|
||||
| `darkDangerItemSelectedColor` | `string` | Color of selected danger menu item in dark mode |
|
||||
| `darkDangerItemActiveBg` | `string` | Background of active danger menu item in dark mode |
|
||||
Reference in new issue
Block a user