From 203e42382f548347519d996a629fd9553642ad35 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Tue, 6 Jul 2021 21:58:05 +0800 Subject: [PATCH 01/28] fix: timepicker error border not show #4331 --- .../__snapshots__/index.test.js.snap | 102 +++++++++--------- .../__snapshots__/index.test.js.snap | 2 +- components/time-picker/index.tsx | 4 + components/vc-time-picker/TimePicker.jsx | 4 +- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap index e7de8ecd50..c002bb2261 100644 --- a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap +++ b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap @@ -609,7 +609,7 @@ exports[`Locale Provider should display the text as ar 1`] = `
- + ~ @@ -1151,7 +1151,7 @@ exports[`Locale Provider should display the text as bg 1`] = `
- + ~ @@ -1693,7 +1693,7 @@ exports[`Locale Provider should display the text as ca 1`] = `
- + ~ @@ -2235,7 +2235,7 @@ exports[`Locale Provider should display the text as cs 1`] = `
- + ~ @@ -2777,7 +2777,7 @@ exports[`Locale Provider should display the text as da 1`] = `
- + ~ @@ -3319,7 +3319,7 @@ exports[`Locale Provider should display the text as de 1`] = `
- + ~ @@ -3861,7 +3861,7 @@ exports[`Locale Provider should display the text as el 1`] = `
- + ~ @@ -4403,7 +4403,7 @@ exports[`Locale Provider should display the text as en 1`] = `
- + ~ @@ -4945,7 +4945,7 @@ exports[`Locale Provider should display the text as en-gb 1`] = `
- + ~ @@ -5487,7 +5487,7 @@ exports[`Locale Provider should display the text as es 1`] = `
- + ~ @@ -6029,7 +6029,7 @@ exports[`Locale Provider should display the text as et 1`] = `
- + ~ @@ -6571,7 +6571,7 @@ exports[`Locale Provider should display the text as fa 1`] = `
- + ~ @@ -7113,7 +7113,7 @@ exports[`Locale Provider should display the text as fi 1`] = `
- + ~ @@ -7655,7 +7655,7 @@ exports[`Locale Provider should display the text as fr 1`] = `
- + ~ @@ -8197,7 +8197,7 @@ exports[`Locale Provider should display the text as fr 2`] = `
- + ~ @@ -8739,7 +8739,7 @@ exports[`Locale Provider should display the text as he 1`] = `
- + ~ @@ -9281,7 +9281,7 @@ exports[`Locale Provider should display the text as hi 1`] = `
- + ~ @@ -9823,7 +9823,7 @@ exports[`Locale Provider should display the text as hr 1`] = `
- + ~ @@ -10365,7 +10365,7 @@ exports[`Locale Provider should display the text as hu 1`] = `
- + ~ @@ -10907,7 +10907,7 @@ exports[`Locale Provider should display the text as hy 1`] = `
- + ~ @@ -11449,7 +11449,7 @@ exports[`Locale Provider should display the text as id 1`] = `
- + ~ @@ -11991,7 +11991,7 @@ exports[`Locale Provider should display the text as is 1`] = `
- + ~ @@ -12533,7 +12533,7 @@ exports[`Locale Provider should display the text as it 1`] = `
- + ~ @@ -13075,7 +13075,7 @@ exports[`Locale Provider should display the text as ja 1`] = `
- + ~ @@ -13617,7 +13617,7 @@ exports[`Locale Provider should display the text as kn 1`] = `
- + ~ @@ -14159,7 +14159,7 @@ exports[`Locale Provider should display the text as ko 1`] = `
- + ~ @@ -14701,7 +14701,7 @@ exports[`Locale Provider should display the text as ku-iq 1`] = `
- + ~ @@ -15243,7 +15243,7 @@ exports[`Locale Provider should display the text as lv 1`] = `
- + ~ @@ -15785,7 +15785,7 @@ exports[`Locale Provider should display the text as mk 1`] = `
- + ~ @@ -16327,7 +16327,7 @@ exports[`Locale Provider should display the text as mn-mn 1`] = `
- + ~ @@ -16869,7 +16869,7 @@ exports[`Locale Provider should display the text as ms-my 1`] = `
- + ~ @@ -17411,7 +17411,7 @@ exports[`Locale Provider should display the text as nb 1`] = `
- + ~ @@ -17953,7 +17953,7 @@ exports[`Locale Provider should display the text as ne-np 1`] = `
- + ~ @@ -18495,7 +18495,7 @@ exports[`Locale Provider should display the text as nl 1`] = `
- + ~ @@ -19037,7 +19037,7 @@ exports[`Locale Provider should display the text as nl-be 1`] = `
- + ~ @@ -19579,7 +19579,7 @@ exports[`Locale Provider should display the text as pl 1`] = `
- + ~ @@ -20121,7 +20121,7 @@ exports[`Locale Provider should display the text as pt 1`] = `
- + ~ @@ -20663,7 +20663,7 @@ exports[`Locale Provider should display the text as pt-br 1`] = `
- + ~ @@ -21205,7 +21205,7 @@ exports[`Locale Provider should display the text as ro 1`] = `
- + ~ @@ -21747,7 +21747,7 @@ exports[`Locale Provider should display the text as ru 1`] = `
- + ~ @@ -22289,7 +22289,7 @@ exports[`Locale Provider should display the text as sk 1`] = `
- + ~ @@ -22831,7 +22831,7 @@ exports[`Locale Provider should display the text as sl 1`] = `
- + ~ @@ -23373,7 +23373,7 @@ exports[`Locale Provider should display the text as sr 1`] = `
- + ~ @@ -23915,7 +23915,7 @@ exports[`Locale Provider should display the text as sv 1`] = `
- + ~ @@ -24457,7 +24457,7 @@ exports[`Locale Provider should display the text as ta 1`] = `
- + ~ @@ -24999,7 +24999,7 @@ exports[`Locale Provider should display the text as th 1`] = `
- + ~ @@ -25541,7 +25541,7 @@ exports[`Locale Provider should display the text as tr 1`] = `
- + ~ @@ -26083,7 +26083,7 @@ exports[`Locale Provider should display the text as uk 1`] = `
- + ~ @@ -26625,7 +26625,7 @@ exports[`Locale Provider should display the text as vi 1`] = `
- + ~ @@ -27167,7 +27167,7 @@ exports[`Locale Provider should display the text as zh-cn 1`] = `
- + ~ @@ -27709,7 +27709,7 @@ exports[`Locale Provider should display the text as zh-tw 1`] = `
- + ~ diff --git a/components/time-picker/__tests__/__snapshots__/index.test.js.snap b/components/time-picker/__tests__/__snapshots__/index.test.js.snap index efdd99b93a..20ee541245 100644 --- a/components/time-picker/__tests__/__snapshots__/index.test.js.snap +++ b/components/time-picker/__tests__/__snapshots__/index.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TimePicker not render clean icon when allowClear is false 1`] = ` - + `; diff --git a/components/time-picker/index.tsx b/components/time-picker/index.tsx index 4795619204..8103baf17d 100644 --- a/components/time-picker/index.tsx +++ b/components/time-picker/index.tsx @@ -20,6 +20,7 @@ import { TimeOrTimesType, } from '../_util/moment-util'; import { tuple, withInstall } from '../_util/type'; +import classNames from '../_util/classNames'; export function generateShowHourMinuteSecond(format: string) { // Ref: http://momentjs.com/docs/#/parsing/string-format/ @@ -213,6 +214,8 @@ const TimePicker = defineComponent({ const { prefixCls: customizePrefixCls, getPopupContainer, placeholder, size } = props; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('time-picker', customizePrefixCls); + const inputPrefixCls = getPrefixCls('input'); + const pickerInputClass = classNames(`${prefixCls}-input`, inputPrefixCls); const format = this.getDefaultFormat(); const pickerClassName = { @@ -236,6 +239,7 @@ const TimePicker = defineComponent({ ...this.$attrs, allowEmpty: this.getAllowClear(), prefixCls, + pickerInputClass, getPopupContainer: getPopupContainer || getContextPopupContainer, format, value: this.sValue, diff --git a/components/vc-time-picker/TimePicker.jsx b/components/vc-time-picker/TimePicker.jsx index 8f16b52be7..acf433302c 100644 --- a/components/vc-time-picker/TimePicker.jsx +++ b/components/vc-time-picker/TimePicker.jsx @@ -35,6 +35,7 @@ export default defineComponent({ return moment(); }, }, + pickerInputClass: String, inputReadOnly: PropTypes.looseBool, disabled: PropTypes.looseBool, allowEmpty: PropTypes.looseBool, @@ -346,6 +347,7 @@ export default defineComponent({ onFocus, onBlur, popupStyle, + pickerInputClass, } = this; const { class: className, style } = this.$attrs; const popupClassName = this.getPopupClassName(); @@ -368,7 +370,7 @@ export default defineComponent({ > Date: Wed, 7 Jul 2021 21:24:14 +0800 Subject: [PATCH 02/28] fix(UploadDragger): fix UploadDrager no export (#4334) --- components/components.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/components.ts b/components/components.ts index f02cb7ad89..e901d6a9a7 100644 --- a/components/components.ts +++ b/components/components.ts @@ -192,6 +192,6 @@ export { export type { UploadProps } from './upload'; -export { default as Upload } from './upload'; +export { default as Upload, UploadDragger } from './upload'; export { default as LocaleProvider } from './locale-provider'; From 427cf36eaa47f41e9f9e34231fb2d74db3a1abf8 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 7 Jul 2021 21:35:41 +0800 Subject: [PATCH 03/28] refactor(switch): support customize checked value #4329 (#4332) * refactor(switch): support customize checked value #4329 * test: add test case * refactor: update props name * refactor: update ts * refactor: optimize --- components/switch/__tests__/index.test.js | 28 +++++++++ components/switch/index.tsx | 74 ++++++++++++++--------- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/components/switch/__tests__/index.test.js b/components/switch/__tests__/index.test.js index 73061036fc..55980437b9 100644 --- a/components/switch/__tests__/index.test.js +++ b/components/switch/__tests__/index.test.js @@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest'; import { resetWarned } from '../../_util/warning'; import mountTest from '../../../tests/shared/mountTest'; import { ref } from 'vue'; +import { asyncExpect } from '@/tests/utils'; describe('Switch', () => { focusTest(Switch); @@ -42,4 +43,31 @@ describe('Switch', () => { ); errorSpy.mockRestore(); }); + + it('customize checked value should work', async () => { + resetWarned(); + const checked = ref(1); + const onUpdate = val => (checked.value = val); + const wrapper = mount({ + render() { + return ( + + ); + }, + }); + await asyncExpect(() => { + wrapper.find('button').trigger('click'); + }); + expect(checked.value).toBe(2); + + await asyncExpect(() => { + wrapper.find('button').trigger('click'); + }); + expect(checked.value).toBe(1); + }); }); diff --git a/components/switch/index.tsx b/components/switch/index.tsx index 4febcce829..647b3961f0 100644 --- a/components/switch/index.tsx +++ b/components/switch/index.tsx @@ -1,4 +1,4 @@ -import type { ExtractPropTypes } from 'vue'; +import type { ExtractPropTypes, PropType } from 'vue'; import { defineComponent, inject, @@ -19,23 +19,35 @@ import { tuple, withInstall } from '../_util/type'; import { getPropsSlot } from '../_util/props-util'; import Omit from 'omit.js'; -export const SwitchSizes = tuple('small', 'default', 'large'); +export const SwitchSizes = tuple('small', 'default'); const switchProps = { prefixCls: PropTypes.string, size: PropTypes.oneOf(SwitchSizes), disabled: PropTypes.looseBool, - checkedChildren: PropTypes.any, - unCheckedChildren: PropTypes.any, + checkedChildren: PropTypes.VNodeChild, + unCheckedChildren: PropTypes.VNodeChild, tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), autofocus: PropTypes.looseBool, loading: PropTypes.looseBool, - checked: PropTypes.looseBool, - onChange: PropTypes.func, - onClick: PropTypes.func, - onKeydown: PropTypes.func, - onMouseup: PropTypes.func, - 'onUpdate:checked': PropTypes.func, + checked: PropTypes.any, + checkedValue: PropTypes.any.def(true), + uncheckedValue: PropTypes.any.def(false), + onChange: { + type: Function as PropType<(checked: any, e: Event) => void>, + }, + onClick: { + type: Function as PropType<(checked: any, e: Event) => void>, + }, + onKeydown: { + type: Function as PropType<(e: Event) => void>, + }, + onMouseup: { + type: Function as PropType<(e: Event) => void>, + }, + 'onUpdate:checked': { + type: Function as PropType<(checked: any) => void>, + }, }; export type SwitchProps = Partial>; @@ -46,7 +58,7 @@ const Switch = defineComponent({ inheritAttrs: false, props: switchProps, emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'], - setup(props: SwitchProps, { attrs, slots, expose, emit }) { + setup(props, { attrs, slots, expose, emit }) { onBeforeMount(() => { warning( !('defaultChecked' in attrs), @@ -59,12 +71,13 @@ const Switch = defineComponent({ '`value` is not validate prop, do you mean `checked`?', ); }); - const checked = ref(props.checked !== undefined ? !!props.checked : !!attrs.defaultChecked); + const checked = ref(props.checked !== undefined ? props.checked : attrs.defaultChecked); + const checkedStatus = computed(() => checked.value === props.checkedValue); watch( () => props.checked, () => { - checked.value = !!props.checked; + checked.value = props.checked; }, ); @@ -92,29 +105,26 @@ const Switch = defineComponent({ }); }); - const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => { + const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => { if (props.disabled) { return; } - if (props.checked === undefined) { - checked.value = check; - } emit('update:checked', check); emit('change', check, e); }; const handleClick = (e: MouseEvent) => { focus(); - const newChecked = !checked.value; + const newChecked = checkedStatus.value ? props.uncheckedValue : props.checkedValue; setChecked(newChecked, e); emit('click', newChecked, e); }; const handleKeyDown = (e: KeyboardEvent) => { if (e.keyCode === KeyCode.LEFT) { - setChecked(false, e); + setChecked(props.uncheckedValue, e); } else if (e.keyCode === KeyCode.RIGHT) { - setChecked(true, e); + setChecked(props.checkedValue, e); } emit('keydown', e); }; @@ -123,6 +133,13 @@ const Switch = defineComponent({ refSwitchNode.value?.blur(); emit('mouseup', e); }; + + const classNames = computed(() => ({ + [`${prefixCls.value}-small`]: props.size === 'small', + [`${prefixCls.value}-loading`]: props.loading, + [`${prefixCls.value}-checked`]: checkedStatus.value, + [`${prefixCls.value}-disabled`]: props.disabled, + })); return () => ( `; diff --git a/components/switch/__tests__/index.test.js b/components/switch/__tests__/index.test.js index 55980437b9..5fb8e501ce 100644 --- a/components/switch/__tests__/index.test.js +++ b/components/switch/__tests__/index.test.js @@ -54,7 +54,7 @@ describe('Switch', () => { ); diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap index 15eaac19f3..a203e4711b 100644 --- a/tests/__snapshots__/index.test.js.snap +++ b/tests/__snapshots__/index.test.js.snap @@ -126,6 +126,7 @@ Array [ "TypographyText", "TypographyTitle", "Upload", + "UploadDragger", "LocaleProvider", ] `; From cf2ffa2bd943ecaa839cf656f7701f559ec4eb59 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 7 Jul 2021 22:09:09 +0800 Subject: [PATCH 06/28] feat: udpate switch ts --- components/switch/index.tsx | 22 ++++++++++++++-------- v2-doc | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/components/switch/index.tsx b/components/switch/index.tsx index cfd7e25917..0d038946a2 100644 --- a/components/switch/index.tsx +++ b/components/switch/index.tsx @@ -11,7 +11,7 @@ import Omit from 'omit.js'; import useConfigInject from '../_util/hooks/useConfigInject'; export const SwitchSizes = tuple('small', 'default'); - +type CheckedType = boolean | string | number; const switchProps = { prefixCls: PropTypes.string, size: PropTypes.oneOf(SwitchSizes), @@ -21,14 +21,20 @@ const switchProps = { tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), autofocus: PropTypes.looseBool, loading: PropTypes.looseBool, - checked: PropTypes.any, - checkedValue: PropTypes.any.def(true), - unCheckedValue: PropTypes.any.def(false), + checked: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.looseBool]), + checkedValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.looseBool]).def( + true, + ), + unCheckedValue: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.looseBool, + ]).def(false), onChange: { - type: Function as PropType<(checked: any, e: Event) => void>, + type: Function as PropType<(checked: CheckedType, e: Event) => void>, }, onClick: { - type: Function as PropType<(checked: any, e: Event) => void>, + type: Function as PropType<(checked: CheckedType, e: Event) => void>, }, onKeydown: { type: Function as PropType<(e: Event) => void>, @@ -37,7 +43,7 @@ const switchProps = { type: Function as PropType<(e: Event) => void>, }, 'onUpdate:checked': { - type: Function as PropType<(checked: any) => void>, + type: Function as PropType<(checked: CheckedType) => void>, }, }; @@ -92,7 +98,7 @@ const Switch = defineComponent({ }); }); - const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => { + const setChecked = (check: CheckedType, e: MouseEvent | KeyboardEvent) => { if (props.disabled) { return; } diff --git a/v2-doc b/v2-doc index 89612874e4..b9934e0de1 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 89612874e476dc788711cdaedfd037b9497e5e78 +Subproject commit b9934e0de1052a73978cdb9fb401fa7b5eaf8ecc From 60aeec7033b226efb424c084502ee8db4f643b37 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 7 Jul 2021 22:16:13 +0800 Subject: [PATCH 07/28] docs: remove ie11 --- CHANGELOG.en-US.md | 1 - CHANGELOG.zh-CN.md | 1 - README-zh_CN.md | 2 +- README.md | 2 +- v2-doc | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 43818f861f..8903181964 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -437,7 +437,6 @@ ### Compatibility adjustment -- The minimum supported version of IE is IE 11. - The minimum supported version of Vue is Vue 3.0. #### Adjusted API diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 80f6cda2de..72155c3eb3 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -440,7 +440,6 @@ ### 兼容性调整 -- IE 最低支持版本为 IE 11。 - Vue 最低支持版本为 Vue 3.0。 #### 调整的 API diff --git a/README-zh_CN.md b/README-zh_CN.md index f1af042d86..b45bf577f3 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -28,7 +28,7 @@ An enterprise-class UI components based on Ant Design and Vue 3. ## 支持环境 -- 现代浏览器和 IE11 及以上。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性)) +- 现代浏览器。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性)) - 支持服务端渲染。 - [Electron](https://electronjs.org/) - 支持 Vue 2 和 Vue 3 diff --git a/README.md b/README.md index 437cb6931a..744b89e363 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ English | [简体中文](./README-zh_CN.md) ## Environment Support -- Modern browsers and Internet Explorer 11+. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility)) +- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility)) - Server-side Rendering - Support Vue 2 & Vue 3 - [Electron](https://electronjs.org/) diff --git a/v2-doc b/v2-doc index b9934e0de1..d965edbb90 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit b9934e0de1052a73978cdb9fb401fa7b5eaf8ecc +Subproject commit d965edbb9045771095acf91489145e13c975cb51 From 1152e8cd71cadf9e8fb4797916adca20c0e35974 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 8 Jul 2021 11:36:32 +0800 Subject: [PATCH 08/28] fix: tree-select throw error when use slot title --- components/vc-tree-select/src/Selector/SingleSelector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vc-tree-select/src/Selector/SingleSelector.jsx b/components/vc-tree-select/src/Selector/SingleSelector.jsx index 1f1ab4e0a4..cff2577332 100644 --- a/components/vc-tree-select/src/Selector/SingleSelector.jsx +++ b/components/vc-tree-select/src/Selector/SingleSelector.jsx @@ -64,7 +64,7 @@ const SingleSelector = { if (selectorValueList.length && !this.mirrorSearchValue) { const { label, value } = selectorValueList[0]; selectedValueNodes.push( - + {label || value} , ); From fb94726a1e1a7a7e4395facfc224ed3aa420a5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BE=E6=96=AF=E7=89=B9=E6=B4=9B?= Date: Sun, 11 Jul 2021 10:00:52 +0800 Subject: [PATCH 09/28] fix: TypeScript definition of Table interface for typescript 4.3.5 (#4353) * fix type for typescript 4.3.5 * Update interface.ts close #4296 --- components/table/interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/interface.ts b/components/table/interface.ts index 1784ecf21c..fe56d51334 100644 --- a/components/table/interface.ts +++ b/components/table/interface.ts @@ -60,7 +60,7 @@ export type ColumnProps = Partial> & { filterIcon?: string; filterDropdown?: string; customRender?: string; - [key: string]: string; + [key: string]: string | undefined; }; }; From c20c1f2a32664c12f9f5320f97503868b575c7e3 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sun, 11 Jul 2021 14:13:07 +0800 Subject: [PATCH 10/28] fix: dropdown submenu style error #4351 close #4351 --- components/dropdown/dropdown.tsx | 2 +- components/dropdown/style/index.less | 4 +++- components/menu/src/Menu.tsx | 4 ++++ components/menu/src/SubMenu.tsx | 9 ++++++--- components/menu/src/hooks/useMenuContext.ts | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/dropdown/dropdown.tsx b/components/dropdown/dropdown.tsx index ffc160b8f3..3021a4d7c9 100644 --- a/components/dropdown/dropdown.tsx +++ b/components/dropdown/dropdown.tsx @@ -63,7 +63,7 @@ const Dropdown = defineComponent({ // menu should be focusable in dropdown defaultly const overlayProps = overlayNode && getPropsData(overlayNode); const { selectable = false, focusable = true } = (overlayProps || {}) as any; - const expandIcon = ( + const expandIcon = () => ( diff --git a/components/dropdown/style/index.less b/components/dropdown/style/index.less index 835adb3219..f61c38aba4 100644 --- a/components/dropdown/style/index.less +++ b/components/dropdown/style/index.less @@ -68,6 +68,9 @@ &-submenu-popup { position: absolute; z-index: @zindex-dropdown; + background: transparent; + box-shadow: none; + transform-origin: 0 0; > .@{dropdown-prefix-cls}-menu { transform-origin: 0 0; @@ -81,7 +84,6 @@ ul { margin-right: 0.3em; margin-left: 0.3em; - padding: 0; } } diff --git a/components/menu/src/Menu.tsx b/components/menu/src/Menu.tsx index 45cd20b190..1f94f14538 100644 --- a/components/menu/src/Menu.tsx +++ b/components/menu/src/Menu.tsx @@ -49,6 +49,8 @@ export const menuProps = { triggerSubMenuAction: { type: String as PropType, default: 'hover' }, getPopupContainer: Function as PropType<(node: HTMLElement) => HTMLElement>, + + expandIcon: Function as PropType<(p?: { isOpen: boolean; [key: string]: any }) => any>, }; export type MenuProps = Partial>; @@ -66,6 +68,7 @@ export default defineComponent({ 'click', 'update:activeKey', ], + slots: ['expandIcon'], setup(props, { slots, emit }) { const { prefixCls, direction } = useConfigInject('menu', props); const store = ref>({}); @@ -371,6 +374,7 @@ export default defineComponent({ unRegisterMenuInfo, selectedSubMenuEventKeys, isRootMenu: true, + expandIcon: props.expandIcon || slots.expandIcon, }); return () => { const childList = flattenChildren(slots.default?.()); diff --git a/components/menu/src/SubMenu.tsx b/components/menu/src/SubMenu.tsx index 762da791b1..0aadbd0c76 100644 --- a/components/menu/src/SubMenu.tsx +++ b/components/menu/src/SubMenu.tsx @@ -24,6 +24,7 @@ const subMenuProps = { popupOffset: Array as PropType, internalPopupClose: Boolean, eventKey: String, + expandIcon: Function as PropType<(p?: { isOpen: boolean; [key: string]: any }) => any>, }; export type SubMenuProps = Partial>; @@ -32,7 +33,7 @@ export default defineComponent({ name: 'ASubMenu', inheritAttrs: false, props: subMenuProps, - slots: ['icon', 'title'], + slots: ['icon', 'title', 'expandIcon'], emits: ['titleClick', 'mouseenter', 'mouseleave'], setup(props, { slots, attrs, emit }) { useProvideFirstLevel(false); @@ -84,6 +85,7 @@ export default defineComponent({ selectedSubMenuEventKeys, motion, defaultMotions, + expandIcon: menuExpandIcon, } = useInjectMenu(); registerMenuInfo(eventKey, menuInfo); @@ -226,6 +228,7 @@ export default defineComponent({ const icon = getPropsSlot(slots, props, 'icon'); const title = renderTitle(getPropsSlot(slots, props, 'title'), icon); const subMenuPrefixClsValue = subMenuPrefixCls.value; + const expandIcon = props.expandIcon || slots.expandIcon || menuExpandIcon; let titleNode = (
)} diff --git a/components/menu/src/hooks/useMenuContext.ts b/components/menu/src/hooks/useMenuContext.ts index c200c613ed..c7815f2b3f 100644 --- a/components/menu/src/hooks/useMenuContext.ts +++ b/components/menu/src/hooks/useMenuContext.ts @@ -77,7 +77,7 @@ export interface MenuContextProps { // // Icon // itemIcon?: RenderIconType; - // expandIcon?: RenderIconType; + expandIcon?: (p?: { isOpen: boolean; [key: string]: any }) => any; // // Function onItemClick: MenuClickEventHandler; From 8ce46ab1a1653dc1ec0fe1fa5fd7428022b265bc Mon Sep 17 00:00:00 2001 From: zanllp Date: Sun, 11 Jul 2021 14:46:45 +0800 Subject: [PATCH 11/28] =?UTF-8?q?fix(notification):=20=E5=AE=8C=E5=96=84no?= =?UTF-8?q?tification=E7=B1=BB=E5=9E=8B=20(#4346)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/notification/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/notification/index.tsx b/components/notification/index.tsx index 21b5730522..c6a8b712c2 100644 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -212,7 +212,7 @@ function notice(args: NotificationArgsProps) { ); } -const api: any = { +const apiBase = { open: notice, close(key: string) { Object.keys(notificationInstance).forEach(cacheKey => @@ -228,7 +228,11 @@ const api: any = { }, }; -['success', 'info', 'warning', 'error'].forEach(type => { +type NotificationApi = typeof apiBase & + Record) => void>; +const api = apiBase as any as NotificationApi; +const iconTypes: IconType[] = ['success', 'info', 'warning', 'error']; +iconTypes.forEach(type => { api[type] = args => api.open({ ...args, From 2ba963babfed96721d02147b1c634999a2ea97b0 Mon Sep 17 00:00:00 2001 From: John Date: Sun, 11 Jul 2021 15:25:40 +0800 Subject: [PATCH 12/28] refactor(progress): use composition API (#4355) * refactor(progress): use composition API * refactor(vc-progress): update --- components/progress/Circle.tsx | 112 +++++++++ components/progress/Line.tsx | 131 ++++++++++ components/progress/Steps.tsx | 56 +++++ .../__snapshots__/index.test.js.snap | 210 +++++++++++----- components/progress/__tests__/index.test.js | 225 +++++++++++++++++- components/progress/circle.tsx | 86 ------- components/progress/index.ts | 2 +- components/progress/line.tsx | 93 -------- components/progress/progress.tsx | 142 ++++++----- components/progress/props.ts | 46 +++- components/progress/style/index.less | 27 ++- components/progress/style/rtl.less | 37 +++ components/progress/utils.ts | 25 +- components/style/themes/default.less | 7 +- components/vc-progress/{index.js => index.ts} | 2 +- components/vc-progress/src/Circle.js | 191 --------------- components/vc-progress/src/Circle.tsx | 174 ++++++++++++++ components/vc-progress/src/Line.js | 84 ------- components/vc-progress/src/Line.tsx | 96 ++++++++ components/vc-progress/src/common.ts | 43 ++++ components/vc-progress/src/enhancer.js | 30 --- .../vc-progress/src/{index.js => index.ts} | 3 +- components/vc-progress/src/types.js | 30 --- components/vc-progress/src/types.ts | 31 +++ 24 files changed, 1221 insertions(+), 662 deletions(-) create mode 100644 components/progress/Circle.tsx create mode 100644 components/progress/Line.tsx create mode 100644 components/progress/Steps.tsx delete mode 100644 components/progress/circle.tsx delete mode 100644 components/progress/line.tsx create mode 100644 components/progress/style/rtl.less rename components/vc-progress/{index.js => index.ts} (62%) delete mode 100644 components/vc-progress/src/Circle.js create mode 100644 components/vc-progress/src/Circle.tsx delete mode 100644 components/vc-progress/src/Line.js create mode 100644 components/vc-progress/src/Line.tsx create mode 100644 components/vc-progress/src/common.ts delete mode 100644 components/vc-progress/src/enhancer.js rename components/vc-progress/src/{index.js => index.ts} (53%) delete mode 100644 components/vc-progress/src/types.js create mode 100644 components/vc-progress/src/types.ts diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx new file mode 100644 index 0000000000..8d8ac2eb4d --- /dev/null +++ b/components/progress/Circle.tsx @@ -0,0 +1,112 @@ +import type { CSSProperties, ExtractPropTypes } from 'vue'; +import { computed, defineComponent } from 'vue'; +import { Circle as VCCircle } from '../vc-progress'; +import { getSuccessPercent, validProgress } from './utils'; +import { progressProps } from './props'; +import PropTypes from '../_util/vue-types'; + +const circleProps = { + ...progressProps, + prefixCls: PropTypes.string, + // progressStatus: PropTypes.string, +}; +export type CircleProps = Partial>; + +const statusColorMap = { + normal: '#108ee9', + exception: '#ff5500', + success: '#87d068', +}; + +function getPercentage( + percent: CircleProps['percent'], + success: CircleProps['success'], + successPercent: CircleProps['successPercent'], +) { + const ptg = validProgress(percent); + const realSuccessPercent = getSuccessPercent(success, successPercent); + if (!realSuccessPercent) { + return ptg; + } + return [ + validProgress(realSuccessPercent), + validProgress(ptg - validProgress(realSuccessPercent)), + ]; +} + +function getStrokeColor( + success: CircleProps['success'], + strokeColor: CircleProps['strokeColor'], + successPercent: CircleProps['successPercent'], +) { + const color = strokeColor || null; + const realSuccessPercent = getSuccessPercent(success, successPercent); + if (!realSuccessPercent) { + return color; + } + return [statusColorMap.success, color]; +} + +export default defineComponent({ + props: progressProps, + inheritAttrs: false, + setup(props, { slots }) { + const gapDeg = computed(() => { + // Support gapDeg = 0 when type = 'dashboard' + if (props.gapDegree || props.gapDegree === 0) { + return props.gapDegree; + } + if (props.type === 'dashboard') { + return 75; + } + return undefined; + }); + + const circleStyle = computed(() => { + const circleSize = props.width || 120; + return { + width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, + height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, + fontSize: `${circleSize * 0.15 + 6}px`, + }; + }); + + const circleWidth = computed(() => props.strokeWidth || 6); + const gapPos = computed( + () => props.gapPosition || (props.type === 'dashboard' && 'bottom') || 'top', + ); + + // using className to style stroke color + const strokeColor = computed(() => + getStrokeColor(props.success, props.strokeColor, props.successPercent), + ); + const percent = computed(() => + getPercentage(props.percent, props.success, props.successPercent), + ); + const isGradient = computed( + () => Object.prototype.toString.call(strokeColor.value) === '[object Object]', + ); + + const wrapperClassName = computed(() => ({ + [`${props.prefixCls}-inner`]: true, + [`${props.prefixCls}-circle-gradient`]: isGradient.value, + })); + + return () => ( +
+ + {slots.default?.()} +
+ ); + }, +}); diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx new file mode 100644 index 0000000000..ca01f86719 --- /dev/null +++ b/components/progress/Line.tsx @@ -0,0 +1,131 @@ +import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'; +import { computed, defineComponent } from 'vue'; +import type { Direction } from '../config-provider'; +import PropTypes from '../_util/vue-types'; +import type { StringGradients, ProgressGradient } from './props'; +import { progressProps } from './props'; +import { getSuccessPercent, validProgress } from './utils'; + +const lineProps = { + ...progressProps, + prefixCls: PropTypes.string, + direction: { + type: String as PropType, + }, +}; + +export type LineProps = Partial>; + +/** + * { + * '0%': '#afc163', + * '75%': '#009900', + * '50%': 'green', ====> '#afc163 0%, #66FF00 25%, #00CC00 50%, #009900 75%, #ffffff 100%' + * '25%': '#66FF00', + * '100%': '#ffffff' + * } + */ +export const sortGradient = (gradients: StringGradients) => { + let tempArr = []; + Object.keys(gradients).forEach(key => { + const formattedKey = parseFloat(key.replace(/%/g, '')); + if (!isNaN(formattedKey)) { + tempArr.push({ + key: formattedKey, + value: gradients[key], + }); + } + }); + tempArr = tempArr.sort((a, b) => a.key - b.key); + return tempArr.map(({ key, value }) => `${value} ${key}%`).join(', '); +}; + +/** + * Then this man came to realize the truth: Besides six pence, there is the moon. Besides bread and + * butter, there is the bug. And... Besides women, there is the code. + * + * @example + * { + * "0%": "#afc163", + * "25%": "#66FF00", + * "50%": "#00CC00", // ====> linear-gradient(to right, #afc163 0%, #66FF00 25%, + * "75%": "#009900", // #00CC00 50%, #009900 75%, #ffffff 100%) + * "100%": "#ffffff" + * } + */ +export const handleGradient = (strokeColor: ProgressGradient, directionConfig: Direction) => { + const { + from = '#1890ff', + to = '#1890ff', + direction = directionConfig === 'rtl' ? 'to left' : 'to right', + ...rest + } = strokeColor; + if (Object.keys(rest).length !== 0) { + const sortedGradients = sortGradient(rest as StringGradients); + return { backgroundImage: `linear-gradient(${direction}, ${sortedGradients})` }; + } + return { backgroundImage: `linear-gradient(${direction}, ${from}, ${to})` }; +}; + +export default defineComponent({ + props: lineProps, + setup(props, { slots }) { + const backgroundProps = computed(() => { + const { strokeColor, direction } = props; + return strokeColor && typeof strokeColor !== 'string' + ? handleGradient(strokeColor, direction) + : { + background: strokeColor, + }; + }); + + const trailStyle = computed(() => + props.trailColor + ? { + backgroundColor: props.trailColor, + } + : undefined, + ); + + const percentStyle = computed(() => { + const { percent, strokeWidth, strokeLinecap, size } = props; + return { + width: `${validProgress(percent)}%`, + height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, + borderRadius: strokeLinecap === 'square' ? 0 : '', + ...(backgroundProps.value as any), + }; + }); + + const successPercent = computed(() => { + return getSuccessPercent(props.success, props.successPercent); + }); + const successPercentStyle = computed(() => { + const { strokeWidth, size, strokeLinecap, success } = props; + return { + width: `${validProgress(successPercent.value)}%`, + height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, + borderRadius: strokeLinecap === 'square' ? 0 : '', + backgroundColor: success?.strokeColor, + }; + }); + + const successSegment = computed(() => + successPercent.value !== undefined ? ( +
+ ) : null, + ); + + return () => ( + <> +
+
+
+ {successSegment.value} +
+
+ {slots.default?.()} + + ); + }, +}); diff --git a/components/progress/Steps.tsx b/components/progress/Steps.tsx new file mode 100644 index 0000000000..e50518bff0 --- /dev/null +++ b/components/progress/Steps.tsx @@ -0,0 +1,56 @@ +import { computed, ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import { defineComponent } from 'vue'; +import PropTypes from '../_util/vue-types'; +import type { ProgressSize } from './props'; +import { progressProps } from './props'; + +const stepsProps = { + ...progressProps, + steps: PropTypes.number, + size: { + type: String as PropType, + }, + strokeColor: PropTypes.string, + trailColor: PropTypes.string, +}; + +export type StepsProps = Partial>; + +export default defineComponent({ + props: stepsProps, + setup(props, { slots }) { + const current = computed(() => Math.round(props.steps * ((props.percent || 0) / 100))); + const stepWidth = computed(() => (props.size === 'small' ? 2 : 14)); + + const styledSteps = computed(() => { + const { steps, strokeWidth = 8, strokeColor, trailColor, prefixCls } = props; + + const temp: VNodeChild[] = []; + for (let i = 0; i < steps; i += 1) { + const cls = { + [`${prefixCls}-steps-item`]: true, + [`${prefixCls}-steps-item-active`]: i <= current.value - 1, + }; + temp.push( +
, + ); + } + return temp; + }); + + return () => ( +
+ {styledSteps.value} + {slots.default?.()} +
+ ); + }, +}); diff --git a/components/progress/__tests__/__snapshots__/index.test.js.snap b/components/progress/__tests__/__snapshots__/index.test.js.snap index 0cb227835b..cc19fbf18d 100644 --- a/components/progress/__tests__/__snapshots__/index.test.js.snap +++ b/components/progress/__tests__/__snapshots__/index.test.js.snap @@ -1,7 +1,115 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Progress render dashboard 295 gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + +exports[`Progress render dashboard 296 gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + +exports[`Progress render dashboard zero gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + exports[`Progress render format 1`] = ` -
+
+
+
+
+
+
+
50 10 +
+`; + +exports[`Progress render negative progress 1`] = ` +
+
+
+
+ +
+
0% +
+`; + +exports[`Progress render negative successPercent 1`] = ` +
+
+
+
+
+
+
50% +
+`; + +exports[`Progress render normal progress 1`] = ` +
+
+
+
+ +
+
0% +
+`; + +exports[`Progress render out-of-range progress 1`] = ` +
+
+
+
+ +
+
+
+`; + +exports[`Progress render out-of-range progress with info 1`] = ` +
+
+
+
+ +
+
+
+`; + +exports[`Progress render strokeColor 1`] = ` +
-
-
-
-
- -
-
0% -
+exports[`Progress render strokeColor 2`] = ` +
+
+
+
+ +
+
50%
`; -exports[`Progress render negetive successPercent 1`] = ` -
-
-
-
-
-
-
-
50% -
+exports[`Progress render strokeColor 3`] = ` +
+
+
+
+ +
+
50%
`; -exports[`Progress render negetive successPercent 2`] = ` -
-
-
-
-
-
-
-
50 10 -
+exports[`Progress render successColor progress 1`] = ` +
+
+
+
+
+
+
60%
`; -exports[`Progress render normal progress 1`] = ` -
-
-
-
-
- -
-
0% -
+exports[`Progress render trailColor progress 1`] = ` +
+
+
+
+ +
+
0%
`; -exports[`Progress render out-of-range progress 1`] = ` -
-
-
-
-
- -
-
+exports[`Progress should support steps 1`] = ` +
+
+
+
+
0%
`; -exports[`Progress render out-of-range progress with info 1`] = ` -
-
-
-
-
- -
-
-
+exports[`Progress steps should have default percent 0 1`] = ` +
+
`; diff --git a/components/progress/__tests__/index.test.js b/components/progress/__tests__/index.test.js index 1eadc98aee..218521be35 100644 --- a/components/progress/__tests__/index.test.js +++ b/components/progress/__tests__/index.test.js @@ -1,13 +1,15 @@ import { mount } from '@vue/test-utils'; import { asyncExpect } from '@/tests/utils'; +import { handleGradient, sortGradient } from '../Line'; import Progress from '..'; +import ProgressSteps from '../Steps'; describe('Progress', () => { it('successPercent should decide the progress status when it exists', async () => { const wrapper = mount(Progress, { props: { percent: 100, - successPercent: 50, + success: { percent: 50 }, }, sync: false, }); @@ -15,12 +17,12 @@ describe('Progress', () => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(0); }); - wrapper.setProps({ percent: 50, successPercent: 100 }); + wrapper.setProps({ percent: 50, success: { percent: 100 } }); await asyncExpect(() => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); }); - wrapper.setProps({ percent: 100, successPercent: 0 }); + wrapper.setProps({ percent: 100, success: { percent: 0 } }); await asyncExpect(() => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(0); }); @@ -51,7 +53,7 @@ describe('Progress', () => { }); }); - it('render negetive progress', async () => { + it('render negative progress', async () => { const wrapper = mount(Progress, { props: { percent: -20, @@ -63,11 +65,11 @@ describe('Progress', () => { }); }); - it('render negetive successPercent', async () => { + it('render negative successPercent', async () => { const wrapper = mount(Progress, { props: { percent: 50, - successPercent: -20, + success: { percent: -20 }, }, sync: false, }); @@ -76,7 +78,7 @@ describe('Progress', () => { }); }); - it('render negetive successPercent', async () => { + it('render format', async () => { const wrapper = mount(Progress, { props: { percent: 50, @@ -90,7 +92,7 @@ describe('Progress', () => { }); }); - it('render format', async () => { + it('render strokeColor', async () => { const wrapper = mount(Progress, { props: { percent: 50, @@ -102,10 +104,217 @@ describe('Progress', () => { await asyncExpect(() => { expect(wrapper.html()).toMatchSnapshot(); }); + wrapper.setProps({ + strokeColor: { + from: '#108ee9', + to: '#87d068', + }, + type: 'line', + }); + await asyncExpect(() => { + expect(wrapper.html()).toMatchSnapshot(); + }); + wrapper.setProps({ + strokeColor: { + '0%': '#108ee9', + '100%': '#87d068', + }, + }); + await asyncExpect(() => { + expect(wrapper.html()).toMatchSnapshot(); + }); }); it('render normal progress', () => { const wrapper = mount(Progress, { props: { status: 'normal' } }); expect(wrapper.html()).toMatchSnapshot(); }); + + it('render trailColor progress', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render successColor progress', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard zero gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard 295 gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard 296 gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('get correct line-gradient', () => { + expect(handleGradient({ from: 'test', to: 'test' }).backgroundImage).toBe( + 'linear-gradient(to right, test, test)', + ); + expect(handleGradient({}).backgroundImage).toBe('linear-gradient(to right, #1890ff, #1890ff)'); + expect(handleGradient({ from: 'test', to: 'test', '0%': 'test' }).backgroundImage).toBe( + 'linear-gradient(to right, test 0%)', + ); + }); + + it('sort gradients correctly', () => { + expect(sortGradient({ '10%': 'test10', '30%': 'test30', '20%': 'test20' })).toBe( + 'test10 10%, test20 20%, test30 30%', + ); + expect(sortGradient({ '10%': 'test10', '30%': 'test30', '20%': 'test20', dummy: 'test' })).toBe( + 'test10 10%, test20 20%, test30 30%', + ); + }); + + it('should show success status when percent is 100', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + }); + + // https://github.com/ant-design/ant-design/issues/15950 + it('should show success status when percent is 100 and status is undefined', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + }); + + // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 + it('should show success status when status is invalid', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + errorSpy.mockRestore(); + }); + + it('should support steps', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('steps should be changable', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(3); + wrapper.setProps({ percent: 40 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(2); + }); + }); + + it('steps should be changable when has strokeColor', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item')[0].element.style.backgroundColor).toBe( + 'rgb(24, 144, 255)', + ); + wrapper.setProps({ percent: 40 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item')[2].element.style.backgroundColor).toBe(''); + expect(wrapper.findAll('.ant-progress-steps-item')[1].element.style.backgroundColor).toBe( + 'rgb(24, 144, 255)', + ); + }); + }); + + it('steps should support trailColor', () => { + const wrapper = mount(); + expect(wrapper.findAll('.ant-progress-steps-item')[1].element.style.backgroundColor).toBe( + 'rgb(24, 144, 238)', + ); + }); + + it('should display correct step', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(2); + wrapper.setProps({ percent: 33.33 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(3); + }); + wrapper.setProps({ percent: 44.44 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(4); + }); + }); + + it('steps should have default percent 0', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should warning if use `progress` in success', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + mount(); + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: [ant-design-vue: Progress] `success.progress` is deprecated. Please use `success.percent` instead.', + ); + }); + + it('should warning if use `progress` in success in type Circle', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + mount({ + render() { + return ; + }, + }); + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: [ant-design-vue: Progress] `success.progress` is deprecated. Please use `success.percent` instead.', + ); + }); }); diff --git a/components/progress/circle.tsx b/components/progress/circle.tsx deleted file mode 100644 index bbf6867e77..0000000000 --- a/components/progress/circle.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type { ExtractPropTypes } from 'vue'; -import { defineComponent } from 'vue'; -import { Circle as VCCircle } from '../vc-progress'; -import PropTypes from '../_util/vue-types'; -import { validProgress } from './utils'; -import { ProgressProps } from './props'; - -const CircleProps = { - ...ProgressProps, - progressStatus: PropTypes.string, -}; - -const statusColorMap: Record = { - normal: '#108ee9', - exception: '#ff5500', - success: '#87d068', -}; - -export type ICircleProps = ExtractPropTypes; - -function getPercentage({ percent, successPercent }: ICircleProps) { - const ptg = validProgress(percent); - if (!successPercent) return ptg; - - const successPtg = validProgress(successPercent); - return [successPercent, validProgress(ptg - successPtg)]; -} - -function getStrokeColor({ progressStatus, successPercent, strokeColor }: ICircleProps) { - const color = strokeColor || statusColorMap[progressStatus]; - if (!successPercent) return color; - return [statusColorMap.success, color]; -} - -const Circle = defineComponent({ - props: CircleProps, - setup(props, { slots }) { - return () => { - const { - prefixCls, - width, - strokeWidth, - trailColor, - strokeLinecap, - gapPosition, - gapDegree, - type, - } = props; - const circleSize = width || 120; - const circleStyle = { - width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, - height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, - fontSize: `${circleSize * 0.15 + 6}px`, - }; - const circleWidth = strokeWidth || 6; - const gapPos = gapPosition || (type === 'dashboard' && 'bottom') || 'top'; - const gapDeg = gapDegree || (type === 'dashboard' && 75); - const strokeColor = getStrokeColor(props); - const isGradient = Object.prototype.toString.call(strokeColor) === '[object Object]'; - - const wrapperClassName = { - [`${prefixCls}-inner`]: true, - [`${prefixCls}-circle-gradient`]: isGradient, - }; - - return ( -
- - {slots?.default()} -
- ); - }; - }, -}); - -export default Circle; diff --git a/components/progress/index.ts b/components/progress/index.ts index ef45c8e8e1..2505b42832 100644 --- a/components/progress/index.ts +++ b/components/progress/index.ts @@ -1,6 +1,6 @@ import Progress from './progress'; import { withInstall } from '../_util/type'; -export { ProgressProps } from './props'; +export type { ProgressProps } from './props'; export default withInstall(Progress); diff --git a/components/progress/line.tsx b/components/progress/line.tsx deleted file mode 100644 index 06afa96854..0000000000 --- a/components/progress/line.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { validProgress } from './utils'; - -/** - * { - * '0%': '#afc163', - * '75%': '#009900', - * '50%': 'green', ====> '#afc163 0%, #66FF00 25%, #00CC00 50%, #009900 75%, #ffffff 100%' - * '25%': '#66FF00', - * '100%': '#ffffff' - * } - */ -export const sortGradient = gradients => { - let tempArr = []; - // eslint-disable-next-line no-restricted-syntax - for (const [key, value] of Object.entries(gradients)) { - const formatKey = parseFloat(key.replace(/%/g, '')); - if (isNaN(formatKey)) { - return {}; - } - tempArr.push({ - key: formatKey, - value, - }); - } - tempArr = tempArr.sort((a, b) => a.key - b.key); - return tempArr.map(({ key, value }) => `${value} ${key}%`).join(', '); -}; - -/** - * { - * '0%': '#afc163', - * '25%': '#66FF00', - * '50%': '#00CC00', ====> linear-gradient(to right, #afc163 0%, #66FF00 25%, - * '75%': '#009900', #00CC00 50%, #009900 75%, #ffffff 100%) - * '100%': '#ffffff' - * } - * - * Then this man came to realize the truth: - * Besides six pence, there is the moon. - * Besides bread and butter, there is the bug. - * And... - * Besides women, there is the code. - */ -export const handleGradient = strokeColor => { - const { from = '#1890ff', to = '#1890ff', direction = 'to right', ...rest } = strokeColor; - if (Object.keys(rest).length !== 0) { - const sortedGradients = sortGradient(rest); - return { backgroundImage: `linear-gradient(${direction}, ${sortedGradients})` }; - } - return { backgroundImage: `linear-gradient(${direction}, ${from}, ${to})` }; -}; - -const Line = (_, { attrs, slots }) => { - const { prefixCls, percent, successPercent, strokeWidth, size, strokeColor, strokeLinecap } = - attrs; - let backgroundProps; - if (strokeColor && typeof strokeColor !== 'string') { - backgroundProps = handleGradient(strokeColor); - } else { - backgroundProps = { - background: strokeColor, - }; - } - const percentStyle = { - width: `${validProgress(percent)}%`, - height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, - background: strokeColor, - borderRadius: strokeLinecap === 'square' ? 0 : '100px', - ...backgroundProps, - }; - const successPercentStyle = { - width: `${validProgress(successPercent)}%`, - height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, - borderRadius: strokeLinecap === 'square' ? 0 : '', - }; - const successSegment = - successPercent !== undefined ? ( -
- ) : null; - return ( -
-
-
-
- {successSegment} -
-
- {slots?.default()} -
- ); -}; - -export default Line; diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index 118732db82..c8d2e1c700 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -1,20 +1,22 @@ -import { defineComponent, inject } from 'vue'; -import classNames from '../_util/classNames'; -import { getOptionProps } from '../_util/props-util'; +import type { VNodeChild } from 'vue'; +import { computed, defineComponent } from 'vue'; import initDefaultProps from '../_util/props-util/initDefaultProps'; -import { defaultConfigProvider } from '../config-provider'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CheckOutlined from '@ant-design/icons-vue/CheckOutlined'; import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled'; import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled'; -import Line from './line'; -import Circle from './circle'; -import { validProgress } from './utils'; -import { ProgressProps, ProgressStatuses } from './props'; +import Line from './Line'; +import Circle from './Circle'; +import Steps from './Steps'; +import { getSuccessPercent, validProgress } from './utils'; +import useConfigInject from '../_util/hooks/useConfigInject'; +import devWarning from '../vc-util/devWarning'; +import type { ProgressStatusesType } from './props'; +import { progressProps, progressStatuses } from './props'; export default defineComponent({ name: 'AProgress', - props: initDefaultProps(ProgressProps, { + props: initDefaultProps(progressProps, { type: 'line', percent: 0, showInfo: true, @@ -24,37 +26,49 @@ export default defineComponent({ gapDegree: 0, strokeLinecap: 'round', }), - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), - }; - }, - methods: { - getPercentNumber() { - const { successPercent, percent = 0 } = this.$props; + setup(props, { slots }) { + const { prefixCls, direction } = useConfigInject('progress', props); + + const classString = computed(() => { + const { type, showInfo, size } = props; + const pre = prefixCls.value; + return { + [pre]: true, + [`${pre}-${(type === 'dashboard' && 'circle') || type}`]: true, + [`${pre}-show-info`]: showInfo, + [`${pre}-${size}`]: size, + [`${pre}-rtl`]: direction.value === 'rtl', + }; + }); + + const getPercentNumber = () => { + const { percent = 0 } = props; + const successPercent = getSuccessPercent(props.success, props.successPercent); return parseInt( successPercent !== undefined ? successPercent.toString() : percent.toString(), 10, ); - }, + }; - getProgressStatus() { - const { status } = this.$props; - if (ProgressStatuses.indexOf(status) < 0 && this.getPercentNumber() >= 100) { + const getProgressStatus = () => { + const { status } = props; + if (progressStatuses.indexOf(status) < 0 && getPercentNumber() >= 100) { return 'success'; } return status || 'normal'; - }, - renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) { - const { showInfo, format, type, percent, successPercent } = this.$props; + }; + + const renderProcessInfo = (prefixCls: string, progressStatus: ProgressStatusesType) => { + const { showInfo, format, type, percent } = props; + const successPercent = getSuccessPercent(props.success, props.successPercent); if (!showInfo) return null; - let text; - const textFormatter = format || this.$slots.format || (percentNumber => `${percentNumber}%`); + let text: VNodeChild; + const textFormatter = format || slots?.format || (percentNumber => `${percentNumber}%`); const isLineType = type === 'line'; if ( format || - this.$slots.format || + slots?.format || (progressStatus !== 'exception' && progressStatus !== 'success') ) { text = textFormatter(validProgress(percent), validProgress(successPercent)); @@ -68,44 +82,50 @@ export default defineComponent({ {text} ); - }, - }, - render() { - const props = getOptionProps(this); - const { prefixCls: customizePrefixCls, size, type, showInfo } = props; - const { getPrefixCls } = this.configProvider; - const prefixCls = getPrefixCls('progress', customizePrefixCls); - const progressStatus = this.getProgressStatus(); - const progressInfo = this.renderProcessInfo(prefixCls, progressStatus); + }; - let progress; + return () => { + const { type, steps, strokeColor } = props; + const progressStatus = getProgressStatus(); + const progressInfo = renderProcessInfo(prefixCls.value, progressStatus); - // Render progress shape - if (type === 'line') { - const lineProps = { - ...props, - prefixCls, - }; - progress = {progressInfo}; - } else if (type === 'circle' || type === 'dashboard') { - const circleProps = { - ...props, - prefixCls, - progressStatus, - }; - progress = {progressInfo}; - } + devWarning( + props.successPercent == undefined, + 'Progress', + '`successPercent` is deprecated. Please use `success.percent` instead.', + ); - const classString = classNames(prefixCls, { - [`${prefixCls}-${(type === 'dashboard' && 'circle') || type}`]: true, - [`${prefixCls}-status-${progressStatus}`]: true, - [`${prefixCls}-show-info`]: showInfo, - [`${prefixCls}-${size}`]: size, - }); + let progress: VNodeChild; + // Render progress shape + if (type === 'line') { + progress = steps ? ( + + {progressInfo} + + ) : ( + + {progressInfo} + + ); + } else if (type === 'circle' || type === 'dashboard') { + progress = ( + + {progressInfo} + + ); + } + + const classNames = { + ...classString.value, + [`${prefixCls.value}-status-${progressStatus}`]: true, + }; - const progressProps = { - class: classString, + return
{progress}
; }; - return
{progress}
; }, }); diff --git a/components/progress/props.ts b/components/progress/props.ts index a18b1c7c24..7933fdf472 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -1,24 +1,48 @@ import PropTypes from '../_util/vue-types'; import { tuple } from '../_util/type'; +import type { PropType, VNodeChild, ExtractPropTypes } from 'vue'; -export const ProgressStatuses = tuple('normal', 'exception', 'active', 'success'); -export const ProgressType = PropTypes.oneOf(tuple('line', 'circle', 'dashboard')); -export const ProgressSize = PropTypes.oneOf(tuple('default', 'small')); +export const progressStatuses = tuple('normal', 'exception', 'active', 'success'); +export type ProgressStatusesType = typeof progressStatuses[number]; +const ProgressType = tuple('line', 'circle', 'dashboard'); +export type ProgressType = typeof ProgressType[number]; +const ProgressSize = tuple('default', 'small'); +export type ProgressSize = typeof ProgressSize[number]; +export type StringGradients = { [percentage: string]: string }; +type FromToGradients = { from: string; to: string }; +export type ProgressGradient = { direction?: string } & (StringGradients | FromToGradients); -export const ProgressProps = { +export interface SuccessProps { + percent?: number; + /** @deprecated Use `percent` instead */ + progress?: number; + strokeColor?: string; +} + +export const progressProps = { prefixCls: PropTypes.string, - type: ProgressType, + type: PropTypes.oneOf(ProgressType), percent: PropTypes.number, - successPercent: PropTypes.number, - format: PropTypes.func, - status: PropTypes.oneOf(ProgressStatuses), + format: { type: Function as PropType<(percent?: number, successPercent?: number) => VNodeChild> }, + status: PropTypes.oneOf(progressStatuses), showInfo: PropTypes.looseBool, strokeWidth: PropTypes.number, - strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']), - strokeColor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + strokeLinecap: PropTypes.oneOf(tuple('butt', 'round', 'square')), + strokeColor: { + type: [String, Object] as PropType, + }, trailColor: PropTypes.string, width: PropTypes.number, + success: { + type: Object as PropType, + default: (): SuccessProps => ({}), + }, gapDegree: PropTypes.number, gapPosition: PropTypes.oneOf(tuple('top', 'bottom', 'left', 'right')), - size: ProgressSize, + size: PropTypes.oneOf(ProgressSize), + steps: PropTypes.number, + /** @deprecated Use `success` instead */ + successPercent: PropTypes.number, }; + +export type ProgressProps = Partial>; diff --git a/components/progress/style/index.less b/components/progress/style/index.less index f2f0bd5e8c..45fc23e7c0 100644 --- a/components/progress/style/index.less +++ b/components/progress/style/index.less @@ -14,6 +14,26 @@ font-size: @font-size-base; } + &-steps { + display: inline-block; + &-outer { + display: flex; + flex-direction: row; + align-items: center; + } + &-item { + flex-shrink: 0; + min-width: 2px; + margin-right: 2px; + background: @progress-steps-item-bg; + transition: all 0.3s; + + &-active { + background: @progress-default-color; + } + } + } + &-small&-line, &-small&-line &-text .@{iconfont-css-prefix} { font-size: @font-size-sm; @@ -73,8 +93,8 @@ display: inline-block; width: 2em; margin-left: 8px; - color: @text-color-secondary; - font-size: 1em; + color: @progress-info-text-color; + font-size: @progress-text-font-size; line-height: 1; white-space: nowrap; text-align: left; @@ -144,6 +164,7 @@ margin: 0; padding: 0; color: @progress-text-color; + font-size: @progress-circle-text-font-size; line-height: 1; white-space: normal; text-align: center; @@ -180,3 +201,5 @@ opacity: 0; } } + +@import './rtl'; diff --git a/components/progress/style/rtl.less b/components/progress/style/rtl.less new file mode 100644 index 0000000000..0756b5f847 --- /dev/null +++ b/components/progress/style/rtl.less @@ -0,0 +1,37 @@ +@import '../../style/themes/index'; +@import '../../style/mixins/index'; + +@progress-prefix-cls: ~'@{ant-prefix}-progress'; + +.@{progress-prefix-cls} { + &-rtl { + direction: rtl; + } + + &-outer { + .@{progress-prefix-cls}-show-info & { + .@{progress-prefix-cls}-rtl& { + margin-right: 0; + margin-left: ~'calc(-2em - 8px)'; + padding-right: 0; + padding-left: ~'calc(2em + 8px)'; + } + } + } + + &-success-bg { + .@{progress-prefix-cls}-rtl & { + right: 0; + left: auto; + } + } + + &-line &-text, + &-steps &-text { + .@{progress-prefix-cls}-rtl& { + margin-right: 8px; + margin-left: 0; + text-align: right; + } + } +} diff --git a/components/progress/utils.ts b/components/progress/utils.ts index 14a930d882..df8b332265 100644 --- a/components/progress/utils.ts +++ b/components/progress/utils.ts @@ -1,4 +1,7 @@ -export function validProgress(progress?: number) { +import devWarning from '../vc-util/devWarning'; +import type { ProgressProps } from './props'; + +export function validProgress(progress: number | undefined) { if (!progress || progress < 0) { return 0; } @@ -7,3 +10,23 @@ export function validProgress(progress?: number) { } return progress; } + +export function getSuccessPercent( + success?: ProgressProps['success'], + successPercent?: ProgressProps['successPercent'], +) { + let percent = successPercent; + /** @deprecated Use `percent` instead */ + if (success && 'progress' in success) { + devWarning( + false, + 'Progress', + '`success.progress` is deprecated. Please use `success.percent` instead.', + ); + percent = success.progress; + } + if (success && 'percent' in success) { + percent = success.percent; + } + return percent; +} diff --git a/components/style/themes/default.less b/components/style/themes/default.less index 1b9b0c3013..dd886957e1 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -496,9 +496,12 @@ // -- @progress-default-color: @processing-color; @progress-remaining-color: @background-color-base; -@progress-text-color: @text-color; +@progress-info-text-color: @progress-text-color; @progress-radius: 100px; - +@progress-steps-item-bg: #f3f3f3; +@progress-text-font-size: 1em; +@progress-text-color: @text-color; // This is for circle text color, should be renamed better +@progress-circle-text-font-size: 1em; // Menu // --- @menu-inline-toplevel-item-height: 40px; diff --git a/components/vc-progress/index.js b/components/vc-progress/index.ts similarity index 62% rename from components/vc-progress/index.js rename to components/vc-progress/index.ts index 5dc2fdf679..199a021db2 100644 --- a/components/vc-progress/index.js +++ b/components/vc-progress/index.ts @@ -1,5 +1,5 @@ // based on rc-progress 2.5.2 -import Progress, { Line, Circle } from './src/'; +import Progress, { Line, Circle } from './src'; export { Line, Circle }; diff --git a/components/vc-progress/src/Circle.js b/components/vc-progress/src/Circle.js deleted file mode 100644 index 8b951253df..0000000000 --- a/components/vc-progress/src/Circle.js +++ /dev/null @@ -1,191 +0,0 @@ -import PropTypes, { withUndefined } from '../../_util/vue-types'; -import { initDefaultProps } from '../../_util/props-util'; -import enhancer from './enhancer'; -import { propTypes, defaultProps } from './types'; -import { defineComponent } from 'vue'; - -const circlePropTypes = { - ...propTypes, - gapPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']), - gapDegree: withUndefined( - PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.looseBool]), - ), -}; - -const circleDefaultProps = { - ...defaultProps, - gapPosition: 'top', -}; - -let gradientSeed = 0; - -function stripPercentToNumber(percent) { - return +percent.replace('%', ''); -} - -function toArray(symArray) { - return Array.isArray(symArray) ? symArray : [symArray]; -} - -function getPathStyles(offset, percent, strokeColor, strokeWidth, gapDegree = 0, gapPosition) { - const radius = 50 - strokeWidth / 2; - let beginPositionX = 0; - let beginPositionY = -radius; - let endPositionX = 0; - let endPositionY = -2 * radius; - switch (gapPosition) { - case 'left': - beginPositionX = -radius; - beginPositionY = 0; - endPositionX = 2 * radius; - endPositionY = 0; - break; - case 'right': - beginPositionX = radius; - beginPositionY = 0; - endPositionX = -2 * radius; - endPositionY = 0; - break; - case 'bottom': - beginPositionY = radius; - endPositionY = 2 * radius; - break; - default: - } - const pathString = `M 50,50 m ${beginPositionX},${beginPositionY} - a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} - a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`; - const len = Math.PI * 2 * radius; - - const pathStyle = { - stroke: strokeColor, - strokeDasharray: `${(percent / 100) * (len - gapDegree)}px ${len}px`, - strokeDashoffset: `-${gapDegree / 2 + (offset / 100) * (len - gapDegree)}px`, - transition: - 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s', // eslint-disable-line - }; - - return { - pathString, - pathStyle, - }; -} - -const Circle = defineComponent({ - name: 'Circle', - props: initDefaultProps(circlePropTypes, circleDefaultProps), - created() { - this.paths = {}; - this.gradientId = gradientSeed; - gradientSeed += 1; - }, - methods: { - getStokeList() { - const { - prefixCls, - percent, - strokeColor, - strokeWidth, - strokeLinecap, - gapDegree, - gapPosition, - } = this.$props; - const percentList = toArray(percent); - const strokeColorList = toArray(strokeColor); - - let stackPtg = 0; - return percentList.map((ptg, index) => { - const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; - const stroke = - Object.prototype.toString.call(color) === '[object Object]' - ? `url(#${prefixCls}-gradient-${this.gradientId})` - : ''; - const { pathString, pathStyle } = getPathStyles( - stackPtg, - ptg, - color, - strokeWidth, - gapDegree, - gapPosition, - ); - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString, - stroke, - 'stroke-linecap': strokeLinecap, - 'stroke-width': strokeWidth, - opacity: ptg === 0 ? 0 : 1, - 'fill-opacity': '0', - class: `${prefixCls}-circle-path`, - style: pathStyle, - }; - return (this.paths[index] = c)} {...pathProps} />; - }); - }, - }, - - render() { - const { - prefixCls, - strokeWidth, - trailWidth, - gapDegree, - gapPosition, - trailColor, - strokeLinecap, - strokeColor, - ...restProps - } = this.$props; - const { pathString, pathStyle } = getPathStyles( - 0, - 100, - trailColor, - strokeWidth, - gapDegree, - gapPosition, - ); - delete restProps.percent; - const strokeColorList = toArray(strokeColor); - const gradient = strokeColorList.find( - color => Object.prototype.toString.call(color) === '[object Object]', - ); - const pathFirst = { - d: pathString, - stroke: trailColor, - 'stroke-linecap': strokeLinecap, - 'stroke-width': trailWidth || strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-circle-trail`, - style: pathStyle, - }; - - return ( - - {gradient && ( - - - {Object.keys(gradient) - .sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b)) - .map((key, index) => ( - - ))} - - - )} - - {this.getStokeList().reverse()} - - ); - }, -}); - -export default enhancer(Circle); diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx new file mode 100644 index 0000000000..abb5c7cfbf --- /dev/null +++ b/components/vc-progress/src/Circle.tsx @@ -0,0 +1,174 @@ +import { useTransitionDuration, defaultProps } from './common'; +import { propTypes, GapPositionType } from './types'; +import { computed, defineComponent, ref } from 'vue'; +import initDefaultProps from '../../_util/props-util/initDefaultProps'; + +let gradientSeed = 0; + +function stripPercentToNumber(percent: string) { + return +percent.replace('%', ''); +} + +function toArray(value: any) { + return Array.isArray(value) ? value : [value]; +} + +function getPathStyles( + offset: number, + percent: number, + strokeColor: string, + strokeWidth: number, + gapDegree = 0, + gapPosition: GapPositionType, +) { + const radius = 50 - strokeWidth / 2; + let beginPositionX = 0; + let beginPositionY = -radius; + let endPositionX = 0; + let endPositionY = -2 * radius; + switch (gapPosition) { + case 'left': + beginPositionX = -radius; + beginPositionY = 0; + endPositionX = 2 * radius; + endPositionY = 0; + break; + case 'right': + beginPositionX = radius; + beginPositionY = 0; + endPositionX = -2 * radius; + endPositionY = 0; + break; + case 'bottom': + beginPositionY = radius; + endPositionY = 2 * radius; + break; + default: + } + const pathString = `M 50,50 m ${beginPositionX},${beginPositionY} + a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} + a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`; + const len = Math.PI * 2 * radius; + + const pathStyle = { + stroke: strokeColor, + strokeDasharray: `${(percent / 100) * (len - gapDegree)}px ${len}px`, + strokeDashoffset: `-${gapDegree / 2 + (offset / 100) * (len - gapDegree)}px`, + transition: + 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s', // eslint-disable-line + }; + + return { + pathString, + pathStyle, + }; +} + +export default defineComponent({ + name: 'VCCircle', + props: initDefaultProps(propTypes, defaultProps), + setup(props) { + gradientSeed += 1; + const gradientId = ref(gradientSeed); + const percentList = computed(() => toArray(props.percent)); + const strokeColorList = computed(() => toArray(props.strokeColor)); + + const paths = useTransitionDuration(percentList); + + const getStokeList = () => { + const { prefixCls, strokeWidth, strokeLinecap, gapDegree, gapPosition } = props; + + let stackPtg = 0; + return percentList.value.map((ptg, index) => { + const color = + strokeColorList.value[index] || strokeColorList.value[strokeColorList.value.length - 1]; + const stroke = + Object.prototype.toString.call(color) === '[object Object]' + ? `url(#${prefixCls}-gradient-${gradientId.value})` + : ''; + const { pathString, pathStyle } = getPathStyles( + stackPtg, + ptg, + color, + strokeWidth, + gapDegree, + gapPosition, + ); + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString, + stroke, + 'stroke-linecap': strokeLinecap, + 'stroke-width': strokeWidth, + opacity: ptg === 0 ? 0 : 1, + 'fill-opacity': '0', + class: `${prefixCls}-circle-path`, + style: pathStyle, + }; + return (paths.value[index].value = c)} {...pathProps} />; + }); + }; + + return () => { + const { + prefixCls, + strokeWidth, + trailWidth, + gapDegree, + gapPosition, + trailColor, + strokeLinecap, + strokeColor, + ...restProps + } = props; + const { pathString, pathStyle } = getPathStyles( + 0, + 100, + trailColor, + strokeWidth, + gapDegree, + gapPosition, + ); + delete restProps.percent; + const gradient = strokeColorList.value.find( + color => Object.prototype.toString.call(color) === '[object Object]', + ); + const pathFirst = { + d: pathString, + stroke: trailColor, + 'stroke-linecap': strokeLinecap, + 'stroke-width': trailWidth || strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-circle-trail`, + style: pathStyle, + }; + + return ( + + {gradient && ( + + + {Object.keys(gradient) + .sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b)) + .map((key, index) => ( + + ))} + + + )} + + {getStokeList().reverse()} + + ); + }; + }, +}); diff --git a/components/vc-progress/src/Line.js b/components/vc-progress/src/Line.js deleted file mode 100644 index 3e881964f4..0000000000 --- a/components/vc-progress/src/Line.js +++ /dev/null @@ -1,84 +0,0 @@ -import { defineComponent } from 'vue'; -import { initDefaultProps } from '../../_util/props-util'; -import enhancer from './enhancer'; -import { propTypes, defaultProps } from './types'; - -const Line = defineComponent({ - name: 'Line', - props: initDefaultProps(propTypes, defaultProps), - created() { - this.paths = {}; - }, - render() { - const { - percent, - prefixCls, - strokeColor, - strokeLinecap, - strokeWidth, - trailColor, - trailWidth, - transition, - ...restProps - } = this.$props; - - delete restProps.gapPosition; - - const percentList = Array.isArray(percent) ? percent : [percent]; - const strokeColorList = Array.isArray(strokeColor) ? strokeColor : [strokeColor]; - - const center = strokeWidth / 2; - const right = 100 - strokeWidth / 2; - const pathString = `M ${strokeLinecap === 'round' ? center : 0},${center} - L ${strokeLinecap === 'round' ? right : 100},${center}`; - const viewBoxString = `0 0 100 ${strokeWidth}`; - - let stackPtg = 0; - - const pathFirst = { - d: pathString, - 'stroke-linecap': strokeLinecap, - stroke: trailColor, - 'stroke-width': trailWidth || strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-trail`, - }; - return ( - - - {percentList.map((ptg, index) => { - const pathStyle = { - strokeDasharray: `${ptg}px, 100px`, - strokeDashoffset: `-${stackPtg}px`, - transition: - transition || - 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', - }; - const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString, - 'stroke-linecap': strokeLinecap, - stroke: color, - 'stroke-width': strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-path`, - style: pathStyle, - }; - - return (this.paths[index] = c)} {...pathProps} />; - })} - - ); - }, -}); - -export default enhancer(Line); diff --git a/components/vc-progress/src/Line.tsx b/components/vc-progress/src/Line.tsx new file mode 100644 index 0000000000..d61a5280d8 --- /dev/null +++ b/components/vc-progress/src/Line.tsx @@ -0,0 +1,96 @@ +import { computed, defineComponent } from 'vue'; +import initDefaultProps from '../../_util/props-util/initDefaultProps'; +import { useTransitionDuration, defaultProps } from './common'; +import { propTypes } from './types'; + +export default defineComponent({ + name: 'Line', + props: initDefaultProps(propTypes, defaultProps), + setup(props) { + const percentList = computed(() => { + const { percent } = props; + return Array.isArray(percent) ? percent : [percent]; + }); + + const strokeColorList = computed(() => { + const { strokeColor } = props; + return Array.isArray(strokeColor) ? strokeColor : [strokeColor]; + }); + + const paths = useTransitionDuration(percentList); + const center = computed(() => props.strokeWidth / 2); + const right = computed(() => 100 - props.strokeWidth / 2); + + const pathString = computed( + () => `M ${props.strokeLinecap === 'round' ? center.value : 0},${center.value} + L ${props.strokeLinecap === 'round' ? right.value : 100},${center.value}`, + ); + + const viewBoxString = computed(() => `0 0 100 ${props.strokeWidth}`); + + const pathFirst = computed(() => ({ + d: pathString.value, + 'stroke-linecap': props.strokeLinecap, + stroke: props.trailColor, + 'stroke-width': props.trailWidth || props.strokeWidth, + 'fill-opacity': '0', + class: `${props.prefixCls}-line-trail`, + })); + + return () => { + const { + percent, + prefixCls, + strokeColor, + strokeLinecap, + strokeWidth, + trailColor, + trailWidth, + transition, + ...restProps + } = props; + + delete restProps.gapPosition; + + let stackPtg = 0; + + return ( + + + {percentList.value.map((ptg, index) => { + const pathStyle = { + strokeDasharray: `${ptg}px, 100px`, + strokeDashoffset: `-${stackPtg}px`, + transition: + transition || + 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', + }; + const color = + strokeColorList.value[index] || + strokeColorList.value[strokeColorList.value.length - 1]; + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString.value, + 'stroke-linecap': strokeLinecap, + stroke: color as string, + 'stroke-width': strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-line-path`, + style: pathStyle, + }; + + return (paths.value[index].value = c)} {...pathProps} />; + })} + + ); + }; + }, +}); diff --git a/components/vc-progress/src/common.ts b/components/vc-progress/src/common.ts new file mode 100644 index 0000000000..334e9577c2 --- /dev/null +++ b/components/vc-progress/src/common.ts @@ -0,0 +1,43 @@ +import type { Ref } from 'vue'; +import { ref, onUpdated, computed } from 'vue'; +import type { ProgressProps } from './types'; + +export const defaultProps: Partial = { + percent: 0, + prefixCls: 'vc-progress', + strokeColor: '#2db7f5', + strokeLinecap: 'round', + strokeWidth: 1, + trailColor: '#D9D9D9', + trailWidth: 1, +}; + +export const useTransitionDuration = (percentList: Ref) => { + const paths = computed(() => percentList.value.map(() => ref())); + const prevTimeStamp = ref(null); + + onUpdated(() => { + const now = Date.now(); + let updated = false; + + Object.keys(paths.value).forEach(key => { + const path = paths.value[key].value; + if (!path) { + return; + } + updated = true; + const pathStyle = path.style; + pathStyle.transitionDuration = '.3s, .3s, .3s, .06s'; + + if (prevTimeStamp.value && now - prevTimeStamp.value < 100) { + pathStyle.transitionDuration = '0s, 0s'; + } + }); + + if (updated) { + prevTimeStamp.value = Date.now(); + } + }); + + return paths; +}; diff --git a/components/vc-progress/src/enhancer.js b/components/vc-progress/src/enhancer.js deleted file mode 100644 index 43ad4794ec..0000000000 --- a/components/vc-progress/src/enhancer.js +++ /dev/null @@ -1,30 +0,0 @@ -function enhancer(Component) { - return { - ...Component, - updated() { - const now = Date.now(); - let updated = false; - - Object.keys(this.paths).forEach(key => { - const path = this.paths[key]; - - if (!path) { - return; - } - - updated = true; - const pathStyle = path.style; - pathStyle.transitionDuration = '.3s, .3s, .3s, .06s'; - - if (this.prevTimeStamp && now - this.prevTimeStamp < 100) { - pathStyle.transitionDuration = '0s, 0s'; - } - }); - if (updated) { - this.prevTimeStamp = Date.now(); - } - }, - }; -} - -export default enhancer; diff --git a/components/vc-progress/src/index.js b/components/vc-progress/src/index.ts similarity index 53% rename from components/vc-progress/src/index.js rename to components/vc-progress/src/index.ts index 01fb4abd79..e6e5727860 100644 --- a/components/vc-progress/src/index.js +++ b/components/vc-progress/src/index.ts @@ -1,7 +1,8 @@ import Line from './Line'; import Circle from './Circle'; +import type { ProgressProps } from './types'; -export { Line, Circle }; +export { Line, Circle, ProgressProps }; export default { Line, diff --git a/components/vc-progress/src/types.js b/components/vc-progress/src/types.js deleted file mode 100644 index f027d27d29..0000000000 --- a/components/vc-progress/src/types.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from '../../_util/vue-types'; - -export const defaultProps = { - // className: '', - percent: 0, - prefixCls: 'rc-progress', - strokeColor: '#2db7f5', - strokeLinecap: 'round', - strokeWidth: 1, - // style: {}, - trailColor: '#D9D9D9', - trailWidth: 1, -}; -const mixedType = PropTypes.oneOfType([PropTypes.number, PropTypes.string]); - -export const propTypes = { - // className: PropTypes.string, - percent: PropTypes.oneOfType([mixedType, PropTypes.arrayOf(mixedType)]), - prefixCls: PropTypes.string, - strokeColor: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object])), - PropTypes.object, - ]), - strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']), - strokeWidth: mixedType, - // style: PropTypes.object, - trailColor: PropTypes.string, - trailWidth: mixedType, -}; diff --git a/components/vc-progress/src/types.ts b/components/vc-progress/src/types.ts new file mode 100644 index 0000000000..474ed7970f --- /dev/null +++ b/components/vc-progress/src/types.ts @@ -0,0 +1,31 @@ +import type { PropType, ExtractPropTypes } from 'vue'; +import PropTypes from '../../_util/vue-types'; + +export type StrokeColorType = string | string[] | object; + +export type GapPositionType = 'top' | 'right' | 'bottom' | 'left'; + +export type StrokeLinecapType = 'round' | 'butt' | 'square'; + +export const propTypes = { + gapDegree: PropTypes.number, + gapPosition: { + type: String as PropType, + }, + percent: { + type: [Array, Number] as PropType, + }, + prefixCls: PropTypes.string, + strokeColor: { + type: [Object, String, Array] as PropType, + }, + strokeLinecap: { + type: String as PropType, + }, + strokeWidth: PropTypes.number, + trailColor: PropTypes.string, + trailWidth: PropTypes.number, + transition: PropTypes.string, +}; + +export type ProgressProps = Partial>; From 01fb7f156f94e35b1cb08a3977f8ee1a73298c07 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Mon, 12 Jul 2021 16:39:54 +0800 Subject: [PATCH 13/28] refactor: progress --- components/_util/hooks/useRef.ts | 6 +- components/progress/Circle.tsx | 47 ++---------- components/progress/Line.tsx | 13 ++-- .../__snapshots__/index.test.js.snap | 12 +++ components/progress/__tests__/index.test.js | 24 +++--- components/progress/progress.tsx | 45 +++++------ components/progress/props.ts | 2 - components/progress/style/index.less | 6 +- .../progress/style/{index.ts => index.tsx} | 0 components/progress/utils.ts | 15 ++-- components/vc-progress/index.ts | 5 +- components/vc-progress/src/Circle.tsx | 6 +- components/vc-progress/src/Line.tsx | 76 +++++++++++-------- components/vc-progress/src/common.ts | 9 +-- package.json | 1 + v2-doc | 2 +- 16 files changed, 133 insertions(+), 136 deletions(-) rename components/progress/style/{index.ts => index.tsx} (100%) diff --git a/components/_util/hooks/useRef.ts b/components/_util/hooks/useRef.ts index 5033e9ea4e..86f6512ecd 100644 --- a/components/_util/hooks/useRef.ts +++ b/components/_util/hooks/useRef.ts @@ -2,9 +2,9 @@ import type { Ref } from 'vue'; import { onBeforeUpdate, ref } from 'vue'; export type UseRef = [(el: any, key: string | number) => void, Ref]; - +export type Refs = Record; export const useRef = (): UseRef => { - const refs = ref({}); + const refs = ref({}); const setRef = (el: any, key: string | number) => { refs.value[key] = el; }; @@ -13,3 +13,5 @@ export const useRef = (): UseRef => { }); return [setRef, refs]; }; + +export default useRef; diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx index 8d8ac2eb4d..c2265d354e 100644 --- a/components/progress/Circle.tsx +++ b/components/progress/Circle.tsx @@ -1,5 +1,6 @@ import type { CSSProperties, ExtractPropTypes } from 'vue'; import { computed, defineComponent } from 'vue'; +import { presetPrimaryColors } from '@ant-design/colors'; import { Circle as VCCircle } from '../vc-progress'; import { getSuccessPercent, validProgress } from './utils'; import { progressProps } from './props'; @@ -12,39 +13,9 @@ const circleProps = { }; export type CircleProps = Partial>; -const statusColorMap = { - normal: '#108ee9', - exception: '#ff5500', - success: '#87d068', -}; - -function getPercentage( - percent: CircleProps['percent'], - success: CircleProps['success'], - successPercent: CircleProps['successPercent'], -) { - const ptg = validProgress(percent); - const realSuccessPercent = getSuccessPercent(success, successPercent); - if (!realSuccessPercent) { - return ptg; - } - return [ - validProgress(realSuccessPercent), - validProgress(ptg - validProgress(realSuccessPercent)), - ]; -} - -function getStrokeColor( - success: CircleProps['success'], - strokeColor: CircleProps['strokeColor'], - successPercent: CircleProps['successPercent'], -) { - const color = strokeColor || null; - const realSuccessPercent = getSuccessPercent(success, successPercent); - if (!realSuccessPercent) { - return color; - } - return [statusColorMap.success, color]; +function getPercentage({ percent, success, successPercent }: CircleProps) { + const realSuccessPercent = validProgress(getSuccessPercent({ success, successPercent })); + return [realSuccessPercent, validProgress(validProgress(percent) - realSuccessPercent)]; } export default defineComponent({ @@ -77,14 +48,10 @@ export default defineComponent({ ); // using className to style stroke color - const strokeColor = computed(() => - getStrokeColor(props.success, props.strokeColor, props.successPercent), - ); - const percent = computed(() => - getPercentage(props.percent, props.success, props.successPercent), - ); + const strokeColor = computed(() => [presetPrimaryColors.green, props.strokeColor || null]); + const percent = computed(() => getPercentage(props)); const isGradient = computed( - () => Object.prototype.toString.call(strokeColor.value) === '[object Object]', + () => Object.prototype.toString.call(props.strokeColor) === '[object Object]', ); const wrapperClassName = computed(() => ({ diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx index ca01f86719..df9bfe42c6 100644 --- a/components/progress/Line.tsx +++ b/components/progress/Line.tsx @@ -69,6 +69,7 @@ export const handleGradient = (strokeColor: ProgressGradient, directionConfig: D export default defineComponent({ props: lineProps, + name: 'Line', setup(props, { slots }) { const backgroundProps = computed(() => { const { strokeColor, direction } = props; @@ -98,7 +99,7 @@ export default defineComponent({ }); const successPercent = computed(() => { - return getSuccessPercent(props.success, props.successPercent); + return getSuccessPercent(props); }); const successPercentStyle = computed(() => { const { strokeWidth, size, strokeLinecap, success } = props; @@ -110,18 +111,14 @@ export default defineComponent({ }; }); - const successSegment = computed(() => - successPercent.value !== undefined ? ( -
- ) : null, - ); - return () => ( <>
- {successSegment.value} + {successPercent.value !== undefined ? ( +
+ ) : null}
{slots.default?.()} diff --git a/components/progress/__tests__/__snapshots__/index.test.js.snap b/components/progress/__tests__/__snapshots__/index.test.js.snap index cc19fbf18d..0f52246db2 100644 --- a/components/progress/__tests__/__snapshots__/index.test.js.snap +++ b/components/progress/__tests__/__snapshots__/index.test.js.snap @@ -10,6 +10,9 @@ exports[`Progress render dashboard 295 gapDegree 1`] = ` + 0%
`; @@ -24,6 +27,9 @@ exports[`Progress render dashboard 296 gapDegree 1`] = ` + 0%
`; @@ -38,6 +44,9 @@ exports[`Progress render dashboard zero gapDegree 1`] = ` + 0%
`; @@ -118,6 +127,9 @@ exports[`Progress render strokeColor 1`] = ` + 50%
`; diff --git a/components/progress/__tests__/index.test.js b/components/progress/__tests__/index.test.js index 218521be35..2694a167f2 100644 --- a/components/progress/__tests__/index.test.js +++ b/components/progress/__tests__/index.test.js @@ -82,7 +82,7 @@ describe('Progress', () => { const wrapper = mount(Progress, { props: { percent: 50, - successPercent: 10, + success: { percent: 10 }, format: (percent, successPercent) => `${percent} ${successPercent}`, }, sync: false, @@ -213,17 +213,17 @@ describe('Progress', () => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); }); - // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 - it('should show success status when status is invalid', () => { - const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - const wrapper = mount({ - render() { - return ; - }, - }); - expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); - errorSpy.mockRestore(); - }); + // // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 + // it('should show success status when status is invalid', () => { + // const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + // const wrapper = mount({ + // render() { + // return ; + // }, + // }); + // expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + // errorSpy.mockRestore(); + // }); it('should support steps', () => { const wrapper = mount({ diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index c8d2e1c700..668470e9e2 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -11,7 +11,6 @@ import Steps from './Steps'; import { getSuccessPercent, validProgress } from './utils'; import useConfigInject from '../_util/hooks/useConfigInject'; import devWarning from '../vc-util/devWarning'; -import type { ProgressStatusesType } from './props'; import { progressProps, progressStatuses } from './props'; export default defineComponent({ @@ -26,9 +25,14 @@ export default defineComponent({ gapDegree: 0, strokeLinecap: 'round', }), + slots: ['format'], setup(props, { slots }) { const { prefixCls, direction } = useConfigInject('progress', props); - + devWarning( + props.successPercent == undefined, + 'Progress', + '`successPercent` is deprecated. Please use `success.percent` instead.', + ); const classString = computed(() => { const { type, showInfo, size } = props; const pre = prefixCls.value; @@ -41,44 +45,44 @@ export default defineComponent({ }; }); - const getPercentNumber = () => { + const percentNumber = computed(() => { const { percent = 0 } = props; - const successPercent = getSuccessPercent(props.success, props.successPercent); + const successPercent = getSuccessPercent(props); return parseInt( successPercent !== undefined ? successPercent.toString() : percent.toString(), 10, ); - }; + }); - const getProgressStatus = () => { + const progressStatus = computed(() => { const { status } = props; - if (progressStatuses.indexOf(status) < 0 && getPercentNumber() >= 100) { + if (progressStatuses.indexOf(status) < 0 && percentNumber.value >= 100) { return 'success'; } return status || 'normal'; - }; + }); - const renderProcessInfo = (prefixCls: string, progressStatus: ProgressStatusesType) => { + const renderProcessInfo = () => { const { showInfo, format, type, percent } = props; - const successPercent = getSuccessPercent(props.success, props.successPercent); + const successPercent = getSuccessPercent(props); if (!showInfo) return null; let text: VNodeChild; - const textFormatter = format || slots?.format || (percentNumber => `${percentNumber}%`); + const textFormatter = format || slots?.format || ((val: number) => `${val}%`); const isLineType = type === 'line'; if ( format || slots?.format || - (progressStatus !== 'exception' && progressStatus !== 'success') + (progressStatus.value !== 'exception' && progressStatus.value !== 'success') ) { text = textFormatter(validProgress(percent), validProgress(successPercent)); - } else if (progressStatus === 'exception') { + } else if (progressStatus.value === 'exception') { text = isLineType ? : ; - } else if (progressStatus === 'success') { + } else if (progressStatus.value === 'success') { text = isLineType ? : ; } return ( - + {text} ); @@ -86,14 +90,7 @@ export default defineComponent({ return () => { const { type, steps, strokeColor } = props; - const progressStatus = getProgressStatus(); - const progressInfo = renderProcessInfo(prefixCls.value, progressStatus); - - devWarning( - props.successPercent == undefined, - 'Progress', - '`successPercent` is deprecated. Please use `success.percent` instead.', - ); + const progressInfo = renderProcessInfo(); let progress: VNodeChild; // Render progress shape @@ -122,7 +119,7 @@ export default defineComponent({ const classNames = { ...classString.value, - [`${prefixCls.value}-status-${progressStatus}`]: true, + [`${prefixCls.value}-status-${progressStatus.value}`]: true, }; return
{progress}
; diff --git a/components/progress/props.ts b/components/progress/props.ts index 7933fdf472..fc038f3f56 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -14,8 +14,6 @@ export type ProgressGradient = { direction?: string } & (StringGradients | FromT export interface SuccessProps { percent?: number; - /** @deprecated Use `percent` instead */ - progress?: number; strokeColor?: string; } diff --git a/components/progress/style/index.less b/components/progress/style/index.less index 45fc23e7c0..dfde2fcbb0 100644 --- a/components/progress/style/index.less +++ b/components/progress/style/index.less @@ -189,15 +189,15 @@ @keyframes ~"@{ant-prefix}-progress-active" { 0% { - width: 0; + transform: translateX(-100%) scaleX(0); opacity: 0.1; } 20% { - width: 0; + transform: translateX(-100%) scaleX(0); opacity: 0.5; } 100% { - width: 100%; + transform: translateX(0) scaleX(1); opacity: 0; } } diff --git a/components/progress/style/index.ts b/components/progress/style/index.tsx similarity index 100% rename from components/progress/style/index.ts rename to components/progress/style/index.tsx diff --git a/components/progress/utils.ts b/components/progress/utils.ts index df8b332265..760d696e2c 100644 --- a/components/progress/utils.ts +++ b/components/progress/utils.ts @@ -1,5 +1,4 @@ import devWarning from '../vc-util/devWarning'; -import type { ProgressProps } from './props'; export function validProgress(progress: number | undefined) { if (!progress || progress < 0) { @@ -11,10 +10,16 @@ export function validProgress(progress: number | undefined) { return progress; } -export function getSuccessPercent( - success?: ProgressProps['success'], - successPercent?: ProgressProps['successPercent'], -) { +export function getSuccessPercent({ + success, + successPercent, +}: { + success?: { + progress?: number; + percent?: number; + }; + successPercent?: number; +}) { let percent = successPercent; /** @deprecated Use `percent` instead */ if (success && 'progress' in success) { diff --git a/components/vc-progress/index.ts b/components/vc-progress/index.ts index 199a021db2..7969fd0472 100644 --- a/components/vc-progress/index.ts +++ b/components/vc-progress/index.ts @@ -1,6 +1,7 @@ -// based on rc-progress 2.5.2 +// based on rc-progress 3.1.4 import Progress, { Line, Circle } from './src'; -export { Line, Circle }; +import type { ProgressProps } from './src'; +export { Line, Circle, ProgressProps }; export default Progress; diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx index abb5c7cfbf..3c8ef3dd01 100644 --- a/components/vc-progress/src/Circle.tsx +++ b/components/vc-progress/src/Circle.tsx @@ -2,6 +2,7 @@ import { useTransitionDuration, defaultProps } from './common'; import { propTypes, GapPositionType } from './types'; import { computed, defineComponent, ref } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; +import { useRef } from '../../_util/hooks/useRef'; let gradientSeed = 0; @@ -73,7 +74,8 @@ export default defineComponent({ const percentList = computed(() => toArray(props.percent)); const strokeColorList = computed(() => toArray(props.strokeColor)); - const paths = useTransitionDuration(percentList); + const [setRef, paths] = useRef(); + useTransitionDuration(paths); const getStokeList = () => { const { prefixCls, strokeWidth, strokeLinecap, gapDegree, gapPosition } = props; @@ -108,7 +110,7 @@ export default defineComponent({ class: `${prefixCls}-circle-path`, style: pathStyle, }; - return (paths.value[index].value = c)} {...pathProps} />; + return setRef(c, index)} {...pathProps} />; }); }; diff --git a/components/vc-progress/src/Line.tsx b/components/vc-progress/src/Line.tsx index d61a5280d8..578e859265 100644 --- a/components/vc-progress/src/Line.tsx +++ b/components/vc-progress/src/Line.tsx @@ -1,3 +1,4 @@ +import { useRef } from '../../_util/hooks/useRef'; import { computed, defineComponent } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; import { useTransitionDuration, defaultProps } from './common'; @@ -11,13 +12,54 @@ export default defineComponent({ const { percent } = props; return Array.isArray(percent) ? percent : [percent]; }); + const percentListProps = computed(() => { + const { prefixCls, strokeLinecap, strokeWidth, transition } = props; + let stackPtg = 0; + return percentList.value.map((ptg, index) => { + let dashPercent = 1; + switch (strokeLinecap) { + case 'round': + dashPercent = 1 - strokeWidth / 100; + break; + case 'square': + dashPercent = 1 - strokeWidth / 2 / 100; + break; + default: + dashPercent = 1; + break; + } + const pathStyle = { + strokeDasharray: `${ptg * dashPercent}px, 100px`, + strokeDashoffset: `-${stackPtg}px`, + transition: + transition || + 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', + }; + const color = + strokeColorList.value[index] || strokeColorList.value[strokeColorList.value.length - 1]; + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString.value, + 'stroke-linecap': strokeLinecap, + stroke: color as string, + 'stroke-width': strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-line-path`, + style: pathStyle, + }; + return pathProps; + }); + }); const strokeColorList = computed(() => { const { strokeColor } = props; return Array.isArray(strokeColor) ? strokeColor : [strokeColor]; }); - - const paths = useTransitionDuration(percentList); + const [setRef, paths] = useRef(); + useTransitionDuration(paths); const center = computed(() => props.strokeWidth / 2); const right = computed(() => 100 - props.strokeWidth / 2); @@ -52,8 +94,6 @@ export default defineComponent({ delete restProps.gapPosition; - let stackPtg = 0; - return ( - {percentList.value.map((ptg, index) => { - const pathStyle = { - strokeDasharray: `${ptg}px, 100px`, - strokeDashoffset: `-${stackPtg}px`, - transition: - transition || - 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', - }; - const color = - strokeColorList.value[index] || - strokeColorList.value[strokeColorList.value.length - 1]; - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString.value, - 'stroke-linecap': strokeLinecap, - stroke: color as string, - 'stroke-width': strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-path`, - style: pathStyle, - }; - - return (paths.value[index].value = c)} {...pathProps} />; + {percentListProps.value.map((pathProps, index) => { + return setRef(c, index)} {...pathProps} />; })} ); diff --git a/components/vc-progress/src/common.ts b/components/vc-progress/src/common.ts index 334e9577c2..25da82c350 100644 --- a/components/vc-progress/src/common.ts +++ b/components/vc-progress/src/common.ts @@ -1,5 +1,5 @@ -import type { Ref } from 'vue'; -import { ref, onUpdated, computed } from 'vue'; +import type { Refs } from '../../_util/hooks/useRef'; +import { ref, onUpdated } from 'vue'; import type { ProgressProps } from './types'; export const defaultProps: Partial = { @@ -12,8 +12,7 @@ export const defaultProps: Partial = { trailWidth: 1, }; -export const useTransitionDuration = (percentList: Ref) => { - const paths = computed(() => percentList.value.map(() => ref())); +export const useTransitionDuration = (paths: Refs) => { const prevTimeStamp = ref(null); onUpdated(() => { @@ -21,7 +20,7 @@ export const useTransitionDuration = (percentList: Ref) => { let updated = false; Object.keys(paths.value).forEach(key => { - const path = paths.value[key].value; + const path = paths.value[key]; if (!path) { return; } diff --git a/package.json b/package.json index 8c262020b4..51fdeaa1d0 100644 --- a/package.json +++ b/package.json @@ -203,6 +203,7 @@ "xhr-mock": "^2.5.1" }, "dependencies": { + "@ant-design/colors": "^5.1.1", "@ant-design/icons-vue": "^6.0.0", "@babel/runtime": "^7.10.5", "@simonwep/pickr": "~1.8.0", diff --git a/v2-doc b/v2-doc index d965edbb90..d2c096476e 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit d965edbb9045771095acf91489145e13c975cb51 +Subproject commit d2c096476ee3e8d27fbb9e1cb7933887501b1fab From 2b6715aaa8fce04a582d632c103a666ed989dce5 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Mon, 12 Jul 2021 17:04:51 +0800 Subject: [PATCH 14/28] refactor: progress --- components/progress/Circle.tsx | 13 ++++--------- components/progress/Line.tsx | 4 ++-- components/progress/Steps.tsx | 5 +++-- components/progress/progress.tsx | 2 +- components/progress/props.ts | 6 +++--- components/vc-progress/src/Circle.tsx | 3 ++- v2-doc | 2 +- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx index c2265d354e..4a055b338f 100644 --- a/components/progress/Circle.tsx +++ b/components/progress/Circle.tsx @@ -1,17 +1,12 @@ -import type { CSSProperties, ExtractPropTypes } from 'vue'; +import type { CSSProperties } from 'vue'; import { computed, defineComponent } from 'vue'; import { presetPrimaryColors } from '@ant-design/colors'; import { Circle as VCCircle } from '../vc-progress'; import { getSuccessPercent, validProgress } from './utils'; +import type { ProgressProps } from './props'; import { progressProps } from './props'; -import PropTypes from '../_util/vue-types'; -const circleProps = { - ...progressProps, - prefixCls: PropTypes.string, - // progressStatus: PropTypes.string, -}; -export type CircleProps = Partial>; +export type CircleProps = ProgressProps; function getPercentage({ percent, success, successPercent }: CircleProps) { const realSuccessPercent = validProgress(getSuccessPercent({ success, successPercent })); @@ -19,8 +14,8 @@ function getPercentage({ percent, success, successPercent }: CircleProps) { } export default defineComponent({ - props: progressProps, inheritAttrs: false, + props: progressProps(), setup(props, { slots }) { const gapDeg = computed(() => { // Support gapDeg = 0 when type = 'dashboard' diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx index df9bfe42c6..db01a2e6c5 100644 --- a/components/progress/Line.tsx +++ b/components/progress/Line.tsx @@ -7,7 +7,7 @@ import { progressProps } from './props'; import { getSuccessPercent, validProgress } from './utils'; const lineProps = { - ...progressProps, + ...progressProps(), prefixCls: PropTypes.string, direction: { type: String as PropType, @@ -68,8 +68,8 @@ export const handleGradient = (strokeColor: ProgressGradient, directionConfig: D }; export default defineComponent({ - props: lineProps, name: 'Line', + props: lineProps, setup(props, { slots }) { const backgroundProps = computed(() => { const { strokeColor, direction } = props; diff --git a/components/progress/Steps.tsx b/components/progress/Steps.tsx index e50518bff0..6d7543aec6 100644 --- a/components/progress/Steps.tsx +++ b/components/progress/Steps.tsx @@ -1,11 +1,12 @@ -import { computed, ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import type { ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import { computed } from 'vue'; import { defineComponent } from 'vue'; import PropTypes from '../_util/vue-types'; import type { ProgressSize } from './props'; import { progressProps } from './props'; const stepsProps = { - ...progressProps, + ...progressProps(), steps: PropTypes.number, size: { type: String as PropType, diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index 668470e9e2..aa5feb0101 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -15,7 +15,7 @@ import { progressProps, progressStatuses } from './props'; export default defineComponent({ name: 'AProgress', - props: initDefaultProps(progressProps, { + props: initDefaultProps(progressProps(), { type: 'line', percent: 0, showInfo: true, diff --git a/components/progress/props.ts b/components/progress/props.ts index fc038f3f56..fb5c2de10a 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -17,7 +17,7 @@ export interface SuccessProps { strokeColor?: string; } -export const progressProps = { +export const progressProps = () => ({ prefixCls: PropTypes.string, type: PropTypes.oneOf(ProgressType), percent: PropTypes.number, @@ -41,6 +41,6 @@ export const progressProps = { steps: PropTypes.number, /** @deprecated Use `success` instead */ successPercent: PropTypes.number, -}; +}); -export type ProgressProps = Partial>; +export type ProgressProps = Partial>>; diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx index 3c8ef3dd01..a4aa86ece8 100644 --- a/components/vc-progress/src/Circle.tsx +++ b/components/vc-progress/src/Circle.tsx @@ -1,5 +1,6 @@ import { useTransitionDuration, defaultProps } from './common'; -import { propTypes, GapPositionType } from './types'; +import type { GapPositionType } from './types'; +import { propTypes } from './types'; import { computed, defineComponent, ref } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; import { useRef } from '../../_util/hooks/useRef'; diff --git a/v2-doc b/v2-doc index d2c096476e..57c08127bf 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit d2c096476ee3e8d27fbb9e1cb7933887501b1fab +Subproject commit 57c08127bf0e3e826b03f845df3e1332820964e5 From a523c39d2748fd66e0506df43da57ecd23762cdf Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Tue, 6 Jul 2021 21:58:05 +0800 Subject: [PATCH 15/28] fix: timepicker error border not show #4331 --- .../__snapshots__/index.test.js.snap | 102 +++++++++--------- .../__snapshots__/index.test.js.snap | 2 +- components/time-picker/index.tsx | 4 + components/vc-time-picker/TimePicker.jsx | 4 +- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap index 7c1beb76dc..10a6cb023d 100644 --- a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap +++ b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap @@ -609,7 +609,7 @@ exports[`Locale Provider should display the text as ar 1`] = `
- + ~ @@ -1155,7 +1155,7 @@ exports[`Locale Provider should display the text as bg 1`] = `
- + ~ @@ -1701,7 +1701,7 @@ exports[`Locale Provider should display the text as ca 1`] = `
- + ~ @@ -2247,7 +2247,7 @@ exports[`Locale Provider should display the text as cs 1`] = `
- + ~ @@ -2793,7 +2793,7 @@ exports[`Locale Provider should display the text as da 1`] = `
- + ~ @@ -3339,7 +3339,7 @@ exports[`Locale Provider should display the text as de 1`] = `
- + ~ @@ -3885,7 +3885,7 @@ exports[`Locale Provider should display the text as el 1`] = `
- + ~ @@ -4431,7 +4431,7 @@ exports[`Locale Provider should display the text as en 1`] = `
- + ~ @@ -4977,7 +4977,7 @@ exports[`Locale Provider should display the text as en-gb 1`] = `
- + ~ @@ -5523,7 +5523,7 @@ exports[`Locale Provider should display the text as es 1`] = `
- + ~ @@ -6069,7 +6069,7 @@ exports[`Locale Provider should display the text as et 1`] = `
- + ~ @@ -6615,7 +6615,7 @@ exports[`Locale Provider should display the text as fa 1`] = `
- + ~ @@ -7161,7 +7161,7 @@ exports[`Locale Provider should display the text as fi 1`] = `
- + ~ @@ -7707,7 +7707,7 @@ exports[`Locale Provider should display the text as fr 1`] = `
- + ~ @@ -8253,7 +8253,7 @@ exports[`Locale Provider should display the text as fr 2`] = `
- + ~ @@ -8799,7 +8799,7 @@ exports[`Locale Provider should display the text as he 1`] = `
- + ~ @@ -9345,7 +9345,7 @@ exports[`Locale Provider should display the text as hi 1`] = `
- + ~ @@ -9891,7 +9891,7 @@ exports[`Locale Provider should display the text as hr 1`] = `
- + ~ @@ -10437,7 +10437,7 @@ exports[`Locale Provider should display the text as hu 1`] = `
- + ~ @@ -10983,7 +10983,7 @@ exports[`Locale Provider should display the text as hy 1`] = `
- + ~ @@ -11529,7 +11529,7 @@ exports[`Locale Provider should display the text as id 1`] = `
- + ~ @@ -12075,7 +12075,7 @@ exports[`Locale Provider should display the text as is 1`] = `
- + ~ @@ -12621,7 +12621,7 @@ exports[`Locale Provider should display the text as it 1`] = `
- + ~ @@ -13167,7 +13167,7 @@ exports[`Locale Provider should display the text as ja 1`] = `
- + ~ @@ -13713,7 +13713,7 @@ exports[`Locale Provider should display the text as kn 1`] = `
- + ~ @@ -14259,7 +14259,7 @@ exports[`Locale Provider should display the text as ko 1`] = `
- + ~ @@ -14805,7 +14805,7 @@ exports[`Locale Provider should display the text as ku-iq 1`] = `
- + ~ @@ -15351,7 +15351,7 @@ exports[`Locale Provider should display the text as lv 1`] = `
- + ~ @@ -15897,7 +15897,7 @@ exports[`Locale Provider should display the text as mk 1`] = `
- + ~ @@ -16443,7 +16443,7 @@ exports[`Locale Provider should display the text as mn-mn 1`] = `
- + ~ @@ -16989,7 +16989,7 @@ exports[`Locale Provider should display the text as ms-my 1`] = `
- + ~ @@ -17535,7 +17535,7 @@ exports[`Locale Provider should display the text as nb 1`] = `
- + ~ @@ -18081,7 +18081,7 @@ exports[`Locale Provider should display the text as ne-np 1`] = `
- + ~ @@ -18627,7 +18627,7 @@ exports[`Locale Provider should display the text as nl 1`] = `
- + ~ @@ -19173,7 +19173,7 @@ exports[`Locale Provider should display the text as nl-be 1`] = `
- + ~ @@ -19719,7 +19719,7 @@ exports[`Locale Provider should display the text as pl 1`] = `
- + ~ @@ -20265,7 +20265,7 @@ exports[`Locale Provider should display the text as pt 1`] = `
- + ~ @@ -20811,7 +20811,7 @@ exports[`Locale Provider should display the text as pt-br 1`] = `
- + ~ @@ -21357,7 +21357,7 @@ exports[`Locale Provider should display the text as ro 1`] = `
- + ~ @@ -21903,7 +21903,7 @@ exports[`Locale Provider should display the text as ru 1`] = `
- + ~ @@ -22449,7 +22449,7 @@ exports[`Locale Provider should display the text as sk 1`] = `
- + ~ @@ -22995,7 +22995,7 @@ exports[`Locale Provider should display the text as sl 1`] = `
- + ~ @@ -23541,7 +23541,7 @@ exports[`Locale Provider should display the text as sr 1`] = `
- + ~ @@ -24087,7 +24087,7 @@ exports[`Locale Provider should display the text as sv 1`] = `
- + ~ @@ -24633,7 +24633,7 @@ exports[`Locale Provider should display the text as ta 1`] = `
- + ~ @@ -25179,7 +25179,7 @@ exports[`Locale Provider should display the text as th 1`] = `
- + ~ @@ -25725,7 +25725,7 @@ exports[`Locale Provider should display the text as tr 1`] = `
- + ~ @@ -26271,7 +26271,7 @@ exports[`Locale Provider should display the text as uk 1`] = `
- + ~ @@ -26817,7 +26817,7 @@ exports[`Locale Provider should display the text as vi 1`] = `
- + ~ @@ -27363,7 +27363,7 @@ exports[`Locale Provider should display the text as zh-cn 1`] = `
- + ~ @@ -27909,7 +27909,7 @@ exports[`Locale Provider should display the text as zh-tw 1`] = `
- + ~ diff --git a/components/time-picker/__tests__/__snapshots__/index.test.js.snap b/components/time-picker/__tests__/__snapshots__/index.test.js.snap index efdd99b93a..20ee541245 100644 --- a/components/time-picker/__tests__/__snapshots__/index.test.js.snap +++ b/components/time-picker/__tests__/__snapshots__/index.test.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TimePicker not render clean icon when allowClear is false 1`] = ` - + `; diff --git a/components/time-picker/index.tsx b/components/time-picker/index.tsx index 4795619204..8103baf17d 100644 --- a/components/time-picker/index.tsx +++ b/components/time-picker/index.tsx @@ -20,6 +20,7 @@ import { TimeOrTimesType, } from '../_util/moment-util'; import { tuple, withInstall } from '../_util/type'; +import classNames from '../_util/classNames'; export function generateShowHourMinuteSecond(format: string) { // Ref: http://momentjs.com/docs/#/parsing/string-format/ @@ -213,6 +214,8 @@ const TimePicker = defineComponent({ const { prefixCls: customizePrefixCls, getPopupContainer, placeholder, size } = props; const getPrefixCls = this.configProvider.getPrefixCls; const prefixCls = getPrefixCls('time-picker', customizePrefixCls); + const inputPrefixCls = getPrefixCls('input'); + const pickerInputClass = classNames(`${prefixCls}-input`, inputPrefixCls); const format = this.getDefaultFormat(); const pickerClassName = { @@ -236,6 +239,7 @@ const TimePicker = defineComponent({ ...this.$attrs, allowEmpty: this.getAllowClear(), prefixCls, + pickerInputClass, getPopupContainer: getPopupContainer || getContextPopupContainer, format, value: this.sValue, diff --git a/components/vc-time-picker/TimePicker.jsx b/components/vc-time-picker/TimePicker.jsx index 8f16b52be7..acf433302c 100644 --- a/components/vc-time-picker/TimePicker.jsx +++ b/components/vc-time-picker/TimePicker.jsx @@ -35,6 +35,7 @@ export default defineComponent({ return moment(); }, }, + pickerInputClass: String, inputReadOnly: PropTypes.looseBool, disabled: PropTypes.looseBool, allowEmpty: PropTypes.looseBool, @@ -346,6 +347,7 @@ export default defineComponent({ onFocus, onBlur, popupStyle, + pickerInputClass, } = this; const { class: className, style } = this.$attrs; const popupClassName = this.getPopupClassName(); @@ -368,7 +370,7 @@ export default defineComponent({ > Date: Wed, 7 Jul 2021 21:24:14 +0800 Subject: [PATCH 16/28] fix(UploadDragger): fix UploadDrager no export (#4334) --- components/components.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/components.ts b/components/components.ts index f02cb7ad89..e901d6a9a7 100644 --- a/components/components.ts +++ b/components/components.ts @@ -192,6 +192,6 @@ export { export type { UploadProps } from './upload'; -export { default as Upload } from './upload'; +export { default as Upload, UploadDragger } from './upload'; export { default as LocaleProvider } from './locale-provider'; From 46c86095aea41ac40a0c11ff015f0cdc9cf39752 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 7 Jul 2021 21:35:41 +0800 Subject: [PATCH 17/28] refactor(switch): support customize checked value #4329 (#4332) * refactor(switch): support customize checked value #4329 * test: add test case * refactor: update props name * refactor: update ts * refactor: optimize --- components/switch/__tests__/index.test.js | 28 +++++++++ components/switch/index.tsx | 74 ++++++++++++++--------- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/components/switch/__tests__/index.test.js b/components/switch/__tests__/index.test.js index 73061036fc..55980437b9 100644 --- a/components/switch/__tests__/index.test.js +++ b/components/switch/__tests__/index.test.js @@ -4,6 +4,7 @@ import focusTest from '../../../tests/shared/focusTest'; import { resetWarned } from '../../_util/warning'; import mountTest from '../../../tests/shared/mountTest'; import { ref } from 'vue'; +import { asyncExpect } from '@/tests/utils'; describe('Switch', () => { focusTest(Switch); @@ -42,4 +43,31 @@ describe('Switch', () => { ); errorSpy.mockRestore(); }); + + it('customize checked value should work', async () => { + resetWarned(); + const checked = ref(1); + const onUpdate = val => (checked.value = val); + const wrapper = mount({ + render() { + return ( + + ); + }, + }); + await asyncExpect(() => { + wrapper.find('button').trigger('click'); + }); + expect(checked.value).toBe(2); + + await asyncExpect(() => { + wrapper.find('button').trigger('click'); + }); + expect(checked.value).toBe(1); + }); }); diff --git a/components/switch/index.tsx b/components/switch/index.tsx index 4febcce829..647b3961f0 100644 --- a/components/switch/index.tsx +++ b/components/switch/index.tsx @@ -1,4 +1,4 @@ -import type { ExtractPropTypes } from 'vue'; +import type { ExtractPropTypes, PropType } from 'vue'; import { defineComponent, inject, @@ -19,23 +19,35 @@ import { tuple, withInstall } from '../_util/type'; import { getPropsSlot } from '../_util/props-util'; import Omit from 'omit.js'; -export const SwitchSizes = tuple('small', 'default', 'large'); +export const SwitchSizes = tuple('small', 'default'); const switchProps = { prefixCls: PropTypes.string, size: PropTypes.oneOf(SwitchSizes), disabled: PropTypes.looseBool, - checkedChildren: PropTypes.any, - unCheckedChildren: PropTypes.any, + checkedChildren: PropTypes.VNodeChild, + unCheckedChildren: PropTypes.VNodeChild, tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), autofocus: PropTypes.looseBool, loading: PropTypes.looseBool, - checked: PropTypes.looseBool, - onChange: PropTypes.func, - onClick: PropTypes.func, - onKeydown: PropTypes.func, - onMouseup: PropTypes.func, - 'onUpdate:checked': PropTypes.func, + checked: PropTypes.any, + checkedValue: PropTypes.any.def(true), + uncheckedValue: PropTypes.any.def(false), + onChange: { + type: Function as PropType<(checked: any, e: Event) => void>, + }, + onClick: { + type: Function as PropType<(checked: any, e: Event) => void>, + }, + onKeydown: { + type: Function as PropType<(e: Event) => void>, + }, + onMouseup: { + type: Function as PropType<(e: Event) => void>, + }, + 'onUpdate:checked': { + type: Function as PropType<(checked: any) => void>, + }, }; export type SwitchProps = Partial>; @@ -46,7 +58,7 @@ const Switch = defineComponent({ inheritAttrs: false, props: switchProps, emits: ['update:checked', 'mouseup', 'change', 'click', 'keydown'], - setup(props: SwitchProps, { attrs, slots, expose, emit }) { + setup(props, { attrs, slots, expose, emit }) { onBeforeMount(() => { warning( !('defaultChecked' in attrs), @@ -59,12 +71,13 @@ const Switch = defineComponent({ '`value` is not validate prop, do you mean `checked`?', ); }); - const checked = ref(props.checked !== undefined ? !!props.checked : !!attrs.defaultChecked); + const checked = ref(props.checked !== undefined ? props.checked : attrs.defaultChecked); + const checkedStatus = computed(() => checked.value === props.checkedValue); watch( () => props.checked, () => { - checked.value = !!props.checked; + checked.value = props.checked; }, ); @@ -92,29 +105,26 @@ const Switch = defineComponent({ }); }); - const setChecked = (check: boolean, e: MouseEvent | KeyboardEvent) => { + const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => { if (props.disabled) { return; } - if (props.checked === undefined) { - checked.value = check; - } emit('update:checked', check); emit('change', check, e); }; const handleClick = (e: MouseEvent) => { focus(); - const newChecked = !checked.value; + const newChecked = checkedStatus.value ? props.uncheckedValue : props.checkedValue; setChecked(newChecked, e); emit('click', newChecked, e); }; const handleKeyDown = (e: KeyboardEvent) => { if (e.keyCode === KeyCode.LEFT) { - setChecked(false, e); + setChecked(props.uncheckedValue, e); } else if (e.keyCode === KeyCode.RIGHT) { - setChecked(true, e); + setChecked(props.checkedValue, e); } emit('keydown', e); }; @@ -123,6 +133,13 @@ const Switch = defineComponent({ refSwitchNode.value?.blur(); emit('mouseup', e); }; + + const classNames = computed(() => ({ + [`${prefixCls.value}-small`]: props.size === 'small', + [`${prefixCls.value}-loading`]: props.loading, + [`${prefixCls.value}-checked`]: checkedStatus.value, + [`${prefixCls.value}-disabled`]: props.disabled, + })); return () => ( `; diff --git a/components/switch/__tests__/index.test.js b/components/switch/__tests__/index.test.js index 55980437b9..5fb8e501ce 100644 --- a/components/switch/__tests__/index.test.js +++ b/components/switch/__tests__/index.test.js @@ -54,7 +54,7 @@ describe('Switch', () => { ); diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap index 15eaac19f3..a203e4711b 100644 --- a/tests/__snapshots__/index.test.js.snap +++ b/tests/__snapshots__/index.test.js.snap @@ -126,6 +126,7 @@ Array [ "TypographyText", "TypographyTitle", "Upload", + "UploadDragger", "LocaleProvider", ] `; From eed264b7e52d9fe3044fab7d2f7b469c48a4a386 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 7 Jul 2021 22:09:09 +0800 Subject: [PATCH 20/28] feat: udpate switch ts --- components/switch/index.tsx | 22 ++++++++++++++-------- v2-doc | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/components/switch/index.tsx b/components/switch/index.tsx index cfd7e25917..0d038946a2 100644 --- a/components/switch/index.tsx +++ b/components/switch/index.tsx @@ -11,7 +11,7 @@ import Omit from 'omit.js'; import useConfigInject from '../_util/hooks/useConfigInject'; export const SwitchSizes = tuple('small', 'default'); - +type CheckedType = boolean | string | number; const switchProps = { prefixCls: PropTypes.string, size: PropTypes.oneOf(SwitchSizes), @@ -21,14 +21,20 @@ const switchProps = { tabindex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), autofocus: PropTypes.looseBool, loading: PropTypes.looseBool, - checked: PropTypes.any, - checkedValue: PropTypes.any.def(true), - unCheckedValue: PropTypes.any.def(false), + checked: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.looseBool]), + checkedValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.looseBool]).def( + true, + ), + unCheckedValue: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.looseBool, + ]).def(false), onChange: { - type: Function as PropType<(checked: any, e: Event) => void>, + type: Function as PropType<(checked: CheckedType, e: Event) => void>, }, onClick: { - type: Function as PropType<(checked: any, e: Event) => void>, + type: Function as PropType<(checked: CheckedType, e: Event) => void>, }, onKeydown: { type: Function as PropType<(e: Event) => void>, @@ -37,7 +43,7 @@ const switchProps = { type: Function as PropType<(e: Event) => void>, }, 'onUpdate:checked': { - type: Function as PropType<(checked: any) => void>, + type: Function as PropType<(checked: CheckedType) => void>, }, }; @@ -92,7 +98,7 @@ const Switch = defineComponent({ }); }); - const setChecked = (check: any, e: MouseEvent | KeyboardEvent) => { + const setChecked = (check: CheckedType, e: MouseEvent | KeyboardEvent) => { if (props.disabled) { return; } diff --git a/v2-doc b/v2-doc index 89612874e4..b9934e0de1 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit 89612874e476dc788711cdaedfd037b9497e5e78 +Subproject commit b9934e0de1052a73978cdb9fb401fa7b5eaf8ecc From 185e51c50087317b317cf049b42aab827e4e50a1 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 7 Jul 2021 22:16:13 +0800 Subject: [PATCH 21/28] docs: remove ie11 --- CHANGELOG.en-US.md | 1 - CHANGELOG.zh-CN.md | 1 - README-zh_CN.md | 2 +- README.md | 2 +- v2-doc | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 43818f861f..8903181964 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -437,7 +437,6 @@ ### Compatibility adjustment -- The minimum supported version of IE is IE 11. - The minimum supported version of Vue is Vue 3.0. #### Adjusted API diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 80f6cda2de..72155c3eb3 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -440,7 +440,6 @@ ### 兼容性调整 -- IE 最低支持版本为 IE 11。 - Vue 最低支持版本为 Vue 3.0。 #### 调整的 API diff --git a/README-zh_CN.md b/README-zh_CN.md index f1af042d86..b45bf577f3 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -28,7 +28,7 @@ An enterprise-class UI components based on Ant Design and Vue 3. ## 支持环境 -- 现代浏览器和 IE11 及以上。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性)) +- 现代浏览器。1.x 版本支持 IE 9+(需要 [polyfills](https://www.antdv.com/docs/vue/getting-started-cn/#兼容性)) - 支持服务端渲染。 - [Electron](https://electronjs.org/) - 支持 Vue 2 和 Vue 3 diff --git a/README.md b/README.md index 437cb6931a..744b89e363 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ English | [简体中文](./README-zh_CN.md) ## Environment Support -- Modern browsers and Internet Explorer 11+. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility)) +- Modern browsers. v1.x support Internet Explorer 9+ (with [polyfills](https://www.antdv.com/docs/vue/getting-started/#Compatibility)) - Server-side Rendering - Support Vue 2 & Vue 3 - [Electron](https://electronjs.org/) diff --git a/v2-doc b/v2-doc index b9934e0de1..d965edbb90 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit b9934e0de1052a73978cdb9fb401fa7b5eaf8ecc +Subproject commit d965edbb9045771095acf91489145e13c975cb51 From 59e483b79f389110ebbcef72234f7599d81e8c14 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Thu, 8 Jul 2021 11:36:32 +0800 Subject: [PATCH 22/28] fix: tree-select throw error when use slot title --- components/vc-tree-select/src/Selector/SingleSelector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vc-tree-select/src/Selector/SingleSelector.jsx b/components/vc-tree-select/src/Selector/SingleSelector.jsx index 1f1ab4e0a4..cff2577332 100644 --- a/components/vc-tree-select/src/Selector/SingleSelector.jsx +++ b/components/vc-tree-select/src/Selector/SingleSelector.jsx @@ -64,7 +64,7 @@ const SingleSelector = { if (selectorValueList.length && !this.mirrorSearchValue) { const { label, value } = selectorValueList[0]; selectedValueNodes.push( - + {label || value} , ); From fc0d6870d99ab86e90b6a42f1fd25dfad4c9e2d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BE=E6=96=AF=E7=89=B9=E6=B4=9B?= Date: Sun, 11 Jul 2021 10:00:52 +0800 Subject: [PATCH 23/28] fix: TypeScript definition of Table interface for typescript 4.3.5 (#4353) * fix type for typescript 4.3.5 * Update interface.ts close #4296 --- components/table/interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/table/interface.ts b/components/table/interface.ts index 1784ecf21c..fe56d51334 100644 --- a/components/table/interface.ts +++ b/components/table/interface.ts @@ -60,7 +60,7 @@ export type ColumnProps = Partial> & { filterIcon?: string; filterDropdown?: string; customRender?: string; - [key: string]: string; + [key: string]: string | undefined; }; }; From 190d65fe3ff25ce3dc36444158ee2c7603437c6b Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Sun, 11 Jul 2021 14:13:07 +0800 Subject: [PATCH 24/28] fix: dropdown submenu style error #4351 close #4351 --- components/dropdown/dropdown.tsx | 2 +- components/dropdown/style/index.less | 4 +++- components/menu/src/Menu.tsx | 4 ++++ components/menu/src/SubMenu.tsx | 9 ++++++--- components/menu/src/hooks/useMenuContext.ts | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/dropdown/dropdown.tsx b/components/dropdown/dropdown.tsx index 8838592c9e..7adc7e8ed7 100644 --- a/components/dropdown/dropdown.tsx +++ b/components/dropdown/dropdown.tsx @@ -63,7 +63,7 @@ const Dropdown = defineComponent({ // menu should be focusable in dropdown defaultly const overlayProps = overlayNode && getPropsData(overlayNode); const { selectable = false, focusable = true } = (overlayProps || {}) as any; - const expandIcon = ( + const expandIcon = () => ( diff --git a/components/dropdown/style/index.less b/components/dropdown/style/index.less index 835adb3219..f61c38aba4 100644 --- a/components/dropdown/style/index.less +++ b/components/dropdown/style/index.less @@ -68,6 +68,9 @@ &-submenu-popup { position: absolute; z-index: @zindex-dropdown; + background: transparent; + box-shadow: none; + transform-origin: 0 0; > .@{dropdown-prefix-cls}-menu { transform-origin: 0 0; @@ -81,7 +84,6 @@ ul { margin-right: 0.3em; margin-left: 0.3em; - padding: 0; } } diff --git a/components/menu/src/Menu.tsx b/components/menu/src/Menu.tsx index 45cd20b190..1f94f14538 100644 --- a/components/menu/src/Menu.tsx +++ b/components/menu/src/Menu.tsx @@ -49,6 +49,8 @@ export const menuProps = { triggerSubMenuAction: { type: String as PropType, default: 'hover' }, getPopupContainer: Function as PropType<(node: HTMLElement) => HTMLElement>, + + expandIcon: Function as PropType<(p?: { isOpen: boolean; [key: string]: any }) => any>, }; export type MenuProps = Partial>; @@ -66,6 +68,7 @@ export default defineComponent({ 'click', 'update:activeKey', ], + slots: ['expandIcon'], setup(props, { slots, emit }) { const { prefixCls, direction } = useConfigInject('menu', props); const store = ref>({}); @@ -371,6 +374,7 @@ export default defineComponent({ unRegisterMenuInfo, selectedSubMenuEventKeys, isRootMenu: true, + expandIcon: props.expandIcon || slots.expandIcon, }); return () => { const childList = flattenChildren(slots.default?.()); diff --git a/components/menu/src/SubMenu.tsx b/components/menu/src/SubMenu.tsx index 762da791b1..0aadbd0c76 100644 --- a/components/menu/src/SubMenu.tsx +++ b/components/menu/src/SubMenu.tsx @@ -24,6 +24,7 @@ const subMenuProps = { popupOffset: Array as PropType, internalPopupClose: Boolean, eventKey: String, + expandIcon: Function as PropType<(p?: { isOpen: boolean; [key: string]: any }) => any>, }; export type SubMenuProps = Partial>; @@ -32,7 +33,7 @@ export default defineComponent({ name: 'ASubMenu', inheritAttrs: false, props: subMenuProps, - slots: ['icon', 'title'], + slots: ['icon', 'title', 'expandIcon'], emits: ['titleClick', 'mouseenter', 'mouseleave'], setup(props, { slots, attrs, emit }) { useProvideFirstLevel(false); @@ -84,6 +85,7 @@ export default defineComponent({ selectedSubMenuEventKeys, motion, defaultMotions, + expandIcon: menuExpandIcon, } = useInjectMenu(); registerMenuInfo(eventKey, menuInfo); @@ -226,6 +228,7 @@ export default defineComponent({ const icon = getPropsSlot(slots, props, 'icon'); const title = renderTitle(getPropsSlot(slots, props, 'title'), icon); const subMenuPrefixClsValue = subMenuPrefixCls.value; + const expandIcon = props.expandIcon || slots.expandIcon || menuExpandIcon; let titleNode = (
)} diff --git a/components/menu/src/hooks/useMenuContext.ts b/components/menu/src/hooks/useMenuContext.ts index c200c613ed..c7815f2b3f 100644 --- a/components/menu/src/hooks/useMenuContext.ts +++ b/components/menu/src/hooks/useMenuContext.ts @@ -77,7 +77,7 @@ export interface MenuContextProps { // // Icon // itemIcon?: RenderIconType; - // expandIcon?: RenderIconType; + expandIcon?: (p?: { isOpen: boolean; [key: string]: any }) => any; // // Function onItemClick: MenuClickEventHandler; From 09eb9766b3fee20f4b0654264ebe40c0c78b9ee8 Mon Sep 17 00:00:00 2001 From: zanllp Date: Sun, 11 Jul 2021 14:46:45 +0800 Subject: [PATCH 25/28] =?UTF-8?q?fix(notification):=20=E5=AE=8C=E5=96=84no?= =?UTF-8?q?tification=E7=B1=BB=E5=9E=8B=20(#4346)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/notification/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/notification/index.tsx b/components/notification/index.tsx index 21b5730522..c6a8b712c2 100644 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -212,7 +212,7 @@ function notice(args: NotificationArgsProps) { ); } -const api: any = { +const apiBase = { open: notice, close(key: string) { Object.keys(notificationInstance).forEach(cacheKey => @@ -228,7 +228,11 @@ const api: any = { }, }; -['success', 'info', 'warning', 'error'].forEach(type => { +type NotificationApi = typeof apiBase & + Record) => void>; +const api = apiBase as any as NotificationApi; +const iconTypes: IconType[] = ['success', 'info', 'warning', 'error']; +iconTypes.forEach(type => { api[type] = args => api.open({ ...args, From 05c0d2e4899d7bd0ece9b9f8bdbb3b4cd26738be Mon Sep 17 00:00:00 2001 From: John Date: Sun, 11 Jul 2021 15:25:40 +0800 Subject: [PATCH 26/28] refactor(progress): use composition API (#4355) * refactor(progress): use composition API * refactor(vc-progress): update --- components/progress/Circle.tsx | 112 +++++++++ components/progress/Line.tsx | 131 ++++++++++ components/progress/Steps.tsx | 56 +++++ .../__snapshots__/index.test.js.snap | 210 +++++++++++----- components/progress/__tests__/index.test.js | 225 +++++++++++++++++- components/progress/circle.tsx | 86 ------- components/progress/index.ts | 2 +- components/progress/line.tsx | 93 -------- components/progress/progress.tsx | 142 ++++++----- components/progress/props.ts | 46 +++- components/progress/style/index.less | 27 ++- components/progress/style/rtl.less | 37 +++ components/progress/utils.ts | 25 +- components/style/themes/default.less | 7 +- components/vc-progress/{index.js => index.ts} | 2 +- components/vc-progress/src/Circle.js | 191 --------------- components/vc-progress/src/Circle.tsx | 174 ++++++++++++++ components/vc-progress/src/Line.js | 84 ------- components/vc-progress/src/Line.tsx | 96 ++++++++ components/vc-progress/src/common.ts | 43 ++++ components/vc-progress/src/enhancer.js | 30 --- .../vc-progress/src/{index.js => index.ts} | 3 +- components/vc-progress/src/types.js | 30 --- components/vc-progress/src/types.ts | 31 +++ 24 files changed, 1221 insertions(+), 662 deletions(-) create mode 100644 components/progress/Circle.tsx create mode 100644 components/progress/Line.tsx create mode 100644 components/progress/Steps.tsx delete mode 100644 components/progress/circle.tsx delete mode 100644 components/progress/line.tsx create mode 100644 components/progress/style/rtl.less rename components/vc-progress/{index.js => index.ts} (62%) delete mode 100644 components/vc-progress/src/Circle.js create mode 100644 components/vc-progress/src/Circle.tsx delete mode 100644 components/vc-progress/src/Line.js create mode 100644 components/vc-progress/src/Line.tsx create mode 100644 components/vc-progress/src/common.ts delete mode 100644 components/vc-progress/src/enhancer.js rename components/vc-progress/src/{index.js => index.ts} (53%) delete mode 100644 components/vc-progress/src/types.js create mode 100644 components/vc-progress/src/types.ts diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx new file mode 100644 index 0000000000..8d8ac2eb4d --- /dev/null +++ b/components/progress/Circle.tsx @@ -0,0 +1,112 @@ +import type { CSSProperties, ExtractPropTypes } from 'vue'; +import { computed, defineComponent } from 'vue'; +import { Circle as VCCircle } from '../vc-progress'; +import { getSuccessPercent, validProgress } from './utils'; +import { progressProps } from './props'; +import PropTypes from '../_util/vue-types'; + +const circleProps = { + ...progressProps, + prefixCls: PropTypes.string, + // progressStatus: PropTypes.string, +}; +export type CircleProps = Partial>; + +const statusColorMap = { + normal: '#108ee9', + exception: '#ff5500', + success: '#87d068', +}; + +function getPercentage( + percent: CircleProps['percent'], + success: CircleProps['success'], + successPercent: CircleProps['successPercent'], +) { + const ptg = validProgress(percent); + const realSuccessPercent = getSuccessPercent(success, successPercent); + if (!realSuccessPercent) { + return ptg; + } + return [ + validProgress(realSuccessPercent), + validProgress(ptg - validProgress(realSuccessPercent)), + ]; +} + +function getStrokeColor( + success: CircleProps['success'], + strokeColor: CircleProps['strokeColor'], + successPercent: CircleProps['successPercent'], +) { + const color = strokeColor || null; + const realSuccessPercent = getSuccessPercent(success, successPercent); + if (!realSuccessPercent) { + return color; + } + return [statusColorMap.success, color]; +} + +export default defineComponent({ + props: progressProps, + inheritAttrs: false, + setup(props, { slots }) { + const gapDeg = computed(() => { + // Support gapDeg = 0 when type = 'dashboard' + if (props.gapDegree || props.gapDegree === 0) { + return props.gapDegree; + } + if (props.type === 'dashboard') { + return 75; + } + return undefined; + }); + + const circleStyle = computed(() => { + const circleSize = props.width || 120; + return { + width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, + height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, + fontSize: `${circleSize * 0.15 + 6}px`, + }; + }); + + const circleWidth = computed(() => props.strokeWidth || 6); + const gapPos = computed( + () => props.gapPosition || (props.type === 'dashboard' && 'bottom') || 'top', + ); + + // using className to style stroke color + const strokeColor = computed(() => + getStrokeColor(props.success, props.strokeColor, props.successPercent), + ); + const percent = computed(() => + getPercentage(props.percent, props.success, props.successPercent), + ); + const isGradient = computed( + () => Object.prototype.toString.call(strokeColor.value) === '[object Object]', + ); + + const wrapperClassName = computed(() => ({ + [`${props.prefixCls}-inner`]: true, + [`${props.prefixCls}-circle-gradient`]: isGradient.value, + })); + + return () => ( +
+ + {slots.default?.()} +
+ ); + }, +}); diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx new file mode 100644 index 0000000000..ca01f86719 --- /dev/null +++ b/components/progress/Line.tsx @@ -0,0 +1,131 @@ +import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'; +import { computed, defineComponent } from 'vue'; +import type { Direction } from '../config-provider'; +import PropTypes from '../_util/vue-types'; +import type { StringGradients, ProgressGradient } from './props'; +import { progressProps } from './props'; +import { getSuccessPercent, validProgress } from './utils'; + +const lineProps = { + ...progressProps, + prefixCls: PropTypes.string, + direction: { + type: String as PropType, + }, +}; + +export type LineProps = Partial>; + +/** + * { + * '0%': '#afc163', + * '75%': '#009900', + * '50%': 'green', ====> '#afc163 0%, #66FF00 25%, #00CC00 50%, #009900 75%, #ffffff 100%' + * '25%': '#66FF00', + * '100%': '#ffffff' + * } + */ +export const sortGradient = (gradients: StringGradients) => { + let tempArr = []; + Object.keys(gradients).forEach(key => { + const formattedKey = parseFloat(key.replace(/%/g, '')); + if (!isNaN(formattedKey)) { + tempArr.push({ + key: formattedKey, + value: gradients[key], + }); + } + }); + tempArr = tempArr.sort((a, b) => a.key - b.key); + return tempArr.map(({ key, value }) => `${value} ${key}%`).join(', '); +}; + +/** + * Then this man came to realize the truth: Besides six pence, there is the moon. Besides bread and + * butter, there is the bug. And... Besides women, there is the code. + * + * @example + * { + * "0%": "#afc163", + * "25%": "#66FF00", + * "50%": "#00CC00", // ====> linear-gradient(to right, #afc163 0%, #66FF00 25%, + * "75%": "#009900", // #00CC00 50%, #009900 75%, #ffffff 100%) + * "100%": "#ffffff" + * } + */ +export const handleGradient = (strokeColor: ProgressGradient, directionConfig: Direction) => { + const { + from = '#1890ff', + to = '#1890ff', + direction = directionConfig === 'rtl' ? 'to left' : 'to right', + ...rest + } = strokeColor; + if (Object.keys(rest).length !== 0) { + const sortedGradients = sortGradient(rest as StringGradients); + return { backgroundImage: `linear-gradient(${direction}, ${sortedGradients})` }; + } + return { backgroundImage: `linear-gradient(${direction}, ${from}, ${to})` }; +}; + +export default defineComponent({ + props: lineProps, + setup(props, { slots }) { + const backgroundProps = computed(() => { + const { strokeColor, direction } = props; + return strokeColor && typeof strokeColor !== 'string' + ? handleGradient(strokeColor, direction) + : { + background: strokeColor, + }; + }); + + const trailStyle = computed(() => + props.trailColor + ? { + backgroundColor: props.trailColor, + } + : undefined, + ); + + const percentStyle = computed(() => { + const { percent, strokeWidth, strokeLinecap, size } = props; + return { + width: `${validProgress(percent)}%`, + height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, + borderRadius: strokeLinecap === 'square' ? 0 : '', + ...(backgroundProps.value as any), + }; + }); + + const successPercent = computed(() => { + return getSuccessPercent(props.success, props.successPercent); + }); + const successPercentStyle = computed(() => { + const { strokeWidth, size, strokeLinecap, success } = props; + return { + width: `${validProgress(successPercent.value)}%`, + height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, + borderRadius: strokeLinecap === 'square' ? 0 : '', + backgroundColor: success?.strokeColor, + }; + }); + + const successSegment = computed(() => + successPercent.value !== undefined ? ( +
+ ) : null, + ); + + return () => ( + <> +
+
+
+ {successSegment.value} +
+
+ {slots.default?.()} + + ); + }, +}); diff --git a/components/progress/Steps.tsx b/components/progress/Steps.tsx new file mode 100644 index 0000000000..e50518bff0 --- /dev/null +++ b/components/progress/Steps.tsx @@ -0,0 +1,56 @@ +import { computed, ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import { defineComponent } from 'vue'; +import PropTypes from '../_util/vue-types'; +import type { ProgressSize } from './props'; +import { progressProps } from './props'; + +const stepsProps = { + ...progressProps, + steps: PropTypes.number, + size: { + type: String as PropType, + }, + strokeColor: PropTypes.string, + trailColor: PropTypes.string, +}; + +export type StepsProps = Partial>; + +export default defineComponent({ + props: stepsProps, + setup(props, { slots }) { + const current = computed(() => Math.round(props.steps * ((props.percent || 0) / 100))); + const stepWidth = computed(() => (props.size === 'small' ? 2 : 14)); + + const styledSteps = computed(() => { + const { steps, strokeWidth = 8, strokeColor, trailColor, prefixCls } = props; + + const temp: VNodeChild[] = []; + for (let i = 0; i < steps; i += 1) { + const cls = { + [`${prefixCls}-steps-item`]: true, + [`${prefixCls}-steps-item-active`]: i <= current.value - 1, + }; + temp.push( +
, + ); + } + return temp; + }); + + return () => ( +
+ {styledSteps.value} + {slots.default?.()} +
+ ); + }, +}); diff --git a/components/progress/__tests__/__snapshots__/index.test.js.snap b/components/progress/__tests__/__snapshots__/index.test.js.snap index 0cb227835b..cc19fbf18d 100644 --- a/components/progress/__tests__/__snapshots__/index.test.js.snap +++ b/components/progress/__tests__/__snapshots__/index.test.js.snap @@ -1,7 +1,115 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Progress render dashboard 295 gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + +exports[`Progress render dashboard 296 gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + +exports[`Progress render dashboard zero gapDegree 1`] = ` +
+
+ + + + 0%
+
+`; + exports[`Progress render format 1`] = ` -
+
+
+
+
+
+
+
50 10 +
+`; + +exports[`Progress render negative progress 1`] = ` +
+
+
+
+ +
+
0% +
+`; + +exports[`Progress render negative successPercent 1`] = ` +
+
+
+
+
+
+
50% +
+`; + +exports[`Progress render normal progress 1`] = ` +
+
+
+
+ +
+
0% +
+`; + +exports[`Progress render out-of-range progress 1`] = ` +
+
+
+
+ +
+
+
+`; + +exports[`Progress render out-of-range progress with info 1`] = ` +
+
+
+
+ +
+
+
+`; + +exports[`Progress render strokeColor 1`] = ` +
-
-
-
-
- -
-
0% -
+exports[`Progress render strokeColor 2`] = ` +
+
+
+
+ +
+
50%
`; -exports[`Progress render negetive successPercent 1`] = ` -
-
-
-
-
-
-
-
50% -
+exports[`Progress render strokeColor 3`] = ` +
+
+
+
+ +
+
50%
`; -exports[`Progress render negetive successPercent 2`] = ` -
-
-
-
-
-
-
-
50 10 -
+exports[`Progress render successColor progress 1`] = ` +
+
+
+
+
+
+
60%
`; -exports[`Progress render normal progress 1`] = ` -
-
-
-
-
- -
-
0% -
+exports[`Progress render trailColor progress 1`] = ` +
+
+
+
+ +
+
0%
`; -exports[`Progress render out-of-range progress 1`] = ` -
-
-
-
-
- -
-
+exports[`Progress should support steps 1`] = ` +
+
+
+
+
0%
`; -exports[`Progress render out-of-range progress with info 1`] = ` -
-
-
-
-
- -
-
-
+exports[`Progress steps should have default percent 0 1`] = ` +
+
`; diff --git a/components/progress/__tests__/index.test.js b/components/progress/__tests__/index.test.js index 1eadc98aee..218521be35 100644 --- a/components/progress/__tests__/index.test.js +++ b/components/progress/__tests__/index.test.js @@ -1,13 +1,15 @@ import { mount } from '@vue/test-utils'; import { asyncExpect } from '@/tests/utils'; +import { handleGradient, sortGradient } from '../Line'; import Progress from '..'; +import ProgressSteps from '../Steps'; describe('Progress', () => { it('successPercent should decide the progress status when it exists', async () => { const wrapper = mount(Progress, { props: { percent: 100, - successPercent: 50, + success: { percent: 50 }, }, sync: false, }); @@ -15,12 +17,12 @@ describe('Progress', () => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(0); }); - wrapper.setProps({ percent: 50, successPercent: 100 }); + wrapper.setProps({ percent: 50, success: { percent: 100 } }); await asyncExpect(() => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); }); - wrapper.setProps({ percent: 100, successPercent: 0 }); + wrapper.setProps({ percent: 100, success: { percent: 0 } }); await asyncExpect(() => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(0); }); @@ -51,7 +53,7 @@ describe('Progress', () => { }); }); - it('render negetive progress', async () => { + it('render negative progress', async () => { const wrapper = mount(Progress, { props: { percent: -20, @@ -63,11 +65,11 @@ describe('Progress', () => { }); }); - it('render negetive successPercent', async () => { + it('render negative successPercent', async () => { const wrapper = mount(Progress, { props: { percent: 50, - successPercent: -20, + success: { percent: -20 }, }, sync: false, }); @@ -76,7 +78,7 @@ describe('Progress', () => { }); }); - it('render negetive successPercent', async () => { + it('render format', async () => { const wrapper = mount(Progress, { props: { percent: 50, @@ -90,7 +92,7 @@ describe('Progress', () => { }); }); - it('render format', async () => { + it('render strokeColor', async () => { const wrapper = mount(Progress, { props: { percent: 50, @@ -102,10 +104,217 @@ describe('Progress', () => { await asyncExpect(() => { expect(wrapper.html()).toMatchSnapshot(); }); + wrapper.setProps({ + strokeColor: { + from: '#108ee9', + to: '#87d068', + }, + type: 'line', + }); + await asyncExpect(() => { + expect(wrapper.html()).toMatchSnapshot(); + }); + wrapper.setProps({ + strokeColor: { + '0%': '#108ee9', + '100%': '#87d068', + }, + }); + await asyncExpect(() => { + expect(wrapper.html()).toMatchSnapshot(); + }); }); it('render normal progress', () => { const wrapper = mount(Progress, { props: { status: 'normal' } }); expect(wrapper.html()).toMatchSnapshot(); }); + + it('render trailColor progress', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render successColor progress', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard zero gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard 295 gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('render dashboard 296 gapDegree', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('get correct line-gradient', () => { + expect(handleGradient({ from: 'test', to: 'test' }).backgroundImage).toBe( + 'linear-gradient(to right, test, test)', + ); + expect(handleGradient({}).backgroundImage).toBe('linear-gradient(to right, #1890ff, #1890ff)'); + expect(handleGradient({ from: 'test', to: 'test', '0%': 'test' }).backgroundImage).toBe( + 'linear-gradient(to right, test 0%)', + ); + }); + + it('sort gradients correctly', () => { + expect(sortGradient({ '10%': 'test10', '30%': 'test30', '20%': 'test20' })).toBe( + 'test10 10%, test20 20%, test30 30%', + ); + expect(sortGradient({ '10%': 'test10', '30%': 'test30', '20%': 'test20', dummy: 'test' })).toBe( + 'test10 10%, test20 20%, test30 30%', + ); + }); + + it('should show success status when percent is 100', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + }); + + // https://github.com/ant-design/ant-design/issues/15950 + it('should show success status when percent is 100 and status is undefined', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + }); + + // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 + it('should show success status when status is invalid', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + errorSpy.mockRestore(); + }); + + it('should support steps', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('steps should be changable', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(3); + wrapper.setProps({ percent: 40 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(2); + }); + }); + + it('steps should be changable when has strokeColor', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item')[0].element.style.backgroundColor).toBe( + 'rgb(24, 144, 255)', + ); + wrapper.setProps({ percent: 40 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item')[2].element.style.backgroundColor).toBe(''); + expect(wrapper.findAll('.ant-progress-steps-item')[1].element.style.backgroundColor).toBe( + 'rgb(24, 144, 255)', + ); + }); + }); + + it('steps should support trailColor', () => { + const wrapper = mount(); + expect(wrapper.findAll('.ant-progress-steps-item')[1].element.style.backgroundColor).toBe( + 'rgb(24, 144, 238)', + ); + }); + + it('should display correct step', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(2); + wrapper.setProps({ percent: 33.33 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(3); + }); + wrapper.setProps({ percent: 44.44 }); + await asyncExpect(() => { + expect(wrapper.findAll('.ant-progress-steps-item-active').length).toBe(4); + }); + }); + + it('steps should have default percent 0', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should warning if use `progress` in success', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + mount(); + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: [ant-design-vue: Progress] `success.progress` is deprecated. Please use `success.percent` instead.', + ); + }); + + it('should warning if use `progress` in success in type Circle', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + mount({ + render() { + return ; + }, + }); + expect(errorSpy).toHaveBeenCalledWith( + 'Warning: [ant-design-vue: Progress] `success.progress` is deprecated. Please use `success.percent` instead.', + ); + }); }); diff --git a/components/progress/circle.tsx b/components/progress/circle.tsx deleted file mode 100644 index bbf6867e77..0000000000 --- a/components/progress/circle.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type { ExtractPropTypes } from 'vue'; -import { defineComponent } from 'vue'; -import { Circle as VCCircle } from '../vc-progress'; -import PropTypes from '../_util/vue-types'; -import { validProgress } from './utils'; -import { ProgressProps } from './props'; - -const CircleProps = { - ...ProgressProps, - progressStatus: PropTypes.string, -}; - -const statusColorMap: Record = { - normal: '#108ee9', - exception: '#ff5500', - success: '#87d068', -}; - -export type ICircleProps = ExtractPropTypes; - -function getPercentage({ percent, successPercent }: ICircleProps) { - const ptg = validProgress(percent); - if (!successPercent) return ptg; - - const successPtg = validProgress(successPercent); - return [successPercent, validProgress(ptg - successPtg)]; -} - -function getStrokeColor({ progressStatus, successPercent, strokeColor }: ICircleProps) { - const color = strokeColor || statusColorMap[progressStatus]; - if (!successPercent) return color; - return [statusColorMap.success, color]; -} - -const Circle = defineComponent({ - props: CircleProps, - setup(props, { slots }) { - return () => { - const { - prefixCls, - width, - strokeWidth, - trailColor, - strokeLinecap, - gapPosition, - gapDegree, - type, - } = props; - const circleSize = width || 120; - const circleStyle = { - width: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, - height: typeof circleSize === 'number' ? `${circleSize}px` : circleSize, - fontSize: `${circleSize * 0.15 + 6}px`, - }; - const circleWidth = strokeWidth || 6; - const gapPos = gapPosition || (type === 'dashboard' && 'bottom') || 'top'; - const gapDeg = gapDegree || (type === 'dashboard' && 75); - const strokeColor = getStrokeColor(props); - const isGradient = Object.prototype.toString.call(strokeColor) === '[object Object]'; - - const wrapperClassName = { - [`${prefixCls}-inner`]: true, - [`${prefixCls}-circle-gradient`]: isGradient, - }; - - return ( -
- - {slots?.default()} -
- ); - }; - }, -}); - -export default Circle; diff --git a/components/progress/index.ts b/components/progress/index.ts index ef45c8e8e1..2505b42832 100644 --- a/components/progress/index.ts +++ b/components/progress/index.ts @@ -1,6 +1,6 @@ import Progress from './progress'; import { withInstall } from '../_util/type'; -export { ProgressProps } from './props'; +export type { ProgressProps } from './props'; export default withInstall(Progress); diff --git a/components/progress/line.tsx b/components/progress/line.tsx deleted file mode 100644 index 06afa96854..0000000000 --- a/components/progress/line.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { validProgress } from './utils'; - -/** - * { - * '0%': '#afc163', - * '75%': '#009900', - * '50%': 'green', ====> '#afc163 0%, #66FF00 25%, #00CC00 50%, #009900 75%, #ffffff 100%' - * '25%': '#66FF00', - * '100%': '#ffffff' - * } - */ -export const sortGradient = gradients => { - let tempArr = []; - // eslint-disable-next-line no-restricted-syntax - for (const [key, value] of Object.entries(gradients)) { - const formatKey = parseFloat(key.replace(/%/g, '')); - if (isNaN(formatKey)) { - return {}; - } - tempArr.push({ - key: formatKey, - value, - }); - } - tempArr = tempArr.sort((a, b) => a.key - b.key); - return tempArr.map(({ key, value }) => `${value} ${key}%`).join(', '); -}; - -/** - * { - * '0%': '#afc163', - * '25%': '#66FF00', - * '50%': '#00CC00', ====> linear-gradient(to right, #afc163 0%, #66FF00 25%, - * '75%': '#009900', #00CC00 50%, #009900 75%, #ffffff 100%) - * '100%': '#ffffff' - * } - * - * Then this man came to realize the truth: - * Besides six pence, there is the moon. - * Besides bread and butter, there is the bug. - * And... - * Besides women, there is the code. - */ -export const handleGradient = strokeColor => { - const { from = '#1890ff', to = '#1890ff', direction = 'to right', ...rest } = strokeColor; - if (Object.keys(rest).length !== 0) { - const sortedGradients = sortGradient(rest); - return { backgroundImage: `linear-gradient(${direction}, ${sortedGradients})` }; - } - return { backgroundImage: `linear-gradient(${direction}, ${from}, ${to})` }; -}; - -const Line = (_, { attrs, slots }) => { - const { prefixCls, percent, successPercent, strokeWidth, size, strokeColor, strokeLinecap } = - attrs; - let backgroundProps; - if (strokeColor && typeof strokeColor !== 'string') { - backgroundProps = handleGradient(strokeColor); - } else { - backgroundProps = { - background: strokeColor, - }; - } - const percentStyle = { - width: `${validProgress(percent)}%`, - height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, - background: strokeColor, - borderRadius: strokeLinecap === 'square' ? 0 : '100px', - ...backgroundProps, - }; - const successPercentStyle = { - width: `${validProgress(successPercent)}%`, - height: `${strokeWidth || (size === 'small' ? 6 : 8)}px`, - borderRadius: strokeLinecap === 'square' ? 0 : '', - }; - const successSegment = - successPercent !== undefined ? ( -
- ) : null; - return ( -
-
-
-
- {successSegment} -
-
- {slots?.default()} -
- ); -}; - -export default Line; diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index 118732db82..c8d2e1c700 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -1,20 +1,22 @@ -import { defineComponent, inject } from 'vue'; -import classNames from '../_util/classNames'; -import { getOptionProps } from '../_util/props-util'; +import type { VNodeChild } from 'vue'; +import { computed, defineComponent } from 'vue'; import initDefaultProps from '../_util/props-util/initDefaultProps'; -import { defaultConfigProvider } from '../config-provider'; import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; import CheckOutlined from '@ant-design/icons-vue/CheckOutlined'; import CheckCircleFilled from '@ant-design/icons-vue/CheckCircleFilled'; import CloseCircleFilled from '@ant-design/icons-vue/CloseCircleFilled'; -import Line from './line'; -import Circle from './circle'; -import { validProgress } from './utils'; -import { ProgressProps, ProgressStatuses } from './props'; +import Line from './Line'; +import Circle from './Circle'; +import Steps from './Steps'; +import { getSuccessPercent, validProgress } from './utils'; +import useConfigInject from '../_util/hooks/useConfigInject'; +import devWarning from '../vc-util/devWarning'; +import type { ProgressStatusesType } from './props'; +import { progressProps, progressStatuses } from './props'; export default defineComponent({ name: 'AProgress', - props: initDefaultProps(ProgressProps, { + props: initDefaultProps(progressProps, { type: 'line', percent: 0, showInfo: true, @@ -24,37 +26,49 @@ export default defineComponent({ gapDegree: 0, strokeLinecap: 'round', }), - setup() { - return { - configProvider: inject('configProvider', defaultConfigProvider), - }; - }, - methods: { - getPercentNumber() { - const { successPercent, percent = 0 } = this.$props; + setup(props, { slots }) { + const { prefixCls, direction } = useConfigInject('progress', props); + + const classString = computed(() => { + const { type, showInfo, size } = props; + const pre = prefixCls.value; + return { + [pre]: true, + [`${pre}-${(type === 'dashboard' && 'circle') || type}`]: true, + [`${pre}-show-info`]: showInfo, + [`${pre}-${size}`]: size, + [`${pre}-rtl`]: direction.value === 'rtl', + }; + }); + + const getPercentNumber = () => { + const { percent = 0 } = props; + const successPercent = getSuccessPercent(props.success, props.successPercent); return parseInt( successPercent !== undefined ? successPercent.toString() : percent.toString(), 10, ); - }, + }; - getProgressStatus() { - const { status } = this.$props; - if (ProgressStatuses.indexOf(status) < 0 && this.getPercentNumber() >= 100) { + const getProgressStatus = () => { + const { status } = props; + if (progressStatuses.indexOf(status) < 0 && getPercentNumber() >= 100) { return 'success'; } return status || 'normal'; - }, - renderProcessInfo(prefixCls: string, progressStatus: typeof ProgressStatuses[number]) { - const { showInfo, format, type, percent, successPercent } = this.$props; + }; + + const renderProcessInfo = (prefixCls: string, progressStatus: ProgressStatusesType) => { + const { showInfo, format, type, percent } = props; + const successPercent = getSuccessPercent(props.success, props.successPercent); if (!showInfo) return null; - let text; - const textFormatter = format || this.$slots.format || (percentNumber => `${percentNumber}%`); + let text: VNodeChild; + const textFormatter = format || slots?.format || (percentNumber => `${percentNumber}%`); const isLineType = type === 'line'; if ( format || - this.$slots.format || + slots?.format || (progressStatus !== 'exception' && progressStatus !== 'success') ) { text = textFormatter(validProgress(percent), validProgress(successPercent)); @@ -68,44 +82,50 @@ export default defineComponent({ {text} ); - }, - }, - render() { - const props = getOptionProps(this); - const { prefixCls: customizePrefixCls, size, type, showInfo } = props; - const { getPrefixCls } = this.configProvider; - const prefixCls = getPrefixCls('progress', customizePrefixCls); - const progressStatus = this.getProgressStatus(); - const progressInfo = this.renderProcessInfo(prefixCls, progressStatus); + }; - let progress; + return () => { + const { type, steps, strokeColor } = props; + const progressStatus = getProgressStatus(); + const progressInfo = renderProcessInfo(prefixCls.value, progressStatus); - // Render progress shape - if (type === 'line') { - const lineProps = { - ...props, - prefixCls, - }; - progress = {progressInfo}; - } else if (type === 'circle' || type === 'dashboard') { - const circleProps = { - ...props, - prefixCls, - progressStatus, - }; - progress = {progressInfo}; - } + devWarning( + props.successPercent == undefined, + 'Progress', + '`successPercent` is deprecated. Please use `success.percent` instead.', + ); - const classString = classNames(prefixCls, { - [`${prefixCls}-${(type === 'dashboard' && 'circle') || type}`]: true, - [`${prefixCls}-status-${progressStatus}`]: true, - [`${prefixCls}-show-info`]: showInfo, - [`${prefixCls}-${size}`]: size, - }); + let progress: VNodeChild; + // Render progress shape + if (type === 'line') { + progress = steps ? ( + + {progressInfo} + + ) : ( + + {progressInfo} + + ); + } else if (type === 'circle' || type === 'dashboard') { + progress = ( + + {progressInfo} + + ); + } + + const classNames = { + ...classString.value, + [`${prefixCls.value}-status-${progressStatus}`]: true, + }; - const progressProps = { - class: classString, + return
{progress}
; }; - return
{progress}
; }, }); diff --git a/components/progress/props.ts b/components/progress/props.ts index a18b1c7c24..7933fdf472 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -1,24 +1,48 @@ import PropTypes from '../_util/vue-types'; import { tuple } from '../_util/type'; +import type { PropType, VNodeChild, ExtractPropTypes } from 'vue'; -export const ProgressStatuses = tuple('normal', 'exception', 'active', 'success'); -export const ProgressType = PropTypes.oneOf(tuple('line', 'circle', 'dashboard')); -export const ProgressSize = PropTypes.oneOf(tuple('default', 'small')); +export const progressStatuses = tuple('normal', 'exception', 'active', 'success'); +export type ProgressStatusesType = typeof progressStatuses[number]; +const ProgressType = tuple('line', 'circle', 'dashboard'); +export type ProgressType = typeof ProgressType[number]; +const ProgressSize = tuple('default', 'small'); +export type ProgressSize = typeof ProgressSize[number]; +export type StringGradients = { [percentage: string]: string }; +type FromToGradients = { from: string; to: string }; +export type ProgressGradient = { direction?: string } & (StringGradients | FromToGradients); -export const ProgressProps = { +export interface SuccessProps { + percent?: number; + /** @deprecated Use `percent` instead */ + progress?: number; + strokeColor?: string; +} + +export const progressProps = { prefixCls: PropTypes.string, - type: ProgressType, + type: PropTypes.oneOf(ProgressType), percent: PropTypes.number, - successPercent: PropTypes.number, - format: PropTypes.func, - status: PropTypes.oneOf(ProgressStatuses), + format: { type: Function as PropType<(percent?: number, successPercent?: number) => VNodeChild> }, + status: PropTypes.oneOf(progressStatuses), showInfo: PropTypes.looseBool, strokeWidth: PropTypes.number, - strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']), - strokeColor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + strokeLinecap: PropTypes.oneOf(tuple('butt', 'round', 'square')), + strokeColor: { + type: [String, Object] as PropType, + }, trailColor: PropTypes.string, width: PropTypes.number, + success: { + type: Object as PropType, + default: (): SuccessProps => ({}), + }, gapDegree: PropTypes.number, gapPosition: PropTypes.oneOf(tuple('top', 'bottom', 'left', 'right')), - size: ProgressSize, + size: PropTypes.oneOf(ProgressSize), + steps: PropTypes.number, + /** @deprecated Use `success` instead */ + successPercent: PropTypes.number, }; + +export type ProgressProps = Partial>; diff --git a/components/progress/style/index.less b/components/progress/style/index.less index f2f0bd5e8c..45fc23e7c0 100644 --- a/components/progress/style/index.less +++ b/components/progress/style/index.less @@ -14,6 +14,26 @@ font-size: @font-size-base; } + &-steps { + display: inline-block; + &-outer { + display: flex; + flex-direction: row; + align-items: center; + } + &-item { + flex-shrink: 0; + min-width: 2px; + margin-right: 2px; + background: @progress-steps-item-bg; + transition: all 0.3s; + + &-active { + background: @progress-default-color; + } + } + } + &-small&-line, &-small&-line &-text .@{iconfont-css-prefix} { font-size: @font-size-sm; @@ -73,8 +93,8 @@ display: inline-block; width: 2em; margin-left: 8px; - color: @text-color-secondary; - font-size: 1em; + color: @progress-info-text-color; + font-size: @progress-text-font-size; line-height: 1; white-space: nowrap; text-align: left; @@ -144,6 +164,7 @@ margin: 0; padding: 0; color: @progress-text-color; + font-size: @progress-circle-text-font-size; line-height: 1; white-space: normal; text-align: center; @@ -180,3 +201,5 @@ opacity: 0; } } + +@import './rtl'; diff --git a/components/progress/style/rtl.less b/components/progress/style/rtl.less new file mode 100644 index 0000000000..0756b5f847 --- /dev/null +++ b/components/progress/style/rtl.less @@ -0,0 +1,37 @@ +@import '../../style/themes/index'; +@import '../../style/mixins/index'; + +@progress-prefix-cls: ~'@{ant-prefix}-progress'; + +.@{progress-prefix-cls} { + &-rtl { + direction: rtl; + } + + &-outer { + .@{progress-prefix-cls}-show-info & { + .@{progress-prefix-cls}-rtl& { + margin-right: 0; + margin-left: ~'calc(-2em - 8px)'; + padding-right: 0; + padding-left: ~'calc(2em + 8px)'; + } + } + } + + &-success-bg { + .@{progress-prefix-cls}-rtl & { + right: 0; + left: auto; + } + } + + &-line &-text, + &-steps &-text { + .@{progress-prefix-cls}-rtl& { + margin-right: 8px; + margin-left: 0; + text-align: right; + } + } +} diff --git a/components/progress/utils.ts b/components/progress/utils.ts index 14a930d882..df8b332265 100644 --- a/components/progress/utils.ts +++ b/components/progress/utils.ts @@ -1,4 +1,7 @@ -export function validProgress(progress?: number) { +import devWarning from '../vc-util/devWarning'; +import type { ProgressProps } from './props'; + +export function validProgress(progress: number | undefined) { if (!progress || progress < 0) { return 0; } @@ -7,3 +10,23 @@ export function validProgress(progress?: number) { } return progress; } + +export function getSuccessPercent( + success?: ProgressProps['success'], + successPercent?: ProgressProps['successPercent'], +) { + let percent = successPercent; + /** @deprecated Use `percent` instead */ + if (success && 'progress' in success) { + devWarning( + false, + 'Progress', + '`success.progress` is deprecated. Please use `success.percent` instead.', + ); + percent = success.progress; + } + if (success && 'percent' in success) { + percent = success.percent; + } + return percent; +} diff --git a/components/style/themes/default.less b/components/style/themes/default.less index 875e1562fc..76e1653b65 100644 --- a/components/style/themes/default.less +++ b/components/style/themes/default.less @@ -496,9 +496,12 @@ // -- @progress-default-color: @processing-color; @progress-remaining-color: @background-color-base; -@progress-text-color: @text-color; +@progress-info-text-color: @progress-text-color; @progress-radius: 100px; - +@progress-steps-item-bg: #f3f3f3; +@progress-text-font-size: 1em; +@progress-text-color: @text-color; // This is for circle text color, should be renamed better +@progress-circle-text-font-size: 1em; // Menu // --- @menu-inline-toplevel-item-height: 40px; diff --git a/components/vc-progress/index.js b/components/vc-progress/index.ts similarity index 62% rename from components/vc-progress/index.js rename to components/vc-progress/index.ts index 5dc2fdf679..199a021db2 100644 --- a/components/vc-progress/index.js +++ b/components/vc-progress/index.ts @@ -1,5 +1,5 @@ // based on rc-progress 2.5.2 -import Progress, { Line, Circle } from './src/'; +import Progress, { Line, Circle } from './src'; export { Line, Circle }; diff --git a/components/vc-progress/src/Circle.js b/components/vc-progress/src/Circle.js deleted file mode 100644 index 8b951253df..0000000000 --- a/components/vc-progress/src/Circle.js +++ /dev/null @@ -1,191 +0,0 @@ -import PropTypes, { withUndefined } from '../../_util/vue-types'; -import { initDefaultProps } from '../../_util/props-util'; -import enhancer from './enhancer'; -import { propTypes, defaultProps } from './types'; -import { defineComponent } from 'vue'; - -const circlePropTypes = { - ...propTypes, - gapPosition: PropTypes.oneOf(['top', 'bottom', 'left', 'right']), - gapDegree: withUndefined( - PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.looseBool]), - ), -}; - -const circleDefaultProps = { - ...defaultProps, - gapPosition: 'top', -}; - -let gradientSeed = 0; - -function stripPercentToNumber(percent) { - return +percent.replace('%', ''); -} - -function toArray(symArray) { - return Array.isArray(symArray) ? symArray : [symArray]; -} - -function getPathStyles(offset, percent, strokeColor, strokeWidth, gapDegree = 0, gapPosition) { - const radius = 50 - strokeWidth / 2; - let beginPositionX = 0; - let beginPositionY = -radius; - let endPositionX = 0; - let endPositionY = -2 * radius; - switch (gapPosition) { - case 'left': - beginPositionX = -radius; - beginPositionY = 0; - endPositionX = 2 * radius; - endPositionY = 0; - break; - case 'right': - beginPositionX = radius; - beginPositionY = 0; - endPositionX = -2 * radius; - endPositionY = 0; - break; - case 'bottom': - beginPositionY = radius; - endPositionY = 2 * radius; - break; - default: - } - const pathString = `M 50,50 m ${beginPositionX},${beginPositionY} - a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} - a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`; - const len = Math.PI * 2 * radius; - - const pathStyle = { - stroke: strokeColor, - strokeDasharray: `${(percent / 100) * (len - gapDegree)}px ${len}px`, - strokeDashoffset: `-${gapDegree / 2 + (offset / 100) * (len - gapDegree)}px`, - transition: - 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s', // eslint-disable-line - }; - - return { - pathString, - pathStyle, - }; -} - -const Circle = defineComponent({ - name: 'Circle', - props: initDefaultProps(circlePropTypes, circleDefaultProps), - created() { - this.paths = {}; - this.gradientId = gradientSeed; - gradientSeed += 1; - }, - methods: { - getStokeList() { - const { - prefixCls, - percent, - strokeColor, - strokeWidth, - strokeLinecap, - gapDegree, - gapPosition, - } = this.$props; - const percentList = toArray(percent); - const strokeColorList = toArray(strokeColor); - - let stackPtg = 0; - return percentList.map((ptg, index) => { - const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; - const stroke = - Object.prototype.toString.call(color) === '[object Object]' - ? `url(#${prefixCls}-gradient-${this.gradientId})` - : ''; - const { pathString, pathStyle } = getPathStyles( - stackPtg, - ptg, - color, - strokeWidth, - gapDegree, - gapPosition, - ); - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString, - stroke, - 'stroke-linecap': strokeLinecap, - 'stroke-width': strokeWidth, - opacity: ptg === 0 ? 0 : 1, - 'fill-opacity': '0', - class: `${prefixCls}-circle-path`, - style: pathStyle, - }; - return (this.paths[index] = c)} {...pathProps} />; - }); - }, - }, - - render() { - const { - prefixCls, - strokeWidth, - trailWidth, - gapDegree, - gapPosition, - trailColor, - strokeLinecap, - strokeColor, - ...restProps - } = this.$props; - const { pathString, pathStyle } = getPathStyles( - 0, - 100, - trailColor, - strokeWidth, - gapDegree, - gapPosition, - ); - delete restProps.percent; - const strokeColorList = toArray(strokeColor); - const gradient = strokeColorList.find( - color => Object.prototype.toString.call(color) === '[object Object]', - ); - const pathFirst = { - d: pathString, - stroke: trailColor, - 'stroke-linecap': strokeLinecap, - 'stroke-width': trailWidth || strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-circle-trail`, - style: pathStyle, - }; - - return ( - - {gradient && ( - - - {Object.keys(gradient) - .sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b)) - .map((key, index) => ( - - ))} - - - )} - - {this.getStokeList().reverse()} - - ); - }, -}); - -export default enhancer(Circle); diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx new file mode 100644 index 0000000000..abb5c7cfbf --- /dev/null +++ b/components/vc-progress/src/Circle.tsx @@ -0,0 +1,174 @@ +import { useTransitionDuration, defaultProps } from './common'; +import { propTypes, GapPositionType } from './types'; +import { computed, defineComponent, ref } from 'vue'; +import initDefaultProps from '../../_util/props-util/initDefaultProps'; + +let gradientSeed = 0; + +function stripPercentToNumber(percent: string) { + return +percent.replace('%', ''); +} + +function toArray(value: any) { + return Array.isArray(value) ? value : [value]; +} + +function getPathStyles( + offset: number, + percent: number, + strokeColor: string, + strokeWidth: number, + gapDegree = 0, + gapPosition: GapPositionType, +) { + const radius = 50 - strokeWidth / 2; + let beginPositionX = 0; + let beginPositionY = -radius; + let endPositionX = 0; + let endPositionY = -2 * radius; + switch (gapPosition) { + case 'left': + beginPositionX = -radius; + beginPositionY = 0; + endPositionX = 2 * radius; + endPositionY = 0; + break; + case 'right': + beginPositionX = radius; + beginPositionY = 0; + endPositionX = -2 * radius; + endPositionY = 0; + break; + case 'bottom': + beginPositionY = radius; + endPositionY = 2 * radius; + break; + default: + } + const pathString = `M 50,50 m ${beginPositionX},${beginPositionY} + a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} + a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`; + const len = Math.PI * 2 * radius; + + const pathStyle = { + stroke: strokeColor, + strokeDasharray: `${(percent / 100) * (len - gapDegree)}px ${len}px`, + strokeDashoffset: `-${gapDegree / 2 + (offset / 100) * (len - gapDegree)}px`, + transition: + 'stroke-dashoffset .3s ease 0s, stroke-dasharray .3s ease 0s, stroke .3s, stroke-width .06s ease .3s, opacity .3s ease 0s', // eslint-disable-line + }; + + return { + pathString, + pathStyle, + }; +} + +export default defineComponent({ + name: 'VCCircle', + props: initDefaultProps(propTypes, defaultProps), + setup(props) { + gradientSeed += 1; + const gradientId = ref(gradientSeed); + const percentList = computed(() => toArray(props.percent)); + const strokeColorList = computed(() => toArray(props.strokeColor)); + + const paths = useTransitionDuration(percentList); + + const getStokeList = () => { + const { prefixCls, strokeWidth, strokeLinecap, gapDegree, gapPosition } = props; + + let stackPtg = 0; + return percentList.value.map((ptg, index) => { + const color = + strokeColorList.value[index] || strokeColorList.value[strokeColorList.value.length - 1]; + const stroke = + Object.prototype.toString.call(color) === '[object Object]' + ? `url(#${prefixCls}-gradient-${gradientId.value})` + : ''; + const { pathString, pathStyle } = getPathStyles( + stackPtg, + ptg, + color, + strokeWidth, + gapDegree, + gapPosition, + ); + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString, + stroke, + 'stroke-linecap': strokeLinecap, + 'stroke-width': strokeWidth, + opacity: ptg === 0 ? 0 : 1, + 'fill-opacity': '0', + class: `${prefixCls}-circle-path`, + style: pathStyle, + }; + return (paths.value[index].value = c)} {...pathProps} />; + }); + }; + + return () => { + const { + prefixCls, + strokeWidth, + trailWidth, + gapDegree, + gapPosition, + trailColor, + strokeLinecap, + strokeColor, + ...restProps + } = props; + const { pathString, pathStyle } = getPathStyles( + 0, + 100, + trailColor, + strokeWidth, + gapDegree, + gapPosition, + ); + delete restProps.percent; + const gradient = strokeColorList.value.find( + color => Object.prototype.toString.call(color) === '[object Object]', + ); + const pathFirst = { + d: pathString, + stroke: trailColor, + 'stroke-linecap': strokeLinecap, + 'stroke-width': trailWidth || strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-circle-trail`, + style: pathStyle, + }; + + return ( + + {gradient && ( + + + {Object.keys(gradient) + .sort((a, b) => stripPercentToNumber(a) - stripPercentToNumber(b)) + .map((key, index) => ( + + ))} + + + )} + + {getStokeList().reverse()} + + ); + }; + }, +}); diff --git a/components/vc-progress/src/Line.js b/components/vc-progress/src/Line.js deleted file mode 100644 index 3e881964f4..0000000000 --- a/components/vc-progress/src/Line.js +++ /dev/null @@ -1,84 +0,0 @@ -import { defineComponent } from 'vue'; -import { initDefaultProps } from '../../_util/props-util'; -import enhancer from './enhancer'; -import { propTypes, defaultProps } from './types'; - -const Line = defineComponent({ - name: 'Line', - props: initDefaultProps(propTypes, defaultProps), - created() { - this.paths = {}; - }, - render() { - const { - percent, - prefixCls, - strokeColor, - strokeLinecap, - strokeWidth, - trailColor, - trailWidth, - transition, - ...restProps - } = this.$props; - - delete restProps.gapPosition; - - const percentList = Array.isArray(percent) ? percent : [percent]; - const strokeColorList = Array.isArray(strokeColor) ? strokeColor : [strokeColor]; - - const center = strokeWidth / 2; - const right = 100 - strokeWidth / 2; - const pathString = `M ${strokeLinecap === 'round' ? center : 0},${center} - L ${strokeLinecap === 'round' ? right : 100},${center}`; - const viewBoxString = `0 0 100 ${strokeWidth}`; - - let stackPtg = 0; - - const pathFirst = { - d: pathString, - 'stroke-linecap': strokeLinecap, - stroke: trailColor, - 'stroke-width': trailWidth || strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-trail`, - }; - return ( - - - {percentList.map((ptg, index) => { - const pathStyle = { - strokeDasharray: `${ptg}px, 100px`, - strokeDashoffset: `-${stackPtg}px`, - transition: - transition || - 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', - }; - const color = strokeColorList[index] || strokeColorList[strokeColorList.length - 1]; - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString, - 'stroke-linecap': strokeLinecap, - stroke: color, - 'stroke-width': strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-path`, - style: pathStyle, - }; - - return (this.paths[index] = c)} {...pathProps} />; - })} - - ); - }, -}); - -export default enhancer(Line); diff --git a/components/vc-progress/src/Line.tsx b/components/vc-progress/src/Line.tsx new file mode 100644 index 0000000000..d61a5280d8 --- /dev/null +++ b/components/vc-progress/src/Line.tsx @@ -0,0 +1,96 @@ +import { computed, defineComponent } from 'vue'; +import initDefaultProps from '../../_util/props-util/initDefaultProps'; +import { useTransitionDuration, defaultProps } from './common'; +import { propTypes } from './types'; + +export default defineComponent({ + name: 'Line', + props: initDefaultProps(propTypes, defaultProps), + setup(props) { + const percentList = computed(() => { + const { percent } = props; + return Array.isArray(percent) ? percent : [percent]; + }); + + const strokeColorList = computed(() => { + const { strokeColor } = props; + return Array.isArray(strokeColor) ? strokeColor : [strokeColor]; + }); + + const paths = useTransitionDuration(percentList); + const center = computed(() => props.strokeWidth / 2); + const right = computed(() => 100 - props.strokeWidth / 2); + + const pathString = computed( + () => `M ${props.strokeLinecap === 'round' ? center.value : 0},${center.value} + L ${props.strokeLinecap === 'round' ? right.value : 100},${center.value}`, + ); + + const viewBoxString = computed(() => `0 0 100 ${props.strokeWidth}`); + + const pathFirst = computed(() => ({ + d: pathString.value, + 'stroke-linecap': props.strokeLinecap, + stroke: props.trailColor, + 'stroke-width': props.trailWidth || props.strokeWidth, + 'fill-opacity': '0', + class: `${props.prefixCls}-line-trail`, + })); + + return () => { + const { + percent, + prefixCls, + strokeColor, + strokeLinecap, + strokeWidth, + trailColor, + trailWidth, + transition, + ...restProps + } = props; + + delete restProps.gapPosition; + + let stackPtg = 0; + + return ( + + + {percentList.value.map((ptg, index) => { + const pathStyle = { + strokeDasharray: `${ptg}px, 100px`, + strokeDashoffset: `-${stackPtg}px`, + transition: + transition || + 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', + }; + const color = + strokeColorList.value[index] || + strokeColorList.value[strokeColorList.value.length - 1]; + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString.value, + 'stroke-linecap': strokeLinecap, + stroke: color as string, + 'stroke-width': strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-line-path`, + style: pathStyle, + }; + + return (paths.value[index].value = c)} {...pathProps} />; + })} + + ); + }; + }, +}); diff --git a/components/vc-progress/src/common.ts b/components/vc-progress/src/common.ts new file mode 100644 index 0000000000..334e9577c2 --- /dev/null +++ b/components/vc-progress/src/common.ts @@ -0,0 +1,43 @@ +import type { Ref } from 'vue'; +import { ref, onUpdated, computed } from 'vue'; +import type { ProgressProps } from './types'; + +export const defaultProps: Partial = { + percent: 0, + prefixCls: 'vc-progress', + strokeColor: '#2db7f5', + strokeLinecap: 'round', + strokeWidth: 1, + trailColor: '#D9D9D9', + trailWidth: 1, +}; + +export const useTransitionDuration = (percentList: Ref) => { + const paths = computed(() => percentList.value.map(() => ref())); + const prevTimeStamp = ref(null); + + onUpdated(() => { + const now = Date.now(); + let updated = false; + + Object.keys(paths.value).forEach(key => { + const path = paths.value[key].value; + if (!path) { + return; + } + updated = true; + const pathStyle = path.style; + pathStyle.transitionDuration = '.3s, .3s, .3s, .06s'; + + if (prevTimeStamp.value && now - prevTimeStamp.value < 100) { + pathStyle.transitionDuration = '0s, 0s'; + } + }); + + if (updated) { + prevTimeStamp.value = Date.now(); + } + }); + + return paths; +}; diff --git a/components/vc-progress/src/enhancer.js b/components/vc-progress/src/enhancer.js deleted file mode 100644 index 43ad4794ec..0000000000 --- a/components/vc-progress/src/enhancer.js +++ /dev/null @@ -1,30 +0,0 @@ -function enhancer(Component) { - return { - ...Component, - updated() { - const now = Date.now(); - let updated = false; - - Object.keys(this.paths).forEach(key => { - const path = this.paths[key]; - - if (!path) { - return; - } - - updated = true; - const pathStyle = path.style; - pathStyle.transitionDuration = '.3s, .3s, .3s, .06s'; - - if (this.prevTimeStamp && now - this.prevTimeStamp < 100) { - pathStyle.transitionDuration = '0s, 0s'; - } - }); - if (updated) { - this.prevTimeStamp = Date.now(); - } - }, - }; -} - -export default enhancer; diff --git a/components/vc-progress/src/index.js b/components/vc-progress/src/index.ts similarity index 53% rename from components/vc-progress/src/index.js rename to components/vc-progress/src/index.ts index 01fb4abd79..e6e5727860 100644 --- a/components/vc-progress/src/index.js +++ b/components/vc-progress/src/index.ts @@ -1,7 +1,8 @@ import Line from './Line'; import Circle from './Circle'; +import type { ProgressProps } from './types'; -export { Line, Circle }; +export { Line, Circle, ProgressProps }; export default { Line, diff --git a/components/vc-progress/src/types.js b/components/vc-progress/src/types.js deleted file mode 100644 index f027d27d29..0000000000 --- a/components/vc-progress/src/types.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from '../../_util/vue-types'; - -export const defaultProps = { - // className: '', - percent: 0, - prefixCls: 'rc-progress', - strokeColor: '#2db7f5', - strokeLinecap: 'round', - strokeWidth: 1, - // style: {}, - trailColor: '#D9D9D9', - trailWidth: 1, -}; -const mixedType = PropTypes.oneOfType([PropTypes.number, PropTypes.string]); - -export const propTypes = { - // className: PropTypes.string, - percent: PropTypes.oneOfType([mixedType, PropTypes.arrayOf(mixedType)]), - prefixCls: PropTypes.string, - strokeColor: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.object])), - PropTypes.object, - ]), - strokeLinecap: PropTypes.oneOf(['butt', 'round', 'square']), - strokeWidth: mixedType, - // style: PropTypes.object, - trailColor: PropTypes.string, - trailWidth: mixedType, -}; diff --git a/components/vc-progress/src/types.ts b/components/vc-progress/src/types.ts new file mode 100644 index 0000000000..474ed7970f --- /dev/null +++ b/components/vc-progress/src/types.ts @@ -0,0 +1,31 @@ +import type { PropType, ExtractPropTypes } from 'vue'; +import PropTypes from '../../_util/vue-types'; + +export type StrokeColorType = string | string[] | object; + +export type GapPositionType = 'top' | 'right' | 'bottom' | 'left'; + +export type StrokeLinecapType = 'round' | 'butt' | 'square'; + +export const propTypes = { + gapDegree: PropTypes.number, + gapPosition: { + type: String as PropType, + }, + percent: { + type: [Array, Number] as PropType, + }, + prefixCls: PropTypes.string, + strokeColor: { + type: [Object, String, Array] as PropType, + }, + strokeLinecap: { + type: String as PropType, + }, + strokeWidth: PropTypes.number, + trailColor: PropTypes.string, + trailWidth: PropTypes.number, + transition: PropTypes.string, +}; + +export type ProgressProps = Partial>; From a0d9372f4657f0fea11f47755859c40f44155bf3 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Mon, 12 Jul 2021 16:39:54 +0800 Subject: [PATCH 27/28] refactor: progress --- components/_util/hooks/useRef.ts | 6 +- components/progress/Circle.tsx | 47 ++---------- components/progress/Line.tsx | 13 ++-- .../__snapshots__/index.test.js.snap | 12 +++ components/progress/__tests__/index.test.js | 24 +++--- components/progress/progress.tsx | 45 +++++------ components/progress/props.ts | 2 - components/progress/style/index.less | 6 +- .../progress/style/{index.ts => index.tsx} | 0 components/progress/utils.ts | 15 ++-- components/vc-progress/index.ts | 5 +- components/vc-progress/src/Circle.tsx | 6 +- components/vc-progress/src/Line.tsx | 76 +++++++++++-------- components/vc-progress/src/common.ts | 9 +-- package.json | 1 + v2-doc | 2 +- 16 files changed, 133 insertions(+), 136 deletions(-) rename components/progress/style/{index.ts => index.tsx} (100%) diff --git a/components/_util/hooks/useRef.ts b/components/_util/hooks/useRef.ts index 5033e9ea4e..86f6512ecd 100644 --- a/components/_util/hooks/useRef.ts +++ b/components/_util/hooks/useRef.ts @@ -2,9 +2,9 @@ import type { Ref } from 'vue'; import { onBeforeUpdate, ref } from 'vue'; export type UseRef = [(el: any, key: string | number) => void, Ref]; - +export type Refs = Record; export const useRef = (): UseRef => { - const refs = ref({}); + const refs = ref({}); const setRef = (el: any, key: string | number) => { refs.value[key] = el; }; @@ -13,3 +13,5 @@ export const useRef = (): UseRef => { }); return [setRef, refs]; }; + +export default useRef; diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx index 8d8ac2eb4d..c2265d354e 100644 --- a/components/progress/Circle.tsx +++ b/components/progress/Circle.tsx @@ -1,5 +1,6 @@ import type { CSSProperties, ExtractPropTypes } from 'vue'; import { computed, defineComponent } from 'vue'; +import { presetPrimaryColors } from '@ant-design/colors'; import { Circle as VCCircle } from '../vc-progress'; import { getSuccessPercent, validProgress } from './utils'; import { progressProps } from './props'; @@ -12,39 +13,9 @@ const circleProps = { }; export type CircleProps = Partial>; -const statusColorMap = { - normal: '#108ee9', - exception: '#ff5500', - success: '#87d068', -}; - -function getPercentage( - percent: CircleProps['percent'], - success: CircleProps['success'], - successPercent: CircleProps['successPercent'], -) { - const ptg = validProgress(percent); - const realSuccessPercent = getSuccessPercent(success, successPercent); - if (!realSuccessPercent) { - return ptg; - } - return [ - validProgress(realSuccessPercent), - validProgress(ptg - validProgress(realSuccessPercent)), - ]; -} - -function getStrokeColor( - success: CircleProps['success'], - strokeColor: CircleProps['strokeColor'], - successPercent: CircleProps['successPercent'], -) { - const color = strokeColor || null; - const realSuccessPercent = getSuccessPercent(success, successPercent); - if (!realSuccessPercent) { - return color; - } - return [statusColorMap.success, color]; +function getPercentage({ percent, success, successPercent }: CircleProps) { + const realSuccessPercent = validProgress(getSuccessPercent({ success, successPercent })); + return [realSuccessPercent, validProgress(validProgress(percent) - realSuccessPercent)]; } export default defineComponent({ @@ -77,14 +48,10 @@ export default defineComponent({ ); // using className to style stroke color - const strokeColor = computed(() => - getStrokeColor(props.success, props.strokeColor, props.successPercent), - ); - const percent = computed(() => - getPercentage(props.percent, props.success, props.successPercent), - ); + const strokeColor = computed(() => [presetPrimaryColors.green, props.strokeColor || null]); + const percent = computed(() => getPercentage(props)); const isGradient = computed( - () => Object.prototype.toString.call(strokeColor.value) === '[object Object]', + () => Object.prototype.toString.call(props.strokeColor) === '[object Object]', ); const wrapperClassName = computed(() => ({ diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx index ca01f86719..df9bfe42c6 100644 --- a/components/progress/Line.tsx +++ b/components/progress/Line.tsx @@ -69,6 +69,7 @@ export const handleGradient = (strokeColor: ProgressGradient, directionConfig: D export default defineComponent({ props: lineProps, + name: 'Line', setup(props, { slots }) { const backgroundProps = computed(() => { const { strokeColor, direction } = props; @@ -98,7 +99,7 @@ export default defineComponent({ }); const successPercent = computed(() => { - return getSuccessPercent(props.success, props.successPercent); + return getSuccessPercent(props); }); const successPercentStyle = computed(() => { const { strokeWidth, size, strokeLinecap, success } = props; @@ -110,18 +111,14 @@ export default defineComponent({ }; }); - const successSegment = computed(() => - successPercent.value !== undefined ? ( -
- ) : null, - ); - return () => ( <>
- {successSegment.value} + {successPercent.value !== undefined ? ( +
+ ) : null}
{slots.default?.()} diff --git a/components/progress/__tests__/__snapshots__/index.test.js.snap b/components/progress/__tests__/__snapshots__/index.test.js.snap index cc19fbf18d..0f52246db2 100644 --- a/components/progress/__tests__/__snapshots__/index.test.js.snap +++ b/components/progress/__tests__/__snapshots__/index.test.js.snap @@ -10,6 +10,9 @@ exports[`Progress render dashboard 295 gapDegree 1`] = ` + 0%
`; @@ -24,6 +27,9 @@ exports[`Progress render dashboard 296 gapDegree 1`] = ` + 0%
`; @@ -38,6 +44,9 @@ exports[`Progress render dashboard zero gapDegree 1`] = ` + 0%
`; @@ -118,6 +127,9 @@ exports[`Progress render strokeColor 1`] = ` + 50%
`; diff --git a/components/progress/__tests__/index.test.js b/components/progress/__tests__/index.test.js index 218521be35..2694a167f2 100644 --- a/components/progress/__tests__/index.test.js +++ b/components/progress/__tests__/index.test.js @@ -82,7 +82,7 @@ describe('Progress', () => { const wrapper = mount(Progress, { props: { percent: 50, - successPercent: 10, + success: { percent: 10 }, format: (percent, successPercent) => `${percent} ${successPercent}`, }, sync: false, @@ -213,17 +213,17 @@ describe('Progress', () => { expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); }); - // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 - it('should show success status when status is invalid', () => { - const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - const wrapper = mount({ - render() { - return ; - }, - }); - expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); - errorSpy.mockRestore(); - }); + // // https://github.com/ant-design/ant-design/pull/15951#discussion_r273062969 + // it('should show success status when status is invalid', () => { + // const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + // const wrapper = mount({ + // render() { + // return ; + // }, + // }); + // expect(wrapper.findAll('.ant-progress-status-success')).toHaveLength(1); + // errorSpy.mockRestore(); + // }); it('should support steps', () => { const wrapper = mount({ diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index c8d2e1c700..668470e9e2 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -11,7 +11,6 @@ import Steps from './Steps'; import { getSuccessPercent, validProgress } from './utils'; import useConfigInject from '../_util/hooks/useConfigInject'; import devWarning from '../vc-util/devWarning'; -import type { ProgressStatusesType } from './props'; import { progressProps, progressStatuses } from './props'; export default defineComponent({ @@ -26,9 +25,14 @@ export default defineComponent({ gapDegree: 0, strokeLinecap: 'round', }), + slots: ['format'], setup(props, { slots }) { const { prefixCls, direction } = useConfigInject('progress', props); - + devWarning( + props.successPercent == undefined, + 'Progress', + '`successPercent` is deprecated. Please use `success.percent` instead.', + ); const classString = computed(() => { const { type, showInfo, size } = props; const pre = prefixCls.value; @@ -41,44 +45,44 @@ export default defineComponent({ }; }); - const getPercentNumber = () => { + const percentNumber = computed(() => { const { percent = 0 } = props; - const successPercent = getSuccessPercent(props.success, props.successPercent); + const successPercent = getSuccessPercent(props); return parseInt( successPercent !== undefined ? successPercent.toString() : percent.toString(), 10, ); - }; + }); - const getProgressStatus = () => { + const progressStatus = computed(() => { const { status } = props; - if (progressStatuses.indexOf(status) < 0 && getPercentNumber() >= 100) { + if (progressStatuses.indexOf(status) < 0 && percentNumber.value >= 100) { return 'success'; } return status || 'normal'; - }; + }); - const renderProcessInfo = (prefixCls: string, progressStatus: ProgressStatusesType) => { + const renderProcessInfo = () => { const { showInfo, format, type, percent } = props; - const successPercent = getSuccessPercent(props.success, props.successPercent); + const successPercent = getSuccessPercent(props); if (!showInfo) return null; let text: VNodeChild; - const textFormatter = format || slots?.format || (percentNumber => `${percentNumber}%`); + const textFormatter = format || slots?.format || ((val: number) => `${val}%`); const isLineType = type === 'line'; if ( format || slots?.format || - (progressStatus !== 'exception' && progressStatus !== 'success') + (progressStatus.value !== 'exception' && progressStatus.value !== 'success') ) { text = textFormatter(validProgress(percent), validProgress(successPercent)); - } else if (progressStatus === 'exception') { + } else if (progressStatus.value === 'exception') { text = isLineType ? : ; - } else if (progressStatus === 'success') { + } else if (progressStatus.value === 'success') { text = isLineType ? : ; } return ( - + {text} ); @@ -86,14 +90,7 @@ export default defineComponent({ return () => { const { type, steps, strokeColor } = props; - const progressStatus = getProgressStatus(); - const progressInfo = renderProcessInfo(prefixCls.value, progressStatus); - - devWarning( - props.successPercent == undefined, - 'Progress', - '`successPercent` is deprecated. Please use `success.percent` instead.', - ); + const progressInfo = renderProcessInfo(); let progress: VNodeChild; // Render progress shape @@ -122,7 +119,7 @@ export default defineComponent({ const classNames = { ...classString.value, - [`${prefixCls.value}-status-${progressStatus}`]: true, + [`${prefixCls.value}-status-${progressStatus.value}`]: true, }; return
{progress}
; diff --git a/components/progress/props.ts b/components/progress/props.ts index 7933fdf472..fc038f3f56 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -14,8 +14,6 @@ export type ProgressGradient = { direction?: string } & (StringGradients | FromT export interface SuccessProps { percent?: number; - /** @deprecated Use `percent` instead */ - progress?: number; strokeColor?: string; } diff --git a/components/progress/style/index.less b/components/progress/style/index.less index 45fc23e7c0..dfde2fcbb0 100644 --- a/components/progress/style/index.less +++ b/components/progress/style/index.less @@ -189,15 +189,15 @@ @keyframes ~"@{ant-prefix}-progress-active" { 0% { - width: 0; + transform: translateX(-100%) scaleX(0); opacity: 0.1; } 20% { - width: 0; + transform: translateX(-100%) scaleX(0); opacity: 0.5; } 100% { - width: 100%; + transform: translateX(0) scaleX(1); opacity: 0; } } diff --git a/components/progress/style/index.ts b/components/progress/style/index.tsx similarity index 100% rename from components/progress/style/index.ts rename to components/progress/style/index.tsx diff --git a/components/progress/utils.ts b/components/progress/utils.ts index df8b332265..760d696e2c 100644 --- a/components/progress/utils.ts +++ b/components/progress/utils.ts @@ -1,5 +1,4 @@ import devWarning from '../vc-util/devWarning'; -import type { ProgressProps } from './props'; export function validProgress(progress: number | undefined) { if (!progress || progress < 0) { @@ -11,10 +10,16 @@ export function validProgress(progress: number | undefined) { return progress; } -export function getSuccessPercent( - success?: ProgressProps['success'], - successPercent?: ProgressProps['successPercent'], -) { +export function getSuccessPercent({ + success, + successPercent, +}: { + success?: { + progress?: number; + percent?: number; + }; + successPercent?: number; +}) { let percent = successPercent; /** @deprecated Use `percent` instead */ if (success && 'progress' in success) { diff --git a/components/vc-progress/index.ts b/components/vc-progress/index.ts index 199a021db2..7969fd0472 100644 --- a/components/vc-progress/index.ts +++ b/components/vc-progress/index.ts @@ -1,6 +1,7 @@ -// based on rc-progress 2.5.2 +// based on rc-progress 3.1.4 import Progress, { Line, Circle } from './src'; -export { Line, Circle }; +import type { ProgressProps } from './src'; +export { Line, Circle, ProgressProps }; export default Progress; diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx index abb5c7cfbf..3c8ef3dd01 100644 --- a/components/vc-progress/src/Circle.tsx +++ b/components/vc-progress/src/Circle.tsx @@ -2,6 +2,7 @@ import { useTransitionDuration, defaultProps } from './common'; import { propTypes, GapPositionType } from './types'; import { computed, defineComponent, ref } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; +import { useRef } from '../../_util/hooks/useRef'; let gradientSeed = 0; @@ -73,7 +74,8 @@ export default defineComponent({ const percentList = computed(() => toArray(props.percent)); const strokeColorList = computed(() => toArray(props.strokeColor)); - const paths = useTransitionDuration(percentList); + const [setRef, paths] = useRef(); + useTransitionDuration(paths); const getStokeList = () => { const { prefixCls, strokeWidth, strokeLinecap, gapDegree, gapPosition } = props; @@ -108,7 +110,7 @@ export default defineComponent({ class: `${prefixCls}-circle-path`, style: pathStyle, }; - return (paths.value[index].value = c)} {...pathProps} />; + return setRef(c, index)} {...pathProps} />; }); }; diff --git a/components/vc-progress/src/Line.tsx b/components/vc-progress/src/Line.tsx index d61a5280d8..578e859265 100644 --- a/components/vc-progress/src/Line.tsx +++ b/components/vc-progress/src/Line.tsx @@ -1,3 +1,4 @@ +import { useRef } from '../../_util/hooks/useRef'; import { computed, defineComponent } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; import { useTransitionDuration, defaultProps } from './common'; @@ -11,13 +12,54 @@ export default defineComponent({ const { percent } = props; return Array.isArray(percent) ? percent : [percent]; }); + const percentListProps = computed(() => { + const { prefixCls, strokeLinecap, strokeWidth, transition } = props; + let stackPtg = 0; + return percentList.value.map((ptg, index) => { + let dashPercent = 1; + switch (strokeLinecap) { + case 'round': + dashPercent = 1 - strokeWidth / 100; + break; + case 'square': + dashPercent = 1 - strokeWidth / 2 / 100; + break; + default: + dashPercent = 1; + break; + } + const pathStyle = { + strokeDasharray: `${ptg * dashPercent}px, 100px`, + strokeDashoffset: `-${stackPtg}px`, + transition: + transition || + 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', + }; + const color = + strokeColorList.value[index] || strokeColorList.value[strokeColorList.value.length - 1]; + + stackPtg += ptg; + + const pathProps = { + key: index, + d: pathString.value, + 'stroke-linecap': strokeLinecap, + stroke: color as string, + 'stroke-width': strokeWidth, + 'fill-opacity': '0', + class: `${prefixCls}-line-path`, + style: pathStyle, + }; + return pathProps; + }); + }); const strokeColorList = computed(() => { const { strokeColor } = props; return Array.isArray(strokeColor) ? strokeColor : [strokeColor]; }); - - const paths = useTransitionDuration(percentList); + const [setRef, paths] = useRef(); + useTransitionDuration(paths); const center = computed(() => props.strokeWidth / 2); const right = computed(() => 100 - props.strokeWidth / 2); @@ -52,8 +94,6 @@ export default defineComponent({ delete restProps.gapPosition; - let stackPtg = 0; - return ( - {percentList.value.map((ptg, index) => { - const pathStyle = { - strokeDasharray: `${ptg}px, 100px`, - strokeDashoffset: `-${stackPtg}px`, - transition: - transition || - 'stroke-dashoffset 0.3s ease 0s, stroke-dasharray .3s ease 0s, stroke 0.3s linear', - }; - const color = - strokeColorList.value[index] || - strokeColorList.value[strokeColorList.value.length - 1]; - - stackPtg += ptg; - - const pathProps = { - key: index, - d: pathString.value, - 'stroke-linecap': strokeLinecap, - stroke: color as string, - 'stroke-width': strokeWidth, - 'fill-opacity': '0', - class: `${prefixCls}-line-path`, - style: pathStyle, - }; - - return (paths.value[index].value = c)} {...pathProps} />; + {percentListProps.value.map((pathProps, index) => { + return setRef(c, index)} {...pathProps} />; })} ); diff --git a/components/vc-progress/src/common.ts b/components/vc-progress/src/common.ts index 334e9577c2..25da82c350 100644 --- a/components/vc-progress/src/common.ts +++ b/components/vc-progress/src/common.ts @@ -1,5 +1,5 @@ -import type { Ref } from 'vue'; -import { ref, onUpdated, computed } from 'vue'; +import type { Refs } from '../../_util/hooks/useRef'; +import { ref, onUpdated } from 'vue'; import type { ProgressProps } from './types'; export const defaultProps: Partial = { @@ -12,8 +12,7 @@ export const defaultProps: Partial = { trailWidth: 1, }; -export const useTransitionDuration = (percentList: Ref) => { - const paths = computed(() => percentList.value.map(() => ref())); +export const useTransitionDuration = (paths: Refs) => { const prevTimeStamp = ref(null); onUpdated(() => { @@ -21,7 +20,7 @@ export const useTransitionDuration = (percentList: Ref) => { let updated = false; Object.keys(paths.value).forEach(key => { - const path = paths.value[key].value; + const path = paths.value[key]; if (!path) { return; } diff --git a/package.json b/package.json index 8c262020b4..51fdeaa1d0 100644 --- a/package.json +++ b/package.json @@ -203,6 +203,7 @@ "xhr-mock": "^2.5.1" }, "dependencies": { + "@ant-design/colors": "^5.1.1", "@ant-design/icons-vue": "^6.0.0", "@babel/runtime": "^7.10.5", "@simonwep/pickr": "~1.8.0", diff --git a/v2-doc b/v2-doc index d965edbb90..d2c096476e 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit d965edbb9045771095acf91489145e13c975cb51 +Subproject commit d2c096476ee3e8d27fbb9e1cb7933887501b1fab From 25eea2a63b34b80c0512ab31df9a7e7ed27a5235 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Mon, 12 Jul 2021 17:04:51 +0800 Subject: [PATCH 28/28] refactor: progress --- components/progress/Circle.tsx | 13 ++++--------- components/progress/Line.tsx | 4 ++-- components/progress/Steps.tsx | 5 +++-- components/progress/progress.tsx | 2 +- components/progress/props.ts | 6 +++--- components/vc-progress/src/Circle.tsx | 3 ++- v2-doc | 2 +- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/components/progress/Circle.tsx b/components/progress/Circle.tsx index c2265d354e..4a055b338f 100644 --- a/components/progress/Circle.tsx +++ b/components/progress/Circle.tsx @@ -1,17 +1,12 @@ -import type { CSSProperties, ExtractPropTypes } from 'vue'; +import type { CSSProperties } from 'vue'; import { computed, defineComponent } from 'vue'; import { presetPrimaryColors } from '@ant-design/colors'; import { Circle as VCCircle } from '../vc-progress'; import { getSuccessPercent, validProgress } from './utils'; +import type { ProgressProps } from './props'; import { progressProps } from './props'; -import PropTypes from '../_util/vue-types'; -const circleProps = { - ...progressProps, - prefixCls: PropTypes.string, - // progressStatus: PropTypes.string, -}; -export type CircleProps = Partial>; +export type CircleProps = ProgressProps; function getPercentage({ percent, success, successPercent }: CircleProps) { const realSuccessPercent = validProgress(getSuccessPercent({ success, successPercent })); @@ -19,8 +14,8 @@ function getPercentage({ percent, success, successPercent }: CircleProps) { } export default defineComponent({ - props: progressProps, inheritAttrs: false, + props: progressProps(), setup(props, { slots }) { const gapDeg = computed(() => { // Support gapDeg = 0 when type = 'dashboard' diff --git a/components/progress/Line.tsx b/components/progress/Line.tsx index df9bfe42c6..db01a2e6c5 100644 --- a/components/progress/Line.tsx +++ b/components/progress/Line.tsx @@ -7,7 +7,7 @@ import { progressProps } from './props'; import { getSuccessPercent, validProgress } from './utils'; const lineProps = { - ...progressProps, + ...progressProps(), prefixCls: PropTypes.string, direction: { type: String as PropType, @@ -68,8 +68,8 @@ export const handleGradient = (strokeColor: ProgressGradient, directionConfig: D }; export default defineComponent({ - props: lineProps, name: 'Line', + props: lineProps, setup(props, { slots }) { const backgroundProps = computed(() => { const { strokeColor, direction } = props; diff --git a/components/progress/Steps.tsx b/components/progress/Steps.tsx index e50518bff0..6d7543aec6 100644 --- a/components/progress/Steps.tsx +++ b/components/progress/Steps.tsx @@ -1,11 +1,12 @@ -import { computed, ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import type { ExtractPropTypes, PropType, VNodeChild } from 'vue'; +import { computed } from 'vue'; import { defineComponent } from 'vue'; import PropTypes from '../_util/vue-types'; import type { ProgressSize } from './props'; import { progressProps } from './props'; const stepsProps = { - ...progressProps, + ...progressProps(), steps: PropTypes.number, size: { type: String as PropType, diff --git a/components/progress/progress.tsx b/components/progress/progress.tsx index 668470e9e2..aa5feb0101 100644 --- a/components/progress/progress.tsx +++ b/components/progress/progress.tsx @@ -15,7 +15,7 @@ import { progressProps, progressStatuses } from './props'; export default defineComponent({ name: 'AProgress', - props: initDefaultProps(progressProps, { + props: initDefaultProps(progressProps(), { type: 'line', percent: 0, showInfo: true, diff --git a/components/progress/props.ts b/components/progress/props.ts index fc038f3f56..fb5c2de10a 100644 --- a/components/progress/props.ts +++ b/components/progress/props.ts @@ -17,7 +17,7 @@ export interface SuccessProps { strokeColor?: string; } -export const progressProps = { +export const progressProps = () => ({ prefixCls: PropTypes.string, type: PropTypes.oneOf(ProgressType), percent: PropTypes.number, @@ -41,6 +41,6 @@ export const progressProps = { steps: PropTypes.number, /** @deprecated Use `success` instead */ successPercent: PropTypes.number, -}; +}); -export type ProgressProps = Partial>; +export type ProgressProps = Partial>>; diff --git a/components/vc-progress/src/Circle.tsx b/components/vc-progress/src/Circle.tsx index 3c8ef3dd01..a4aa86ece8 100644 --- a/components/vc-progress/src/Circle.tsx +++ b/components/vc-progress/src/Circle.tsx @@ -1,5 +1,6 @@ import { useTransitionDuration, defaultProps } from './common'; -import { propTypes, GapPositionType } from './types'; +import type { GapPositionType } from './types'; +import { propTypes } from './types'; import { computed, defineComponent, ref } from 'vue'; import initDefaultProps from '../../_util/props-util/initDefaultProps'; import { useRef } from '../../_util/hooks/useRef'; diff --git a/v2-doc b/v2-doc index d2c096476e..57c08127bf 160000 --- a/v2-doc +++ b/v2-doc @@ -1 +1 @@ -Subproject commit d2c096476ee3e8d27fbb9e1cb7933887501b1fab +Subproject commit 57c08127bf0e3e826b03f845df3e1332820964e5