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
pull/2386/head^2
zkwolf 4 years ago committed by GitHub
parent 36bec7eb5a
commit 65f49b97d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -47,3 +47,7 @@ v-model -> v-model:selectedKeys :openKeys.sync -> v-mdoel:openKeys
## dropdown
v-model -> v-model:visible
## Steps
v-model -> v-model:current

@ -302,6 +302,10 @@ export function getComponentName(opts) {
return opts && (opts.Ctor.options.name || opts.tag);
}
export function isFragment(c) {
return c.length === 1 && c[0].type === Fragment;
}
export function isEmptyElement(c) {
return c.type === Comment || (c.type === Text && c.children.trim() === '');
}
@ -311,6 +315,9 @@ export function isStringElement(c) {
}
export function filterEmpty(children = []) {
if (isFragment(children)) {
return children[0].children.filter(c => !isEmptyElement(c));
}
return children.filter(c => !isEmptyElement(c));
}
const initDefaultProps = (propTypes, defaultProps) => {

@ -1,10 +1,10 @@
import { inject } 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 { initDefaultProps, getOptionProps, getListeners } from '../_util/props-util';
import { initDefaultProps, getOptionProps, getComponent } from '../_util/props-util';
import VcSteps from '../vc-steps';
import { ConfigConsumerProps } from '../config-provider';
import Base from '../base';
const getStepsProps = (defaultProps = {}) => {
const props = {
@ -27,12 +27,10 @@ const Steps = {
props: getStepsProps({
current: 0,
}),
inject: {
configProvider: { default: () => ConfigConsumerProps },
},
model: {
prop: 'current',
event: 'change',
setup() {
return {
configProvider: inject('configProvider', ConfigConsumerProps),
};
},
Step: { ...VcSteps.Step, name: 'AStep' },
render() {
@ -41,30 +39,27 @@ const Steps = {
const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('steps', customizePrefixCls);
const iconPrefix = getPrefixCls('', customizeIconPrefixCls);
const progressDot = getComponent(this, 'progressDot', this, false);
const icons = {
finish: <CheckOutlined class={`${prefixCls}-finish-icon`} />,
error: <CloseOutlined class={`${prefixCls}-error-icon`} />,
};
const stepsProps = {
props: {
icons,
iconPrefix,
prefixCls,
...props,
},
on: getListeners(this),
scopedSlots: this.$scopedSlots,
icons,
iconPrefix,
prefixCls,
progressDot,
...props,
};
return <VcSteps {...stepsProps}>{this.$slots.default}</VcSteps>;
return <VcSteps {...stepsProps}>{this.$slots.default && this.$slots.default()}</VcSteps>;
},
};
/* istanbul ignore next */
Steps.install = function(Vue) {
Vue.use(Base);
Vue.component(Steps.name, Steps);
Vue.component(Steps.Step.name, Steps.Step);
Steps.install = function(app) {
app.component(Steps.name, Steps);
app.component(Steps.Step.name, Steps.Step);
};
export default Steps;

@ -1,5 +1,5 @@
import PropTypes from '../_util/vue-types';
import { getOptionProps, getComponentFromProp, getListeners } from '../_util/props-util';
import { getOptionProps, getComponent } from '../_util/props-util';
function isString(str) {
return typeof str === 'string';
@ -28,21 +28,26 @@ export default {
finish: PropTypes.any,
error: PropTypes.any,
}).loose,
onClick: PropTypes.func,
onStepClick: PropTypes.func,
},
methods: {
onClick(...args) {
this.$emit('click', ...args);
onItemClick(...args) {
const { onClick } = this.$props;
if (onClick) {
this.$emit('click', ...args);
}
this.$emit('stepClick', this.stepIndex);
},
renderIconNode() {
const { prefixCls, stepNumber, status, iconPrefix, icons } = getOptionProps(this);
let progressDot = this.progressDot;
if (progressDot === undefined) {
progressDot = this.$scopedSlots.progressDot;
}
const icon = getComponentFromProp(this, 'icon');
const title = getComponentFromProp(this, 'title');
const description = getComponentFromProp(this, 'description');
const { prefixCls, stepNumber, status, iconPrefix, icons, progressDot } = getOptionProps(
this,
);
const icon = getComponent(this, 'icon');
const title = getComponent(this, 'title');
const description = getComponent(this, 'description');
let iconNode;
const iconClassName = {
[`${prefixCls}-icon`]: true,
@ -86,22 +91,23 @@ export default {
tailContent,
adjustMarginRight,
disabled,
onClick,
onStepClick,
} = getOptionProps(this);
const title = getComponentFromProp(this, 'title');
const subTitle = getComponentFromProp(this, 'subTitle');
const description = getComponentFromProp(this, 'description');
const title = getComponent(this, 'title');
const subTitle = getComponent(this, 'subTitle');
const description = getComponent(this, 'description');
const classString = {
[`${prefixCls}-item`]: true,
[`${prefixCls}-item-${status}`]: true,
[`${prefixCls}-item-custom`]: getComponentFromProp(this, 'icon'),
[`${prefixCls}-item-custom`]: getComponent(this, 'icon'),
[`${prefixCls}-item-active`]: active,
[`${prefixCls}-item-disabled`]: disabled === true,
};
const stepProps = {
class: classString,
on: getListeners(this),
};
const stepItemStyle = {};
if (itemWidth) {
@ -110,17 +116,15 @@ export default {
if (adjustMarginRight) {
stepItemStyle.marginRight = adjustMarginRight;
}
const listeners = getListeners(this);
const accessibilityProps = {
attrs: {},
on: {
click: listeners.click || noop,
},
onClick: onClick || noop,
};
if (listeners.stepClick && !disabled) {
accessibilityProps.attrs.role = 'button';
accessibilityProps.attrs.tabIndex = 0;
accessibilityProps.on.click = this.onClick;
if (onStepClick && !disabled) {
accessibilityProps.role = 'button';
accessibilityProps.tabIndex = 0;
accessibilityProps.onClick = this.onItemClick;
}
return (
<div {...stepProps} style={stepItemStyle}>

@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types';
import BaseMixin from '../_util/BaseMixin';
import debounce from 'lodash/debounce';
import isFlexSupported from '../_util/isFlexSupported';
import { filterEmpty, getEvents, getPropsData, getListeners } from '../_util/props-util';
import { filterEmpty } from '../_util/props-util';
import { cloneElement } from '../_util/vnode';
export default {
@ -59,6 +59,7 @@ export default {
const { current } = this.$props;
if (current !== next) {
this.$emit('change', next);
this.$emit('update:current', next);
}
},
calcStepOffsetWidth() {
@ -97,17 +98,14 @@ export default {
status,
size,
current,
$scopedSlots,
$slots,
progressDot,
initial,
icons,
} = this;
const isNav = type === 'navigation';
let progressDot = this.progressDot;
if (progressDot === undefined) {
progressDot = $scopedSlots.progressDot;
}
const { lastStepOffsetWidth, flexSupported } = this;
const filteredChildren = filterEmpty(this.$slots.default);
const filteredChildren = filterEmpty($slots.default && $slots.default());
const lastIndex = filteredChildren.length - 1;
const adjustedlabelPlacement = progressDot ? 'vertical' : labelPlacement;
const classString = {
@ -119,42 +117,36 @@ export default {
[`${prefixCls}-navigation`]: isNav,
[`${prefixCls}-flex-not-supported`]: !flexSupported,
};
const listeners = getListeners(this);
const stepsProps = {
class: classString,
ref: 'vcStepsRef',
on: listeners,
};
return (
<div {...stepsProps}>
{filteredChildren.map((child, index) => {
const childProps = getPropsData(child);
const childProps = child.props || {};
const stepNumber = initial + index;
const stepProps = {
props: {
stepNumber: `${stepNumber + 1}`,
stepIndex: stepNumber,
prefixCls,
iconPrefix,
progressDot: this.progressDot,
icons,
...childProps,
},
on: getEvents(child),
scopedSlots: $scopedSlots,
stepNumber: `${stepNumber + 1}`,
stepIndex: stepNumber,
prefixCls,
iconPrefix,
progressDot,
icons,
...childProps,
};
if (listeners.change) {
stepProps.on.stepClick = this.onStepClick;
const { onChange } = this.$attrs;
if (onChange || this.$attrs['onUpdate:current']) {
stepProps.onStepClick = this.onStepClick;
}
if (!flexSupported && direction !== 'vertical') {
if (isNav) {
stepProps.props.itemWidth = `${100 / (lastIndex + 1)}%`;
stepProps.props.adjustMarginRight = 0;
stepProps.itemWidth = `${100 / (lastIndex + 1)}%`;
stepProps.adjustMarginRight = 0;
} else if (index !== lastIndex) {
stepProps.props.itemWidth = `${100 / lastIndex}%`;
stepProps.props.adjustMarginRight = `${-Math.round(
lastStepOffsetWidth / lastIndex + 1,
)}px`;
stepProps.itemWidth = `${100 / lastIndex}%`;
stepProps.adjustMarginRight = `${-Math.round(lastStepOffsetWidth / lastIndex + 1)}px`;
}
}
// fix tail color
@ -163,14 +155,14 @@ export default {
}
if (!childProps.status) {
if (stepNumber === current) {
stepProps.props.status = status;
stepProps.status = status;
} else if (stepNumber < current) {
stepProps.props.status = 'finish';
stepProps.status = 'finish';
} else {
stepProps.props.status = 'wait';
stepProps.status = 'wait';
}
}
stepProps.props.active = stepNumber === current;
stepProps.active = stepNumber === current;
return cloneElement(child, stepProps);
})}
</div>

@ -33,6 +33,7 @@ import Modal from 'ant-design-vue/modal';
import Menu from 'ant-design-vue/menu';
import Mentions from 'ant-design-vue/mentions';
import Dropdown from 'ant-design-vue/dropdown';
import Steps from 'ant-design-vue/steps';
import 'ant-design-vue/style.js';
const basic = {
@ -79,4 +80,5 @@ app
.use(Menu)
.use(Mentions)
.use(Dropdown)
.use(Steps)
.mount('#app');

Loading…
Cancel
Save