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
|
## dropdown
|
||||||
|
|
||||||
v-model -> v-model:visible
|
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);
|
return opts && (opts.Ctor.options.name || opts.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFragment(c) {
|
||||||
|
return c.length === 1 && c[0].type === Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
export function isEmptyElement(c) {
|
export function isEmptyElement(c) {
|
||||||
return c.type === Comment || (c.type === Text && c.children.trim() === '');
|
return c.type === Comment || (c.type === Text && c.children.trim() === '');
|
||||||
}
|
}
|
||||||
|
@ -311,6 +315,9 @@ export function isStringElement(c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterEmpty(children = []) {
|
export function filterEmpty(children = []) {
|
||||||
|
if (isFragment(children)) {
|
||||||
|
return children[0].children.filter(c => !isEmptyElement(c));
|
||||||
|
}
|
||||||
return children.filter(c => !isEmptyElement(c));
|
return children.filter(c => !isEmptyElement(c));
|
||||||
}
|
}
|
||||||
const initDefaultProps = (propTypes, defaultProps) => {
|
const initDefaultProps = (propTypes, defaultProps) => {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { inject } from 'vue';
|
||||||
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||||||
import CheckOutlined from '@ant-design/icons-vue/CheckOutlined';
|
import CheckOutlined from '@ant-design/icons-vue/CheckOutlined';
|
||||||
import PropTypes from '../_util/vue-types';
|
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 VcSteps from '../vc-steps';
|
||||||
import { ConfigConsumerProps } from '../config-provider';
|
import { ConfigConsumerProps } from '../config-provider';
|
||||||
import Base from '../base';
|
|
||||||
|
|
||||||
const getStepsProps = (defaultProps = {}) => {
|
const getStepsProps = (defaultProps = {}) => {
|
||||||
const props = {
|
const props = {
|
||||||
|
@ -27,12 +27,10 @@ const Steps = {
|
||||||
props: getStepsProps({
|
props: getStepsProps({
|
||||||
current: 0,
|
current: 0,
|
||||||
}),
|
}),
|
||||||
inject: {
|
setup() {
|
||||||
configProvider: { default: () => ConfigConsumerProps },
|
return {
|
||||||
},
|
configProvider: inject('configProvider', ConfigConsumerProps),
|
||||||
model: {
|
};
|
||||||
prop: 'current',
|
|
||||||
event: 'change',
|
|
||||||
},
|
},
|
||||||
Step: { ...VcSteps.Step, name: 'AStep' },
|
Step: { ...VcSteps.Step, name: 'AStep' },
|
||||||
render() {
|
render() {
|
||||||
|
@ -41,30 +39,27 @@ const Steps = {
|
||||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||||
const prefixCls = getPrefixCls('steps', customizePrefixCls);
|
const prefixCls = getPrefixCls('steps', customizePrefixCls);
|
||||||
const iconPrefix = getPrefixCls('', customizeIconPrefixCls);
|
const iconPrefix = getPrefixCls('', customizeIconPrefixCls);
|
||||||
|
const progressDot = getComponent(this, 'progressDot', this, false);
|
||||||
|
|
||||||
const icons = {
|
const icons = {
|
||||||
finish: <CheckOutlined class={`${prefixCls}-finish-icon`} />,
|
finish: <CheckOutlined class={`${prefixCls}-finish-icon`} />,
|
||||||
error: <CloseOutlined class={`${prefixCls}-error-icon`} />,
|
error: <CloseOutlined class={`${prefixCls}-error-icon`} />,
|
||||||
};
|
};
|
||||||
const stepsProps = {
|
const stepsProps = {
|
||||||
props: {
|
|
||||||
icons,
|
icons,
|
||||||
iconPrefix,
|
iconPrefix,
|
||||||
prefixCls,
|
prefixCls,
|
||||||
|
progressDot,
|
||||||
...props,
|
...props,
|
||||||
},
|
|
||||||
on: getListeners(this),
|
|
||||||
scopedSlots: this.$scopedSlots,
|
|
||||||
};
|
};
|
||||||
return <VcSteps {...stepsProps}>{this.$slots.default}</VcSteps>;
|
return <VcSteps {...stepsProps}>{this.$slots.default && this.$slots.default()}</VcSteps>;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
Steps.install = function(Vue) {
|
Steps.install = function(app) {
|
||||||
Vue.use(Base);
|
app.component(Steps.name, Steps);
|
||||||
Vue.component(Steps.name, Steps);
|
app.component(Steps.Step.name, Steps.Step);
|
||||||
Vue.component(Steps.Step.name, Steps.Step);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Steps;
|
export default Steps;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import PropTypes from '../_util/vue-types';
|
import PropTypes from '../_util/vue-types';
|
||||||
import { getOptionProps, getComponentFromProp, getListeners } from '../_util/props-util';
|
import { getOptionProps, getComponent } from '../_util/props-util';
|
||||||
|
|
||||||
function isString(str) {
|
function isString(str) {
|
||||||
return typeof str === 'string';
|
return typeof str === 'string';
|
||||||
|
@ -28,21 +28,26 @@ export default {
|
||||||
finish: PropTypes.any,
|
finish: PropTypes.any,
|
||||||
error: PropTypes.any,
|
error: PropTypes.any,
|
||||||
}).loose,
|
}).loose,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
onStepClick: PropTypes.func,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onClick(...args) {
|
onItemClick(...args) {
|
||||||
|
const { onClick } = this.$props;
|
||||||
|
|
||||||
|
if (onClick) {
|
||||||
this.$emit('click', ...args);
|
this.$emit('click', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
this.$emit('stepClick', this.stepIndex);
|
this.$emit('stepClick', this.stepIndex);
|
||||||
},
|
},
|
||||||
renderIconNode() {
|
renderIconNode() {
|
||||||
const { prefixCls, stepNumber, status, iconPrefix, icons } = getOptionProps(this);
|
const { prefixCls, stepNumber, status, iconPrefix, icons, progressDot } = getOptionProps(
|
||||||
let progressDot = this.progressDot;
|
this,
|
||||||
if (progressDot === undefined) {
|
);
|
||||||
progressDot = this.$scopedSlots.progressDot;
|
const icon = getComponent(this, 'icon');
|
||||||
}
|
const title = getComponent(this, 'title');
|
||||||
const icon = getComponentFromProp(this, 'icon');
|
const description = getComponent(this, 'description');
|
||||||
const title = getComponentFromProp(this, 'title');
|
|
||||||
const description = getComponentFromProp(this, 'description');
|
|
||||||
let iconNode;
|
let iconNode;
|
||||||
const iconClassName = {
|
const iconClassName = {
|
||||||
[`${prefixCls}-icon`]: true,
|
[`${prefixCls}-icon`]: true,
|
||||||
|
@ -86,22 +91,23 @@ export default {
|
||||||
tailContent,
|
tailContent,
|
||||||
adjustMarginRight,
|
adjustMarginRight,
|
||||||
disabled,
|
disabled,
|
||||||
|
onClick,
|
||||||
|
onStepClick,
|
||||||
} = getOptionProps(this);
|
} = getOptionProps(this);
|
||||||
|
|
||||||
const title = getComponentFromProp(this, 'title');
|
const title = getComponent(this, 'title');
|
||||||
const subTitle = getComponentFromProp(this, 'subTitle');
|
const subTitle = getComponent(this, 'subTitle');
|
||||||
const description = getComponentFromProp(this, 'description');
|
const description = getComponent(this, 'description');
|
||||||
|
|
||||||
const classString = {
|
const classString = {
|
||||||
[`${prefixCls}-item`]: true,
|
[`${prefixCls}-item`]: true,
|
||||||
[`${prefixCls}-item-${status}`]: true,
|
[`${prefixCls}-item-${status}`]: true,
|
||||||
[`${prefixCls}-item-custom`]: getComponentFromProp(this, 'icon'),
|
[`${prefixCls}-item-custom`]: getComponent(this, 'icon'),
|
||||||
[`${prefixCls}-item-active`]: active,
|
[`${prefixCls}-item-active`]: active,
|
||||||
[`${prefixCls}-item-disabled`]: disabled === true,
|
[`${prefixCls}-item-disabled`]: disabled === true,
|
||||||
};
|
};
|
||||||
const stepProps = {
|
const stepProps = {
|
||||||
class: classString,
|
class: classString,
|
||||||
on: getListeners(this),
|
|
||||||
};
|
};
|
||||||
const stepItemStyle = {};
|
const stepItemStyle = {};
|
||||||
if (itemWidth) {
|
if (itemWidth) {
|
||||||
|
@ -110,17 +116,15 @@ export default {
|
||||||
if (adjustMarginRight) {
|
if (adjustMarginRight) {
|
||||||
stepItemStyle.marginRight = adjustMarginRight;
|
stepItemStyle.marginRight = adjustMarginRight;
|
||||||
}
|
}
|
||||||
const listeners = getListeners(this);
|
|
||||||
const accessibilityProps = {
|
const accessibilityProps = {
|
||||||
attrs: {},
|
onClick: onClick || noop,
|
||||||
on: {
|
|
||||||
click: listeners.click || noop,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
if (listeners.stepClick && !disabled) {
|
|
||||||
accessibilityProps.attrs.role = 'button';
|
if (onStepClick && !disabled) {
|
||||||
accessibilityProps.attrs.tabIndex = 0;
|
accessibilityProps.role = 'button';
|
||||||
accessibilityProps.on.click = this.onClick;
|
accessibilityProps.tabIndex = 0;
|
||||||
|
accessibilityProps.onClick = this.onItemClick;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div {...stepProps} style={stepItemStyle}>
|
<div {...stepProps} style={stepItemStyle}>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import PropTypes from '../_util/vue-types';
|
||||||
import BaseMixin from '../_util/BaseMixin';
|
import BaseMixin from '../_util/BaseMixin';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import isFlexSupported from '../_util/isFlexSupported';
|
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';
|
import { cloneElement } from '../_util/vnode';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -59,6 +59,7 @@ export default {
|
||||||
const { current } = this.$props;
|
const { current } = this.$props;
|
||||||
if (current !== next) {
|
if (current !== next) {
|
||||||
this.$emit('change', next);
|
this.$emit('change', next);
|
||||||
|
this.$emit('update:current', next);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calcStepOffsetWidth() {
|
calcStepOffsetWidth() {
|
||||||
|
@ -97,17 +98,14 @@ export default {
|
||||||
status,
|
status,
|
||||||
size,
|
size,
|
||||||
current,
|
current,
|
||||||
$scopedSlots,
|
$slots,
|
||||||
|
progressDot,
|
||||||
initial,
|
initial,
|
||||||
icons,
|
icons,
|
||||||
} = this;
|
} = this;
|
||||||
const isNav = type === 'navigation';
|
const isNav = type === 'navigation';
|
||||||
let progressDot = this.progressDot;
|
|
||||||
if (progressDot === undefined) {
|
|
||||||
progressDot = $scopedSlots.progressDot;
|
|
||||||
}
|
|
||||||
const { lastStepOffsetWidth, flexSupported } = this;
|
const { lastStepOffsetWidth, flexSupported } = this;
|
||||||
const filteredChildren = filterEmpty(this.$slots.default);
|
const filteredChildren = filterEmpty($slots.default && $slots.default());
|
||||||
const lastIndex = filteredChildren.length - 1;
|
const lastIndex = filteredChildren.length - 1;
|
||||||
const adjustedlabelPlacement = progressDot ? 'vertical' : labelPlacement;
|
const adjustedlabelPlacement = progressDot ? 'vertical' : labelPlacement;
|
||||||
const classString = {
|
const classString = {
|
||||||
|
@ -119,42 +117,36 @@ export default {
|
||||||
[`${prefixCls}-navigation`]: isNav,
|
[`${prefixCls}-navigation`]: isNav,
|
||||||
[`${prefixCls}-flex-not-supported`]: !flexSupported,
|
[`${prefixCls}-flex-not-supported`]: !flexSupported,
|
||||||
};
|
};
|
||||||
const listeners = getListeners(this);
|
|
||||||
const stepsProps = {
|
const stepsProps = {
|
||||||
class: classString,
|
class: classString,
|
||||||
ref: 'vcStepsRef',
|
ref: 'vcStepsRef',
|
||||||
on: listeners,
|
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div {...stepsProps}>
|
<div {...stepsProps}>
|
||||||
{filteredChildren.map((child, index) => {
|
{filteredChildren.map((child, index) => {
|
||||||
const childProps = getPropsData(child);
|
const childProps = child.props || {};
|
||||||
const stepNumber = initial + index;
|
const stepNumber = initial + index;
|
||||||
const stepProps = {
|
const stepProps = {
|
||||||
props: {
|
|
||||||
stepNumber: `${stepNumber + 1}`,
|
stepNumber: `${stepNumber + 1}`,
|
||||||
stepIndex: stepNumber,
|
stepIndex: stepNumber,
|
||||||
prefixCls,
|
prefixCls,
|
||||||
iconPrefix,
|
iconPrefix,
|
||||||
progressDot: this.progressDot,
|
progressDot,
|
||||||
icons,
|
icons,
|
||||||
...childProps,
|
...childProps,
|
||||||
},
|
|
||||||
on: getEvents(child),
|
|
||||||
scopedSlots: $scopedSlots,
|
|
||||||
};
|
};
|
||||||
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 (!flexSupported && direction !== 'vertical') {
|
||||||
if (isNav) {
|
if (isNav) {
|
||||||
stepProps.props.itemWidth = `${100 / (lastIndex + 1)}%`;
|
stepProps.itemWidth = `${100 / (lastIndex + 1)}%`;
|
||||||
stepProps.props.adjustMarginRight = 0;
|
stepProps.adjustMarginRight = 0;
|
||||||
} else if (index !== lastIndex) {
|
} else if (index !== lastIndex) {
|
||||||
stepProps.props.itemWidth = `${100 / lastIndex}%`;
|
stepProps.itemWidth = `${100 / lastIndex}%`;
|
||||||
stepProps.props.adjustMarginRight = `${-Math.round(
|
stepProps.adjustMarginRight = `${-Math.round(lastStepOffsetWidth / lastIndex + 1)}px`;
|
||||||
lastStepOffsetWidth / lastIndex + 1,
|
|
||||||
)}px`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// fix tail color
|
// fix tail color
|
||||||
|
@ -163,14 +155,14 @@ export default {
|
||||||
}
|
}
|
||||||
if (!childProps.status) {
|
if (!childProps.status) {
|
||||||
if (stepNumber === current) {
|
if (stepNumber === current) {
|
||||||
stepProps.props.status = status;
|
stepProps.status = status;
|
||||||
} else if (stepNumber < current) {
|
} else if (stepNumber < current) {
|
||||||
stepProps.props.status = 'finish';
|
stepProps.status = 'finish';
|
||||||
} else {
|
} else {
|
||||||
stepProps.props.status = 'wait';
|
stepProps.status = 'wait';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stepProps.props.active = stepNumber === current;
|
stepProps.active = stepNumber === current;
|
||||||
return cloneElement(child, stepProps);
|
return cloneElement(child, stepProps);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,6 +33,7 @@ import Modal from 'ant-design-vue/modal';
|
||||||
import Menu from 'ant-design-vue/menu';
|
import Menu from 'ant-design-vue/menu';
|
||||||
import Mentions from 'ant-design-vue/mentions';
|
import Mentions from 'ant-design-vue/mentions';
|
||||||
import Dropdown from 'ant-design-vue/dropdown';
|
import Dropdown from 'ant-design-vue/dropdown';
|
||||||
|
import Steps from 'ant-design-vue/steps';
|
||||||
import 'ant-design-vue/style.js';
|
import 'ant-design-vue/style.js';
|
||||||
|
|
||||||
const basic = {
|
const basic = {
|
||||||
|
@ -79,4 +80,5 @@ app
|
||||||
.use(Menu)
|
.use(Menu)
|
||||||
.use(Mentions)
|
.use(Mentions)
|
||||||
.use(Dropdown)
|
.use(Dropdown)
|
||||||
|
.use(Steps)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
|
Loading…
Reference in New Issue