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 breakchangepull/2386/head^2
parent
36bec7eb5a
commit
65f49b97d7
|
@ -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…
Reference in New Issue