{iconNode}
@@ -57,23 +58,42 @@ export default defineComponent({
},
})
-const renderIcon = (prefixCls: string, iconSlot: Slot | undefined, icon: string | VNode | undefined) => {
+const renderIcon = (
+ prefixCls: string,
+ iconSlot: Slot | undefined,
+ icon: string | VNode | (() => VNodeChild) | undefined,
+) => {
if (!iconSlot && !icon) {
return null
}
- let children: VNodeTypes
+ let children: VNodeChild
if (iconSlot) {
children = iconSlot()
+ } else if (isFunction(icon)) {
+ children = icon()
} else {
children = isString(icon) ?
: icon!
}
return
{children}
}
-const renderTitle = (prefixCls: string, titleSlot: Slot | undefined, title: string | VNode | undefined) => {
+const renderTitle = (
+ prefixCls: string,
+ titleSlot: Slot | undefined,
+ title: string | VNode | (() => VNodeChild) | undefined,
+) => {
if (!titleSlot && !title) {
return null
}
- const children = titleSlot ? titleSlot() : title
+
+ let children: VNodeChild
+ if (titleSlot) {
+ children = titleSlot()
+ } else if (isFunction(title)) {
+ children = title()
+ } else {
+ children = title
+ }
+
return
{children}
}
diff --git a/packages/components/modal/src/ModalProvider.tsx b/packages/components/modal/src/ModalProvider.tsx
index 32cd503da..393b2a6b9 100644
--- a/packages/components/modal/src/ModalProvider.tsx
+++ b/packages/components/modal/src/ModalProvider.tsx
@@ -10,6 +10,8 @@ import type { VKey } from '@idux/cdk/utils'
import { cloneVNode, defineComponent, isVNode, provide, shallowRef } from 'vue'
+import { isFunction } from 'lodash-es'
+
import { NoopFunction, callEmit, convertArray, uniqueId } from '@idux/cdk/utils'
import Modal from './Modal'
@@ -34,7 +36,11 @@ export default defineComponent({
const onAfterClose = destroyOnHide ? () => destroy(key!) : undefined
const mergedProps = { key, visible, ref: setRef, 'onUpdate:visible': onUpdateVisible, onAfterClose }
- const contentNode = isVNode(content) ? cloneVNode(content, contentProps, true) : content
+ const contentNode = isFunction(content)
+ ? content()
+ : isVNode(content)
+ ? cloneVNode(content, contentProps, true)
+ : content
return
})
return (
diff --git a/packages/components/modal/src/types.ts b/packages/components/modal/src/types.ts
index 1170895fa..849839a12 100644
--- a/packages/components/modal/src/types.ts
+++ b/packages/components/modal/src/types.ts
@@ -11,13 +11,13 @@ import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray, VKey }
import type { ɵFooterButtonProps } from '@idux/components/_private/footer'
import type { ButtonProps } from '@idux/components/button'
import type { HeaderProps } from '@idux/components/header'
-import type { DefineComponent, HTMLAttributes, PropType, VNode, VNodeProps } from 'vue'
+import type { DefineComponent, HTMLAttributes, PropType, VNode, VNodeChild, VNodeProps } from 'vue'
export type ModalType = 'default' | 'confirm' | 'info' | 'success' | 'warning' | 'error'
export type ModalButtonProps = ɵFooterButtonProps
export interface ModalOptions
extends ModalPublicProps {
key?: K
- content?: string | VNode
+ content?: string | VNode | (() => VNodeChild)
contentProps?: Record | VNodeProps
onDestroy?: (key: K) => void
}
@@ -42,7 +42,7 @@ export const modalProps = {
type: Boolean,
default: undefined,
},
- closeIcon: [String, Object] as PropType,
+ closeIcon: [String, Object, Function] as PropType VNodeChild)>,
closeOnDeactivated: {
type: Boolean,
default: true,
@@ -61,11 +61,11 @@ export const modalProps = {
},
draggable: { type: Boolean, default: false },
footer: {
- type: [Boolean, Array, Object] as PropType,
+ type: [Boolean, Array, Object, Function] as PropType VNodeChild)>,
default: true,
},
header: [String, Object] as PropType,
- icon: [String, Object] as PropType,
+ icon: [String, Object, Function] as PropType VNodeChild)>,
mask: {
type: Boolean,
default: undefined,
@@ -85,7 +85,7 @@ export const modalProps = {
okButton: Object as PropType,
okText: String,
scrollStrategy: Object as PropType,
- title: [String, Object] as PropType,
+ title: [String, Object, Function] as PropType VNodeChild)>,
type: {
type: String as PropType,
default: 'default',
@@ -105,7 +105,7 @@ export const modalProps = {
onOk: [Function, Array] as PropType unknown>>,
// private
- __content_node: [String, Object] as PropType,
+ __content_node: [String, Object] as PropType,
} as const
export type ModalProps = ExtractInnerPropTypes
diff --git a/packages/components/notification/demo/Content.vue b/packages/components/notification/demo/Content.vue
index eeb361ef5..aafd9ae64 100644
--- a/packages/components/notification/demo/Content.vue
+++ b/packages/components/notification/demo/Content.vue
@@ -9,9 +9,10 @@ import { useNotification } from '@idux/components/notification'
const notification = useNotification()
-const title = h('h3', { style: { color: 'red' } }, 'vnode title')
+const title = () => h('h3', { style: { color: 'red' } }, 'vnode title')
-const content = h('ul', {}, [h('li', {}, 'This is a VNode content data'), h('li', {}, 'This is a VNode content data')])
+const content = () =>
+ h('ul', {}, [h('li', {}, 'This is a VNode content data'), h('li', {}, 'This is a VNode content data')])
const open = () =>
notification.info({
diff --git a/packages/components/notification/demo/Footer.vue b/packages/components/notification/demo/Footer.vue
index 3e9e935ee..80db7f8c9 100644
--- a/packages/components/notification/demo/Footer.vue
+++ b/packages/components/notification/demo/Footer.vue
@@ -20,13 +20,13 @@ const openStringFooter = () =>
footer: 'system message',
})
-const footerVNode = h('div', { style: { color: 'red' } }, 'system message')
+const renderFooter = () => h('div', { style: { color: 'red' } }, 'system message')
const oenVNodeFooter = () =>
info({
title: 'VNode',
content: 'The bottom area is configured as VNode',
- footer: footerVNode,
+ footer: renderFooter,
})
const openIxButton = () => {
diff --git a/packages/components/notification/demo/Icon.vue b/packages/components/notification/demo/Icon.vue
index de10c8b44..0e01c7822 100644
--- a/packages/components/notification/demo/Icon.vue
+++ b/packages/components/notification/demo/Icon.vue
@@ -20,10 +20,10 @@ const openCustomIconByName = () =>
title: 'icon',
content: 'This is a custom icon by name',
})
-const iconVNode = h(IxIcon, { name: 'star', style: { color: 'red' } })
+const renderIcon = () => h(IxIcon, { name: 'star', style: { color: 'red' } })
const openCustomIconByVNode = () =>
info({
- icon: iconVNode,
+ icon: renderIcon,
title: 'icon',
content: 'This is a custom icon by VNode',
})
@@ -34,10 +34,10 @@ const openCustomCloseIconByName = () =>
title: 'close icon',
content: 'This is a custom close icon by name',
})
-const closeIconVNode = h(IxIcon, { name: 'right', style: { color: 'red' } })
+const renderCloseIcon = () => h(IxIcon, { name: 'right', style: { color: 'red' } })
const openCustomCloseIconByVNode = () =>
info({
- closeIcon: closeIconVNode,
+ closeIcon: renderCloseIcon,
title: 'close icon',
content: 'This is a custom close icon by VNode',
})
diff --git a/packages/components/notification/demo/Id.vue b/packages/components/notification/demo/Id.vue
index 619aeb3b7..88b4e5021 100644
--- a/packages/components/notification/demo/Id.vue
+++ b/packages/components/notification/demo/Id.vue
@@ -12,7 +12,7 @@ import { useNotification } from '@idux/components/notification'
const { info, destroyAll, destroy } = useNotification()
let count = 0
-let notificationKey: string
+let notificationKey: string | number | symbol
const open = () => {
const content = `click count: ${count++}`
if (!notificationKey) {
diff --git a/packages/components/notification/docs/Api.zh.md b/packages/components/notification/docs/Api.zh.md
index 5e3a3b392..8cf32a3e1 100644
--- a/packages/components/notification/docs/Api.zh.md
+++ b/packages/components/notification/docs/Api.zh.md
@@ -8,8 +8,8 @@
| `maxCount` | 同一时间可展示的最大提示数量 | `number` | `5` | - |
| `destroyOnHover` | 鼠标悬浮时是否允许销毁 | `boolean` | `false` | - |
| `duration` | 自动销毁的延时,单位毫秒 | `number` | `4500` | - |
-| `icon` | 自定义通知图标映射表 | `Partial>` | `{ success: 'check-circle', error: 'close-circle', info: 'info-circle', warning: 'exclamation-circle' }` | - |
-| `closeIcon` | 自定义关闭图标 | `string \| VNode` | `close` | - |
+| `icon` | 自定义通知图标映射表 | `Partial VNodeChild)>>` | `{ success: 'check-circle', error: 'close-circle', info: 'info-circle', warning: 'exclamation-circle' }` | - |
+| `closeIcon` | 自定义关闭图标 | `string \| VNode \| (() => VNodeChild)` | `close` | - |
| `offset` | 通知消息弹出时,距离边缘的位置 | `number \| string \|[number \| string, number \| string]` | `24` | number时:单位为px;
string时:可为`vh` \ `vw` \| `%` \| `px`;
array时:[上下边缘,左右边缘];
设置为非array时上下边缘和左右边缘相等 |
| `placement` | 弹出的位置 | `'topStart' \| 'topEnd' \| 'bottomStart' \| 'bottomEnd'` | `'topEnd'` | - |
@@ -57,14 +57,14 @@ const openNotification = ()=> notification.info({
| --- | --- | --- | --- | --- | --- |
| `destroyOnHover` | 鼠标悬浮时是否允许销毁 | `boolean` | `false` | ☑️ | - |
| `duration` | 自动销毁的延时,单位毫秒 | `number` | 4500 | ☑️ | - |
-| `icon` | 自定义通知图标 | `string \| VNode` | - | - | - |
-| `closeIcon` | 自定义关闭图标 | `string \| VNode` | - | - | - |
+| `icon` | 自定义通知图标 | `string \| VNode \| (() => VNodeChild)` | - | - | - |
+| `closeIcon` | 自定义关闭图标 | `string \| VNode \| (() => VNodeChild)` | - | - | - |
| `type` | 通知类型 | `'info' \| 'success' \| 'warning' \| 'error'` | - | - | - |
| `key` | 唯一标识 | `string \| number \| symbol` | - | - | - |
| `placement` | 弹出的位置 | `'topStart' \| 'topEnd' \| 'bottomStart' \| 'bottomEnd'` | `'topEnd'` | ☑️ | - |
-| `title` | 通知的标题 | `string \| VNode` | - | - | 必填 |
-| `content` | 通知的内容 | `string \| VNode` | - | - | 必填 |
-| `footer` | 自定义底部按钮 | `string \| VNode \| ButtonProps[]` | - | - | 底部区域flex布局 |
+| `title` | 通知的标题 | `string \| VNode \| (() => VNodeChild)` | - | - | 必填 |
+| `content` | 通知的内容 | `string \| VNode \| (() => VNodeChild)` | - | - | 必填 |
+| `footer` | 自定义底部按钮 | `string \| VNode \| (() => VNodeChild) \| ButtonProps[]` | - | - | 底部区域flex布局 |
| `onDestroy` | 关闭通知时触发的回调 | `(key: VKey) => void` | - | - | - |
```ts
@@ -114,8 +114,7 @@ const contentRef = ref()
const openNotification = () => open({
title: 'Basic Notification',
- content: h('div', 'Some contents...'),
- contentProps: { ref: contentRef }
+ content: () => h('div', { ref: contentRef }, 'Some contents...'),
})
```
diff --git a/packages/components/notification/src/Notification.tsx b/packages/components/notification/src/Notification.tsx
index 0d296879c..ee44917c1 100644
--- a/packages/components/notification/src/Notification.tsx
+++ b/packages/components/notification/src/Notification.tsx
@@ -5,19 +5,19 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/
-import type { NotificationNodePropKey, NotificationProps, SlotProps } from './types'
+import type { NotificationProps, SlotProps } from './types'
import type { NotificationConfig } from '@idux/components/config'
import type { ComputedRef, Slots } from 'vue'
import { computed, defineComponent, onBeforeUnmount, onMounted, watchEffect } from 'vue'
-import { isArray, isString } from 'lodash-es'
+import { isArray, isFunction } from 'lodash-es'
import { callEmit, useControlledProp } from '@idux/cdk/utils'
import { IxButton } from '@idux/components/button'
import { useGlobalConfig } from '@idux/components/config'
-import { IxIcon } from '@idux/components/icon'
import { IxSpace } from '@idux/components/space'
+import { convertIconVNode, convertStringVNode } from '@idux/components/utils'
import { notificationProps } from './types'
@@ -43,11 +43,12 @@ export default defineComponent({
const { visible, close, onMouseEnter, onMouseLeave } = useVisible(props, config)
return () => {
- const iconNode = icon.value && getIconNode(icon.value)
- const closeIconNode = getIconNode(closeIcon.value)
+ const iconNode = convertIconVNode(undefined, icon.value)
+ const closeIconNode = convertIconVNode(undefined, closeIcon.value)
- const contentNode = getNode(props, slots, 'content')
- const footerNode = getNode(props, slots, 'footer', {
+ const titleNode = convertStringVNode(slots, props, 'title')
+ const contentNode = convertStringVNode(slots.default, props.content)
+ const footerNode = getFooterNode(slots, props.footer, {
visible: visible.value,
close,
})
@@ -67,7 +68,7 @@ export default defineComponent({
{closeIconNode}
-
{getNode(props, slots, 'title')}
+
{titleNode}
{contentNode &&
{contentNode}
}