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
`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
`2021-06-08`

View File

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

View File

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

View File

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

View File

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

View File

@ -108,7 +108,7 @@ export default defineComponent({
__ANT_NEW_FORM_ITEM: true,
props: formItemProps,
slots: ['help', 'label', 'extra'],
setup(props, { slots }) {
setup(props, { slots, attrs, expose }) {
warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`);
const eventKey = `form-item-${++indexGuid}`;
const { prefixCls } = useConfigInject('form', props);
@ -272,6 +272,12 @@ export default defineComponent({
control.focus();
}
};
expose({
onFieldBlur,
onFieldChange,
clearValidate,
resetField,
});
formContext.addField(eventKey, {
fieldValue,
fieldId,
@ -336,9 +342,11 @@ export default defineComponent({
}
return (
<Row
{...attrs}
class={[
itemClassName.value,
domErrorVisible.value || !!help ? `${prefixCls.value}-item-with-help` : '',
attrs.class,
]}
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]);
export const colSize = PropTypes.shape({
export const colSize = PropTypes.shape<ColSize>({
span: stringOrNumber,
order: stringOrNumber,
offset: stringOrNumber,

View File

@ -1,3 +1,4 @@
import { computed } from 'vue';
import { Ref, inject, InjectionKey, provide, ComputedRef } from 'vue';
export interface RowContext {
@ -13,7 +14,11 @@ const useProvideRow = (state: RowContext) => {
};
const useInjectRow = () => {
return inject(RowContextKey);
return inject(RowContextKey, {
gutter: computed(() => undefined),
wrap: computed(() => undefined),
supportFlexGap: computed(() => undefined),
});
};
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 ImageInternal from '../vc-image';
import { ImageProps, ImagePropsType } from '../vc-image/src/Image';
import { imageProps } from '../vc-image/src/Image';
import PreviewGroup from './PreviewGroup';
const Image = defineComponent({
export type ImageProps = Partial<
ExtractPropTypes<typeof imageProps> & Omit<ImgHTMLAttributes, 'placeholder' | 'onClick'>
>;
const Image = defineComponent<ImageProps>({
name: 'AImage',
inheritAttrs: false,
props: ImageProps,
props: imageProps as any,
setup(props, ctx) {
const { slots, attrs } = ctx;
const configProvider = inject('configProvider', defaultConfigProvider);
@ -19,7 +22,7 @@ const Image = defineComponent({
},
});
export { ImageProps, ImagePropsType };
export { imageProps };
Image.PreviewGroup = PreviewGroup;

View File

@ -1,13 +1,12 @@
import { defineComponent } from 'vue';
import { useInjectMenu } from './hooks/useMenuContext';
export default defineComponent({
name: 'AMenuDivider',
props: {
prefixCls: String,
},
setup(props) {
setup() {
const { prefixCls } = useInjectMenu();
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,
defaultMotions: computed(() => (isMounted.value ? defaultMotions : null)),
motion: computed(() => (isMounted.value ? props.motion : null)),
overflowDisabled: computed(() => props.disabledOverflow),
overflowDisabled: computed(() => undefined),
onOpenChange: onInternalOpenChange,
onItemClick: onInternalClick,
registerMenuInfo,

View File

@ -260,13 +260,13 @@ export default defineComponent({
</div>
);
if (!overflowDisabled.value) {
if (!overflowDisabled.value && mode.value !== 'inline') {
const triggerMode = triggerModeRef.value;
titleNode = (
<PopupTrigger
mode={triggerMode}
prefixCls={subMenuPrefixClsValue}
visible={!props.internalPopupClose && open.value && mode.value !== 'inline'}
visible={!props.internalPopupClose && open.value}
popupClassName={popupClassName.value}
popupOffset={props.popupOffset}
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 classNames from '../_util/classNames';
import RcSelect, { Option, OptGroup, SelectProps as RcSelectProps, BaseProps } from '../vc-select';
import { OptionProps as OptionPropsType } from '../vc-select/Option';
import { defaultConfigProvider } from '../config-provider';
import getIcons from './utils/iconUtil';
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
type RawValue = string | number;
@ -74,11 +74,10 @@ const Select = defineComponent({
props: SelectProps(),
SECRET_COMBOBOX_MODE_DO_NOT_USE: 'SECRET_COMBOBOX_MODE_DO_NOT_USE',
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 configProvider = inject('configProvider', defaultConfigProvider);
const focus = () => {
if (selectRef.value) {
selectRef.value.focus();
@ -104,60 +103,37 @@ const Select = defineComponent({
return mode;
});
const prefixCls = computed(() => {
return configProvider.getPrefixCls('select', props.prefixCls);
});
const { prefixCls, direction, configProvider } = useConfigInject('select', props);
const mergedClassName = computed(() =>
classNames(
{
classNames({
[`${prefixCls.value}-lg`]: props.size === 'large',
[`${prefixCls.value}-sm`]: props.size === 'small',
[`${prefixCls.value}-rtl`]: props.direction === 'rtl',
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-borderless`]: !props.bordered,
},
attrs.class,
),
}),
);
const triggerChange = (...args: any[]) => {
emit('update:value', args[0]);
emit('change', ...args);
};
return {
selectRef,
mergedClassName,
mode,
focus,
expose({
blur,
configProvider,
triggerChange,
prefixCls,
};
},
render() {
const {
configProvider,
mode,
mergedClassName,
triggerChange,
prefixCls,
$slots: slots,
$props,
} = this as any;
const props: SelectTypes = $props;
focus,
});
return () => {
const {
notFoundContent,
listHeight = 256,
listItemHeight = 24,
getPopupContainer,
dropdownClassName,
direction,
virtual,
dropdownMatchSelectWidth,
} = props;
const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider;
const isMultiple = mode === 'multiple' || mode === 'tags';
const isMultiple = mode.value === 'multiple' || mode.value === 'tags';
// ===================== Empty =====================
let mergedNotFound: VNodeChild;
@ -165,7 +141,7 @@ const Select = defineComponent({
mergedNotFound = notFoundContent;
} else if (slots.notFoundContent) {
mergedNotFound = slots.notFoundContent();
} else if (mode === 'combobox') {
} else if (mode.value === 'combobox') {
mergedNotFound = null;
} else {
mergedNotFound = renderEmpty('Select') as any;
@ -174,9 +150,9 @@ const Select = defineComponent({
// ===================== Icons =====================
const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons(
{
...this.$props,
...props,
multiple: isMultiple,
prefixCls,
prefixCls: prefixCls.value,
},
slots,
);
@ -189,37 +165,38 @@ const Select = defineComponent({
'clearIcon',
'size',
'bordered',
]) as any;
]);
const rcSelectRtlDropDownClassName = classNames(dropdownClassName, {
[`${prefixCls}-dropdown-${direction}`]: direction === 'rtl',
[`${prefixCls.value}-dropdown-${direction.value}`]: direction.value === 'rtl',
});
return (
<RcSelect
ref="selectRef"
ref={selectRef}
virtual={virtual}
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
{...selectProps}
{...this.$attrs}
{...attrs}
listHeight={listHeight}
listItemHeight={listItemHeight}
mode={mode}
prefixCls={prefixCls}
direction={direction}
mode={mode.value}
prefixCls={prefixCls.value}
direction={direction.value}
inputIcon={suffixIcon}
menuItemSelectedIcon={itemIcon}
removeIcon={removeIcon}
clearIcon={clearIcon}
notFoundContent={mergedNotFound}
class={mergedClassName}
class={[mergedClassName.value, attrs.class]}
getPopupContainer={getPopupContainer || getContextPopupContainer}
dropdownClassName={rcSelectRtlDropDownClassName}
onChange={triggerChange}
dropdownRender={selectProps.dropdownRender || this.$slots.dropdownRender}
dropdownRender={selectProps.dropdownRender || slots.dropdownRender}
>
{slots.default?.()}
</RcSelect>
);
};
},
});
/* 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 interopDefault from '../_util/interopDefault';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import Statistic, { StatisticProps } from './Statistic';
import { formatCountdown, countdownValueType, FormatConfig } from './utils';
import { formatCountdown as formatCD, countdownValueType, FormatConfig } from './utils';
const REFRESH_INTERVAL = 1000 / 30;
@ -16,73 +16,77 @@ export default defineComponent({
props: initDefaultProps(StatisticProps, {
format: 'HH:mm:ss',
}),
setup() {
return {
countdownId: undefined,
} as { countdownId: number };
},
mounted() {
this.syncTimer();
},
updated() {
this.syncTimer();
},
beforeUnmount() {
this.stopTimer();
},
methods: {
syncTimer() {
const { value } = this.$props;
emits: ['finish', 'change'],
setup(props, { emit }) {
const countdownId = ref<number>();
const statistic = ref();
const syncTimer = () => {
const { value } = props;
const timestamp = getTime(value);
if (timestamp >= Date.now()) {
this.startTimer();
startTimer();
} else {
this.stopTimer();
stopTimer();
}
},
};
startTimer() {
if (this.countdownId) return;
this.countdownId = window.setInterval(() => {
(this.$refs.statistic as any).$forceUpdate();
this.syncTimer();
const startTimer = () => {
if (countdownId.value) return;
const timestamp = getTime(props.value);
countdownId.value = window.setInterval(() => {
statistic.value.$forceUpdate();
if (timestamp > Date.now()) {
emit('change', timestamp - Date.now());
}
syncTimer();
}, REFRESH_INTERVAL);
},
};
stopTimer() {
const { value } = this.$props;
if (this.countdownId) {
clearInterval(this.countdownId);
this.countdownId = undefined;
const stopTimer = () => {
const { value } = props;
if (countdownId.value) {
clearInterval(countdownId.value);
countdownId.value = undefined;
const timestamp = getTime(value);
if (timestamp < Date.now()) {
this.$emit('finish');
emit('finish');
}
}
},
};
formatCountdown({ value, config }: { value: countdownValueType; config: FormatConfig }) {
const { format } = this.$props;
return formatCountdown(value, { ...config, format });
},
const formatCountdown = ({
value,
config,
}: {
value: countdownValueType;
config: FormatConfig;
}) => {
const { format } = props;
return formatCD(value, { ...config, format });
};
valueRenderHtml: node => node,
},
render() {
const valueRenderHtml = (node: any) => node;
onMounted(() => {
syncTimer();
});
onUpdated(() => {
syncTimer();
});
onBeforeUnmount(() => {
stopTimer();
});
return () => {
return (
<Statistic
ref="statistic"
ref={statistic}
{...{
...this.$props,
valueRender: this.valueRenderHtml,
formatter: this.formatCountdown,
...props,
valueRender: valueRenderHtml,
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 { getComponent } from '../_util/props-util';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import { defaultConfigProvider } from '../config-provider';
import StatisticNumber from './Number';
import { countdownValueType } from './utils';
import Skeleton from '../skeleton/Skeleton';
import useConfigInject from '../_util/hooks/useConfigInject';
export const StatisticProps = {
prefixCls: PropTypes.string,
@ -22,6 +22,7 @@ export const StatisticProps = {
suffix: PropTypes.VNodeChild,
title: PropTypes.VNodeChild,
onFinish: PropTypes.func,
loading: PropTypes.looseBool,
};
export default defineComponent({
@ -29,45 +30,41 @@ export default defineComponent({
props: initDefaultProps(StatisticProps, {
decimalSeparator: '.',
groupSeparator: ',',
loading: false,
}),
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
};
},
render() {
const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props;
const { getPrefixCls } = this.configProvider;
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,
};
slots: ['title', 'prefix', 'suffix', 'formatter'],
setup(props, { slots }) {
const { prefixCls, direction } = useConfigInject('statistic', props);
return () => {
const { value = 0, valueStyle, valueRender } = props;
const pre = prefixCls.value;
const title = props.title ?? slots.title?.();
const prefix = props.prefix ?? slots.prefix?.();
const suffix = props.suffix ?? slots.suffix?.();
const formatter = props.formatter ?? slots.formatter;
// data-for-update just for update component
// 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) {
valueNode = valueRender(valueNode);
}
return (
<div class={prefixCls}>
{title && <div class={`${prefixCls}-title`}>{title}</div>}
<div style={valueStyle} class={`${prefixCls}-content`}>
{prefix && <span class={`${prefixCls}-content-prefix`}>{prefix}</span>}
<div class={[pre, { [`${pre}-rtl`]: direction.value === 'rtl' }]}>
{title && <div class={`${pre}-title`}>{title}</div>}
<Skeleton paragraph={false} loading={props.loading}>
<div style={valueStyle} class={`${pre}-content`}>
{prefix && <span class={`${pre}-content-prefix`}>{prefix}</span>}
{valueNode}
{suffix && <span class={`${prefixCls}-content-suffix`}>{suffix}</span>}
{suffix && <span class={`${pre}-content-suffix`}>{suffix}</span>}
</div>
</Skeleton>
</div>
);
};
},
});

View File

@ -7,7 +7,7 @@
.reset-component();
&-title {
margin-bottom: 4px;
margin-bottom: @margin-xss;
color: @text-color-secondary;
font-size: @statistic-title-font-size;
}
@ -18,9 +18,8 @@
font-family: @statistic-font-family;
&-value {
&-decimal {
font-size: @statistic-unit-font-size;
}
display: inline-block;
direction: ltr;
}
&-prefix,
@ -34,7 +33,8 @@
&-suffix {
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 moment from 'moment';
import padStart from 'lodash-es/padStart';
import interopDefault from '../_util/interopDefault';
export type valueType = number | string;
export type countdownValueType = valueType | string;
@ -39,15 +36,15 @@ const timeUnits: [string, number][] = [
export function formatTimeStr(duration: number, format: string) {
let leftDuration: number = duration;
const escapeRegex = /\[[^\]]*\]/g;
const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
const escapeRegex = /\[[^\]]*]/g;
const keepList: string[] = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
const templateText = format.replace(escapeRegex, '[]');
const replacedText = timeUnits.reduce((current, [name, unit]) => {
if (current.indexOf(name) !== -1) {
const value = Math.floor(leftDuration / 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;
return padStart(value.toString(), len, '0');
});
@ -65,8 +62,9 @@ export function formatTimeStr(duration: number, format: string) {
export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) {
const { format = '' } = config;
const target = interopDefault(moment)(value).valueOf();
const current = interopDefault(moment)().valueOf();
const target = new Date(value).getTime();
const current = Date.now();
const diff = Math.max(target - current, 0);
return formatTimeStr(diff, format);
}

View File

@ -9,6 +9,16 @@
.@{name}-leave {
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);

View File

@ -9,6 +9,16 @@
.@{name}-leave {
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);

View File

@ -14,6 +14,20 @@
.@{name}-leave {
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

View File

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

View File

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

View File

@ -16,13 +16,8 @@
background: @tag-default-bg;
border: @border-width-base @border-style-base @border-color-base;
border-radius: @border-radius-base;
cursor: default;
opacity: 1;
transition: all 0.3s @ease-in-out-circ;
&:hover {
opacity: 0.85;
}
transition: all 0.3s;
&,
a,
@ -36,14 +31,12 @@
padding: 0 8px;
}
.@{iconfont-css-prefix}-close {
.iconfont-size-under-12px(10px);
&-close-icon {
margin-left: 3px;
color: @text-color-secondary;
font-weight: bold;
font-size: 10px;
cursor: pointer;
transition: all 0.3s @ease-in-out-circ;
transition: all 0.3s;
&:hover {
color: @heading-color;
@ -91,8 +84,9 @@
@lightColor: '@{color}-1';
@lightBorderColor: '@{color}-3';
@darkColor: '@{color}-6';
@textColor: '@{color}-7';
&-@{color} {
color: @@darkColor;
color: @@textColor;
background: @@lightColor;
border-color: @@lightBorderColor;
}
@ -127,3 +121,5 @@
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';
const Editable = defineComponent({
name: 'Editable',
props: {
prefixCls: PropTypes.string,
value: PropTypes.string,
@ -95,6 +96,7 @@ const Editable = defineComponent({
function onBlur() {
confirmChange();
emit('end');
}
function confirmChange() {

View File

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

View File

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

View File

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

View File

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

View File

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