From 70a8f76baad295a2e5af6a4e25e823b1c436c715 Mon Sep 17 00:00:00 2001 From: danranVm Date: Tue, 25 Jan 2022 15:18:04 +0800 Subject: [PATCH] docs(comp:*): api review (#741) --- .../__snapshots__/header.spec.ts.snap | 3 +- packages/components/button/docs/Index.zh.md | 12 +-- packages/components/button/src/Button.tsx | 6 +- .../components/button/src/ButtonGroup.tsx | 2 +- .../__snapshots__/collapse.spec.ts.snap | 22 ++--- .../header/__tests__/header.spec.ts | 4 +- packages/components/header/docs/Index.zh.md | 9 +-- packages/components/header/src/Header.tsx | 80 ++++++++----------- packages/components/header/src/types.ts | 1 + packages/components/header/style/index.less | 32 ++++---- packages/components/icon/docs/Index.zh.md | 6 +- .../__snapshots__/popover.spec.ts.snap | 2 +- packages/components/utils/index.ts | 1 + packages/components/utils/src/covertVNode.ts | 54 +++++++++++++ 14 files changed, 129 insertions(+), 105 deletions(-) create mode 100644 packages/components/utils/src/covertVNode.ts diff --git a/packages/components/_private/header/__tests__/__snapshots__/header.spec.ts.snap b/packages/components/_private/header/__tests__/__snapshots__/header.spec.ts.snap index c526c4a30..a38dc2890 100644 --- a/packages/components/_private/header/__tests__/__snapshots__/header.spec.ts.snap +++ b/packages/components/_private/header/__tests__/__snapshots__/header.spec.ts.snap @@ -5,8 +5,7 @@ exports[`Header render work 1`] = `
Header title - -
+
" diff --git a/packages/components/button/docs/Index.zh.md b/packages/components/button/docs/Index.zh.md index ebdc61ad5..5ed076789 100644 --- a/packages/components/button/docs/Index.zh.md +++ b/packages/components/button/docs/Index.zh.md @@ -16,15 +16,15 @@ subtitle: 按钮 | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | -| `mode` | 设置按钮种类 | `'primary' \| 'default' \| 'dashed' \| 'text' \| 'link'` | `'default'` | - |- | +| `block` | 将按钮宽度调整为自适应其父元素的宽度 | `boolean` | - | - |- | | `danger` | 设置危险状态 | `boolean` | - | - |- | -| `ghost` | 设置幽灵状态 | `boolean` | - | - |- | | `disabled` | 设置禁用状态 | `boolean` | - | - |- | +| `ghost` | 设置幽灵状态 | `boolean` | - | - |- | +| `icon` | 设置图标类型 | `string \| #icon` | - | - | `loading` 为 `true` 时无效 | | `loading` | 设置加载中状态 | `boolean` | - | - |- | -| `size` | 设置按钮大小 | `'lg' \| 'md' \| 'sm'` | `'md'` | - |- | +| `mode` | 设置按钮种类 | `'primary' \| 'default' \| 'dashed' \| 'text' \| 'link'` | `'default'` | - |- | | `shape` | 设置按钮形状 | `'circle' \| 'round'` | - | - |- | -| `block` | 将按钮宽度调整为自适应其父元素的宽度 | `boolean` | - | - |- | -| `icon` | 设置图标类型 | `string \| #icon` | - | - | `loading` 为 `true` 时无效 | +| `size` | 设置按钮大小 | `'lg' \| 'md' \| 'sm'` | `'md'` | - |- | | `type` | 原生 `button` 的 `type` 属性 | `'button' \| 'submit' \| 'reset'` | `'button'` | - | 参考 [HTML 标准](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type) | ### IxButtonGroup @@ -34,7 +34,7 @@ subtitle: 按钮 | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | | `mode` | 设置组内按钮种类 | `'primary' \| 'default' \| 'dashed' \| 'text' \| 'link'` | - | - |- | -| `size` | 设置组内按钮大小 | `'lg' \| 'md' \| 'sm'` | - | - |- | | `shape` | 设置组内按钮形状 | `'circle' \| 'round'` | - | - |- | +| `size` | 设置组内按钮大小 | `'lg' \| 'md' \| 'sm'` | - | - |- | ### 主题变量 diff --git a/packages/components/button/src/Button.tsx b/packages/components/button/src/Button.tsx index 3770bf0f5..745e4f513 100644 --- a/packages/components/button/src/Button.tsx +++ b/packages/components/button/src/Button.tsx @@ -9,7 +9,6 @@ import type { VNodeTypes } from 'vue' import { computed, defineComponent, inject, normalizeClass } from 'vue' -import { hasSlot } from '@idux/cdk/utils' import { useGlobalConfig } from '@idux/components/config' import { FORM_TOKEN } from '@idux/components/form' import { IxIcon } from '@idux/components/icon' @@ -28,9 +27,8 @@ export default defineComponent({ const formContext = inject(FORM_TOKEN, null) const mode = computed(() => props.mode ?? groupProps.mode ?? 'default') - const hasDefaultSlot = computed(() => hasSlot(slots)) - const size = computed(() => props.size ?? groupProps.size ?? formContext?.size.value ?? 'md') + const classes = computed(() => { const { block, danger, disabled, ghost, loading, icon, shape = groupProps.shape } = props const prefixCls = mergedPrefixCls.value @@ -41,7 +39,7 @@ export default defineComponent({ [`${prefixCls}-disabled`]: disabled || loading, [`${prefixCls}-ghost`]: ghost, [`${prefixCls}-loading`]: loading, - [`${prefixCls}-icon-only`]: !hasDefaultSlot.value && (icon || loading), + [`${prefixCls}-icon-only`]: !slots.default && (icon || loading), [`${prefixCls}-${mode.value}`]: mode.value !== 'default', [`${prefixCls}-${shape}`]: !!shape, [`${prefixCls}-${size.value}`]: true, diff --git a/packages/components/button/src/ButtonGroup.tsx b/packages/components/button/src/ButtonGroup.tsx index f7725372c..d4a5d85cd 100644 --- a/packages/components/button/src/ButtonGroup.tsx +++ b/packages/components/button/src/ButtonGroup.tsx @@ -21,6 +21,6 @@ export default defineComponent({ provide(buttonToken, props) - return () =>
{slots.default?.()}
+ return () =>
{slots.default && slots.default()}
}, }) diff --git a/packages/components/collapse/__tests__/__snapshots__/collapse.spec.ts.snap b/packages/components/collapse/__tests__/__snapshots__/collapse.spec.ts.snap index 52e71b498..c97c7a907 100644 --- a/packages/components/collapse/__tests__/__snapshots__/collapse.spec.ts.snap +++ b/packages/components/collapse/__tests__/__snapshots__/collapse.spec.ts.snap @@ -4,8 +4,7 @@ exports[`Collapse panel work header work 1`] = ` "
-
-
+
header 0 @@ -27,15 +26,9 @@ exports[`Collapse panel work header work 2`] = ` "
-
-
- -
+
hello header - -
- -
+
@@ -54,8 +47,7 @@ exports[`Collapse render work 1`] = ` "
-
-
+
header 0 @@ -72,8 +64,7 @@ exports[`Collapse render work 1`] = `
-
-
+
header 1 @@ -90,8 +81,7 @@ exports[`Collapse render work 1`] = `
-
-
+
header 2 diff --git a/packages/components/header/__tests__/header.spec.ts b/packages/components/header/__tests__/header.spec.ts index 35d6ad448..cc2bf4dd7 100644 --- a/packages/components/header/__tests__/header.spec.ts +++ b/packages/components/header/__tests__/header.spec.ts @@ -124,11 +124,11 @@ describe('Header', () => { test('showBar work', async () => { const wrapper = HeaderMount({ props: { showBar: true } }) - expect(wrapper.find('.ix-header-bar').exists()).toBe(true) + expect(wrapper.classes()).toContain('ix-header-with-bar') await wrapper.setProps({ showBar: false }) - expect(wrapper.find('.ix-header-bar').exists()).toBe(false) + expect(wrapper.classes()).not.toContain('ix-header-with-bar') }) test('subTitle work', async () => { diff --git a/packages/components/header/docs/Index.zh.md b/packages/components/header/docs/Index.zh.md index 83fc728fc..0630d3629 100644 --- a/packages/components/header/docs/Index.zh.md +++ b/packages/components/header/docs/Index.zh.md @@ -15,18 +15,13 @@ order: 0 | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | | `avatar` | 自定义头像 | `string \| AvatarProps \| #avatar` | - | - | 传入 `string` 时,为头像的图标 | +| `description` | 标题下方的说明文字 | `string \| #description` | - | - | - | | `disabled` | 是否禁用 | `boolean` | `false` | - | - | | `prefix` | 标题前缀图标 | `string \| VNode \| #prefix` | - | - | - | -| `size` | 标题大小 | `xl \| lg \| md \| sm` | `md` | - | 分别对应 `h1 \| h2 \| h3 \| h4` | +| `size` | 标题大小 | `xl \| lg \| md \| sm` | `md` | - | - | | `showBar` | 是否显示标题前的竖条 | `boolean` | `false` | - | - | | `subTitle` | 二级标题文字 | `string \| #subTitle` | - | - | - | | `suffix` | 标题后缀图标 | `string \| VNode \| #suffix` | - | - | 通常用于额外操作 | | `title` | 标题文字 | `string \| #default` | - | - | - | | `onPrefixClick` | 前缀图标被点击 | `(evt: MouseEvent) => void` | - | - | - | | `onSuffixClick` | 后缀图标被点击 | `(evt: MouseEvent) => void` | - | - | - | - -#### HeaderSlots - -| 名称 | 说明 | 参数类型 | 备注 | -| --- | --- | --- | --- | -| `description` | 标题下方的说明文字 | - | - | diff --git a/packages/components/header/src/Header.tsx b/packages/components/header/src/Header.tsx index 9a2bb5133..eb8da2008 100644 --- a/packages/components/header/src/Header.tsx +++ b/packages/components/header/src/Header.tsx @@ -5,18 +5,16 @@ * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE */ -import type { HeaderProps } from './types' import type { AvatarProps } from '@idux/components/avatar' -import type { ComputedRef, Slot, VNode } from 'vue' -import { computed, defineComponent, isVNode } from 'vue' +import { type ComputedRef, type Slot, computed, defineComponent, normalizeClass } from 'vue' import { isString } from 'lodash-es' import { callEmit } from '@idux/cdk/utils' import { IxAvatar } from '@idux/components/avatar' import { useGlobalConfig } from '@idux/components/config' -import { IxIcon } from '@idux/components/icon' +import { covertIconVNode, covertLabelVNode } from '@idux/components/utils' import { headerProps } from './types' @@ -34,7 +32,15 @@ export default defineComponent({ const common = useGlobalConfig('common') const mergedPrefixCls = computed(() => `${common.prefixCls}-header`) - const classes = useClasses(props, mergedPrefixCls) + const classes = computed(() => { + const prefixCls = mergedPrefixCls.value + return normalizeClass({ + [prefixCls]: true, + [`${prefixCls}-disabled`]: props.disabled, + [`${prefixCls}-with-bar`]: props.showBar, + [`${prefixCls}-${props.size}`]: true, + }) + }) const avatarSize = computed(() => avatarSizeTransformMap[props.size]) @@ -42,60 +48,38 @@ export default defineComponent({ const onSuffixClick = (evt: MouseEvent) => !props.disabled && callEmit(props.onSuffixClick, evt) return () => { - const { prefix, suffix, avatar, title, subTitle } = props const prefixCls = mergedPrefixCls.value + + const prefixIconNode = covertIconVNode(slots, props, 'prefix') + const suffixIconNode = covertIconVNode(slots, props, 'suffix') + const titleNode = covertLabelVNode(slots.default, props.title) + const subTitleNode = covertLabelVNode(slots, props, 'subTitle') + const descriptionNode = covertLabelVNode(slots, props, 'description') + return (
- {renderIcon(slots.prefix, prefix, onPrefixClick, `${prefixCls}-prefix`)} - {renderAvatar(slots.avatar, avatar, avatarSize)} - {renderTitle(slots.default, title, `${prefixCls}-title`)} - {renderTitle(slots.subTitle, subTitle, `${prefixCls}-sub-title`)} - {renderIcon(slots.suffix, suffix, onSuffixClick, `${prefixCls}-suffix`)} + {prefixIconNode && ( + + {prefixIconNode} + + )} + {renderAvatar(slots.avatar, props.avatar, avatarSize)} + {titleNode && {titleNode}} + {subTitleNode && {subTitleNode}} + {suffixIconNode && ( + + {suffixIconNode} + + )}
- {slots.description ?
{slots.description()}
: null} + {descriptionNode &&
{descriptionNode}
}
) } }, }) -const useClasses = (props: HeaderProps, mergedPrefixCls: ComputedRef) => { - return computed(() => { - const prefixCls = mergedPrefixCls.value - return { - [prefixCls]: true, - [`${prefixCls}-bar`]: props.showBar, - [`${prefixCls}-disabled`]: props.disabled, - [`${prefixCls}-${props.size}`]: true, - } - }) -} - -const renderIcon = ( - slot: Slot | undefined, - icon: string | VNode | undefined, - onClick: (evt: MouseEvent) => void, - wrapperClassName: string, -) => { - if (!slot && !icon) { - return null - } - const iconNode = slot ? slot() : isVNode(icon) ? icon : - return ( -
- {iconNode} -
- ) -} - -const renderTitle = (slot: Slot | undefined, title: string | undefined, wrapperClassName: string) => { - if (!slot && !title) { - return null - } - return {slot?.() ?? title} -} - const renderAvatar = ( slot: Slot | undefined, avatar: string | AvatarProps | undefined, diff --git a/packages/components/header/src/types.ts b/packages/components/header/src/types.ts index 287465351..eb412dfff 100644 --- a/packages/components/header/src/types.ts +++ b/packages/components/header/src/types.ts @@ -15,6 +15,7 @@ export type HeaderSize = 'xl' | 'lg' | 'md' | 'sm' export const headerProps = { avatar: IxPropTypes.oneOfType([String, IxPropTypes.object()]), + description: IxPropTypes.string, disabled: IxPropTypes.bool.def(false), prefix: IxPropTypes.oneOfType([String, IxPropTypes.vNode]), size: IxPropTypes.oneOf(['xl', 'lg', 'md', 'sm']).def('md'), diff --git a/packages/components/header/style/index.less b/packages/components/header/style/index.less index adf84201c..e100610d8 100644 --- a/packages/components/header/style/index.less +++ b/packages/components/header/style/index.less @@ -14,22 +14,6 @@ background-color: @header-background-color; transition: @header-transition; - &-bar &-content { - padding-left: @header-bar-width * 2; - - &::before { - position: absolute; - left: 0; - top: 0; - bottom: 0; - margin: @header-bar-margin; - width: @header-bar-width; - background-color: @header-bar-background-color; - border-radius: calc(@header-bar-width / 2); - content: ''; - } - } - &-content { position: relative; width: 100%; @@ -98,6 +82,22 @@ } } + &-with-bar &-content { + padding-left: @header-bar-width * 2; + + &::before { + position: absolute; + left: 0; + top: 0; + bottom: 0; + margin: @header-bar-margin; + width: @header-bar-width; + background-color: @header-bar-background-color; + border-radius: calc(@header-bar-width / 2); + content: ''; + } + } + &-xl { .header-size(@header-height-xl, @header-title-font-size-xl); } diff --git a/packages/components/icon/docs/Index.zh.md b/packages/components/icon/docs/Index.zh.md index 3a1c849dd..22ad6ae6c 100644 --- a/packages/components/icon/docs/Index.zh.md +++ b/packages/components/icon/docs/Index.zh.md @@ -10,13 +10,15 @@ order: 0 ### IxIcon -#### Props +#### IconProps | 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 | | --- | --- | --- | --- | --- | --- | | `name`| 图标名称 | `string` | - | - | - | +| `color` | 图标颜色 | `string` | - | - | - | | `iconfont` | 图标是否来自 `iconfont` | `boolean` | - | - | - | | `rotate` | 图标旋转角度 | `boolean \| number` | `false` | - | 为 `true` 时会循环旋转 | +| `size` | 图标大小 | `number \| string` | - | - | - | ### 辅助函数 @@ -72,7 +74,7 @@ const loadIconDynamically = (iconName: string) => { useGlobalConfig('icon', { loadIconDynamically }) ``` -你可以执行 `node ./node_modules/@idux/components/bin icon` 命令将默认图标拷贝到 `public/idux-icons` 目录下。 +你可以执行 `node ./node_modules/@idux/components/bin icon` 命令将默认图标拷贝到 `public/idux-icons` 目录下。 当然,你也可以手动拷贝,默认图标的存放地址为 `./node_modules/@idux/components/icon/svg`。 #### 静态加载 diff --git a/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap b/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap index 27c6de3ec..d6a9cdc87 100644 --- a/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap +++ b/packages/components/popover/__tests__/__snapshots__/popover.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Popover header props work 1`] = `"
TitlesubTitle
"`; +exports[`Popover header props work 1`] = `"
TitlesubTitle
"`; exports[`Popover render work 1`] = ` "
trigger
diff --git a/packages/components/utils/index.ts b/packages/components/utils/index.ts index 4d227b13f..dfaf0ec64 100644 --- a/packages/components/utils/index.ts +++ b/packages/components/utils/index.ts @@ -7,6 +7,7 @@ export * from './src/colors' export * from './src/covertTarget' +export * from './src/covertVNode' export * from './src/useFormAccessor' export * from './src/useFormElement' export * from './src/useKey' diff --git a/packages/components/utils/src/covertVNode.ts b/packages/components/utils/src/covertVNode.ts new file mode 100644 index 000000000..80aacb295 --- /dev/null +++ b/packages/components/utils/src/covertVNode.ts @@ -0,0 +1,54 @@ +/** + * @license + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE + */ + +import { Slot, Slots, VNode, type VNodeChild, createVNode } from 'vue' + +import { isString } from 'lodash-es' + +import { IxIcon } from '@idux/components/icon' + +export function covertIconVNode(slot: Slot | undefined, prop: string | VNode | undefined): VNodeChild +export function covertIconVNode(slots: Slots | undefined, props: unknown, key: string): VNodeChild +export function covertIconVNode(slots: Slot | Slots | undefined, props: unknown, key?: string): VNodeChild { + let iconSlot: Slot | undefined + let iconName: string | VNode | undefined + if (key) { + iconSlot = (slots as Slots)[key] + if (!iconSlot) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + iconName = (props as any)[key] + } + } else { + iconSlot = slots as Slot | undefined + iconName = props as string | VNode | undefined + } + + if (iconSlot) { + return iconSlot() + } + + return isString(iconName) ? createVNode(IxIcon, { name: iconName }, null) : iconName +} + +export function covertLabelVNode(slot: Slot | undefined, prop: string | VNode | undefined): VNodeChild +export function covertLabelVNode(slots: Slots | undefined, props: unknown, key: string): VNodeChild +export function covertLabelVNode(slots: Slot | Slots | undefined, props: unknown, key?: string): VNodeChild { + let labelSlot: Slot | undefined + let label: string | VNode | undefined + if (key) { + labelSlot = (slots as Slots)[key] + if (!labelSlot) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + label = (props as any)[key] + } + } else { + labelSlot = slots as Slot | undefined + label = props as string | VNode | undefined + } + + return labelSlot ? labelSlot() : label +}