Skip to content

Commit

Permalink
feat(comp:drawer,header,message,modal,notification): all VNode props …
Browse files Browse the repository at this point in the history
…support render function now (#1717)
  • Loading branch information
sallerli1 committed Oct 30, 2023
1 parent b653faf commit 69013e8
Show file tree
Hide file tree
Showing 29 changed files with 202 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Expand Up @@ -99,7 +99,7 @@ module.exports = {
'comma-style': ['error', 'last'],
'comma-dangle': ['error', 'always-multiline'],
'eol-last': 'error',
indent: ['error', 2, { SwitchCase: 1 }],
indent: 'off',
'no-trailing-spaces': 'error',
'object-curly-spacing': ['error', 'always'],
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
Expand Down
11 changes: 8 additions & 3 deletions packages/components/_private/footer/src/Footer.tsx
Expand Up @@ -6,10 +6,12 @@
*/

import type { FooterButtonProps } from './types'
import type { VNodeTypes } from 'vue'
import type { VNodeChild } from 'vue'

import { computed, defineComponent, isVNode } from 'vue'

import { isFunction } from 'lodash-es'

import { IxButton } from '@idux/components/button'

import { footerProps } from './types'
Expand Down Expand Up @@ -54,9 +56,11 @@ export default defineComponent({
return undefined
}

let children: VNodeTypes
let children: VNodeChild
if (footerSlot) {
children = footerSlot(props)
} else if (isFunction(footerProp)) {
children = footerProp()
} else if (isVNode(footerProp)) {
children = footerProp
} else {
Expand All @@ -70,7 +74,8 @@ export default defineComponent({
}
children = buttonProps.map(item => {
const { text, ...rest } = item
return <IxButton {...rest}>{text}</IxButton>
const _text = isFunction(text) ? text() : text
return <IxButton {...rest}>{_text}</IxButton>
})
}

Expand Down
6 changes: 3 additions & 3 deletions packages/components/_private/footer/src/types.ts
Expand Up @@ -7,12 +7,12 @@

import type { ExtractInnerPropTypes, ExtractPublicPropTypes, VKey } from '@idux/cdk/utils'
import type { ButtonProps } from '@idux/components/button'
import type { ButtonHTMLAttributes, DefineComponent, HTMLAttributes, PropType, VNode } from 'vue'
import type { ButtonHTMLAttributes, DefineComponent, HTMLAttributes, PropType, VNode, VNodeChild } from 'vue'

export interface FooterButtonProps extends ButtonHTMLAttributes, ButtonProps {
disabled?: boolean
key?: VKey
text?: string | VNode
text?: string | VNode | (() => VNodeChild)
onClick?: (evt: Event) => void
}

Expand All @@ -25,7 +25,7 @@ export const footerProps = {
type: Boolean,
default: true,
},
footer: [Boolean, Array, Object] as PropType<boolean | FooterButtonProps[] | VNode>,
footer: [Boolean, Array, Object, Function] as PropType<boolean | FooterButtonProps[] | VNode | (() => VNodeChild)>,
ok: Function as PropType<(evt?: Event | unknown) => Promise<void> | void>,
okButton: Object as PropType<ButtonProps>,
okLoading: Boolean,
Expand Down
4 changes: 2 additions & 2 deletions packages/components/_private/header/src/types.ts
Expand Up @@ -6,11 +6,11 @@
*/

import type { HeaderProps as IxHeaderProps } from '@idux/components/header'
import type { VNode } from 'vue'
import type { VNode, VNodeChild } from 'vue'

export interface HeaderProps {
closable?: boolean
closeIcon?: string | VNode
closeIcon?: string | VNode | (() => VNodeChild)
header?: string | IxHeaderProps
size?: 'lg' | 'md' | 'sm'
onClose?: (evt: Event) => void
Expand Down
6 changes: 3 additions & 3 deletions packages/components/config/src/types.ts
Expand Up @@ -316,7 +316,7 @@ export interface MessageConfig {
container?: PortalTargetType
destroyOnHover: boolean
duration: number
icon: Partial<Record<MessageType, string | VNode>>
icon: Partial<Record<MessageType, string | VNode | (() => VNodeChild)>>
maxCount: number
top?: number | string
}
Expand All @@ -328,7 +328,7 @@ export interface ModalConfig {
closeIcon: string
closeOnEsc: boolean
container?: PortalTargetType
icon?: Partial<Record<ModalType, string | VNode>>
icon?: Partial<Record<ModalType, string | VNode | (() => VNodeChild)>>
mask: boolean
maskClosable: boolean
/**
Expand All @@ -341,7 +341,7 @@ export interface NotificationConfig {
container?: PortalTargetType
destroyOnHover: boolean
duration: number
icon?: Partial<Record<NotificationType, string | VNode>>
icon?: Partial<Record<NotificationType, string | VNode | (() => VNodeChild)>>
closeIcon?: string | VNode
maxCount: number
offset: number | string | (string | number)[]
Expand Down
6 changes: 3 additions & 3 deletions packages/components/drawer/docs/Api.zh.md
Expand Up @@ -7,12 +7,12 @@
| --- | --- | --- | --- | --- | --- |
| `v-model:visible` | 是否可见 | `boolean` | - | - | - |
| `closable` | 是否显示右上角的关闭按钮 | `boolean` | `true` || - |
| `closeIcon` | 自定义关闭图标 | `string \| VNode \| #closeIcon` | `close` || - |
| `closeIcon` | 自定义关闭图标 | `string \| VNode \| (() => VNodeChild) \| #closeIcon` | `close` || - |
| `closeOnDeactivated` | 是否在 [onDeactivated](https://cn.vuejs.org/api/composition-api-lifecycle.html#ondeactivated) 时关闭 | `boolean` | `true` | - | - |
| `closeOnEsc` | 是否支持键盘 `esc` 关闭 | `boolean` | `true` || - |
| `container` | 自定义抽屉挂载的容器节点 | `string \| HTMLElement \| () => string \| HTMLElement` | - || - |
| `destroyOnHide` | 关闭时销毁子元素 | `boolean` | `false` | - | - |
| `footer` | 自定义底部按钮 | `DrawerButtonProps[] \| VNode \| #footer` | - | - | - |
| `footer` | 自定义底部按钮 | `DrawerButtonProps[] \| VNode \| (() => VNodeChild) \| #footer` | - | - | - |
| `header` | 抽屉的标题 | `string \| HeaderProps \| #header={closable, closeIcon, onClose}` | - | - | - |
| `height` | 抽屉高度 | `string \| number` | `'256'` || 默认值仅在 `placement为``top/bottom` 时生效,其他情况默认为 `100%` |
| `mask` | 是否展示蒙层 | `boolean` | `true` || - |
Expand Down Expand Up @@ -90,7 +90,7 @@ export interface DrawerProviderRef {
export interface DrawerOptions extends DrawerProps {
key?: VKey
// 抽屉的内容
content?: string | VNode
content?: string | VNode | (() => VNodeChild)
// 当 content 为 VNode 时, 可以传递 props 或者绑定 ref
contentProps?: Record<string, unknown> | VNodeProps
// 抽屉销毁后的回调
Expand Down
8 changes: 7 additions & 1 deletion packages/components/drawer/src/DrawerProvider.tsx
Expand Up @@ -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 Drawer from './Drawer'
Expand All @@ -33,7 +35,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 (
<Drawer {...mergedProps} {...rest}>
{contentNode}
Expand Down
8 changes: 4 additions & 4 deletions packages/components/drawer/src/types.ts
Expand Up @@ -10,15 +10,15 @@ import type { ScrollStrategy } from '@idux/cdk/scroll'
import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray, VKey } from '@idux/cdk/utils'
import type { ɵFooterButtonProps } from '@idux/components/_private/footer'
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 DrawerPlacement = 'top' | 'bottom' | 'start' | 'end'

export type DrawerButtonProps = ɵFooterButtonProps

export interface DrawerOptions<K = VKey> extends DrawerPublicProps {
key?: K
content?: string | VNode
content?: string | VNode | (() => VNodeChild)
contentProps?: Record<string, unknown> | VNodeProps
onDestroy?: (key: K) => void
}
Expand All @@ -41,7 +41,7 @@ export const drawerProps = {
type: Boolean,
default: undefined,
},
closeIcon: [String, Object] as PropType<string | VNode>,
closeIcon: [String, Object, Function] as PropType<string | VNode | (() => VNodeChild)>,
closeOnDeactivated: {
type: Boolean,
default: true,
Expand All @@ -58,7 +58,7 @@ export const drawerProps = {
type: Boolean,
default: false,
},
footer: [Array, Object] as PropType<DrawerButtonProps[] | VNode>,
footer: [Array, Object, Function] as PropType<DrawerButtonProps[] | VNode | (() => VNodeChild)>,
header: [String, Object] as PropType<string | HeaderProps>,
height: [String, Number] as PropType<string | number>,
mask: {
Expand Down
10 changes: 8 additions & 2 deletions packages/components/header/src/types.ts
Expand Up @@ -15,11 +15,17 @@ export const headerProps = {
avatar: { type: [String, Object] as PropType<string | AvatarProps>, default: undefined },
description: { type: String, default: undefined },
disabled: { type: Boolean, default: false },
prefix: { type: [String, Object] as PropType<string | VNodeChild>, default: undefined },
prefix: {
type: [String, Object, Function] as PropType<string | VNodeChild | (() => VNodeChild)>,
default: undefined,
},
size: { type: String as PropType<HeaderSize>, default: 'md' },
showBar: { type: Boolean, default: false },
subTitle: { type: String, default: undefined },
suffix: { type: [String, Object] as PropType<string | VNodeChild>, default: undefined },
suffix: {
type: [String, Object, Function] as PropType<string | VNodeChild | (() => VNodeChild)>,
default: undefined,
},
title: { type: String, default: undefined },

// events
Expand Down
2 changes: 1 addition & 1 deletion packages/components/message/demo/Content.md
Expand Up @@ -7,7 +7,7 @@ order: 1

## zh

通过传入一个 `VNode` 自定义提示内容
通过传入一个 `VNode` 或函数来自定义提示内容

## en

Expand Down
2 changes: 1 addition & 1 deletion packages/components/message/demo/Content.vue
Expand Up @@ -12,7 +12,7 @@ export default defineComponent({
setup() {
const message = useMessage()
const content = h('span', {}, [h('span', {}, 'This is a VNode content '), h(IxIcon, { name: 'star' })])
const content = () => h('span', {}, [h('span', {}, 'This is a VNode content '), h(IxIcon, { name: 'star' })])
const open = () => message.info(content)
Expand Down
4 changes: 2 additions & 2 deletions packages/components/message/demo/Icon.md
Expand Up @@ -7,8 +7,8 @@ order: 3

## zh

自定义图标:支持 `string` `VNode` 两种类型。
自定义图标:支持 `string` , `VNode` , `() => VNodeChild`

## en

Customized icon: supports `string` and `VNode` type.
Customized icon: supports `string` , `VNode` and `() => VNodeChild` type.
4 changes: 2 additions & 2 deletions packages/components/message/demo/Icon.vue
Expand Up @@ -16,8 +16,8 @@ export default defineComponent({
const { info } = useMessage()
const open = () => info('This is a star message', { icon: 'star' })
const iconVNode = h(IxIcon, { name: 'star', style: { color: 'red' } })
const openVNode = () => info('This is a star message', { icon: iconVNode })
const renderIcon = () => h(IxIcon, { name: 'star', style: { color: 'red' } })
const openVNode = () => info('This is a star message', { icon: renderIcon })
return { open, openVNode }
},
Expand Down
14 changes: 7 additions & 7 deletions packages/components/message/docs/Api.zh.md
Expand Up @@ -8,7 +8,7 @@
| `v-model:visible` | 是否可见 | `boolean` | - | - | - |
| `destroyOnHover` | 鼠标悬浮时自动销毁 | `boolean` | `false` || - |
| `duration` | 自动销毁的延时,单位毫秒 | `number` | - | - | 传入 `0` 表示不自动销毁 |
| `icon` | 自定义图标 | `string \| VNode` | - || - |
| `icon` | 自定义图标 | `string \| VNode \| (() => VNodeChild)` | - || - |
| `type` | 提示类型 | `'info' \| 'success' \| 'warning' \| 'error' \| 'loading'` | `info` | - | - |
| `onClose` | 提示框关闭的回调 | `() => void` | - | - | - |

Expand Down Expand Up @@ -55,11 +55,11 @@ export const useMessage: () => MessageProviderRef;
export interface MessageProviderRef {
// 打开提示框
open: (options: MessageOptions) => MessageRef
info: (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
success: (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
warning: (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
error: (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
loading: (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
info: (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
success: (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
warning: (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
error: (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
loading: (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) => MessageRef
// 更新指定 key 的提示框的配置信息
update: (key: VKey, options: MessageOptions) => void
// 销毁指定 key 的提示框
Expand All @@ -71,7 +71,7 @@ export interface MessageProviderRef {
export interface MessageOptions extends MessageProps {
key?: string
// 提示框的内容
content?: string | VNode
content?: string | VNode | (() => VNodeChild)
// 提示框销毁后的回调
onDestroy?: (key: VKey) => void
}
Expand Down
6 changes: 2 additions & 4 deletions packages/components/message/src/Message.tsx
Expand Up @@ -10,11 +10,9 @@ import type { MessageConfig } from '@idux/components/config'

import { computed, defineComponent, onBeforeUnmount, onMounted, watchEffect } from 'vue'

import { isString } from 'lodash-es'

import { callEmit, useControlledProp } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/config'
import { IxIcon } from '@idux/components/icon'
import { convertIconVNode } from '@idux/components/utils'

import { messageProps } from './types'

Expand All @@ -40,7 +38,7 @@ export default defineComponent({

return () => {
const icon = mergedIcon.value
const iconNode = isString(icon) ? <IxIcon name={icon}></IxIcon> : icon
const iconNode = convertIconVNode(slots.icon, icon)
const prefixCls = mergedPrefixCls.value
return (
<div v-show={visible.value} class={classes.value} onMouseenter={onMouseEnter} onMouseleave={onMouseLeave}>
Expand Down
18 changes: 14 additions & 4 deletions packages/components/message/src/MessageProvider.tsx
Expand Up @@ -5,12 +5,21 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { type ComputedRef, TransitionGroup, type VNode, computed, defineComponent, provide, ref } from 'vue'
import {
type ComputedRef,
TransitionGroup,
type VNode,
type VNodeChild,
computed,
defineComponent,
provide,
ref,
} from 'vue'

import { CdkPortal } from '@idux/cdk/portal'
import { VKey, callEmit, convertArray, convertCssPixel, uniqueId } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/config'
import { usePortalTarget } from '@idux/components/utils'
import { convertStringVNode, usePortalTarget } from '@idux/components/utils'

import Message from './Message'
import { MESSAGE_PROVIDER_TOKEN } from './token'
Expand Down Expand Up @@ -42,9 +51,10 @@ export default defineComponent({
const { key, content, visible = true, onDestroy, ...restProps } = item
const onClose = () => destroy(key!)
const mergedProps = { key, visible, onClose }
const contentNode = convertStringVNode(undefined, content)
return (
<Message {...mergedProps} {...restProps}>
{content}
{contentNode}
</Message>
)
})
Expand Down Expand Up @@ -133,7 +143,7 @@ const useMessage = (maxCount: ComputedRef<number>) => {

const messageTypes = ['info', 'success', 'warning', 'error', 'loading'] as const
const [info, success, warning, error, loading] = messageTypes.map(type => {
return (content: string | VNode, options?: Omit<MessageOptions, 'type' | 'content'>) =>
return (content: string | VNode | (() => VNodeChild), options?: Omit<MessageOptions, 'type' | 'content'>) =>
open({ ...options, content, type })
})

Expand Down

0 comments on commit 69013e8

Please sign in to comment.