Skip to content

Commit 65f49b9

Browse files
authored
feat: update step (#2379)
* feat: update step * fix: step use v-model can't click * fix: avoid step auto mount attrs * chore: use props to transfer event * docs: add steps breakchange
1 parent 36bec7e commit 65f49b9

File tree

6 files changed

+83
-79
lines changed

6 files changed

+83
-79
lines changed

breakChange-2.x.md

+4
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,7 @@ v-model -> v-model:selectedKeys :openKeys.sync -> v-mdoel:openKeys
4747
## dropdown
4848

4949
v-model -> v-model:visible
50+
51+
## Steps
52+
53+
v-model -> v-model:current

components/_util/props-util.js

+7
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ export function getComponentName(opts) {
302302
return opts && (opts.Ctor.options.name || opts.tag);
303303
}
304304

305+
export function isFragment(c) {
306+
return c.length === 1 && c[0].type === Fragment;
307+
}
308+
305309
export function isEmptyElement(c) {
306310
return c.type === Comment || (c.type === Text && c.children.trim() === '');
307311
}
@@ -311,6 +315,9 @@ export function isStringElement(c) {
311315
}
312316

313317
export function filterEmpty(children = []) {
318+
if (isFragment(children)) {
319+
return children[0].children.filter(c => !isEmptyElement(c));
320+
}
314321
return children.filter(c => !isEmptyElement(c));
315322
}
316323
const initDefaultProps = (propTypes, defaultProps) => {

components/steps/index.jsx

+16-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import { inject } from 'vue';
12
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
23
import CheckOutlined from '@ant-design/icons-vue/CheckOutlined';
34
import PropTypes from '../_util/vue-types';
4-
import { initDefaultProps, getOptionProps, getListeners } from '../_util/props-util';
5+
import { initDefaultProps, getOptionProps, getComponent } from '../_util/props-util';
56
import VcSteps from '../vc-steps';
67
import { ConfigConsumerProps } from '../config-provider';
7-
import Base from '../base';
88

99
const getStepsProps = (defaultProps = {}) => {
1010
const props = {
@@ -27,12 +27,10 @@ const Steps = {
2727
props: getStepsProps({
2828
current: 0,
2929
}),
30-
inject: {
31-
configProvider: { default: () => ConfigConsumerProps },
32-
},
33-
model: {
34-
prop: 'current',
35-
event: 'change',
30+
setup() {
31+
return {
32+
configProvider: inject('configProvider', ConfigConsumerProps),
33+
};
3634
},
3735
Step: { ...VcSteps.Step, name: 'AStep' },
3836
render() {
@@ -41,30 +39,27 @@ const Steps = {
4139
const getPrefixCls = this.configProvider.getPrefixCls;
4240
const prefixCls = getPrefixCls('steps', customizePrefixCls);
4341
const iconPrefix = getPrefixCls('', customizeIconPrefixCls);
42+
const progressDot = getComponent(this, 'progressDot', this, false);
4443

4544
const icons = {
4645
finish: <CheckOutlined class={`${prefixCls}-finish-icon`} />,
4746
error: <CloseOutlined class={`${prefixCls}-error-icon`} />,
4847
};
4948
const stepsProps = {
50-
props: {
51-
icons,
52-
iconPrefix,
53-
prefixCls,
54-
...props,
55-
},
56-
on: getListeners(this),
57-
scopedSlots: this.$scopedSlots,
49+
icons,
50+
iconPrefix,
51+
prefixCls,
52+
progressDot,
53+
...props,
5854
};
59-
return <VcSteps {...stepsProps}>{this.$slots.default}</VcSteps>;
55+
return <VcSteps {...stepsProps}>{this.$slots.default && this.$slots.default()}</VcSteps>;
6056
},
6157
};
6258

6359
/* istanbul ignore next */
64-
Steps.install = function(Vue) {
65-
Vue.use(Base);
66-
Vue.component(Steps.name, Steps);
67-
Vue.component(Steps.Step.name, Steps.Step);
60+
Steps.install = function(app) {
61+
app.component(Steps.name, Steps);
62+
app.component(Steps.Step.name, Steps.Step);
6863
};
6964

7065
export default Steps;

components/vc-steps/Step.jsx

+29-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import PropTypes from '../_util/vue-types';
2-
import { getOptionProps, getComponentFromProp, getListeners } from '../_util/props-util';
2+
import { getOptionProps, getComponent } from '../_util/props-util';
33

44
function isString(str) {
55
return typeof str === 'string';
@@ -28,21 +28,26 @@ export default {
2828
finish: PropTypes.any,
2929
error: PropTypes.any,
3030
}).loose,
31+
onClick: PropTypes.func,
32+
onStepClick: PropTypes.func,
3133
},
3234
methods: {
33-
onClick(...args) {
34-
this.$emit('click', ...args);
35+
onItemClick(...args) {
36+
const { onClick } = this.$props;
37+
38+
if (onClick) {
39+
this.$emit('click', ...args);
40+
}
41+
3542
this.$emit('stepClick', this.stepIndex);
3643
},
3744
renderIconNode() {
38-
const { prefixCls, stepNumber, status, iconPrefix, icons } = getOptionProps(this);
39-
let progressDot = this.progressDot;
40-
if (progressDot === undefined) {
41-
progressDot = this.$scopedSlots.progressDot;
42-
}
43-
const icon = getComponentFromProp(this, 'icon');
44-
const title = getComponentFromProp(this, 'title');
45-
const description = getComponentFromProp(this, 'description');
45+
const { prefixCls, stepNumber, status, iconPrefix, icons, progressDot } = getOptionProps(
46+
this,
47+
);
48+
const icon = getComponent(this, 'icon');
49+
const title = getComponent(this, 'title');
50+
const description = getComponent(this, 'description');
4651
let iconNode;
4752
const iconClassName = {
4853
[`${prefixCls}-icon`]: true,
@@ -86,22 +91,23 @@ export default {
8691
tailContent,
8792
adjustMarginRight,
8893
disabled,
94+
onClick,
95+
onStepClick,
8996
} = getOptionProps(this);
9097

91-
const title = getComponentFromProp(this, 'title');
92-
const subTitle = getComponentFromProp(this, 'subTitle');
93-
const description = getComponentFromProp(this, 'description');
98+
const title = getComponent(this, 'title');
99+
const subTitle = getComponent(this, 'subTitle');
100+
const description = getComponent(this, 'description');
94101

95102
const classString = {
96103
[`${prefixCls}-item`]: true,
97104
[`${prefixCls}-item-${status}`]: true,
98-
[`${prefixCls}-item-custom`]: getComponentFromProp(this, 'icon'),
105+
[`${prefixCls}-item-custom`]: getComponent(this, 'icon'),
99106
[`${prefixCls}-item-active`]: active,
100107
[`${prefixCls}-item-disabled`]: disabled === true,
101108
};
102109
const stepProps = {
103110
class: classString,
104-
on: getListeners(this),
105111
};
106112
const stepItemStyle = {};
107113
if (itemWidth) {
@@ -110,17 +116,15 @@ export default {
110116
if (adjustMarginRight) {
111117
stepItemStyle.marginRight = adjustMarginRight;
112118
}
113-
const listeners = getListeners(this);
119+
114120
const accessibilityProps = {
115-
attrs: {},
116-
on: {
117-
click: listeners.click || noop,
118-
},
121+
onClick: onClick || noop,
119122
};
120-
if (listeners.stepClick && !disabled) {
121-
accessibilityProps.attrs.role = 'button';
122-
accessibilityProps.attrs.tabIndex = 0;
123-
accessibilityProps.on.click = this.onClick;
123+
124+
if (onStepClick && !disabled) {
125+
accessibilityProps.role = 'button';
126+
accessibilityProps.tabIndex = 0;
127+
accessibilityProps.onClick = this.onItemClick;
124128
}
125129
return (
126130
<div {...stepProps} style={stepItemStyle}>

components/vc-steps/Steps.jsx

+25-33
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types';
22
import BaseMixin from '../_util/BaseMixin';
33
import debounce from 'lodash/debounce';
44
import isFlexSupported from '../_util/isFlexSupported';
5-
import { filterEmpty, getEvents, getPropsData, getListeners } from '../_util/props-util';
5+
import { filterEmpty } from '../_util/props-util';
66
import { cloneElement } from '../_util/vnode';
77

88
export default {
@@ -59,6 +59,7 @@ export default {
5959
const { current } = this.$props;
6060
if (current !== next) {
6161
this.$emit('change', next);
62+
this.$emit('update:current', next);
6263
}
6364
},
6465
calcStepOffsetWidth() {
@@ -97,17 +98,14 @@ export default {
9798
status,
9899
size,
99100
current,
100-
$scopedSlots,
101+
$slots,
102+
progressDot,
101103
initial,
102104
icons,
103105
} = this;
104106
const isNav = type === 'navigation';
105-
let progressDot = this.progressDot;
106-
if (progressDot === undefined) {
107-
progressDot = $scopedSlots.progressDot;
108-
}
109107
const { lastStepOffsetWidth, flexSupported } = this;
110-
const filteredChildren = filterEmpty(this.$slots.default);
108+
const filteredChildren = filterEmpty($slots.default && $slots.default());
111109
const lastIndex = filteredChildren.length - 1;
112110
const adjustedlabelPlacement = progressDot ? 'vertical' : labelPlacement;
113111
const classString = {
@@ -119,42 +117,36 @@ export default {
119117
[`${prefixCls}-navigation`]: isNav,
120118
[`${prefixCls}-flex-not-supported`]: !flexSupported,
121119
};
122-
const listeners = getListeners(this);
123120
const stepsProps = {
124121
class: classString,
125122
ref: 'vcStepsRef',
126-
on: listeners,
127123
};
128124
return (
129125
<div {...stepsProps}>
130126
{filteredChildren.map((child, index) => {
131-
const childProps = getPropsData(child);
127+
const childProps = child.props || {};
132128
const stepNumber = initial + index;
133129
const stepProps = {
134-
props: {
135-
stepNumber: `${stepNumber + 1}`,
136-
stepIndex: stepNumber,
137-
prefixCls,
138-
iconPrefix,
139-
progressDot: this.progressDot,
140-
icons,
141-
...childProps,
142-
},
143-
on: getEvents(child),
144-
scopedSlots: $scopedSlots,
130+
stepNumber: `${stepNumber + 1}`,
131+
stepIndex: stepNumber,
132+
prefixCls,
133+
iconPrefix,
134+
progressDot,
135+
icons,
136+
...childProps,
145137
};
146-
if (listeners.change) {
147-
stepProps.on.stepClick = this.onStepClick;
138+
139+
const { onChange } = this.$attrs;
140+
if (onChange || this.$attrs['onUpdate:current']) {
141+
stepProps.onStepClick = this.onStepClick;
148142
}
149143
if (!flexSupported && direction !== 'vertical') {
150144
if (isNav) {
151-
stepProps.props.itemWidth = `${100 / (lastIndex + 1)}%`;
152-
stepProps.props.adjustMarginRight = 0;
145+
stepProps.itemWidth = `${100 / (lastIndex + 1)}%`;
146+
stepProps.adjustMarginRight = 0;
153147
} else if (index !== lastIndex) {
154-
stepProps.props.itemWidth = `${100 / lastIndex}%`;
155-
stepProps.props.adjustMarginRight = `${-Math.round(
156-
lastStepOffsetWidth / lastIndex + 1,
157-
)}px`;
148+
stepProps.itemWidth = `${100 / lastIndex}%`;
149+
stepProps.adjustMarginRight = `${-Math.round(lastStepOffsetWidth / lastIndex + 1)}px`;
158150
}
159151
}
160152
// fix tail color
@@ -163,14 +155,14 @@ export default {
163155
}
164156
if (!childProps.status) {
165157
if (stepNumber === current) {
166-
stepProps.props.status = status;
158+
stepProps.status = status;
167159
} else if (stepNumber < current) {
168-
stepProps.props.status = 'finish';
160+
stepProps.status = 'finish';
169161
} else {
170-
stepProps.props.status = 'wait';
162+
stepProps.status = 'wait';
171163
}
172164
}
173-
stepProps.props.active = stepNumber === current;
165+
stepProps.active = stepNumber === current;
174166
return cloneElement(child, stepProps);
175167
})}
176168
</div>

examples/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import Modal from 'ant-design-vue/modal';
3333
import Menu from 'ant-design-vue/menu';
3434
import Mentions from 'ant-design-vue/mentions';
3535
import Dropdown from 'ant-design-vue/dropdown';
36+
import Steps from 'ant-design-vue/steps';
3637
import 'ant-design-vue/style.js';
3738

3839
const basic = {
@@ -79,4 +80,5 @@ app
7980
.use(Menu)
8081
.use(Mentions)
8182
.use(Dropdown)
83+
.use(Steps)
8284
.mount('#app');

0 commit comments

Comments
 (0)