Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(comp:drawer,header,message,modal,notification): all VNode props support render function now #1717

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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