From 18f742af95864c50ce9c5de0120304a79071d5c5 Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Wed, 29 Mar 2023 15:33:16 +0800 Subject: [PATCH 01/12] refactor(steps): add items prop and variants --- components/steps/index.tsx | 15 +++- components/steps/useLegacyItems.ts | 31 ++++++++ components/vc-steps/Step.tsx | 44 +++++++----- components/vc-steps/Steps.tsx | 110 +++++++++++++++++------------ 4 files changed, 133 insertions(+), 67 deletions(-) create mode 100644 components/steps/useLegacyItems.ts diff --git a/components/steps/index.tsx b/components/steps/index.tsx index 6300eb1ba8..bb0ff540e8 100644 --- a/components/steps/index.tsx +++ b/components/steps/index.tsx @@ -3,20 +3,24 @@ import { computed, defineComponent } from 'vue'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CheckOutlined from '@ant-design/icons-vue/CheckOutlined'; import PropTypes from '../_util/vue-types'; +import type { VueNode } from '../_util/type'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import VcSteps, { Step as VcStep } from '../vc-steps'; import useConfigInject from '../config-provider/hooks/useConfigInject'; import useBreakpoint from '../_util/hooks/useBreakpoint'; +import useLegacyItems from './useLegacyItems'; import classNames from '../_util/classNames'; import Progress from '../progress'; import omit from '../_util/omit'; +import Tooltip from '../tooltip'; import { VcStepProps } from '../vc-steps/Step'; import type { ProgressDotRender } from '../vc-steps/Steps'; import type { MouseEventHandler } from '../_util/EventInterface'; -import { booleanType, stringType, functionType, someType } from '../_util/type'; +import { booleanType, stringType, functionType, someType, arrayType } from '../_util/type'; // CSSINJS import useStyle from './style'; +import { filterEmpty } from '../_util/props-util'; export const stepsProps = () => ({ prefixCls: String, @@ -25,6 +29,7 @@ export const stepsProps = () => ({ initial: Number, percent: Number, responsive: booleanType(), + items: arrayType(), labelPlacement: stringType<'horizontal' | 'vertical'>(), status: stringType<'wait' | 'process' | 'finish' | 'error'>(), size: stringType<'default' | 'small'>(), @@ -62,7 +67,8 @@ const Steps = defineComponent({ // emits: ['update:current', 'change'], setup(props, { attrs, slots, emit }) { const { prefixCls, direction: rtlDirection, configProvider } = useConfigInject('steps', props); - + const items = computed(() => props.items); + const mergedItems = useLegacyItems(items, filterEmpty(slots.default?.())); // style const [wrapSSR, hashId] = useStyle(prefixCls); @@ -119,17 +125,22 @@ const Steps = defineComponent({ attrs.class, hashId.value, ); + const itemRender = (item: StepProps, stepItem: VueNode) => + item.description ? {stepItem} : stepItem; return wrapSSR( , ); diff --git a/components/steps/useLegacyItems.ts b/components/steps/useLegacyItems.ts new file mode 100644 index 0000000000..ddade5b9f8 --- /dev/null +++ b/components/steps/useLegacyItems.ts @@ -0,0 +1,31 @@ +import type { ComputedRef } from 'vue'; +import { computed } from 'vue'; +import type { StepProps } from '.'; +import { isValidElement } from '../_util/props-util'; +import type { VueNode } from '../_util/type'; +import warning from '../_util/warning'; + +function filter(items: (T | null)[]): T[] { + return items.filter(item => item) as T[]; +} + +export default function useLegacyItems(items?: ComputedRef, children?: VueNode[]) { + if (items.value) { + return items; + } + warning(!children, 'Steps', 'Step is deprecated. Please use `items` directly.'); + const childrenItems = computed(() => { + const items = children.map((node: any) => { + if (isValidElement(node)) { + const { props } = node; + const item: StepProps = { + ...props, + }; + return item; + } + }); + return filter(items); + }); + + return childrenItems; +} diff --git a/components/vc-steps/Step.tsx b/components/vc-steps/Step.tsx index 6790f91577..f9044df747 100644 --- a/components/vc-steps/Step.tsx +++ b/components/vc-steps/Step.tsx @@ -2,12 +2,14 @@ import PropTypes, { withUndefined } from '../_util/vue-types'; import type { CSSProperties, PropType } from 'vue'; import { defineComponent } from 'vue'; import type { EventHandler } from '../_util/EventInterface'; +import classNames from '../_util/classNames'; function isString(str: any): str is string { return typeof str === 'string'; } function noop() {} export const VcStepProps = () => ({ + class: PropTypes.string, prefixCls: String, wrapperStyle: { type: Object as PropType, default: undefined as CSSProperties }, itemWidth: String, @@ -19,6 +21,7 @@ export const VcStepProps = () => ({ adjustMarginRight: String, stepNumber: Number, stepIndex: Number, + style: PropTypes.style, description: PropTypes.any, title: PropTypes.any, subTitle: PropTypes.any, @@ -55,27 +58,25 @@ export default defineComponent({ stepIcon = slots.stepIcon, } = props; - let iconNode: any; - const iconClassName = { - [`${prefixCls}-icon`]: true, - [`${iconPrefix}icon`]: true, + let iconNode; + const iconClassName = classNames(`${prefixCls}-icon`, `${iconPrefix}icon`, { [`${iconPrefix}icon-${icon}`]: icon && isString(icon), - [`${iconPrefix}icon-check`]: !icon && status === 'finish' && icons && !icons.finish, - [`${iconPrefix}icon-close`]: !icon && status === 'error' && icons && !icons.error, - }; + [`${iconPrefix}icon-check`]: + !icon && status === 'finish' && ((icons && !icons.finish) || !icons), + [`${iconPrefix}icon-cross`]: + !icon && status === 'error' && ((icons && !icons.error) || !icons), + }); const iconDot = ; // `progressDot` enjoy the highest priority if (progressDot) { if (typeof progressDot === 'function') { iconNode = ( - {progressDot({ - iconDot, + {progressDot(iconDot, { index: stepNumber - 1, status, title, description, - prefixCls, })} ); @@ -103,12 +104,14 @@ export default defineComponent({ node: iconNode, }); } + return iconNode; }; return () => { const { prefixCls, itemWidth, + style, active, status = 'wait', tailContent, @@ -121,18 +124,21 @@ export default defineComponent({ onClick, onStepClick, } = props; - - const classString = { - [`${prefixCls}-item`]: true, - [`${prefixCls}-item-${status}`]: true, - [`${prefixCls}-item-custom`]: icon, - [`${prefixCls}-item-active`]: active, - [`${prefixCls}-item-disabled`]: disabled === true, - }; + const mergedStatus = status || 'wait'; + const classString = classNames( + `${prefixCls}-item`, + `${prefixCls}-item-${mergedStatus}`, + props.class, + { + [`${prefixCls}-item-custom`]: icon, + [`${prefixCls}-item-active`]: active, + [`${prefixCls}-item-disabled`]: disabled === true, + }, + ); const stepProps = { class: classString, }; - const stepItemStyle: CSSProperties = {}; + const stepItemStyle: CSSProperties = { ...style }; if (itemWidth) { stepItemStyle.width = itemWidth; } diff --git a/components/vc-steps/Steps.tsx b/components/vc-steps/Steps.tsx index a07b4c0b74..a0a236a99b 100644 --- a/components/vc-steps/Steps.tsx +++ b/components/vc-steps/Steps.tsx @@ -1,8 +1,7 @@ import PropTypes from '../_util/vue-types'; -import { filterEmpty } from '../_util/props-util'; -import { cloneElement } from '../_util/vnode'; import { defineComponent } from 'vue'; import classNames from '../_util/classNames'; +import Step from './Step'; export type Status = 'error' | 'process' | 'finish' | 'wait'; export type StepIconRender = (info: { @@ -35,11 +34,15 @@ export default defineComponent({ progressDot: PropTypes.oneOfType([PropTypes.looseBool, PropTypes.func]).def(undefined), initial: PropTypes.number.def(0), current: PropTypes.number.def(0), + items: PropTypes.array.def(() => []), icons: PropTypes.shape({ finish: PropTypes.any, error: PropTypes.any, }).loose, + style: PropTypes.style, stepIcon: Function, + isInline: PropTypes.looseBool, + itemRender: Function, }, slots: ['stepIcon', 'progressDot'], emits: ['change'], @@ -54,6 +57,7 @@ export default defineComponent({ const { prefixCls, direction, + style = {}, type, labelPlacement, iconPrefix, @@ -64,56 +68,70 @@ export default defineComponent({ initial, icons, stepIcon = slots.stepIcon, + items, + isInline, + itemRender, + ...restProps } = props; const isNav = type === 'navigation'; - const adjustedLabelPlacement = progressDot ? 'vertical' : labelPlacement; + const mergedProgressDot = isInline || progressDot; + const mergedDirection = isInline ? 'horizontal' : direction; + const mergedSize = isInline ? undefined : size; + + const adjustedLabelPlacement = mergedProgressDot ? 'vertical' : labelPlacement; const classString = classNames(prefixCls, `${prefixCls}-${direction}`, { - [`${prefixCls}-${size}`]: size, - [`${prefixCls}-label-${adjustedLabelPlacement}`]: direction === 'horizontal', - [`${prefixCls}-dot`]: !!progressDot, + [`${prefixCls}-${mergedSize}`]: mergedSize, + [`${prefixCls}-label-${adjustedLabelPlacement}`]: mergedDirection === 'horizontal', + [`${prefixCls}-dot`]: !!mergedProgressDot, [`${prefixCls}-navigation`]: isNav, + [`${prefixCls}-inline`]: isInline, }); - const children = filterEmpty(slots.default?.()); - return ( -
- {children.map((child, index) => { - // description: PropTypes.any, - // icon: PropTypes.any, - // status: PropTypes.oneOf(tuple('wait', 'process', 'finish', 'error')), - // disabled: { type: Boolean, default: undefined }, - // title: PropTypes.any, - // subTitle: PropTypes.any, - const { prefixCls: pre = prefixCls, ...restProps } = child.props || {}; - const stepNumber = initial + index; - const stepProps = { - ...restProps, - stepNumber: stepNumber + 1, - stepIndex: stepNumber, - key: stepNumber, - prefixCls: pre, - iconPrefix, - progressDot, - icons, - stepIcon, - onStepClick, - }; + const renderStep = (item, index) => { + const { prefixCls: pre = prefixCls, ...restProps } = item || {}; + const stepNumber = initial + index; + const stepProps = { + ...restProps, + stepNumber: stepNumber + 1, + stepIndex: stepNumber, + key: stepNumber, + prefixCls: pre, + iconPrefix, + progressDot: mergedProgressDot, + icons, + stepIcon, + onStepClick, + active: stepNumber === current, + }; + + // fix tail color + if (status === 'error' && index === current - 1) { + stepProps.class = `${prefixCls}-next-error`; + } - // fix tail color - if (status === 'error' && index === current - 1) { - stepProps.class = `${prefixCls}-next-error`; - } - if (!restProps.status) { - if (stepNumber === current) { - stepProps.status = status; - } else if (stepNumber < current) { - stepProps.status = 'finish'; - } else { - stepProps.status = 'wait'; - } - } - stepProps.active = stepNumber === current; - return cloneElement(child, stepProps); - })} + if (!restProps.status) { + if (stepNumber === current) { + stepProps.status = status; + } else if (stepNumber < current) { + stepProps.status = 'finish'; + } else { + stepProps.status = 'wait'; + } + } + + if (isInline) { + stepProps.icon = undefined; + stepProps.subTitle = undefined; + } + stepProps.active = stepNumber === current; + const stepNode = ; + if (itemRender) { + return itemRender(stepProps, stepNode); + } + return stepNode; + }; + return ( +
+ {items.map(renderStep)}
); }; From 839a60ee96a64f48df961b342c88e524bb7ad5ef Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Wed, 29 Mar 2023 15:34:14 +0800 Subject: [PATCH 02/12] feat(steps): add Label Placement and Inline Steps demo --- components/steps/demo/index.vue | 6 ++ components/steps/demo/inline.vue | 97 +++++++++++++++++++++++ components/steps/demo/label-placement.vue | 51 ++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 components/steps/demo/inline.vue create mode 100644 components/steps/demo/label-placement.vue diff --git a/components/steps/demo/index.vue b/components/steps/demo/index.vue index 12447bd84f..e6524aaef9 100644 --- a/components/steps/demo/index.vue +++ b/components/steps/demo/index.vue @@ -12,6 +12,8 @@
`; +exports[`renders ./components/steps/demo/inline.vue correctly 1`] = ` +
+ + +
+ +
+
    +
  • +
    +
    +
    +

    Ant Design Title 1

    +
    Ant Design, a design language for background applications, is refined by Ant UED Team
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 1 + +
    +
    This is a Step 1.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 2 + +
    +
    This is a Step 2.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 3 + +
    +
    This is a Step 3.
    +
    +
    +
    +
    + + +
  • +
  • +
    +
    +
    +

    Ant Design Title 2

    +
    Ant Design, a design language for background applications, is refined by Ant UED Team
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 1 + +
    +
    This is a Step 1.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 2 + +
    +
    This is a Step 2.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 3 + +
    +
    This is a Step 3.
    +
    +
    +
    +
    + + +
  • +
  • +
    +
    +
    +

    Ant Design Title 3

    +
    Ant Design, a design language for background applications, is refined by Ant UED Team
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 1 + +
    +
    This is a Step 1.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 2 + +
    +
    This is a Step 2.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 3 + +
    +
    This is a Step 3.
    +
    +
    +
    +
    + + +
  • +
  • +
    +
    +
    +

    Ant Design Title 4

    +
    Ant Design, a design language for background applications, is refined by Ant UED Team
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 1 + +
    +
    This is a Step 1.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 2 + +
    +
    This is a Step 2.
    +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Step 3 + +
    +
    This is a Step 3.
    +
    +
    +
    +
    + + +
  • +
+
+
+ + +
+`; + +exports[`renders ./components/steps/demo/label-placement.vue correctly 1`] = ` +
+
+
+
+
+ +
+
+
+
Finished + +
+
This is a description.
+
+
+
+
+
+
+ +
+
2
+
+
In Progress + +
+
This is a description.
+
+
+
+
+
+
+ +
+
3
+
+
Waiting + +
+
This is a description.
+
+
+
+

+
+
+
+
+ +
+
+
+
Finished + +
+
This is a description.
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + +
+
2 +
+
+
+
In Progress + +
+
This is a description.
+
+
+
+
+
+
+ +
+
3
+
+
Waiting + +
+
This is a description.
+
+
+
+

+
+
+
+
+ +
+
+
+
Finished + +
+
This is a description.
+
+
+
+
+
+
+ +
+
2
+
+
In Progress + +
+
This is a description.
+
+
+
+
+
+
+ +
+
3
+
+
Waiting + +
+
This is a description.
+
+
+
+
+
+`; + exports[`renders ./components/steps/demo/nav.vue correctly 1`] = `
From 10c563114e4058f8418ed7a2031622adf43f10eb Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Wed, 29 Mar 2023 15:39:52 +0800 Subject: [PATCH 04/12] test(steps): Steps demo snap --- .../__tests__/__snapshots__/demo.test.js.snap | 153 ++++++++++++------ 1 file changed, 107 insertions(+), 46 deletions(-) diff --git a/components/steps/__tests__/__snapshots__/demo.test.js.snap b/components/steps/__tests__/__snapshots__/demo.test.js.snap index 242a746aaf..66c9c98611 100644 --- a/components/steps/__tests__/__snapshots__/demo.test.js.snap +++ b/components/steps/__tests__/__snapshots__/demo.test.js.snap @@ -2,7 +2,7 @@ exports[`renders ./components/steps/demo/clickable.vue correctly 1`] = `
-
+
@@ -46,10 +46,10 @@ exports[`renders ./components/steps/demo/clickable.vue correctly 1`] = `
-