mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-24 12:45:18 +08:00
feat(menu): add render-icon prop (#569)
* feat(menu): n-menu add render-icon prop * feat: optimization
This commit is contained in:
parent
5d7ae9ea18
commit
a833d9cfee
@ -4,6 +4,7 @@
|
||||
|
||||
### Feats
|
||||
|
||||
- `n-menu` add `render-icon` prop.
|
||||
- `n-upload` add `show-file-list` prop.
|
||||
- `n-dropdown` add `render-icon` prop.
|
||||
- `n-checkbox-group` add `min` and `max` prop.
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
### Feats
|
||||
|
||||
- `n-menu` 新增 `render-icon` 属性
|
||||
- `n-upload` 新增 `show-file-list` 属性
|
||||
- `n-dropdown` 新增 `render-icon` 属性
|
||||
- `n-checkbox-group` 新增 `min` 和 `max` 属性
|
||||
|
@ -36,6 +36,7 @@ long-label
|
||||
| inverted | `boolean` | `false` | Use inverted style. |
|
||||
| options | `Array<MenuOption \| MenuOptionGroup>` | `[]` | Items data of menu. |
|
||||
| mode | `'vertical' \| 'horizontal'` | `'vertical'` | Menu layout. |
|
||||
| render-icon | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | Render function that renders all icons. |
|
||||
| render-label | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | Render function that renders all labels. |
|
||||
| root-indent | `number` | `undefined` | The indent of menu's first level children. If not set, menu will use `indent` in place of it. |
|
||||
| value | `string \| null` | `undefined` | The selected name of menu. |
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Render Label
|
||||
|
||||
The `render-label` can be used to batch render menu options.
|
||||
The `render-label`, `render-icon` can be used to batch render menu options.
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
@ -22,6 +22,7 @@ The `render-label` can be used to batch render menu options.
|
||||
:collapsed-icon-size="22"
|
||||
:options="menuOptions"
|
||||
:render-label="renderMenuLabel"
|
||||
:render-icon="renderMenuIcon"
|
||||
/>
|
||||
</n-layout-sider>
|
||||
<n-layout>
|
||||
@ -34,27 +35,17 @@ The `render-label` can be used to batch render menu options.
|
||||
```js
|
||||
import { h, ref, defineComponent } from 'vue'
|
||||
import { NIcon } from 'naive-ui'
|
||||
import {
|
||||
BookOutline as BookIcon,
|
||||
PersonOutline as PersonIcon,
|
||||
WineOutline as WineIcon
|
||||
} from '@vicons/ionicons5'
|
||||
|
||||
function renderIcon (icon) {
|
||||
return () => h(NIcon, null, { default: () => h(icon) })
|
||||
}
|
||||
import { HappyOutline } from '@vicons/ionicons5'
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
label: 'Hear the Wind Sing',
|
||||
key: 'hear-the-wind-sing',
|
||||
icon: renderIcon(BookIcon),
|
||||
href: 'https://en.wikipedia.org/wiki/Hear_the_Wind_Sing'
|
||||
},
|
||||
{
|
||||
label: 'Pinball 1973',
|
||||
key: 'pinball-1973',
|
||||
icon: renderIcon(BookIcon),
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
@ -66,13 +57,11 @@ const menuOptions = [
|
||||
{
|
||||
label: 'A Wild Sheep Chase',
|
||||
key: 'a-wild-sheep-chase',
|
||||
disabled: true,
|
||||
icon: renderIcon(BookIcon)
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: 'Dance Dance Dance',
|
||||
key: 'Dance Dance Dance',
|
||||
icon: renderIcon(BookIcon),
|
||||
children: [
|
||||
{
|
||||
type: 'group',
|
||||
@ -81,20 +70,17 @@ const menuOptions = [
|
||||
children: [
|
||||
{
|
||||
label: 'Narrator',
|
||||
key: 'narrator',
|
||||
icon: renderIcon(PersonIcon)
|
||||
key: 'narrator'
|
||||
},
|
||||
{
|
||||
label: 'Sheep Man',
|
||||
key: 'sheep-man',
|
||||
icon: renderIcon(PersonIcon)
|
||||
key: 'sheep-man'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Beverage',
|
||||
key: 'beverage',
|
||||
icon: renderIcon(WineIcon),
|
||||
children: [
|
||||
{
|
||||
label: 'Whisky',
|
||||
@ -131,6 +117,9 @@ export default defineComponent({
|
||||
return h('a', { href: option.href, target: '_blank' }, option.label)
|
||||
}
|
||||
return option.label
|
||||
},
|
||||
renderMenuIcon () {
|
||||
return h(NIcon, null, { default: () => h(HappyOutline) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ long-label
|
||||
| inverted | `boolean` | `false` | 使用反转样式 |
|
||||
| options | `Array<MenuOption \| MenuOptionGroup>` | `[]` | 菜单的数据 |
|
||||
| mode | `'vertical' \| 'horizontal'` | `'vertical'` | 菜单的布局方式 |
|
||||
| render-label | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | 批量处理菜单渲染 |
|
||||
| render-icon | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | 批量处理菜单图标渲染 |
|
||||
| render-label | `(option: MenuOption \| MenuGroupOption) => VNodeChild` | `undefined` | 批量处理菜单标签渲染 |
|
||||
| root-indent | `number` | `32` | 菜单第一级的缩进,如果没有设定,使用 `indent` 代替 |
|
||||
| value | `string \| null` | `undefined` | 菜单当前的选中值 |
|
||||
| on-update:expanded-keys | `(keys: string[]) => void` | `undefined` | `keys` 是展开菜单项的 `key` 的数组 |
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 批量处理菜单渲染
|
||||
|
||||
使用 `render-label` 可以批量控制菜单的选项渲染。
|
||||
使用 `render-label`、`render-icon` 可以批量控制菜单的选项渲染。
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
@ -22,6 +22,7 @@
|
||||
:collapsed-icon-size="22"
|
||||
:options="menuOptions"
|
||||
:render-label="renderMenuLabel"
|
||||
:render-icon="renderMenuIcon"
|
||||
/>
|
||||
</n-layout-sider>
|
||||
<n-layout>
|
||||
@ -34,27 +35,17 @@
|
||||
```js
|
||||
import { h, ref, defineComponent } from 'vue'
|
||||
import { NIcon } from 'naive-ui'
|
||||
import {
|
||||
BookOutline as BookIcon,
|
||||
PersonOutline as PersonIcon,
|
||||
WineOutline as WineIcon
|
||||
} from '@vicons/ionicons5'
|
||||
|
||||
function renderIcon (icon) {
|
||||
return () => h(NIcon, null, { default: () => h(icon) })
|
||||
}
|
||||
import { HappyOutline } from '@vicons/ionicons5'
|
||||
|
||||
const menuOptions = [
|
||||
{
|
||||
label: '且听风吟',
|
||||
key: 'hear-the-wind-sing',
|
||||
icon: renderIcon(BookIcon),
|
||||
href: 'https://baike.baidu.com/item/%E4%B8%94%E5%90%AC%E9%A3%8E%E5%90%9F/3199'
|
||||
},
|
||||
{
|
||||
label: '1973年的弹珠玩具',
|
||||
key: 'pinball-1973',
|
||||
icon: renderIcon(BookIcon),
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
@ -66,13 +57,11 @@ const menuOptions = [
|
||||
{
|
||||
label: '寻羊冒险记',
|
||||
key: 'a-wild-sheep-chase',
|
||||
disabled: true,
|
||||
icon: renderIcon(BookIcon)
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: '舞,舞,舞',
|
||||
key: 'dance-dance-dance',
|
||||
icon: renderIcon(BookIcon),
|
||||
children: [
|
||||
{
|
||||
type: 'group',
|
||||
@ -81,20 +70,17 @@ const menuOptions = [
|
||||
children: [
|
||||
{
|
||||
label: '叙事者',
|
||||
key: 'narrator',
|
||||
icon: renderIcon(PersonIcon)
|
||||
key: 'narrator'
|
||||
},
|
||||
{
|
||||
label: '羊男',
|
||||
key: 'sheep-man',
|
||||
icon: renderIcon(PersonIcon)
|
||||
key: 'sheep-man'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '饮品',
|
||||
key: 'beverage',
|
||||
icon: renderIcon(WineIcon),
|
||||
children: [
|
||||
{
|
||||
label: '威士忌',
|
||||
@ -131,6 +117,9 @@ export default defineComponent({
|
||||
return h('a', { href: option.href, target: '_blank' }, option.label)
|
||||
}
|
||||
return option.label
|
||||
},
|
||||
renderMenuIcon () {
|
||||
return h(NIcon, null, { default: () => h(HappyOutline) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,9 @@ const menuProps = {
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
renderIcon: Function as PropType<
|
||||
(option: MenuOption | MenuGroupOption) => VNodeChild
|
||||
>,
|
||||
renderLabel: Function as PropType<
|
||||
(option: MenuOption | MenuGroupOption) => VNodeChild
|
||||
>,
|
||||
|
@ -63,7 +63,7 @@ export default defineComponent({
|
||||
const {
|
||||
clsPrefix,
|
||||
tmNode,
|
||||
menuProps: { renderLabel }
|
||||
menuProps: { renderIcon, renderLabel }
|
||||
} = this
|
||||
return (
|
||||
<div
|
||||
@ -80,13 +80,13 @@ export default defineComponent({
|
||||
]}
|
||||
style={this.style}
|
||||
>
|
||||
{this.icon ? (
|
||||
{renderIcon || this.icon ? (
|
||||
<div
|
||||
class={`${clsPrefix}-menu-item-content__icon`}
|
||||
style={this.iconStyle}
|
||||
role="none"
|
||||
>
|
||||
{render(this.icon)}
|
||||
{renderIcon ? renderIcon(tmNode.rawNode) : render(this.icon)}
|
||||
</div>
|
||||
) : null}
|
||||
<div class={`${clsPrefix}-menu-item-content-header`} role="none">
|
||||
|
@ -116,7 +116,7 @@ export default defineComponent({
|
||||
render () {
|
||||
const {
|
||||
mergedClsPrefix,
|
||||
menuProps: { renderLabel }
|
||||
menuProps: { renderIcon, renderLabel }
|
||||
} = this
|
||||
const createSubmenuItem = (): VNode => {
|
||||
const {
|
||||
@ -184,6 +184,7 @@ export default defineComponent({
|
||||
options={this.rawNodes}
|
||||
onSelect={this.doSelect}
|
||||
inverted={this.inverted}
|
||||
renderIcon={renderIcon}
|
||||
renderLabel={renderLabel}
|
||||
>
|
||||
{{
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { HappyOutline } from '@vicons/ionicons5'
|
||||
import { h } from 'vue'
|
||||
import { sleep } from 'seemly'
|
||||
import { NMenu } from '../index'
|
||||
import { NIcon } from '../../icon'
|
||||
|
||||
describe('n-menu', () => {
|
||||
it('should work with import on demand', () => {
|
||||
@ -133,4 +135,50 @@ describe('n-menu', () => {
|
||||
expect(document.querySelectorAll('a').length).toEqual(3)
|
||||
expect(document.querySelectorAll('a.fantasy').length).toEqual(1)
|
||||
})
|
||||
|
||||
it('should dropdown work with `render-icon` props', async () => {
|
||||
const options = [
|
||||
{
|
||||
label: 'jj',
|
||||
key: 'jj'
|
||||
},
|
||||
{
|
||||
label: 'jay',
|
||||
key: 'jay',
|
||||
children: [
|
||||
{
|
||||
type: 'group',
|
||||
label: 'song-group',
|
||||
key: 'group',
|
||||
children: [
|
||||
{
|
||||
label: 'fantasy',
|
||||
key: 'fantasy'
|
||||
},
|
||||
{
|
||||
label: 'mojito',
|
||||
key: 'mojito'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
function renderMenuIcon (): any {
|
||||
return h(NIcon, null, { default: () => h(HappyOutline) })
|
||||
}
|
||||
const wrapper = mount(NMenu, {
|
||||
props: {
|
||||
options: options,
|
||||
collapsed: true,
|
||||
renderIcon: renderMenuIcon
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.n-submenu').exists()).toBe(true)
|
||||
await wrapper.find('.n-submenu').trigger('mouseenter')
|
||||
// Popover has delay, so we need to wait
|
||||
await sleep(150)
|
||||
expect(document.body.querySelector('.n-dropdown')).not.toEqual(null)
|
||||
expect(document.querySelectorAll('.n-icon').length).toEqual(2)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user