Merge branch 'next' into v3

pull/4135/head
tangjinzhou 2021-06-19 21:58:10 +08:00
commit 9ce43befc4
32 changed files with 406 additions and 337 deletions

View File

@ -10,6 +10,21 @@
--- ---
## 2.2.0-beta.3
`2021-06-11`
- 🎉 Refactor Breadcrumb, Statistic, Tag components
- 🌟 Statistic supports loading attribute
- 🐞 Fix the problem of Menu rendering multiple sub-components to improve performance [6ae707](https://github.com/vueComponent/ant-design-vue/commit/6ae707edf508a9c5e8dca7dacf1410de5251bcf8)
- 🐞 Fix FormItem custom class invalidation [617e53](https://github.com/vueComponent/ant-design-vue/commit/617e534fda2ae6d468b5e9d3eb43370f8a4b0000)
- 🐞 Fix MenuDivider class error [#4195](https://github.com/vueComponent/ant-design-vue/issues/4195)
- 🐞 Fix Tag and Image type errors
- 🐞 Fix the issue of missing component animations such as Modal [#4191](https://github.com/vueComponent/ant-design-vue/issues/4191)
- 🐞 Fix the issue that Select class cannot be dynamically updated [#4194](https://github.com/vueComponent/ant-design-vue/issues/4194)
- 🐞 Fix the problem that the Dropdown mail expands and cannot be collapsed by clicking [#4198](https://github.com/vueComponent/ant-design-vue/issues/4198)
- 🐞 Fix the issue of missing some export methods of FormItem [#4183](https://github.com/vueComponent/ant-design-vue/issues/4183)
## 2.2.0-beta.2 ## 2.2.0-beta.2
`2021-06-08` `2021-06-08`

View File

@ -10,6 +10,21 @@
--- ---
## 2.2.0-beta.3
`2021-06-11`
- 🎉 重构 Breadcrumb、Statistic、Tag 组件
- 🌟 Statistic 支持 loading 属性
- 🐞 修复 Menu 渲染多次子组件问题,提升性能 [6ae707](https://github.com/vueComponent/ant-design-vue/commit/6ae707edf508a9c5e8dca7dacf1410de5251bcf8)
- 🐞 修复 FormItem 自定义 class 失效 [617e53](https://github.com/vueComponent/ant-design-vue/commit/617e534fda2ae6d468b5e9d3eb43370f8a4b0000)
- 🐞 修复 MenuDivider class 错误问题 [#4195](https://github.com/vueComponent/ant-design-vue/issues/4195)
- 🐞 修复 Tag、Image 类型错误
- 🐞 修复 Modal 等组件动画丢失问题 [#4191](https://github.com/vueComponent/ant-design-vue/issues/4191)
- 🐞 修复 Select class 不能动态更新问题 [#4194](https://github.com/vueComponent/ant-design-vue/issues/4194)
- 🐞 修复 Dropdown 邮件展开,不能点击收起的问题 [#4198](https://github.com/vueComponent/ant-design-vue/issues/4198)
- 🐞 修复 FormItem 缺少部分导出方法问题 [#4183](https://github.com/vueComponent/ant-design-vue/issues/4183)
## 2.2.0-beta.2 ## 2.2.0-beta.2
`2021-06-08` `2021-06-08`

View File

@ -139,9 +139,13 @@ const ConfigProvider = defineComponent({
getPrefixCls: getPrefixClsWrapper, getPrefixCls: getPrefixClsWrapper,
renderEmpty: renderEmptyComponent, renderEmpty: renderEmptyComponent,
}); });
Object.keys(props).forEach(key => {
watch(props, () => { watch(
Object.assign(configProvider, props); () => props[key],
() => {
configProvider[key] = props[key];
},
);
}); });
provide('configProvider', configProvider); provide('configProvider', configProvider);

View File

@ -92,7 +92,7 @@ const Dropdown = defineComponent({
class: classNames(child?.props?.class, `${prefixCls}-trigger`), class: classNames(child?.props?.class, `${prefixCls}-trigger`),
disabled, disabled,
}); });
const triggerActions = disabled ? [] : trigger; const triggerActions = disabled ? [] : typeof trigger === 'string' ? [trigger] : trigger;
let alignPoint; let alignPoint;
if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) { if (triggerActions && triggerActions.indexOf('contextmenu') !== -1) {
alignPoint = true; alignPoint = true;

View File

@ -3,8 +3,10 @@ import { PropType } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
export default () => ({ export default () => ({
trigger: { trigger: {
type: Array as PropType<('click' | 'hover' | 'contextmenu')[]>, type: [Array, String] as PropType<
default: () => ['hover'], ('click' | 'hover' | 'contextmenu')[] | 'click' | 'hover' | 'contextmenu'
>,
default: 'hover',
}, },
overlay: PropTypes.any, overlay: PropTypes.any,
visible: PropTypes.looseBool, visible: PropTypes.looseBool,

View File

@ -21,7 +21,7 @@ export default defineComponent({
const { prefixCls, status } = useInjectFormItemPrefix(); const { prefixCls, status } = useInjectFormItemPrefix();
const visible = ref(!!(props.errors && props.errors.length)); const visible = ref(!!(props.errors && props.errors.length));
const innerStatus = ref(status.value); const innerStatus = ref(status.value);
let timeout = ref(); const timeout = ref();
const cacheErrors = ref([...props.errors]); const cacheErrors = ref([...props.errors]);
watch([() => [...props.errors], () => props.help], newValues => { watch([() => [...props.errors], () => props.help], newValues => {
window.clearTimeout(timeout.value); window.clearTimeout(timeout.value);

View File

@ -108,7 +108,7 @@ export default defineComponent({
__ANT_NEW_FORM_ITEM: true, __ANT_NEW_FORM_ITEM: true,
props: formItemProps, props: formItemProps,
slots: ['help', 'label', 'extra'], slots: ['help', 'label', 'extra'],
setup(props, { slots }) { setup(props, { slots, attrs, expose }) {
warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`); warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`);
const eventKey = `form-item-${++indexGuid}`; const eventKey = `form-item-${++indexGuid}`;
const { prefixCls } = useConfigInject('form', props); const { prefixCls } = useConfigInject('form', props);
@ -272,6 +272,12 @@ export default defineComponent({
control.focus(); control.focus();
} }
}; };
expose({
onFieldBlur,
onFieldChange,
clearValidate,
resetField,
});
formContext.addField(eventKey, { formContext.addField(eventKey, {
fieldValue, fieldValue,
fieldId, fieldId,
@ -336,9 +342,11 @@ export default defineComponent({
} }
return ( return (
<Row <Row
{...attrs}
class={[ class={[
itemClassName.value, itemClassName.value,
domErrorVisible.value || !!help ? `${prefixCls.value}-item-with-help` : '', domErrorVisible.value || !!help ? `${prefixCls.value}-item-with-help` : '',
attrs.class,
]} ]}
key="row" key="row"
> >
@ -370,44 +378,4 @@ export default defineComponent({
); );
}; };
}, },
// data() {
// warning(!hasProp(this, 'prop'), `\`prop\` is deprecated. Please use \`name\` instead.`);
// return {
// validateState: this.validateStatus,
// validateMessage: '',
// validateDisabled: false,
// validator: {},
// helpShow: false,
// errors: [],
// initialValue: undefined,
// };
// },
// render() {
// const { autoLink } = getOptionProps(this);
// const children = getSlot(this);
// let firstChildren = children[0];
// if (this.fieldName && autoLink && isValidElement(firstChildren)) {
// const originalEvents = getEvents(firstChildren);
// const originalBlur = originalEvents.onBlur;
// const originalChange = originalEvents.onChange;
// firstChildren = cloneElement(firstChildren, {
// ...(this.fieldId ? { id: this.fieldId } : undefined),
// onBlur: (...args: any[]) => {
// originalBlur && originalBlur(...args);
// this.onFieldBlur();
// },
// onChange: (...args: any[]) => {
// if (Array.isArray(originalChange)) {
// for (let i = 0, l = originalChange.length; i < l; i++) {
// originalChange[i](...args);
// }
// } else if (originalChange) {
// originalChange(...args);
// }
// this.onFieldChange();
// },
// });
// }
// return this.renderFormItem([firstChildren, children.slice(1)]);
// },
}); });

View File

@ -29,7 +29,7 @@ function parseFlex(flex: FlexType): string {
} }
const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
export const colSize = PropTypes.shape({ export const colSize = PropTypes.shape<ColSize>({
span: stringOrNumber, span: stringOrNumber,
order: stringOrNumber, order: stringOrNumber,
offset: stringOrNumber, offset: stringOrNumber,

View File

@ -1,3 +1,4 @@
import { computed } from 'vue';
import { Ref, inject, InjectionKey, provide, ComputedRef } from 'vue'; import { Ref, inject, InjectionKey, provide, ComputedRef } from 'vue';
export interface RowContext { export interface RowContext {
@ -13,7 +14,11 @@ const useProvideRow = (state: RowContext) => {
}; };
const useInjectRow = () => { const useInjectRow = () => {
return inject(RowContextKey); return inject(RowContextKey, {
gutter: computed(() => undefined),
wrap: computed(() => undefined),
supportFlexGap: computed(() => undefined),
});
}; };
export { useInjectRow, useProvideRow }; export { useInjectRow, useProvideRow };

View File

@ -1,13 +1,16 @@
import { App, defineComponent, inject, Plugin } from 'vue'; import { App, defineComponent, ExtractPropTypes, ImgHTMLAttributes, inject, Plugin } from 'vue';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import ImageInternal from '../vc-image'; import ImageInternal from '../vc-image';
import { ImageProps, ImagePropsType } from '../vc-image/src/Image'; import { imageProps } from '../vc-image/src/Image';
import PreviewGroup from './PreviewGroup'; import PreviewGroup from './PreviewGroup';
const Image = defineComponent({
export type ImageProps = Partial<
ExtractPropTypes<typeof imageProps> & Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'>
>;
const Image = defineComponent<ImageProps>({
name: 'AImage', name: 'AImage',
inheritAttrs: false, inheritAttrs: false,
props: ImageProps, props: imageProps as any,
setup(props, ctx) { setup(props, ctx) {
const { slots, attrs } = ctx; const { slots, attrs } = ctx;
const configProvider = inject('configProvider', defaultConfigProvider); const configProvider = inject('configProvider', defaultConfigProvider);
@ -19,7 +22,7 @@ const Image = defineComponent({
}, },
}); });
export { ImageProps, ImagePropsType }; export { imageProps };
Image.PreviewGroup = PreviewGroup; Image.PreviewGroup = PreviewGroup;

View File

@ -1,13 +1,12 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useInjectMenu } from './hooks/useMenuContext';
export default defineComponent({ export default defineComponent({
name: 'AMenuDivider', name: 'AMenuDivider',
props: { setup() {
prefixCls: String, const { prefixCls } = useInjectMenu();
},
setup(props) {
return () => { return () => {
return <li class={`${props.prefixCls}-item-divider`} />; return <li class={`${prefixCls.value}-item-divider`} />;
}; };
}, },
}); });

View File

@ -372,7 +372,7 @@ export default defineComponent({
siderCollapsed, siderCollapsed,
defaultMotions: computed(() => (isMounted.value ? defaultMotions : null)), defaultMotions: computed(() => (isMounted.value ? defaultMotions : null)),
motion: computed(() => (isMounted.value ? props.motion : null)), motion: computed(() => (isMounted.value ? props.motion : null)),
overflowDisabled: computed(() => props.disabledOverflow), overflowDisabled: computed(() => undefined),
onOpenChange: onInternalOpenChange, onOpenChange: onInternalOpenChange,
onItemClick: onInternalClick, onItemClick: onInternalClick,
registerMenuInfo, registerMenuInfo,

View File

@ -260,13 +260,13 @@ export default defineComponent({
</div> </div>
); );
if (!overflowDisabled.value) { if (!overflowDisabled.value && mode.value !== 'inline') {
const triggerMode = triggerModeRef.value; const triggerMode = triggerModeRef.value;
titleNode = ( titleNode = (
<PopupTrigger <PopupTrigger
mode={triggerMode} mode={triggerMode}
prefixCls={subMenuPrefixClsValue} prefixCls={subMenuPrefixClsValue}
visible={!props.internalPopupClose && open.value && mode.value !== 'inline'} visible={!props.internalPopupClose && open.value}
popupClassName={popupClassName.value} popupClassName={popupClassName.value}
popupOffset={props.popupOffset} popupOffset={props.popupOffset}
disabled={mergedDisabled.value} disabled={mergedDisabled.value}

View File

@ -1,12 +1,12 @@
import { computed, defineComponent, inject, ref, VNodeChild, App, PropType, Plugin } from 'vue'; import { computed, defineComponent, ref, VNodeChild, App, PropType, Plugin } from 'vue';
import omit from 'omit.js'; import omit from 'omit.js';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, BaseProps } from '../vc-select'; import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, BaseProps } from '../vc-select';
import { OptionProps as OptionPropsType } from '../vc-select/Option'; import { OptionProps as OptionPropsType } from '../vc-select/Option';
import { defaultConfigProvider } from '../config-provider';
import getIcons from './utils/iconUtil'; import getIcons from './utils/iconUtil';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
type RawValue = string | number; type RawValue = string | number;
@ -74,11 +74,10 @@ const Select = defineComponent({
props: SelectProps(), props: SelectProps(),
SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE', SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE',
emits: ['change', 'update:value'], emits: ['change', 'update:value'],
setup(props: any, { attrs, emit }) { slots: ['notFoundContent', 'suffixIcon', 'itemIcon', 'removeIcon', 'clearIcon', 'dropdownRender'],
setup(props, { attrs, emit, slots, expose }) {
const selectRef = ref(null); const selectRef = ref(null);
const configProvider = inject('configProvider', defaultConfigProvider);
const focus = () => { const focus = () => {
if (selectRef.value) { if (selectRef.value) {
selectRef.value.focus(); selectRef.value.focus();
@ -104,60 +103,37 @@ const Select = defineComponent({
return mode; return mode;
}); });
const prefixCls = computed(() => { const { prefixCls, direction, configProvider } = useConfigInject('select', props);
return configProvider.getPrefixCls('select', props.prefixCls);
});
const mergedClassName = computed(() => const mergedClassName = computed(() =>
classNames( classNames({
{
[`${prefixCls.value}-lg`]: props.size === 'large', [`${prefixCls.value}-lg`]: props.size === 'large',
[`${prefixCls.value}-sm`]: props.size === 'small', [`${prefixCls.value}-sm`]: props.size === 'small',
[`${prefixCls.value}-rtl`]: props.direction === 'rtl', [`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-borderless`]: !props.bordered, [`${prefixCls.value}-borderless`]: !props.bordered,
}, }),
attrs.class,
),
); );
const triggerChange = (...args: any[]) => { const triggerChange = (...args: any[]) => {
emit('update:value', args[0]); emit('update:value', args[0]);
emit('change', ...args); emit('change', ...args);
}; };
return { expose({
selectRef,
mergedClassName,
mode,
focus,
blur, blur,
configProvider, focus,
triggerChange, });
prefixCls, return () => {
};
},
render() {
const {
configProvider,
mode,
mergedClassName,
triggerChange,
prefixCls,
$slots: slots,
$props,
} = this as any;
const props: SelectTypes = $props;
const { const {
notFoundContent, notFoundContent,
listHeight = 256, listHeight = 256,
listItemHeight = 24, listItemHeight = 24,
getPopupContainer, getPopupContainer,
dropdownClassName, dropdownClassName,
direction,
virtual, virtual,
dropdownMatchSelectWidth, dropdownMatchSelectWidth,
} = props; } = props;
const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider; const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider;
const isMultiple = mode === 'multiple' || mode === 'tags'; const isMultiple = mode.value === 'multiple' || mode.value === 'tags';
// ===================== Empty ===================== // ===================== Empty =====================
let mergedNotFound: VNodeChild; let mergedNotFound: VNodeChild;
@ -165,7 +141,7 @@ const Select = defineComponent({
mergedNotFound = notFoundContent; mergedNotFound = notFoundContent;
} else if (slots.notFoundContent) { } else if (slots.notFoundContent) {
mergedNotFound = slots.notFoundContent(); mergedNotFound = slots.notFoundContent();
} else if (mode === 'combobox') { } else if (mode.value === 'combobox') {
mergedNotFound = null; mergedNotFound = null;
} else { } else {
mergedNotFound = renderEmpty('Select') as any; mergedNotFound = renderEmpty('Select') as any;
@ -174,9 +150,9 @@ const Select = defineComponent({
// ===================== Icons ===================== // ===================== Icons =====================
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons( const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons(
{ {
...this.$props, ...props,
multiple: isMultiple, multiple: isMultiple,
prefixCls, prefixCls: prefixCls.value,
}, },
slots, slots,
); );
@ -189,37 +165,38 @@ const Select = defineComponent({
'clearIcon', 'clearIcon',
'size', 'size',
'bordered', 'bordered',
]) as any; ]);
const rcSelectRtlDropDownClassName = classNames(dropdownClassName, { const rcSelectRtlDropDownClassName = classNames(dropdownClassName, {
[`${prefixCls}-dropdown-${direction}`]: direction === 'rtl', [`${prefixCls.value}-dropdown-${direction.value}`]: direction.value === 'rtl',
}); });
return ( return (
<RcSelect <RcSelect
ref="selectRef" ref={selectRef}
virtual={virtual} virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth} dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps} {...selectProps}
{...this.$attrs} {...attrs}
listHeight={listHeight} listHeight={listHeight}
listItemHeight={listItemHeight} listItemHeight={listItemHeight}
mode={mode} mode={mode.value}
prefixCls={prefixCls} prefixCls={prefixCls.value}
direction={direction} direction={direction.value}
inputIcon={suffixIcon} inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon} menuItemSelectedIcon={itemIcon}
removeIcon={removeIcon} removeIcon={removeIcon}
clearIcon={clearIcon} clearIcon={clearIcon}
notFoundContent={mergedNotFound} notFoundContent={mergedNotFound}
class={mergedClassName} class={[mergedClassName.value, attrs.class]}
getPopupContainer={getPopupContainer || getContextPopupContainer} getPopupContainer={getPopupContainer || getContextPopupContainer}
dropdownClassName={rcSelectRtlDropDownClassName} dropdownClassName={rcSelectRtlDropDownClassName}
onChange={triggerChange} onChange={triggerChange}
dropdownRender={selectProps.dropdownRender || this.$slots.dropdownRender} dropdownRender={selectProps.dropdownRender || slots.dropdownRender}
> >
{slots.default?.()} {slots.default?.()}
</RcSelect> </RcSelect>
); );
};
}, },
}); });
/* istanbul ignore next */ /* istanbul ignore next */

View File

@ -1,9 +1,9 @@
import { defineComponent } from 'vue'; import { defineComponent, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue';
import moment from 'moment'; import moment from 'moment';
import interopDefault from '../_util/interopDefault'; import interopDefault from '../_util/interopDefault';
import initDefaultProps from '../_util/props-util/initDefaultProps'; import initDefaultProps from '../_util/props-util/initDefaultProps';
import Statistic, { StatisticProps } from './Statistic'; import Statistic, { StatisticProps } from './Statistic';
import { formatCountdown, countdownValueType, FormatConfig } from './utils'; import { formatCountdown as formatCD, countdownValueType, FormatConfig } from './utils';
const REFRESH_INTERVAL = 1000 / 30; const REFRESH_INTERVAL = 1000 / 30;
@ -16,73 +16,77 @@ export default defineComponent({
props: initDefaultProps(StatisticProps, { props: initDefaultProps(StatisticProps, {
format: 'HH:mm:ss', format: 'HH:mm:ss',
}), }),
setup() { emits: ['finish', 'change'],
return { setup(props, { emit }) {
countdownId: undefined, const countdownId = ref<number>();
} as { countdownId: number }; const statistic = ref();
}, const syncTimer = () => {
mounted() { const { value } = props;
this.syncTimer();
},
updated() {
this.syncTimer();
},
beforeUnmount() {
this.stopTimer();
},
methods: {
syncTimer() {
const { value } = this.$props;
const timestamp = getTime(value); const timestamp = getTime(value);
if (timestamp >= Date.now()) { if (timestamp >= Date.now()) {
this.startTimer(); startTimer();
} else { } else {
this.stopTimer(); stopTimer();
} }
}, };
startTimer() { const startTimer = () => {
if (this.countdownId) return; if (countdownId.value) return;
this.countdownId = window.setInterval(() => { const timestamp = getTime(props.value);
(this.$refs.statistic as any).$forceUpdate(); countdownId.value = window.setInterval(() => {
this.syncTimer(); statistic.value.$forceUpdate();
if (timestamp > Date.now()) {
emit('change', timestamp - Date.now());
}
syncTimer();
}, REFRESH_INTERVAL); }, REFRESH_INTERVAL);
}, };
stopTimer() { const stopTimer = () => {
const { value } = this.$props; const { value } = props;
if (this.countdownId) { if (countdownId.value) {
clearInterval(this.countdownId); clearInterval(countdownId.value);
this.countdownId = undefined; countdownId.value = undefined;
const timestamp = getTime(value); const timestamp = getTime(value);
if (timestamp < Date.now()) { if (timestamp < Date.now()) {
this.$emit('finish'); emit('finish');
} }
} }
}, };
formatCountdown({ value, config }: { value: countdownValueType; config: FormatConfig }) { const formatCountdown = ({
const { format } = this.$props; value,
return formatCountdown(value, { ...config, format }); config,
}, }: {
value: countdownValueType;
config: FormatConfig;
}) => {
const { format } = props;
return formatCD(value, { ...config, format });
};
valueRenderHtml: node => node, const valueRenderHtml = (node: any) => node;
}, onMounted(() => {
syncTimer();
render() { });
onUpdated(() => {
syncTimer();
});
onBeforeUnmount(() => {
stopTimer();
});
return () => {
return ( return (
<Statistic <Statistic
ref="statistic" ref={statistic}
{...{ {...{
...this.$props, ...props,
valueRender: this.valueRenderHtml, valueRender: valueRenderHtml,
formatter: this.formatCountdown, formatter: formatCountdown,
}} }}
/> />
); );
};
}, },
}); });

View File

@ -1,10 +1,10 @@
import { defineComponent, inject, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getComponent } from '../_util/props-util';
import initDefaultProps from '../_util/props-util/initDefaultProps'; import initDefaultProps from '../_util/props-util/initDefaultProps';
import { defaultConfigProvider } from '../config-provider';
import StatisticNumber from './Number'; import StatisticNumber from './Number';
import { countdownValueType } from './utils'; import { countdownValueType } from './utils';
import Skeleton from '../skeleton/Skeleton';
import useConfigInject from '../_util/hooks/useConfigInject';
export const StatisticProps = { export const StatisticProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
@ -22,6 +22,7 @@ export const StatisticProps = {
suffix: PropTypes.VNodeChild, suffix: PropTypes.VNodeChild,
title: PropTypes.VNodeChild, title: PropTypes.VNodeChild,
onFinish: PropTypes.func, onFinish: PropTypes.func,
loading: PropTypes.looseBool,
}; };
export default defineComponent({ export default defineComponent({
@ -29,45 +30,41 @@ export default defineComponent({
props: initDefaultProps(StatisticProps, { props: initDefaultProps(StatisticProps, {
decimalSeparator: '.', decimalSeparator: '.',
groupSeparator: ',', groupSeparator: ',',
loading: false,
}), }),
slots: ['title', 'prefix', 'suffix', 'formatter'],
setup() { setup(props, { slots }) {
return { const { prefixCls, direction } = useConfigInject('statistic', props);
configProvider: inject('configProvider', defaultConfigProvider), return () => {
}; const { value = 0, valueStyle, valueRender } = props;
}, const pre = prefixCls.value;
const title = props.title ?? slots.title?.();
render() { const prefix = props.prefix ?? slots.prefix?.();
const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props; const suffix = props.suffix ?? slots.suffix?.();
const { getPrefixCls } = this.configProvider; const formatter = props.formatter ?? slots.formatter;
const prefixCls = getPrefixCls('statistic', customizePrefixCls);
const title = getComponent(this, 'title');
const prefix = getComponent(this, 'prefix');
const suffix = getComponent(this, 'suffix');
const formatter = getComponent(this, 'formatter', {}, false);
const props = {
...this.$props,
prefixCls,
value,
formatter,
};
// data-for-update just for update component // data-for-update just for update component
// https://github.com/vueComponent/ant-design-vue/pull/3170 // https://github.com/vueComponent/ant-design-vue/pull/3170
let valueNode = <StatisticNumber data-for-update={Date.now()} {...props} />; let valueNode = (
<StatisticNumber
data-for-update={Date.now()}
{...{ ...props, prefixCls: pre, value, formatter }}
/>
);
if (valueRender) { if (valueRender) {
valueNode = valueRender(valueNode); valueNode = valueRender(valueNode);
} }
return ( return (
<div class={prefixCls}> <div class={[pre, { [`${pre}-rtl`]: direction.value === 'rtl' }]}>
{title && <div class={`${prefixCls}-title`}>{title}</div>} {title && <div class={`${pre}-title`}>{title}</div>}
<div style={valueStyle} class={`${prefixCls}-content`}> <Skeleton paragraph={false} loading={props.loading}>
{prefix && <span class={`${prefixCls}-content-prefix`}>{prefix}</span>} <div style={valueStyle} class={`${pre}-content`}>
{prefix && <span class={`${pre}-content-prefix`}>{prefix}</span>}
{valueNode} {valueNode}
{suffix && <span class={`${prefixCls}-content-suffix`}>{suffix}</span>} {suffix && <span class={`${pre}-content-suffix`}>{suffix}</span>}
</div> </div>
</Skeleton>
</div> </div>
); );
};
}, },
}); });

View File

@ -7,7 +7,7 @@
.reset-component(); .reset-component();
&-title { &-title {
margin-bottom: 4px; margin-bottom: @margin-xss;
color: @text-color-secondary; color: @text-color-secondary;
font-size: @statistic-title-font-size; font-size: @statistic-title-font-size;
} }
@ -18,9 +18,8 @@
font-family: @statistic-font-family; font-family: @statistic-font-family;
&-value { &-value {
&-decimal { display: inline-block;
font-size: @statistic-unit-font-size; direction: ltr;
}
} }
&-prefix, &-prefix,
@ -34,7 +33,8 @@
&-suffix { &-suffix {
margin-left: 4px; margin-left: 4px;
font-size: @statistic-unit-font-size;
} }
} }
} }
@import './rtl';

View File

@ -0,0 +1,21 @@
.@{statistic-prefix-cls} {
&-rtl {
direction: rtl;
}
&-content {
&-prefix {
.@{statistic-prefix-cls}-rtl & {
margin-right: 0;
margin-left: 4px;
}
}
&-suffix {
.@{statistic-prefix-cls}-rtl & {
margin-right: 4px;
margin-left: 0;
}
}
}
}

View File

@ -1,9 +1,6 @@
import { VNodeTypes } from 'vue'; import { VNodeTypes } from 'vue';
import moment from 'moment';
import padStart from 'lodash-es/padStart'; import padStart from 'lodash-es/padStart';
import interopDefault from '../_util/interopDefault';
export type valueType = number | string; export type valueType = number | string;
export type countdownValueType = valueType | string; export type countdownValueType = valueType | string;
@ -39,15 +36,15 @@ const timeUnits: [string, number][] = [
export function formatTimeStr(duration: number, format: string) { export function formatTimeStr(duration: number, format: string) {
let leftDuration: number = duration; let leftDuration: number = duration;
const escapeRegex = /\[[^\]]*\]/g; const escapeRegex = /\[[^\]]*]/g;
const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1)); const keepList: string[] = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
const templateText = format.replace(escapeRegex, '[]'); const templateText = format.replace(escapeRegex, '[]');
const replacedText = timeUnits.reduce((current, [name, unit]) => { const replacedText = timeUnits.reduce((current, [name, unit]) => {
if (current.indexOf(name) !== -1) { if (current.indexOf(name) !== -1) {
const value = Math.floor(leftDuration / unit); const value = Math.floor(leftDuration / unit);
leftDuration -= value * unit; leftDuration -= value * unit;
return current.replace(new RegExp(`${name}+`, 'g'), match => { return current.replace(new RegExp(`${name}+`, 'g'), (match: string) => {
const len = match.length; const len = match.length;
return padStart(value.toString(), len, '0'); return padStart(value.toString(), len, '0');
}); });
@ -65,8 +62,9 @@ export function formatTimeStr(duration: number, format: string) {
export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) { export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) {
const { format = '' } = config; const { format = '' } = config;
const target = interopDefault(moment)(value).valueOf(); const target = new Date(value).getTime();
const current = interopDefault(moment)().valueOf(); const current = Date.now();
const diff = Math.max(target - current, 0); const diff = Math.max(target - current, 0);
return formatTimeStr(diff, format); return formatTimeStr(diff, format);
} }

View File

@ -9,6 +9,16 @@
.@{name}-leave { .@{name}-leave {
animation-timing-function: linear; animation-timing-function: linear;
} }
.make-motion(@className, @keyframeName);
.@{className}-enter,
.@{className}-appear {
opacity: 0;
animation-timing-function: linear;
}
.@{className}-leave {
animation-timing-function: linear;
}
} }
.fade-motion(fade, antFade); .fade-motion(fade, antFade);

View File

@ -9,6 +9,16 @@
.@{name}-leave { .@{name}-leave {
animation-timing-function: @ease-in-circ; animation-timing-function: @ease-in-circ;
} }
.make-motion(@className, @keyframeName);
.@{className}-enter,
.@{className}-appear {
opacity: 0;
animation-timing-function: @ease-out-circ;
}
.@{className}-leave {
animation-timing-function: @ease-in-circ;
}
} }
.move-motion(move-up, antMoveUp); .move-motion(move-up, antMoveUp);

View File

@ -14,6 +14,20 @@
.@{name}-leave { .@{name}-leave {
animation-timing-function: @ease-in-out-circ; animation-timing-function: @ease-in-out-circ;
} }
.make-motion(@className, @keyframeName, @duration);
.@{className}-enter,
.@{className}-appear {
transform: scale(0); // need this by yiminghe
opacity: 0;
animation-timing-function: @ease-out-circ;
&-prepare {
transform: none;
}
}
.@{className}-leave {
animation-timing-function: @ease-in-out-circ;
}
} }
// For Modal, Select choosen item // For Modal, Select choosen item

View File

@ -1,7 +1,7 @@
import { inject, defineComponent, PropType } from 'vue'; import { defineComponent, PropType, computed } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { defaultConfigProvider } from '../config-provider';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import useConfigInject from '../_util/hooks/useConfigInject';
const CheckableTag = defineComponent({ const CheckableTag = defineComponent({
name: 'ACheckableTag', name: 'ACheckableTag',
@ -17,7 +17,7 @@ const CheckableTag = defineComponent({
}, },
emits: ['update:checked', 'change', 'click'], emits: ['update:checked', 'change', 'click'],
setup(props, { slots, emit }) { setup(props, { slots, emit }) {
const { getPrefixCls } = inject('configProvider', defaultConfigProvider); const { prefixCls } = useConfigInject('tag', props);
const handleClick = (e: MouseEvent) => { const handleClick = (e: MouseEvent) => {
const { checked } = props; const { checked } = props;
emit('update:checked', !checked); emit('update:checked', !checked);
@ -25,16 +25,16 @@ const CheckableTag = defineComponent({
emit('click', e); emit('click', e);
}; };
return () => { const cls = computed(() =>
const { checked, prefixCls: customizePrefixCls } = props; classNames(prefixCls.value, {
const prefixCls = getPrefixCls('tag', customizePrefixCls); [`${prefixCls.value}-checkable`]: true,
const cls = classNames(prefixCls, { [`${prefixCls.value}-checkable-checked`]: props.checked,
[`${prefixCls}-checkable`]: true, }),
[`${prefixCls}-checkable-checked`]: checked, );
});
return () => {
return ( return (
<span class={cls} onClick={handleClick}> <span class={cls.value} onClick={handleClick}>
{slots.default?.()} {slots.default?.()}
</span> </span>
); );

View File

@ -1,5 +1,4 @@
import { import {
inject,
ref, ref,
HTMLAttributes, HTMLAttributes,
defineComponent, defineComponent,
@ -8,6 +7,7 @@ import {
PropType, PropType,
ExtractPropTypes, ExtractPropTypes,
Plugin, Plugin,
computed,
} from 'vue'; } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
@ -20,8 +20,8 @@ import {
PresetStatusColorType, PresetStatusColorType,
} from '../_util/colors'; } from '../_util/colors';
import { LiteralUnion } from '../_util/type'; import { LiteralUnion } from '../_util/type';
import { defaultConfigProvider } from '../config-provider';
import CheckableTag from './CheckableTag'; import CheckableTag from './CheckableTag';
import useConfigInject from '../_util/hooks/useConfigInject';
const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`); const PresetColorRegex = new RegExp(`^(${PresetColorTypes.join('|')})(-inverse)?$`);
const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`); const PresetStatusColorRegex = new RegExp(`^(${PresetStatusColorTypes.join('|')})$`);
@ -46,8 +46,9 @@ const Tag = defineComponent({
name: 'ATag', name: 'ATag',
props: tagProps, props: tagProps,
emits: ['update:visible', 'close'], emits: ['update:visible', 'close'],
slots: ['closeIcon', 'icon'],
setup(props: TagProps, { slots, emit, attrs }) { setup(props: TagProps, { slots, emit, attrs }) {
const { getPrefixCls } = inject('configProvider', defaultConfigProvider); const { prefixCls, direction } = useConfigInject('tag', props);
const visible = ref(true); const visible = ref(true);
@ -70,26 +71,31 @@ const Tag = defineComponent({
} }
}; };
const isPresetColor = (): boolean => { const isPresetColor = computed(() => {
const { color } = props; const { color } = props;
if (!color) { if (!color) {
return false; return false;
} }
return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color); return PresetColorRegex.test(color) || PresetStatusColorRegex.test(color);
}; });
const tagClassName = computed(() =>
classNames(prefixCls.value, {
[`${prefixCls.value}-${props.color}`]: isPresetColor.value,
[`${prefixCls.value}-has-color`]: props.color && !isPresetColor.value,
[`${prefixCls.value}-hidden`]: !visible.value,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
}),
);
return () => { return () => {
const { const {
prefixCls: customizePrefixCls,
icon = slots.icon?.(), icon = slots.icon?.(),
color, color,
closeIcon = slots.closeIcon?.(), closeIcon = slots.closeIcon?.(),
closable = false, closable = false,
} = props; } = props;
const presetColor = isPresetColor();
const prefixCls = getPrefixCls('tag', customizePrefixCls);
const renderCloseIcon = () => { const renderCloseIcon = () => {
if (closable) { if (closable) {
return closeIcon ? ( return closeIcon ? (
@ -104,15 +110,9 @@ const Tag = defineComponent({
}; };
const tagStyle = { const tagStyle = {
backgroundColor: color && !isPresetColor() ? color : undefined, backgroundColor: color && !isPresetColor.value ? color : undefined,
}; };
const tagClassName = classNames(prefixCls, {
[`${prefixCls}-${color}`]: presetColor,
[`${prefixCls}-has-color`]: color && !presetColor,
[`${prefixCls}-hidden`]: !visible.value,
});
const iconNode = icon || null; const iconNode = icon || null;
const children = slots.default?.(); const children = slots.default?.();
const kids = iconNode ? ( const kids = iconNode ? (
@ -127,7 +127,7 @@ const Tag = defineComponent({
const isNeedWave = 'onClick' in attrs; const isNeedWave = 'onClick' in attrs;
const tagNode = ( const tagNode = (
<span class={tagClassName} style={tagStyle}> <span class={tagClassName.value} style={tagStyle}>
{kids} {kids}
{renderCloseIcon()} {renderCloseIcon()}
</span> </span>

View File

@ -16,13 +16,8 @@
background: @tag-default-bg; background: @tag-default-bg;
border: @border-width-base @border-style-base @border-color-base; border: @border-width-base @border-style-base @border-color-base;
border-radius: @border-radius-base; border-radius: @border-radius-base;
cursor: default;
opacity: 1; opacity: 1;
transition: all 0.3s @ease-in-out-circ; transition: all 0.3s;
&:hover {
opacity: 0.85;
}
&, &,
a, a,
@ -36,14 +31,12 @@
padding: 0 8px; padding: 0 8px;
} }
.@{iconfont-css-prefix}-close { &-close-icon {
.iconfont-size-under-12px(10px);
margin-left: 3px; margin-left: 3px;
color: @text-color-secondary; color: @text-color-secondary;
font-weight: bold; font-size: 10px;
cursor: pointer; cursor: pointer;
transition: all 0.3s @ease-in-out-circ; transition: all 0.3s;
&:hover { &:hover {
color: @heading-color; color: @heading-color;
@ -91,8 +84,9 @@
@lightColor: '@{color}-1'; @lightColor: '@{color}-1';
@lightBorderColor: '@{color}-3'; @lightBorderColor: '@{color}-3';
@darkColor: '@{color}-6'; @darkColor: '@{color}-6';
@textColor: '@{color}-7';
&-@{color} { &-@{color} {
color: @@darkColor; color: @@textColor;
background: @@lightColor; background: @@lightColor;
border-color: @@lightBorderColor; border-color: @@lightBorderColor;
} }
@ -127,3 +121,5 @@
margin-left: 7px; margin-left: 7px;
} }
} }
@import './rtl';

View File

@ -0,0 +1,28 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@tag-prefix-cls: ~'@{ant-prefix}-tag';
.@{tag-prefix-cls} {
&&-rtl {
margin-right: 0;
margin-left: 8px;
direction: rtl;
text-align: right;
}
&-close-icon {
.@{tag-prefix-cls}-rtl & {
margin-right: 3px;
margin-left: 0;
}
}
> .@{iconfont-css-prefix} + span,
> span + .@{iconfont-css-prefix} {
.@{tag-prefix-cls}-rtl& {
margin-right: 7px;
margin-left: 0;
}
}
}

View File

@ -5,6 +5,7 @@ import EnterOutlined from '@ant-design/icons-vue/EnterOutlined';
import { defineComponent, ref, reactive, watch, onMounted } from 'vue'; import { defineComponent, ref, reactive, watch, onMounted } from 'vue';
const Editable = defineComponent({ const Editable = defineComponent({
name: 'Editable',
props: { props: {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
@ -95,6 +96,7 @@ const Editable = defineComponent({
function onBlur() { function onBlur() {
confirmChange(); confirmChange();
emit('end');
} }
function confirmChange() { function confirmChange() {

View File

@ -196,7 +196,7 @@ export default defineComponent({
return () => { return () => {
const child = slots?.default(); const child = slots?.default();
if (child) { if (child) {
return cloneElement(child[0], { ref: nodeRef }); return cloneElement(child[0], { ref: nodeRef }, true, true);
} }
return child && child[0]; return child && child[0];
}; };

View File

@ -25,10 +25,12 @@ export default defineComponent({
overlayStyle: PropTypes.object.def(() => ({})), overlayStyle: PropTypes.object.def(() => ({})),
placement: PropTypes.string.def('bottomLeft'), placement: PropTypes.string.def('bottomLeft'),
overlay: PropTypes.any, overlay: PropTypes.any,
trigger: PropTypes.array.def(['hover']), trigger: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]).def(
'hover',
),
alignPoint: PropTypes.looseBool, alignPoint: PropTypes.looseBool,
showAction: PropTypes.array.def([]), showAction: PropTypes.array,
hideAction: PropTypes.array.def([]), hideAction: PropTypes.array,
getPopupContainer: PropTypes.func, getPopupContainer: PropTypes.func,
visible: PropTypes.looseBool, visible: PropTypes.looseBool,
defaultVisible: PropTypes.looseBool.def(false), defaultVisible: PropTypes.looseBool.def(false),
@ -175,7 +177,6 @@ export default defineComponent({
if (!triggerHideAction && trigger.indexOf('contextmenu') !== -1) { if (!triggerHideAction && trigger.indexOf('contextmenu') !== -1) {
triggerHideAction = ['click']; triggerHideAction = ['click'];
} }
const triggerProps = { const triggerProps = {
...otherProps, ...otherProps,
prefixCls, prefixCls,

View File

@ -36,7 +36,7 @@ export interface ImagePropsType extends Omit<ImgHTMLAttributes, 'placeholder' |
fallback?: string; fallback?: string;
preview?: boolean | ImagePreviewType; preview?: boolean | ImagePreviewType;
} }
export const ImageProps = { export const imageProps = {
src: PropTypes.string, src: PropTypes.string,
wrapperClassName: PropTypes.string, wrapperClassName: PropTypes.string,
wrapperStyle: PropTypes.style, wrapperStyle: PropTypes.style,
@ -69,7 +69,7 @@ const ImageInternal = defineComponent({
name: 'Image', name: 'Image',
mixins: [BaseMixin], mixins: [BaseMixin],
inheritAttrs: false, inheritAttrs: false,
props: ImageProps, props: imageProps,
emits: ['click'], emits: ['click'],
setup(props, { attrs, slots, emit }) { setup(props, { attrs, slots, emit }) {
const prefixCls = computed(() => props.prefixCls); const prefixCls = computed(() => props.prefixCls);

View File

@ -131,7 +131,7 @@ const Overflow = defineComponent({
}); });
const omittedItems = computed(() => { const omittedItems = computed(() => {
if (isResponsive) { if (isResponsive.value) {
return props.data.slice(mergedDisplayCount.value + 1); return props.data.slice(mergedDisplayCount.value + 1);
} }
return props.data.slice(mergedData.value.length); return props.data.slice(mergedData.value.length);
@ -362,7 +362,7 @@ const Overflow = defineComponent({
<Item <Item
{...itemSharedProps} {...itemSharedProps}
order={mergedDisplayCount.value} order={mergedDisplayCount.value}
class={`${itemPrefixCls}-suffix`} class={`${itemPrefixCls.value}-suffix`}
registerSize={registerSuffixSize} registerSize={registerSuffixSize}
display display
style={suffixStyle} style={suffixStyle}

View File

@ -1,6 +1,6 @@
{ {
"name": "ant-design-vue", "name": "ant-design-vue",
"version": "2.2.0-beta.2", "version": "2.2.0-beta.3",
"title": "Ant Design Vue", "title": "Ant Design Vue",
"description": "An enterprise-class UI design language and Vue-based implementation", "description": "An enterprise-class UI design language and Vue-based implementation",
"keywords": [ "keywords": [