From 6cff37bb03c13c2206e73051ad0e9af4ba6cbe5d Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Tue, 8 Jun 2021 21:54:26 +0800 Subject: [PATCH 01/19] refactor: Breadcrumb (#4175) --- components/breadcrumb/Breadcrumb.tsx | 110 ++++++++++-------- components/breadcrumb/BreadcrumbItem.tsx | 85 +++++++------- components/breadcrumb/BreadcrumbSeparator.tsx | 42 ++++--- .../breadcrumb/__tests__/Breadcrumb.test.js | 21 ++++ .../__snapshots__/Breadcrumb.test.js.snap | 8 +- components/breadcrumb/index.ts | 4 + components/breadcrumb/style/index.less | 5 +- components/breadcrumb/style/rtl.less | 29 +++++ components/vc-mentions/src/DropdownMenu.jsx | 4 +- examples/index.js | 10 +- v2-doc | 2 +- 11 files changed, 197 insertions(+), 123 deletions(-) create mode 100644 components/breadcrumb/style/rtl.less diff --git a/components/breadcrumb/Breadcrumb.tsx b/components/breadcrumb/Breadcrumb.tsx index 7c7ee346e..3caf72ac5 100644 --- a/components/breadcrumb/Breadcrumb.tsx +++ b/components/breadcrumb/Breadcrumb.tsx @@ -1,11 +1,11 @@ -import { inject, cloneVNode, defineComponent, PropType } from 'vue'; +import { cloneVNode, defineComponent, PropType, ExtractPropTypes } from 'vue'; import PropTypes from '../_util/vue-types'; -import { filterEmpty, getComponent, getSlot } from '../_util/props-util'; +import { flattenChildren, getPropsSlot } from '../_util/props-util'; import warning from '../_util/warning'; -import { defaultConfigProvider } from '../config-provider'; import BreadcrumbItem from './BreadcrumbItem'; import Menu from '../menu'; import { Omit, VueNode } from '../_util/type'; +import useConfigInject from '../_util/hooks/useConfigInject'; export interface Route { path: string; @@ -13,11 +13,11 @@ export interface Route { children?: Omit[]; } -const BreadcrumbProps = { +const breadcrumbProps = { prefixCls: PropTypes.string, routes: { type: Array as PropType }, params: PropTypes.any, - separator: PropTypes.VNodeChild, + separator: PropTypes.any, itemRender: { type: Function as PropType< (opt: { route: Route; params: unknown; routes: Route[]; paths: string[] }) => VueNode @@ -25,6 +25,8 @@ const BreadcrumbProps = { }, }; +export type BreadcrumbProps = Partial>; + function getBreadcrumbName(route: Route, params: unknown) { if (!route.breadcrumbName) { return null; @@ -50,34 +52,37 @@ function defaultItemRender(opt: { export default defineComponent({ name: 'ABreadcrumb', - props: BreadcrumbProps, - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), - }; - }, - methods: { - getPath(path: string, params: unknown) { + props: breadcrumbProps, + slots: ['separator', 'itemRender'], + setup(props, { slots }) { + const { prefixCls, direction } = useConfigInject('breadcrumb', props); + + const getPath = (path: string, params: unknown) => { path = (path || '').replace(/^\//, ''); Object.keys(params).forEach(key => { path = path.replace(`:${key}`, params[key]); }); return path; - }, + }; - addChildPath(paths: string[], childPath = '', params: unknown) { + const addChildPath = (paths: string[], childPath = '', params: unknown) => { const originalPaths = [...paths]; - const path = this.getPath(childPath, params); + const path = getPath(childPath, params); if (path) { originalPaths.push(path); } return originalPaths; - }, + }; - genForRoutes({ routes = [], params = {}, separator, itemRender = defaultItemRender }: any) { + const genForRoutes = ({ + routes = [], + params = {}, + separator, + itemRender = defaultItemRender, + }: any) => { const paths = []; return routes.map((route: Route) => { - const path = this.getPath(route.path, params); + const path = getPath(route.path, params); if (path) { paths.push(path); @@ -94,7 +99,7 @@ export default defineComponent({ route: child, params, routes, - paths: this.addChildPath(tempPaths, child.path, params), + paths: addChildPath(tempPaths, child.path, params), })} ))} @@ -112,36 +117,41 @@ export default defineComponent({ ); }); - }, - }, - render() { - let crumbs: VueNode[]; - const { prefixCls: customizePrefixCls, routes, params = {}, $slots } = this; - const getPrefixCls = this.configProvider.getPrefixCls; - const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); + }; + return () => { + let crumbs: VueNode[]; - const children = filterEmpty(getSlot(this)); - const separator = getComponent(this, 'separator'); - const itemRender = this.itemRender || $slots.itemRender || defaultItemRender; - if (routes && routes.length > 0) { - // generated by route - crumbs = this.genForRoutes({ - routes, - params, - separator, - itemRender, - }); - } else if (children.length) { - crumbs = children.map((element, index) => { - warning( - typeof element.type === 'object' && - (element.type.__ANT_BREADCRUMB_ITEM || element.type.__ANT_BREADCRUMB_SEPARATOR), - 'Breadcrumb', - "Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children", - ); - return cloneVNode(element, { separator, key: index }); - }); - } - return
{crumbs}
; + const { routes, params = {} } = props; + + const children = flattenChildren(getPropsSlot(slots, props)); + const separator = getPropsSlot(slots, props, 'separator') ?? '/'; + + const itemRender = props.itemRender || slots.itemRender || defaultItemRender; + if (routes && routes.length > 0) { + // generated by route + crumbs = genForRoutes({ + routes, + params, + separator, + itemRender, + }); + } else if (children.length) { + crumbs = children.map((element, index) => { + warning( + typeof element.type === 'object' && + (element.type.__ANT_BREADCRUMB_ITEM || element.type.__ANT_BREADCRUMB_SEPARATOR), + 'Breadcrumb', + "Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children", + ); + return cloneVNode(element, { separator, key: index }); + }); + } + + const breadcrumbClassName = { + [prefixCls.value]: true, + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + }; + return
{crumbs}
; + }; }, }); diff --git a/components/breadcrumb/BreadcrumbItem.tsx b/components/breadcrumb/BreadcrumbItem.tsx index 423dda1a5..a675e57db 100644 --- a/components/breadcrumb/BreadcrumbItem.tsx +++ b/components/breadcrumb/BreadcrumbItem.tsx @@ -1,31 +1,31 @@ -import { defineComponent, inject } from 'vue'; +import { defineComponent, ExtractPropTypes } from 'vue'; import PropTypes from '../_util/vue-types'; -import { hasProp, getComponent, getSlot } from '../_util/props-util'; -import { defaultConfigProvider } from '../config-provider'; +import { getPropsSlot } from '../_util/props-util'; import DropDown from '../dropdown/dropdown'; import DownOutlined from '@ant-design/icons-vue/DownOutlined'; +import useConfigInject from '../_util/hooks/useConfigInject'; +const breadcrumbItemProps = { + prefixCls: PropTypes.string, + href: PropTypes.string, + separator: PropTypes.any, + overlay: PropTypes.any, +}; + +export type BreadcrumbItemProps = Partial>; export default defineComponent({ name: 'ABreadcrumbItem', __ANT_BREADCRUMB_ITEM: true, - props: { - prefixCls: PropTypes.string, - href: PropTypes.string, - separator: PropTypes.VNodeChild.def('/'), - overlay: PropTypes.VNodeChild, - }, - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), - }; - }, - methods: { + props: breadcrumbItemProps, + slots: ['separator', 'overlay'], + setup(props, { slots }) { + const { prefixCls } = useConfigInject('breadcrumb', props); /** * if overlay is have * Wrap a DropDown */ - renderBreadcrumbNode(breadcrumbItem: JSX.Element, prefixCls: string) { - const overlay = getComponent(this, 'overlay'); + const renderBreadcrumbNode = (breadcrumbItem: JSX.Element, prefixCls: string) => { + const overlay = getPropsSlot(slots, props, 'overlay'); if (overlay) { return ( @@ -37,32 +37,29 @@ export default defineComponent({ ); } return breadcrumbItem; - }, - }, - render() { - const { prefixCls: customizePrefixCls } = this; - const getPrefixCls = this.configProvider.getPrefixCls; - const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); - const separator = getComponent(this, 'separator'); - const children = getSlot(this); - let link: JSX.Element; - if (hasProp(this, 'href')) { - link = {children}; - } else { - link = {children}; - } - // wrap to dropDown - link = this.renderBreadcrumbNode(link, prefixCls); - if (children) { - return ( - - {link} - {separator && separator !== '' && ( - {separator} - )} - - ); - } - return null; + }; + + return () => { + const separator = getPropsSlot(slots, props, 'separator') ?? '/'; + const children = getPropsSlot(slots, props); + let link: JSX.Element; + + if (props.href !== undefined) { + link = {children}; + } else { + link = {children}; + } + // wrap to dropDown + link = renderBreadcrumbNode(link, prefixCls.value); + if (children) { + return ( + + {link} + {separator && {separator}} + + ); + } + return null; + }; }, }); diff --git a/components/breadcrumb/BreadcrumbSeparator.tsx b/components/breadcrumb/BreadcrumbSeparator.tsx index 80f99f8ce..119e2dbbc 100644 --- a/components/breadcrumb/BreadcrumbSeparator.tsx +++ b/components/breadcrumb/BreadcrumbSeparator.tsx @@ -1,31 +1,29 @@ -import { defineComponent, inject } from 'vue'; -import { defaultConfigProvider } from '../config-provider'; +import { defineComponent, ExtractPropTypes } from 'vue'; import PropTypes from '../_util/vue-types'; -import { getSlot } from '../_util/props-util'; +import { flattenChildren } from '../_util/props-util'; +import useConfigInject from '../_util/hooks/useConfigInject'; + +const breadcrumbSeparator = { + prefixCls: PropTypes.string, +}; +export type BreadcrumbSeparator = Partial>; export default defineComponent({ name: 'ABreadcrumbSeparator', __ANT_BREADCRUMB_SEPARATOR: true, inheritAttrs: false, - props: { - prefixCls: PropTypes.string, - }, - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), + props: breadcrumbSeparator, + setup(props, { slots, attrs }) { + const { prefixCls } = useConfigInject('breadcrumb', props); + + return () => { + const { separator, class: className, ...restAttrs } = attrs; + const children = flattenChildren(slots.default?.()); + return ( + + {children.length > 0 ? children : '/'} + + ); }; }, - render() { - const { prefixCls: customizePrefixCls } = this; - const { separator, class: className, ...restAttrs } = this.$attrs; - const getPrefixCls = this.configProvider.getPrefixCls; - const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); - - const children = getSlot(this); - return ( - - {children.length > 0 ? children : '/'} - - ); - }, }); diff --git a/components/breadcrumb/__tests__/Breadcrumb.test.js b/components/breadcrumb/__tests__/Breadcrumb.test.js index f6168468e..4375e885d 100644 --- a/components/breadcrumb/__tests__/Breadcrumb.test.js +++ b/components/breadcrumb/__tests__/Breadcrumb.test.js @@ -107,4 +107,25 @@ describe('Breadcrumb', () => { }); expect(wrapper.html()).toMatchSnapshot(); }); + + // https://github.com/ant-design/ant-design/issues/25975 + it('should support Breadcrumb.Item default separator', () => { + const MockComponent = () => ( + + Mock Node + + ); + const wrapper = mount({ + render() { + return ( + + Location + + Application Center + + ); + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); }); diff --git a/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.js.snap b/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.js.snap index cfc921ec2..e0ffda91b 100644 --- a/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.js.snap +++ b/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.js.snap @@ -2,8 +2,14 @@ exports[`Breadcrumb should allow Breadcrumb.Item is null or undefined 1`] = `
Home/
`; -exports[`Breadcrumb should not display Breadcrumb Item when its children is falsy 1`] = `
/xxx/yyy/
`; +exports[`Breadcrumb should not display Breadcrumb Item when its children is falsy 1`] = ` +
+ xxx/yyy/ +
+`; exports[`Breadcrumb should render a menu 1`] = `
home/first/second/
`; +exports[`Breadcrumb should support Breadcrumb.Item default separator 1`] = `
Location/Mock Node/Application Center/
`; + exports[`Breadcrumb should support custom attribute 1`] = `
xxx/yyy/
`; diff --git a/components/breadcrumb/index.ts b/components/breadcrumb/index.ts index bb636d694..01f3c0f2a 100644 --- a/components/breadcrumb/index.ts +++ b/components/breadcrumb/index.ts @@ -3,6 +3,10 @@ import Breadcrumb from './Breadcrumb'; import BreadcrumbItem from './BreadcrumbItem'; import BreadcrumbSeparator from './BreadcrumbSeparator'; +export { BreadcrumbProps } from './Breadcrumb'; +export { BreadcrumbItemProps } from './BreadcrumbItem'; +export { BreadcrumbSeparator } from './BreadcrumbSeparator'; + Breadcrumb.Item = BreadcrumbItem; Breadcrumb.Separator = BreadcrumbSeparator; diff --git a/components/breadcrumb/style/index.less b/components/breadcrumb/style/index.less index 1777e3de0..52bba1202 100644 --- a/components/breadcrumb/style/index.less +++ b/components/breadcrumb/style/index.less @@ -38,7 +38,8 @@ } &-link { - > .@{iconfont-css-prefix} + span { + > .@{iconfont-css-prefix} + span, + > .@{iconfont-css-prefix} + a { margin-left: 4px; } } @@ -49,3 +50,5 @@ } } } + +@import './rtl'; diff --git a/components/breadcrumb/style/rtl.less b/components/breadcrumb/style/rtl.less new file mode 100644 index 000000000..c1141c993 --- /dev/null +++ b/components/breadcrumb/style/rtl.less @@ -0,0 +1,29 @@ +.@{breadcrumb-prefix-cls} { + &-rtl { + .clearfix(); + direction: rtl; + + > span { + float: right; + } + } + + &-link { + > .@{iconfont-css-prefix} + span, + > .@{iconfont-css-prefix} + a { + .@{breadcrumb-prefix-cls}-rtl & { + margin-right: 4px; + margin-left: 0; + } + } + } + + &-overlay-link { + > .@{iconfont-css-prefix} { + .@{breadcrumb-prefix-cls}-rtl & { + margin-right: 4px; + margin-left: 0; + } + } + } +} diff --git a/components/vc-mentions/src/DropdownMenu.jsx b/components/vc-mentions/src/DropdownMenu.jsx index b525e48fa..d7934e202 100644 --- a/components/vc-mentions/src/DropdownMenu.jsx +++ b/components/vc-mentions/src/DropdownMenu.jsx @@ -1,10 +1,8 @@ -import Menu from '../../menu'; +import Menu, { Item as MenuItem } from '../../menu'; import PropTypes from '../../_util/vue-types'; import { OptionProps } from './Option'; import { inject } from 'vue'; -const MenuItem = Menu.Item; - function noop() {} export default { name: 'DropdownMenu', diff --git a/examples/index.js b/examples/index.js index 2eac35440..0273e0218 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,6 +1,7 @@ import '@babel/polyfill'; import 'ant-design-vue/style'; import { createApp, version } from 'vue'; +import { createRouter, createWebHistory } from 'vue-router'; import App from './App.vue'; import antd from 'ant-design-vue/index.ts'; @@ -9,9 +10,16 @@ console.log('Vue version: ', version); const basic = (_, { slots }) => { return slots && slots.default && slots.default(); }; + +const router = createRouter({ + history: createWebHistory(), + fallback: false, + routes: [], +}); const app = createApp(App); +app.use(router); app - .component('demo-sort', basic) + .component('DemoSort', basic) .component('md', basic) .component('api', basic) .component('CN', basic) diff --git a/v2-doc b/v2-doc index 6819090fb..0f6d531d0 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 6819090fbcc94b248bc761d5f26162f29c04b2ef +Subproject commit 0f6d531d088d5283250c8cec1c7e8be0e0d36a36 From 617e534fda2ae6d468b5e9d3eb43370f8a4b0000 Mon Sep 17 00:00:00 2001 From: tanjinzhou <415800467@qq.com> Date: Wed, 9 Jun 2021 10:31:56 +0800 Subject: [PATCH 02/19] fix: form-item class not work --- components/form/FormItem.tsx | 44 +++--------------------------------- v2-doc | 2 +- 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/components/form/FormItem.tsx b/components/form/FormItem.tsx index 4a43b43e1..4e78d2be5 100644 --- a/components/form/FormItem.tsx +++ b/components/form/FormItem.tsx @@ -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 }) { warning(props.prop === undefined, `\`prop\` is deprecated. Please use \`name\` instead.`); const eventKey = `form-item-${++indexGuid}`; const { prefixCls } = useConfigInject('form', props); @@ -336,9 +336,11 @@ export default defineComponent({ } return ( @@ -370,44 +372,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)]); - // }, }); diff --git a/v2-doc b/v2-doc index 0f6d531d0..6819090fb 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 0f6d531d088d5283250c8cec1c7e8be0e0d36a36 +Subproject commit 6819090fbcc94b248bc761d5f26162f29c04b2ef From a2e5d966e1c45389690ac0fc3409d20790cbc75f Mon Sep 17 00:00:00 2001 From: tanjinzhou <415800467@qq.com> Date: Wed, 9 Jun 2021 11:21:49 +0800 Subject: [PATCH 03/19] fix: col error when not in row --- components/grid/context.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/grid/context.ts b/components/grid/context.ts index 38fb8a174..a50be1857 100644 --- a/components/grid/context.ts +++ b/components/grid/context.ts @@ -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 }; From 791ae38bf8f431dc3ff90f16a3cb64066c18905f Mon Sep 17 00:00:00 2001 From: tanjinzhou <415800467@qq.com> Date: Wed, 9 Jun 2021 11:33:08 +0800 Subject: [PATCH 04/19] fix: form-item expose some method #4183 --- components/form/FormItem.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/form/FormItem.tsx b/components/form/FormItem.tsx index 4e78d2be5..4b526b9e7 100644 --- a/components/form/FormItem.tsx +++ b/components/form/FormItem.tsx @@ -108,7 +108,7 @@ export default defineComponent({ __ANT_NEW_FORM_ITEM: true, props: formItemProps, slots: ['help', 'label', 'extra'], - setup(props, { slots, attrs }) { + 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, From 6ae707edf508a9c5e8dca7dacf1410de5251bcf8 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 9 Jun 2021 21:55:15 +0800 Subject: [PATCH 05/19] perf: menu --- components/menu/src/Menu.tsx | 2 +- components/menu/src/SubMenu.tsx | 4 ++-- v2-doc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/menu/src/Menu.tsx b/components/menu/src/Menu.tsx index 5dd595ebe..6697e48fe 100644 --- a/components/menu/src/Menu.tsx +++ b/components/menu/src/Menu.tsx @@ -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, diff --git a/components/menu/src/SubMenu.tsx b/components/menu/src/SubMenu.tsx index a641ad69b..be7292cad 100644 --- a/components/menu/src/SubMenu.tsx +++ b/components/menu/src/SubMenu.tsx @@ -260,13 +260,13 @@ export default defineComponent({ ); - if (!overflowDisabled.value) { + if (!overflowDisabled.value && mode.value !== 'inline') { const triggerMode = triggerModeRef.value; titleNode = ( Date: Wed, 9 Jun 2021 22:30:32 +0800 Subject: [PATCH 06/19] fix: image type error #4185 --- components/image/index.tsx | 15 +++++++++------ components/vc-image/src/Image.tsx | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/components/image/index.tsx b/components/image/index.tsx index b9110c8a8..670ffa358 100644 --- a/components/image/index.tsx +++ b/components/image/index.tsx @@ -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 & Omit +>; +const Image = defineComponent({ 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; diff --git a/components/vc-image/src/Image.tsx b/components/vc-image/src/Image.tsx index 3a5020aee..d4012a22b 100644 --- a/components/vc-image/src/Image.tsx +++ b/components/vc-image/src/Image.tsx @@ -36,7 +36,7 @@ export interface ImagePropsType extends Omit props.prefixCls); From 02e134f81d7cb7ac3db7aacda80f34850aaa3841 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 9 Jun 2021 22:50:40 +0800 Subject: [PATCH 07/19] refactor: tag --- components/tag/CheckableTag.tsx | 22 ++++++++++----------- components/tag/index.tsx | 34 ++++++++++++++++----------------- components/tag/style/index.less | 20 ++++++++----------- components/tag/style/rtl.less | 28 +++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 40 deletions(-) create mode 100644 components/tag/style/rtl.less diff --git a/components/tag/CheckableTag.tsx b/components/tag/CheckableTag.tsx index a4f197d54..36a6602e2 100644 --- a/components/tag/CheckableTag.tsx +++ b/components/tag/CheckableTag.tsx @@ -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 ( - + {slots.default?.()} ); diff --git a/components/tag/index.tsx b/components/tag/index.tsx index f0c24c43a..bdd3a9a5c 100644 --- a/components/tag/index.tsx +++ b/components/tag/index.tsx @@ -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 = ( - + {kids} {renderCloseIcon()} diff --git a/components/tag/style/index.less b/components/tag/style/index.less index f4945f05f..aabb7029e 100644 --- a/components/tag/style/index.less +++ b/components/tag/style/index.less @@ -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'; diff --git a/components/tag/style/rtl.less b/components/tag/style/rtl.less new file mode 100644 index 000000000..af7d0dfc5 --- /dev/null +++ b/components/tag/style/rtl.less @@ -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; + } + } +} From 74b66ecbfb7e3af569d0f2d61650518032aa9aaa Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 10 Jun 2021 21:39:50 +0800 Subject: [PATCH 08/19] fix: select class not update #4194 --- components/select/index.tsx | 203 ++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 113 deletions(-) diff --git a/components/select/index.tsx b/components/select/index.tsx index c6a9f10fc..30c2a8bc7 100644 --- a/components/select/index.tsx +++ b/components/select/index.tsx @@ -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,122 +103,100 @@ 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( - { - [`${prefixCls.value}-lg`]: props.size === 'large', - [`${prefixCls.value}-sm`]: props.size === 'small', - [`${prefixCls.value}-rtl`]: props.direction === 'rtl', - [`${prefixCls.value}-borderless`]: !props.bordered, - }, - attrs.class, - ), + classNames({ + [`${prefixCls.value}-lg`]: props.size === 'large', + [`${prefixCls.value}-sm`]: props.size === 'small', + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + [`${prefixCls.value}-borderless`]: !props.bordered, + }), ); 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; - const { - notFoundContent, - listHeight = 256, - listItemHeight = 24, - getPopupContainer, - dropdownClassName, - direction, - virtual, - dropdownMatchSelectWidth, - } = props; - - const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider; - - const isMultiple = mode === 'multiple' || mode === 'tags'; - - // ===================== Empty ===================== - let mergedNotFound: VNodeChild; - if (notFoundContent !== undefined) { - mergedNotFound = notFoundContent; - } else if (slots.notFoundContent) { - mergedNotFound = slots.notFoundContent(); - } else if (mode === 'combobox') { - mergedNotFound = null; - } else { - mergedNotFound = renderEmpty('Select') as any; - } - - // ===================== Icons ===================== - const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons( - { - ...this.$props, - multiple: isMultiple, - prefixCls, - }, - slots, - ); - - const selectProps = omit(props, [ - 'prefixCls', - 'suffixIcon', - 'itemIcon', - 'removeIcon', - 'clearIcon', - 'size', - 'bordered', - ]) as any; - - const rcSelectRtlDropDownClassName = classNames(dropdownClassName, { - [`${prefixCls}-dropdown-${direction}`]: direction === 'rtl', + focus, }); - return ( - - {slots.default?.()} - - ); + return () => { + const { + notFoundContent, + listHeight = 256, + listItemHeight = 24, + getPopupContainer, + dropdownClassName, + virtual, + dropdownMatchSelectWidth, + } = props; + + const { renderEmpty, getPopupContainer: getContextPopupContainer } = configProvider; + + const isMultiple = mode.value === 'multiple' || mode.value === 'tags'; + + // ===================== Empty ===================== + let mergedNotFound: VNodeChild; + if (notFoundContent !== undefined) { + mergedNotFound = notFoundContent; + } else if (slots.notFoundContent) { + mergedNotFound = slots.notFoundContent(); + } else if (mode.value === 'combobox') { + mergedNotFound = null; + } else { + mergedNotFound = renderEmpty('Select') as any; + } + + // ===================== Icons ===================== + const { suffixIcon, itemIcon, removeIcon, clearIcon } = getIcons( + { + ...props, + multiple: isMultiple, + prefixCls: prefixCls.value, + }, + slots, + ); + + const selectProps = omit(props, [ + 'prefixCls', + 'suffixIcon', + 'itemIcon', + 'removeIcon', + 'clearIcon', + 'size', + 'bordered', + ]); + + const rcSelectRtlDropDownClassName = classNames(dropdownClassName, { + [`${prefixCls.value}-dropdown-${direction.value}`]: direction.value === 'rtl', + }); + return ( + + {slots.default?.()} + + ); + }; }, }); /* istanbul ignore next */ From 980ca7f4ff606ed0cd1e6fab5760ef475bc6557f Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 10 Jun 2021 21:54:03 +0800 Subject: [PATCH 09/19] fix: menu divider class error #4195 --- components/menu/src/Divider.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/components/menu/src/Divider.tsx b/components/menu/src/Divider.tsx index bdd8409f3..99357752e 100644 --- a/components/menu/src/Divider.tsx +++ b/components/menu/src/Divider.tsx @@ -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
  • ; + return
  • ; }; }, }); From fc6c358e9ad241d57a491f5465d5119379a9d116 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 10 Jun 2021 22:17:41 +0800 Subject: [PATCH 10/19] fix: modal motion lose #4191 --- components/style/core/motion/fade.less | 10 ++++++++++ components/style/core/motion/move.less | 10 ++++++++++ components/style/core/motion/zoom.less | 14 ++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/components/style/core/motion/fade.less b/components/style/core/motion/fade.less index c703b5973..ac70f2d56 100644 --- a/components/style/core/motion/fade.less +++ b/components/style/core/motion/fade.less @@ -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); diff --git a/components/style/core/motion/move.less b/components/style/core/motion/move.less index e7972d77a..68ae3eff0 100644 --- a/components/style/core/motion/move.less +++ b/components/style/core/motion/move.less @@ -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); diff --git a/components/style/core/motion/zoom.less b/components/style/core/motion/zoom.less index 8c2c57aca..39c447b90 100644 --- a/components/style/core/motion/zoom.less +++ b/components/style/core/motion/zoom.less @@ -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 From 7624645d58eb9276de6e8ee0014ac5c5e3a3d1a6 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 10 Jun 2021 23:04:57 +0800 Subject: [PATCH 11/19] refactor: statistic --- components/statistic/Countdown.tsx | 115 +++++++++++++------------- components/statistic/Statistic.tsx | 79 +++++++++--------- components/statistic/style/index.less | 10 +-- components/statistic/style/rtl.less | 21 +++++ components/statistic/utils.ts | 14 ++-- 5 files changed, 129 insertions(+), 110 deletions(-) create mode 100644 components/statistic/style/rtl.less diff --git a/components/statistic/Countdown.tsx b/components/statistic/Countdown.tsx index ce4be77ae..55b386d29 100644 --- a/components/statistic/Countdown.tsx +++ b/components/statistic/Countdown.tsx @@ -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,76 @@ 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(); + 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()); + } }, REFRESH_INTERVAL); - }, + }; - stopTimer() { - const { value } = this.$props; - if (this.countdownId) { - clearInterval(this.countdownId); - this.countdownId = undefined; + const stopTimer = () => { + const { value } = props; + if (countdownId) { + 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() { - return ( - - ); + const valueRenderHtml = (node: any) => node; + onMounted(() => { + syncTimer(); + }); + onUpdated(() => { + syncTimer(); + }); + onBeforeUnmount(() => { + stopTimer(); + }); + return () => { + return ( + + ); + }; }, }); diff --git a/components/statistic/Statistic.tsx b/components/statistic/Statistic.tsx index d82faba80..b323579b5 100644 --- a/components/statistic/Statistic.tsx +++ b/components/statistic/Statistic.tsx @@ -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, - }; - // data-for-update just for update component - // https://github.com/vueComponent/ant-design-vue/pull/3170 - let valueNode = ; - if (valueRender) { - valueNode = valueRender(valueNode); - } - - return ( -
    - {title &&
    {title}
    } -
    - {prefix && {prefix}} - {valueNode} - {suffix && {suffix}} + 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 = ( + + ); + if (valueRender) { + valueNode = valueRender(valueNode); + } + return ( +
    + {title &&
    {title}
    } + +
    + {prefix && {prefix}} + {valueNode} + {suffix && {suffix}} +
    +
    -
    - ); + ); + }; }, }); diff --git a/components/statistic/style/index.less b/components/statistic/style/index.less index f236fdb3f..17d023ffa 100644 --- a/components/statistic/style/index.less +++ b/components/statistic/style/index.less @@ -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'; diff --git a/components/statistic/style/rtl.less b/components/statistic/style/rtl.less new file mode 100644 index 000000000..93e264a35 --- /dev/null +++ b/components/statistic/style/rtl.less @@ -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; + } + } + } +} diff --git a/components/statistic/utils.ts b/components/statistic/utils.ts index e0f69618e..f335e1f72 100644 --- a/components/statistic/utils.ts +++ b/components/statistic/utils.ts @@ -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); } From ce6f44f918b3836eaccd5d0d68df9e684bdebf74 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 11 Jun 2021 21:14:16 +0800 Subject: [PATCH 12/19] feat(dropdown): allow pass string for prop trigger (#4199) --- components/dropdown/getDropdownProps.ts | 6 ++++-- components/vc-dropdown/src/Dropdown.jsx | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/dropdown/getDropdownProps.ts b/components/dropdown/getDropdownProps.ts index 8495d308d..8b0442284 100644 --- a/components/dropdown/getDropdownProps.ts +++ b/components/dropdown/getDropdownProps.ts @@ -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, diff --git a/components/vc-dropdown/src/Dropdown.jsx b/components/vc-dropdown/src/Dropdown.jsx index 79faa73a9..318b67032 100644 --- a/components/vc-dropdown/src/Dropdown.jsx +++ b/components/vc-dropdown/src/Dropdown.jsx @@ -25,7 +25,9 @@ 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([]), From 0cf37b3f8ee3ddc5f0dd77c616a95e897f07b84f Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 11 Jun 2021 21:48:12 +0800 Subject: [PATCH 13/19] fix: dropdown click close for contextmenu #4198 --- components/dropdown/dropdown.tsx | 2 +- components/vc-dropdown/src/Dropdown.jsx | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/components/dropdown/dropdown.tsx b/components/dropdown/dropdown.tsx index 5424150b9..143ed3d31 100644 --- a/components/dropdown/dropdown.tsx +++ b/components/dropdown/dropdown.tsx @@ -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; diff --git a/components/vc-dropdown/src/Dropdown.jsx b/components/vc-dropdown/src/Dropdown.jsx index 318b67032..b80da0eeb 100644 --- a/components/vc-dropdown/src/Dropdown.jsx +++ b/components/vc-dropdown/src/Dropdown.jsx @@ -29,8 +29,8 @@ export default defineComponent({ '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), @@ -177,7 +177,6 @@ export default defineComponent({ if (!triggerHideAction && trigger.indexOf('contextmenu') !== -1) { triggerHideAction = ['click']; } - const triggerProps = { ...otherProps, prefixCls, From 0dcdd92ba2aaa18945e8acee5575ba9c0a0a40b5 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 11 Jun 2021 22:06:25 +0800 Subject: [PATCH 14/19] release 2.2.0-beta.3 --- CHANGELOG.en-US.md | 15 +++++++++++++++ CHANGELOG.zh-CN.md | 15 +++++++++++++++ components/form/ErrorList.tsx | 2 +- components/statistic/Countdown.tsx | 2 +- components/vc-overflow/Overflow.tsx | 4 ++-- package.json | 2 +- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 81084546d..9a788a0e7 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -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` diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 9305bf8eb..be9f72d0b 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -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` diff --git a/components/form/ErrorList.tsx b/components/form/ErrorList.tsx index d9b73e951..c7d26b754 100644 --- a/components/form/ErrorList.tsx +++ b/components/form/ErrorList.tsx @@ -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); diff --git a/components/statistic/Countdown.tsx b/components/statistic/Countdown.tsx index 55b386d29..3c328d109 100644 --- a/components/statistic/Countdown.tsx +++ b/components/statistic/Countdown.tsx @@ -43,7 +43,7 @@ export default defineComponent({ const stopTimer = () => { const { value } = props; - if (countdownId) { + if (countdownId.value) { clearInterval(countdownId.value); countdownId.value = undefined; diff --git a/components/vc-overflow/Overflow.tsx b/components/vc-overflow/Overflow.tsx index 54659d237..09ed88e6b 100644 --- a/components/vc-overflow/Overflow.tsx +++ b/components/vc-overflow/Overflow.tsx @@ -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({ Date: Fri, 18 Jun 2021 16:09:03 +0800 Subject: [PATCH 15/19] fix(countdown): onFinish cannot trigger (#4222) --- components/statistic/Countdown.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/statistic/Countdown.tsx b/components/statistic/Countdown.tsx index 3c328d109..97adb7cf8 100644 --- a/components/statistic/Countdown.tsx +++ b/components/statistic/Countdown.tsx @@ -38,6 +38,7 @@ export default defineComponent({ if (timestamp > Date.now()) { emit('change', timestamp - Date.now()); } + syncTimer(); }, REFRESH_INTERVAL); }; From edca829e09f933110a010c9d28d5ba9656e25dae Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 18 Jun 2021 16:41:46 +0800 Subject: [PATCH 16/19] fix: config provider error when vue3.1.1 #4225 --- components/config-provider/index.tsx | 10 +++++++--- v2-doc | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index e70086852..8fb128efc 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -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); diff --git a/v2-doc b/v2-doc index 0f6d531d0..082898c01 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 0f6d531d088d5283250c8cec1c7e8be0e0d36a36 +Subproject commit 082898c0188de1c83f27696487bd6d4db7f29749 From f9e6217bc99a43fdbc27cd79de23f98265fc6e27 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Fri, 18 Jun 2021 17:12:57 +0800 Subject: [PATCH 17/19] fix: dropdown submenu error #4205 --- components/vc-align/Align.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vc-align/Align.tsx b/components/vc-align/Align.tsx index 7a3b5c4cf..85173fa13 100644 --- a/components/vc-align/Align.tsx +++ b/components/vc-align/Align.tsx @@ -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]; }; From a1a8b799d029fd1144b6da80c8d55e34a93bfbbf Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sat, 19 Jun 2021 15:29:07 +0800 Subject: [PATCH 18/19] fix: col type error #4226 --- components/grid/Col.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/grid/Col.tsx b/components/grid/Col.tsx index 1875b9e0f..bebe9bb2d 100644 --- a/components/grid/Col.tsx +++ b/components/grid/Col.tsx @@ -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({ span: stringOrNumber, order: stringOrNumber, offset: stringOrNumber, From 700d04badc199da64a0423b0c5904e60444edde0 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sat, 19 Jun 2021 15:49:39 +0800 Subject: [PATCH 19/19] fix: typography not trigger onEnd in blur #4227 --- components/typography/Editable.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/typography/Editable.tsx b/components/typography/Editable.tsx index a46557d6f..2f7c0ceb0 100644 --- a/components/typography/Editable.tsx +++ b/components/typography/Editable.tsx @@ -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() {