feat(dropdown): support options with type='render'

This commit is contained in:
07akioni 2021-10-05 02:05:31 +08:00
parent 93db614c4d
commit 1e5c6d0fa6
11 changed files with 225 additions and 8 deletions

View File

@ -19,6 +19,7 @@
- `n-checkbox` add `unchecked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
- Add `n-collapse-transition` component, closes [#829](https://github.com/TuSimple/naive-ui/issues/829).
- Add `n-scrollbar` component.
- `n-dropdown` support options with `type='render'`.
### Fixes

View File

@ -19,6 +19,7 @@
- `n-checkbox` 新增 `unchecked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
- 新增 `n-collapse-transition` 组件,关闭 [#829](https://github.com/TuSimple/naive-ui/issues/829).
- 新增 `n-scrollbar` 组件
- `n-dropdown` 支持 `type='render'` 的选项
### Fixes

View File

@ -14,6 +14,7 @@ placement
size
manual-position
batch-render
render
option-props
```
@ -29,7 +30,7 @@ option-props
| keyboard | `boolean` | `true` | Whether the component supports keyboard operation. (Be careful about the potential conflicts with other components keyboard operations) |
| key-field | `string` | `'key'` | Field name of key. |
| label-field | `string` | `'label'` | Field name of label. |
| options | `Array<DropdownOption \| DropdownDividerOption>` | `[]` | Dropdown options. |
| options | `Array<DropdownOption \| DropdownGroupOption \| DropdownDividerOption \| DropdownRenderOption>` | `[]` | Dropdown options. |
| render-icon | `(option: DropdownOption) => VNodeChild` | `undefined` | Render function that renders option icons. |
| render-label | `(option: DropdownOption) => VNodeChild` | `undefined` | Render function that renders option labels. |
| size | `'small'\|'medium'\|'large'\|'huge'` | `'medium'` | Dropdown size. |
@ -65,3 +66,11 @@ For other props, for example `placement`, please see [Popover Props](popover#Pro
| icon? | `() => VNodeChild` | Custom rendering function of the group icon. |
| key | `string \| number` | Group ID (should be unique). |
| children | `Array<DropdownOption \| DropdownDividerOption>` | Children options of DropdownGroupOption. |
#### DropdownRenderOption Type
| Property | Type | Description |
| -------- | ------------------ | -------------------------------------- |
| type | `'render'` | The type of the DropdownRenderOption. |
| key | `string \| number` | Render option ID (should be unique). |
| render | `() => VNodeChild` | Render function of the option content. |

View File

@ -0,0 +1,74 @@
# 纯渲染的内容
你可以单纯的只是想渲染一些内容,和选项数据无关。此时你可以加入 `type='render'` 的选项。
```html
<n-dropdown trigger="hover" @select="handleSelect" :options="options">
<n-button>2021年 第36周</n-button>
</n-dropdown>
```
```js
import { defineComponent, h } from 'vue'
import { useMessage, NAvatar, NText } from 'naive-ui'
function renderCustomHeader () {
return h(
'div',
{
style: 'display: flex; align-items: center; padding: 8px 12px;'
},
[
h(NAvatar, {
round: true,
style: 'margin-right: 12px;',
src: 'https://07akioni.oss-cn-beijing.aliyuncs.com/demo1.JPG'
}),
h('div', null, [
h('div', null, [h(NText, { depth: 2 }, { default: () => '打工仔' })]),
h('div', { style: 'font-size: 12px;' }, [
h(
NText,
{ depth: 3 },
{ default: () => '毫无疑问,你是办公室里最亮的星' }
)
])
])
]
)
}
export default defineComponent({
setup () {
const message = useMessage()
return {
options: [
{
key: 'header',
type: 'render',
render: renderCustomHeader
},
{
key: 'header-divider',
type: 'divider'
},
{
label: '处理群消息 342 条',
key: 'stmt1'
},
{
label: '被 @ 58 次',
key: 'stmt2'
},
{
label: '加入群 17 个',
key: 'stmt3'
}
],
handleSelect (key) {
message.info(key)
}
}
}
})
```

View File

@ -12,9 +12,10 @@ cascade
arrow
placement
size
manual-position
group-debug
manual-position
batch-render
render
option-props
```
@ -30,7 +31,7 @@ option-props
| keyboard | `boolean` | `true` | 是否支持键盘操作(注意和其他内容键盘操作可能的冲突) |
| key-field | `string` | `'key'` | key 的字段名 |
| label-field | `string` | `'label'` | label 的字段名 |
| options | `Array<DropdownOption \| DropdownDividerOption>` | `[]` | 下拉菜单传入的 options |
| options | `Array<DropdownOption \| DropdownGroupOption \| DropdownDividerOption \| DropdownRenderOption>` | `[]` | 下拉菜单传入的 options |
| render-icon | `(option: DropdownOption) => VNodeChild` | `undefined` | 批量处理下拉菜单图标渲染 |
| render-label | `(option: DropdownOption) => VNodeChild` | `undefined` | 批量处理下拉菜单渲染 |
| size | `'small'\|'medium'\|'large'\|'huge'` | `'medium'` | 下拉菜单的尺寸大小 |
@ -66,3 +67,11 @@ option-props
| icon? | `() => VNodeChild` | 支持通过 render 方法自定义 icon |
| key | `string \| number` | 需要唯一 |
| children | `Array<DropdownOption \| DropdownDividerOption>` | DropdownGroupOption 的 children 项 |
#### DropdownRenderOption Type
| 属性 | 类型 | 说明 |
| ------ | ------------------ | -------------------------------------- |
| type | `'render'` | The type of the DropdownRenderOption. |
| key | `string \| number` | Render option ID (should be unique). |
| render | `() => VNodeChild` | Render function of the option content. |

View File

@ -0,0 +1,74 @@
# 纯渲染的内容
你可以单纯的只是想渲染一些内容,和选项数据无关。此时你可以加入 `type='render'` 的选项。
```html
<n-dropdown trigger="hover" @select="handleSelect" :options="options">
<n-button>2021年 第36周</n-button>
</n-dropdown>
```
```js
import { defineComponent, h } from 'vue'
import { useMessage, NAvatar, NText } from 'naive-ui'
function renderCustomHeader () {
return h(
'div',
{
style: 'display: flex; align-items: center; padding: 8px 12px;'
},
[
h(NAvatar, {
round: true,
style: 'margin-right: 12px;',
src: 'https://07akioni.oss-cn-beijing.aliyuncs.com/demo1.JPG'
}),
h('div', null, [
h('div', null, [h(NText, { depth: 2 }, { default: () => '打工仔' })]),
h('div', { style: 'font-size: 12px;' }, [
h(
NText,
{ depth: 3 },
{ default: () => '毫无疑问,你是办公室里最亮的星' }
)
])
])
]
)
}
export default defineComponent({
setup () {
const message = useMessage()
return {
options: [
{
key: 'header',
type: 'render',
render: renderCustomHeader
},
{
key: 'header-divider',
type: 'divider'
},
{
label: '处理群消息 342 条',
key: 'stmt1'
},
{
label: '被 @ 58 次',
key: 'stmt2'
},
{
label: '加入群 17 个',
key: 'stmt3'
}
],
handleSelect (key) {
message.info(key)
}
}
}
})
```

View File

@ -143,7 +143,7 @@ export default defineComponent({
return node.disabled === true
},
getIgnored (node) {
return node.type === 'divider'
return node.type === 'divider' || node.type === 'render'
},
getChildren (node) {
return node[childrenField] as any

View File

@ -14,12 +14,19 @@ import { renderArrow } from '../../popover/src/PopoverBody'
import NDropdownOption from './DropdownOption'
import NDropdownDivider from './DropdownDivider'
import NDropdownGroup from './DropdownGroup'
import { isSubmenuNode, isGroupNode, isDividerNode } from './utils'
import NDropdownRenderOption from './DropdownRenderOption'
import {
isSubmenuNode,
isGroupNode,
isDividerNode,
isRenderNode
} from './utils'
import { dropdownInjectionKey } from './Dropdown'
import {
DropdownGroupOption,
DropdownIgnoredOption,
DropdownOption
DropdownOption,
DropdownRenderOption
} from './interface'
export interface NDropdownMenuInjection {
@ -87,6 +94,14 @@ export default defineComponent({
return (
<div class={`${clsPrefix}-dropdown-menu`}>
{this.tmNodes.map((tmNode) => {
if (isRenderNode(tmNode.rawNode)) {
return (
<NDropdownRenderOption
tmNode={tmNode as unknown as TreeNode<DropdownRenderOption>}
key={tmNode.key}
/>
)
}
if (isDividerNode(tmNode.rawNode)) {
return <NDropdownDivider clsPrefix={clsPrefix} key={tmNode.key} />
}

View File

@ -0,0 +1,19 @@
import { h, defineComponent, PropType } from 'vue'
import { TreeNode } from 'treemate'
import { DropdownRenderOption } from './interface'
export default defineComponent({
name: 'DropdownRenderOption',
props: {
tmNode: {
type: Object as PropType<TreeNode<DropdownRenderOption>>,
required: true
}
},
render () {
const {
rawNode: { render, props }
} = this.tmNode
return h('div', props, [render?.()])
}
})

View File

@ -13,8 +13,9 @@ export type DropdownGroupOption = MenuGroupOption & {
}
export interface DropdownIgnoredOption {
key?: Key
type: 'ignored' | 'divider'
type: 'render' | 'divider'
props?: HTMLAttributes
render?: () => VNodeChild
[key: string]: unknown
}
@ -27,6 +28,14 @@ export type DropdownIntersectionOption = DropdownOption &
DropdownGroupOption &
DropdownIgnoredOption
export interface DropdownRenderOption {
key?: Key
type: 'render'
props?: HTMLAttributes
render?: () => VNodeChild
[key: string]: unknown
}
export interface DropdownDividerOption {
key?: Key
type: 'divider'

View File

@ -1,4 +1,4 @@
import type { DropdownMixedOption } from './interface'
import type { DropdownMixedOption, DropdownRenderOption } from './interface'
export function isSubmenuNode (
rawNode: DropdownMixedOption,
@ -17,3 +17,9 @@ export function isGroupNode (rawNode: DropdownMixedOption): boolean {
export function isDividerNode (rawNode: DropdownMixedOption): boolean {
return rawNode.type === 'divider'
}
export function isRenderNode (
rawNode: DropdownMixedOption
): rawNode is DropdownRenderOption {
return rawNode.type === 'render'
}