mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-03-07 13:48:31 +08:00
feat(dropdown): support options with type='render'
This commit is contained in:
parent
93db614c4d
commit
1e5c6d0fa6
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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. |
|
||||
|
74
src/dropdown/demos/enUS/render.demo.md
Normal file
74
src/dropdown/demos/enUS/render.demo.md
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -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. |
|
||||
|
74
src/dropdown/demos/zhCN/render.demo.md
Normal file
74
src/dropdown/demos/zhCN/render.demo.md
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -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
|
||||
|
@ -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} />
|
||||
}
|
||||
|
19
src/dropdown/src/DropdownRenderOption.tsx
Normal file
19
src/dropdown/src/DropdownRenderOption.tsx
Normal 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?.()])
|
||||
}
|
||||
})
|
@ -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'
|
||||
|
@ -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'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user