refactor(3.x/button): use composition api (#4291)
* refactor(button): use composition api * refactor: add rtl * refactor: sync antd * refactor: update css * refactor: add dev warning * test: add test case * refactor: export component type * refactor: optimizepull/4297/head
							parent
							
								
									9ce43befc4
								
							
						
					
					
						commit
						389b233a40
					
				|  | @ -21,6 +21,7 @@ export default ( | ||||||
|   form?: ComputedRef<{ |   form?: ComputedRef<{ | ||||||
|     requiredMark?: RequiredMark; |     requiredMark?: RequiredMark; | ||||||
|   }>; |   }>; | ||||||
|  |   autoInsertSpaceInButton: ComputedRef<Boolean>; | ||||||
| } => { | } => { | ||||||
|   const configProvider = inject<UnwrapRef<ConfigProviderProps>>( |   const configProvider = inject<UnwrapRef<ConfigProviderProps>>( | ||||||
|     'configProvider', |     'configProvider', | ||||||
|  | @ -28,6 +29,7 @@ export default ( | ||||||
|   ); |   ); | ||||||
|   const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls)); |   const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls)); | ||||||
|   const direction = computed(() => configProvider.direction); |   const direction = computed(() => configProvider.direction); | ||||||
|  |   const autoInsertSpaceInButton = computed(() => configProvider.autoInsertSpaceInButton); | ||||||
|   const space = computed(() => configProvider.space); |   const space = computed(() => configProvider.space); | ||||||
|   const pageHeader = computed(() => configProvider.pageHeader); |   const pageHeader = computed(() => configProvider.pageHeader); | ||||||
|   const form = computed(() => configProvider.form); |   const form = computed(() => configProvider.form); | ||||||
|  | @ -42,5 +44,6 @@ export default ( | ||||||
|     space, |     space, | ||||||
|     pageHeader, |     pageHeader, | ||||||
|     form, |     form, | ||||||
|  |     autoInsertSpaceInButton, | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ exports[`Button renders Chinese characters correctly 1`] = ` | ||||||
| 
 | 
 | ||||||
| exports[`Button renders Chinese characters correctly 2`] = ` | exports[`Button renders Chinese characters correctly 2`] = ` | ||||||
| <button class="ant-btn" type="button"> | <button class="ant-btn" type="button"> | ||||||
|   <!----><span role="img" aria-label="search" class="anticon anticon-search"><svg class="" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896" focusable="false"><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></span><span>按钮</span> |   <!----><span role="img" aria-label="search" class="anticon anticon-search"><svg focusable="false" class="" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></span><span>按钮</span> | ||||||
| </button> | </button> | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
|  | @ -36,9 +36,9 @@ exports[`Button renders Chinese characters correctly 3`] = ` | ||||||
| </button> | </button> | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
| exports[`Button renders Chinese characters correctly 4`] = `<button class="ant-btn ant-btn-loading" type="button"><span role="img" aria-label="loading" class="anticon anticon-loading"><svg class="anticon-spin" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024" focusable="false"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span>按 钮</span></button>`; | exports[`Button renders Chinese characters correctly 4`] = `<button class="ant-btn ant-btn-loading" type="button"><span role="img" aria-label="loading" class="anticon anticon-loading"><svg focusable="false" class="anticon-spin" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span>按 钮</span></button>`; | ||||||
| 
 | 
 | ||||||
| exports[`Button renders Chinese characters correctly 5`] = `<button class="ant-btn ant-btn-loading" type="button"><span role="img" aria-label="loading" class="anticon anticon-loading"><svg class="anticon-spin" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024" focusable="false"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span>按 钮</span></button>`; | exports[`Button renders Chinese characters correctly 5`] = `<button class="ant-btn ant-btn-loading" type="button"><span role="img" aria-label="loading" class="anticon anticon-loading"><svg focusable="false" class="anticon-spin" data-icon="loading" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="0 0 1024 1024"><path d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path></svg></span><span>按 钮</span></button>`; | ||||||
| 
 | 
 | ||||||
| exports[`Button renders Chinese characters correctly 6`] = ` | exports[`Button renders Chinese characters correctly 6`] = ` | ||||||
| <button class="ant-btn ant-btn-two-chinese-chars" type="button"> | <button class="ant-btn ant-btn-two-chinese-chars" type="button"> | ||||||
|  | @ -59,7 +59,7 @@ exports[`Button should not render as link button when href is undefined 1`] = ` | ||||||
| `; | `; | ||||||
| 
 | 
 | ||||||
| exports[`Button should support link button 1`] = ` | exports[`Button should support link button 1`] = ` | ||||||
| <a target="_blank" class="ant-btn" href="http://ant.design"> | <a class="ant-btn" href="http://ant.design" target="_blank"> | ||||||
|   <!----><span>link button</span> |   <!----><span>link button</span> | ||||||
| </a> | </a> | ||||||
| `; | `; | ||||||
|  |  | ||||||
|  | @ -2,9 +2,9 @@ import Button from '../index'; | ||||||
| import SearchOutlined from '@ant-design/icons-vue/SearchOutlined'; | import SearchOutlined from '@ant-design/icons-vue/SearchOutlined'; | ||||||
| import { mount } from '@vue/test-utils'; | import { mount } from '@vue/test-utils'; | ||||||
| import { nextTick } from 'vue'; | import { nextTick } from 'vue'; | ||||||
| import { asyncExpect } from '@/tests/utils'; | import { asyncExpect, sleep } from '@/tests/utils'; | ||||||
| import { sleep } from '../../../tests/utils'; | import mountTest from '@/tests/shared/mountTest'; | ||||||
| import mountTest from '../../../tests/shared/mountTest'; | import { resetWarned } from '../../_util/warning'; | ||||||
| 
 | 
 | ||||||
| describe('Button', () => { | describe('Button', () => { | ||||||
|   mountTest(Button); |   mountTest(Button); | ||||||
|  | @ -27,7 +27,7 @@ describe('Button', () => { | ||||||
|     expect(wrapper.find('.ant-btn-primary').exists()).toBe(true); |     expect(wrapper.find('.ant-btn-primary').exists()).toBe(true); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   it('renders Chinese characters correctly', done => { |   it('renders Chinese characters correctly', (done) => { | ||||||
|     const wrapper = mount({ |     const wrapper = mount({ | ||||||
|       render() { |       render() { | ||||||
|         return <Button>按钮</Button>; |         return <Button>按钮</Button>; | ||||||
|  | @ -247,4 +247,51 @@ describe('Button', () => { | ||||||
|       wrapper.unmount(); |       wrapper.unmount(); | ||||||
|     }).not.toThrow(); |     }).not.toThrow(); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   it('should warning when pass type=link and ghost=true', () => { | ||||||
|  |     resetWarned(); | ||||||
|  |     const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); | ||||||
|  |     mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="link" ghost />; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     expect(warnSpy).toHaveBeenCalledWith( | ||||||
|  |       "Warning: [ant-design-vue: Button] `link` or `text` button can't be a `ghost` button.", | ||||||
|  |     ); | ||||||
|  |     warnSpy.mockRestore(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should warning when pass type=text and ghost=true', () => { | ||||||
|  |     resetWarned(); | ||||||
|  |     const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); | ||||||
|  |     mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="text" ghost />; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     expect(warnSpy).toHaveBeenCalledWith( | ||||||
|  |       "Warning: [ant-design-vue: Button] `link` or `text` button can't be a `ghost` button.", | ||||||
|  |     ); | ||||||
|  |     warnSpy.mockRestore(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should not redirect when button is disabled', async () => { | ||||||
|  |     const onClick = jest.fn(); | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return ( | ||||||
|  |           <Button href="https://ant.design" onClick={onClick} disabled> | ||||||
|  |             click me | ||||||
|  |           </Button> | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await asyncExpect(() => { | ||||||
|  |       wrapper.trigger('click'); | ||||||
|  |     }); | ||||||
|  |     await asyncExpect(() => { | ||||||
|  |       expect(onClick).not.toHaveBeenCalled(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,79 @@ | ||||||
|  | import Button from '../index'; | ||||||
|  | import { mount } from '@vue/test-utils'; | ||||||
|  | import { asyncExpect, sleep } from '@/tests/utils'; | ||||||
|  | 
 | ||||||
|  | describe('click wave effect', () => { | ||||||
|  |   async function clickButton(wrapper) { | ||||||
|  |     await asyncExpect(() => { | ||||||
|  |       wrapper.find('.ant-btn').trigger('click'); | ||||||
|  |     }); | ||||||
|  |     wrapper.find('.ant-btn').element.dispatchEvent(new Event('transitionstart')); | ||||||
|  |     await sleep(20); | ||||||
|  |     wrapper.find('.ant-btn').element.dispatchEvent(new Event('animationend')); | ||||||
|  |     await sleep(20); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   it('should have click wave effect for primary button', async () => { | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="primary">button</Button>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await clickButton(wrapper); | ||||||
|  |     expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe( | ||||||
|  |       'true', | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should have click wave effect for default button', async () => { | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button>button</Button>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await clickButton(wrapper); | ||||||
|  |     expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe( | ||||||
|  |       'true', | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should not have click wave effect for link type button', async () => { | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="link">button</Button>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await clickButton(wrapper); | ||||||
|  |     expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe( | ||||||
|  |       undefined, | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should not have click wave effect for text type button', async () => { | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="text">button</Button>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await clickButton(wrapper); | ||||||
|  |     expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe( | ||||||
|  |       undefined, | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should handle transitionstart', async () => { | ||||||
|  |     const wrapper = mount({ | ||||||
|  |       render() { | ||||||
|  |         return <Button type="primary">button</Button>; | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |     await clickButton(wrapper); | ||||||
|  |     const buttonNode = wrapper.find('.ant-btn').element; | ||||||
|  |     buttonNode.dispatchEvent(new Event('transitionstart')); | ||||||
|  |     expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe( | ||||||
|  |       'true', | ||||||
|  |     ); | ||||||
|  |     wrapper.unmount(); | ||||||
|  |     buttonNode.dispatchEvent(new Event('transitionstart')); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | @ -1,53 +1,49 @@ | ||||||
| import { defineComponent, inject } from 'vue'; | import { defineComponent } from 'vue'; | ||||||
| import { filterEmpty, getSlot } from '../_util/props-util'; | import { flattenChildren } from '../_util/props-util'; | ||||||
| import PropTypes from '../_util/vue-types'; | import PropTypes from '../_util/vue-types'; | ||||||
| import { defaultConfigProvider } from '../config-provider'; | import useConfigInject from '../_util/hooks/useConfigInject'; | ||||||
| import { tuple } from '../_util/type'; |  | ||||||
| 
 | 
 | ||||||
| const ButtonGroupProps = { | import type { ExtractPropTypes, PropType } from 'vue'; | ||||||
|  | import type { SizeType } from '../config-provider'; | ||||||
|  | 
 | ||||||
|  | const buttonGroupProps = { | ||||||
|   prefixCls: PropTypes.string, |   prefixCls: PropTypes.string, | ||||||
|   size: PropTypes.oneOf(tuple('small', 'large', 'default')), |   size: { | ||||||
|  |     type: String as PropType<SizeType>, | ||||||
|  |   }, | ||||||
| }; | }; | ||||||
| export { ButtonGroupProps }; | export { buttonGroupProps }; | ||||||
|  | 
 | ||||||
|  | export type ButtonGroupProps = Partial<ExtractPropTypes<typeof buttonGroupProps>>; | ||||||
|  | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|   name: 'AButtonGroup', |   name: 'AButtonGroup', | ||||||
|   props: ButtonGroupProps, |   props: buttonGroupProps, | ||||||
|   setup() { |   setup(props, { slots }) { | ||||||
|     const configProvider = inject('configProvider', defaultConfigProvider); |     const { prefixCls, direction } = useConfigInject('btn-group', props); | ||||||
|     return { |  | ||||||
|       configProvider, |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|   data() { |  | ||||||
|     return { |  | ||||||
|       sizeMap: { |  | ||||||
|         large: 'lg', |  | ||||||
|         small: 'sm', |  | ||||||
|       }, |  | ||||||
|     }; |  | ||||||
|   }, |  | ||||||
|   render() { |  | ||||||
|     const { prefixCls: customizePrefixCls, size } = this; |  | ||||||
|     const getPrefixCls = this.configProvider.getPrefixCls; |  | ||||||
|     const prefixCls = getPrefixCls('btn-group', customizePrefixCls); |  | ||||||
| 
 | 
 | ||||||
|     // large => lg |     return () => { | ||||||
|     // small => sm |       const { size } = props; | ||||||
|     let sizeCls = ''; | 
 | ||||||
|     switch (size) { |       // large => lg | ||||||
|       case 'large': |       // small => sm | ||||||
|         sizeCls = 'lg'; |       let sizeCls = ''; | ||||||
|         break; |       switch (size) { | ||||||
|       case 'small': |         case 'large': | ||||||
|         sizeCls = 'sm'; |           sizeCls = 'lg'; | ||||||
|         break; |           break; | ||||||
|       default: |         case 'small': | ||||||
|         break; |           sizeCls = 'sm'; | ||||||
|     } |           break; | ||||||
|     const classes = { |         default: | ||||||
|       [`${prefixCls}`]: true, |           break; | ||||||
|       [`${prefixCls}-${sizeCls}`]: sizeCls, |       } | ||||||
|  |       const classes = { | ||||||
|  |         [`${prefixCls.value}`]: true, | ||||||
|  |         [`${prefixCls.value}-${sizeCls}`]: sizeCls, | ||||||
|  |         [`${prefixCls.value}-rtl`]: direction.value === 'rtl', | ||||||
|  |       }; | ||||||
|  |       return <div class={classes}>{flattenChildren(slots.default?.())}</div>; | ||||||
|     }; |     }; | ||||||
|     return <div class={classes}>{filterEmpty(getSlot(this))}</div>; |  | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,80 +1,54 @@ | ||||||
| import { defineComponent, inject, Text, VNode } from 'vue'; | import { | ||||||
|  |   computed, | ||||||
|  |   defineComponent, | ||||||
|  |   onBeforeUnmount, | ||||||
|  |   onMounted, | ||||||
|  |   onUpdated, | ||||||
|  |   ref, | ||||||
|  |   Text, | ||||||
|  |   watch, | ||||||
|  |   watchEffect, | ||||||
|  | } from 'vue'; | ||||||
| import Wave from '../_util/wave'; | import Wave from '../_util/wave'; | ||||||
| import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; |  | ||||||
| import buttonTypes from './buttonTypes'; | import buttonTypes from './buttonTypes'; | ||||||
| import { getSlot, getComponent } from '../_util/props-util'; | import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; | ||||||
| import { defaultConfigProvider } from '../config-provider'; | import { flattenChildren, getPropsSlot } from '../_util/props-util'; | ||||||
| // eslint-disable-next-line no-console | import useConfigInject from '../_util/hooks/useConfigInject'; | ||||||
|  | import devWarning from '../vc-util/devWarning'; | ||||||
|  | 
 | ||||||
|  | import type { ButtonType } from './buttonTypes'; | ||||||
|  | import type { VNode } from 'vue'; | ||||||
|  | 
 | ||||||
| const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; | const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; | ||||||
| const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); | const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); | ||||||
| const props = buttonTypes(); | const props = buttonTypes(); | ||||||
|  | 
 | ||||||
|  | function isUnborderedButtonType(type: ButtonType | undefined) { | ||||||
|  |   return type === 'text' || type === 'link'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export default defineComponent({ | export default defineComponent({ | ||||||
|   name: 'AButton', |   name: 'AButton', | ||||||
|   inheritAttrs: false, |   inheritAttrs: false, | ||||||
|   __ANT_BUTTON: true, |   __ANT_BUTTON: true, | ||||||
|   props, |   props, | ||||||
|   setup() { |   slots: ['icon'], | ||||||
|     return { |   emits: ['click'], | ||||||
|       configProvider: inject('configProvider', defaultConfigProvider), |   setup(props, { slots, attrs, emit }) { | ||||||
|       children: [], |     const { prefixCls, autoInsertSpaceInButton, direction } = useConfigInject('btn', props); | ||||||
|       iconCom: undefined, | 
 | ||||||
|       delayTimeout: undefined, |     const buttonNodeRef = ref<HTMLElement>(null); | ||||||
|     }; |     const delayTimeout = ref(undefined); | ||||||
|   }, |     const iconCom = ref<VNode>(null); | ||||||
|   data() { |     const children = ref<VNode[]>([]); | ||||||
|     return { | 
 | ||||||
|       sizeMap: { |     const sLoading = ref(props.loading); | ||||||
|         large: 'lg', |     const hasTwoCNChar = ref(false); | ||||||
|         small: 'sm', | 
 | ||||||
|       }, |     const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false); | ||||||
|       sLoading: false, | 
 | ||||||
|       hasTwoCNChar: false, |     const getClasses = () => { | ||||||
|     }; |       const { type, shape, size, ghost, block, danger } = props; | ||||||
|   }, |  | ||||||
|   watch: { |  | ||||||
|     loading: { |  | ||||||
|       handler(val, preVal) { |  | ||||||
|         if (preVal && typeof preVal !== 'boolean') { |  | ||||||
|           clearTimeout(this.delayTimeout); |  | ||||||
|         } |  | ||||||
|         if (val && typeof val !== 'boolean' && val.delay) { |  | ||||||
|           this.delayTimeout = setTimeout(() => { |  | ||||||
|             this.sLoading = !!val; |  | ||||||
|           }, val.delay); |  | ||||||
|         } else { |  | ||||||
|           this.sLoading = !!val; |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       immediate: true, |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   mounted() { |  | ||||||
|     this.fixTwoCNChar(); |  | ||||||
|   }, |  | ||||||
|   updated() { |  | ||||||
|     this.fixTwoCNChar(); |  | ||||||
|   }, |  | ||||||
|   beforeUnmount() { |  | ||||||
|     if (this.delayTimeout) { |  | ||||||
|       clearTimeout(this.delayTimeout); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
|   methods: { |  | ||||||
|     getClasses() { |  | ||||||
|       const { |  | ||||||
|         prefixCls: customizePrefixCls, |  | ||||||
|         type, |  | ||||||
|         shape, |  | ||||||
|         size, |  | ||||||
|         hasTwoCNChar, |  | ||||||
|         sLoading, |  | ||||||
|         ghost, |  | ||||||
|         block, |  | ||||||
|         $attrs, |  | ||||||
|       } = this; |  | ||||||
|       const getPrefixCls = this.configProvider.getPrefixCls; |  | ||||||
|       const prefixCls = getPrefixCls('btn', customizePrefixCls); |  | ||||||
|       const autoInsertSpace = this.configProvider.autoInsertSpaceInButton !== false; |  | ||||||
| 
 | 
 | ||||||
|       // large => lg |       // large => lg | ||||||
|       // small => sm |       // small => sm | ||||||
|  | @ -89,43 +63,49 @@ export default defineComponent({ | ||||||
|         default: |         default: | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|       const iconType = sLoading ? 'loading' : this.iconCom; |       const iconType = sLoading.value ? 'loading' : iconCom.value; | ||||||
|       return { |       return { | ||||||
|         [$attrs.class as string]: $attrs.class, |         [attrs.class as string]: attrs.class, | ||||||
|         [`${prefixCls}`]: true, |         [`${prefixCls.value}`]: true, | ||||||
|         [`${prefixCls}-${type}`]: type, |         [`${prefixCls.value}-${type}`]: type, | ||||||
|         [`${prefixCls}-${shape}`]: shape, |         [`${prefixCls.value}-${shape}`]: shape, | ||||||
|         [`${prefixCls}-${sizeCls}`]: sizeCls, |         [`${prefixCls.value}-${sizeCls}`]: sizeCls, | ||||||
|         [`${prefixCls}-icon-only`]: this.children.length === 0 && iconType, |         [`${prefixCls.value}-icon-only`]: children.value.length === 0 && !!iconType, | ||||||
|         [`${prefixCls}-loading`]: sLoading, |         [`${prefixCls.value}-loading`]: sLoading.value, | ||||||
|         [`${prefixCls}-background-ghost`]: ghost || type === 'ghost', |         [`${prefixCls.value}-background-ghost`]: ghost && !isUnborderedButtonType(type), | ||||||
|         [`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace, |         [`${prefixCls.value}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value, | ||||||
|         [`${prefixCls}-block`]: block, |         [`${prefixCls.value}-block`]: block, | ||||||
|  |         [`${prefixCls.value}-dangerous`]: !!danger, | ||||||
|  |         [`${prefixCls.value}-rtl`]: direction.value === 'rtl', | ||||||
|       }; |       }; | ||||||
|     }, |     }; | ||||||
|     fixTwoCNChar() { | 
 | ||||||
|  |     const fixTwoCNChar = () => { | ||||||
|       // Fix for HOC usage like <FormatMessage /> |       // Fix for HOC usage like <FormatMessage /> | ||||||
|       const node = this.$refs.buttonNode as HTMLElement; |       const node = buttonNodeRef.value!; | ||||||
|       if (!node) { |       if (!node || autoInsertSpaceInButton.value === false) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       const buttonText = node.textContent; |       const buttonText = node.textContent; | ||||||
|       if (this.isNeedInserted() && isTwoCNChar(buttonText)) { | 
 | ||||||
|         if (!this.hasTwoCNChar) { |       if (isNeedInserted() && isTwoCNChar(buttonText)) { | ||||||
|           this.hasTwoCNChar = true; |         if (!hasTwoCNChar.value) { | ||||||
|  |           hasTwoCNChar.value = true; | ||||||
|         } |         } | ||||||
|       } else if (this.hasTwoCNChar) { |       } else if (hasTwoCNChar.value) { | ||||||
|         this.hasTwoCNChar = false; |         hasTwoCNChar.value = false; | ||||||
|       } |       } | ||||||
|     }, |     }; | ||||||
|     handleClick(event: Event) { |     const handleClick = (event: Event) => { | ||||||
|       const { sLoading } = this.$data; |       // https://github.com/ant-design/ant-design/issues/30207 | ||||||
|       if (sLoading) { |       if (sLoading.value || props.disabled) { | ||||||
|  |         event.preventDefault(); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       this.$emit('click', event); |       emit('click', event); | ||||||
|     }, |     }; | ||||||
|     insertSpace(child: VNode, needInserted: boolean) { | 
 | ||||||
|  |     const insertSpace = (child: VNode, needInserted: boolean) => { | ||||||
|       const SPACE = needInserted ? ' ' : ''; |       const SPACE = needInserted ? ' ' : ''; | ||||||
|       if (child.type === Text) { |       if (child.type === Text) { | ||||||
|         let text = (child.children as string).trim(); |         let text = (child.children as string).trim(); | ||||||
|  | @ -135,53 +115,86 @@ export default defineComponent({ | ||||||
|         return <span>{text}</span>; |         return <span>{text}</span>; | ||||||
|       } |       } | ||||||
|       return child; |       return child; | ||||||
|     }, |  | ||||||
|     isNeedInserted() { |  | ||||||
|       const { iconCom, type } = this; |  | ||||||
|       return this.children.length === 1 && !iconCom && type !== 'link'; |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|   render() { |  | ||||||
|     this.iconCom = getComponent(this, 'icon'); |  | ||||||
|     const { type, htmlType, iconCom, disabled, handleClick, sLoading, href, title, $attrs } = this; |  | ||||||
|     const children = getSlot(this); |  | ||||||
|     this.children = children; |  | ||||||
|     const classes = this.getClasses(); |  | ||||||
| 
 |  | ||||||
|     const buttonProps = { |  | ||||||
|       ...$attrs, |  | ||||||
|       title, |  | ||||||
|       disabled, |  | ||||||
|       class: classes, |  | ||||||
|       onClick: handleClick, |  | ||||||
|     }; |     }; | ||||||
|     const iconNode = sLoading ? <LoadingOutlined /> : iconCom; |  | ||||||
| 
 | 
 | ||||||
|     const autoInsertSpace = this.configProvider.autoInsertSpaceInButton !== false; |     const isNeedInserted = () => | ||||||
|     const kids = children.map(child => |       children.value.length === 1 && !iconCom.value && !isUnborderedButtonType(props.type); | ||||||
|       this.insertSpace(child, this.isNeedInserted() && autoInsertSpace), | 
 | ||||||
|  |     watchEffect(() => { | ||||||
|  |       devWarning( | ||||||
|  |         !(props.ghost && isUnborderedButtonType(props.type)), | ||||||
|  |         'Button', | ||||||
|  |         "`link` or `text` button can't be a `ghost` button.", | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     watch( | ||||||
|  |       () => props.loading, | ||||||
|  |       (val, preVal) => { | ||||||
|  |         if (preVal && typeof preVal !== 'boolean') { | ||||||
|  |           clearTimeout(delayTimeout.value); | ||||||
|  |         } | ||||||
|  |         if (val && typeof val !== 'boolean' && val.delay) { | ||||||
|  |           delayTimeout.value = setTimeout(() => { | ||||||
|  |             sLoading.value = !!val; | ||||||
|  |           }, val.delay); | ||||||
|  |         } else { | ||||||
|  |           sLoading.value = !!val; | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         immediate: true, | ||||||
|  |       }, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     if (href !== undefined) { |     onMounted(fixTwoCNChar); | ||||||
|       return ( |     onUpdated(fixTwoCNChar); | ||||||
|         <a {...buttonProps} href={href} ref="buttonNode"> | 
 | ||||||
|  |     onBeforeUnmount(() => { | ||||||
|  |       delayTimeout.value && clearTimeout(delayTimeout.value); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return () => { | ||||||
|  |       iconCom.value = getPropsSlot(slots, props, 'icon'); | ||||||
|  |       children.value = flattenChildren(getPropsSlot(slots, props)); | ||||||
|  | 
 | ||||||
|  |       const { type, htmlType, disabled, href, title, target } = props; | ||||||
|  |       const classes = getClasses(); | ||||||
|  | 
 | ||||||
|  |       const buttonProps = { | ||||||
|  |         ...attrs, | ||||||
|  |         title, | ||||||
|  |         disabled, | ||||||
|  |         class: classes, | ||||||
|  |         onClick: handleClick, | ||||||
|  |       }; | ||||||
|  |       const iconNode = sLoading.value ? <LoadingOutlined /> : iconCom.value; | ||||||
|  | 
 | ||||||
|  |       const kids = children.value.map((child) => | ||||||
|  |         insertSpace(child, isNeedInserted() && autoInsertSpace.value), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       if (href !== undefined) { | ||||||
|  |         return ( | ||||||
|  |           <a {...buttonProps} href={href} target={target} ref={buttonNodeRef}> | ||||||
|  |             {iconNode} | ||||||
|  |             {kids} | ||||||
|  |           </a> | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const buttonNode = ( | ||||||
|  |         <button {...buttonProps} ref={buttonNodeRef} type={htmlType}> | ||||||
|           {iconNode} |           {iconNode} | ||||||
|           {kids} |           {kids} | ||||||
|         </a> |         </button> | ||||||
|       ); |       ); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     const buttonNode = ( |       if (isUnborderedButtonType(type)) { | ||||||
|       <button {...buttonProps} ref="buttonNode" type={htmlType || 'button'}> |         return buttonNode; | ||||||
|         {iconNode} |       } | ||||||
|         {kids} |  | ||||||
|       </button> |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     if (type === 'link') { |       return <Wave ref="wave">{buttonNode}</Wave>; | ||||||
|       return buttonNode; |     }; | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return <Wave ref="wave">{buttonNode}</Wave>; |  | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,32 +1,48 @@ | ||||||
| import { ExtractPropTypes } from 'vue'; |  | ||||||
| 
 |  | ||||||
| import { tuple } from '../_util/type'; | import { tuple } from '../_util/type'; | ||||||
| import PropTypes, { withUndefined } from '../_util/vue-types'; | import PropTypes from '../_util/vue-types'; | ||||||
| 
 | 
 | ||||||
| const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'danger', 'link'); | import type { ExtractPropTypes, PropType } from 'vue'; | ||||||
|  | import type { SizeType } from '../config-provider'; | ||||||
|  | 
 | ||||||
|  | const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'link', 'text'); | ||||||
| export type ButtonType = typeof ButtonTypes[number]; | export type ButtonType = typeof ButtonTypes[number]; | ||||||
| const ButtonShapes = tuple('circle', 'circle-outline', 'round'); | const ButtonShapes = tuple('circle', 'circle-outline', 'round'); | ||||||
| export type ButtonShape = typeof ButtonShapes[number]; | export type ButtonShape = typeof ButtonShapes[number]; | ||||||
| const ButtonSizes = tuple('large', 'default', 'small'); | 
 | ||||||
| export type ButtonSize = typeof ButtonSizes[number]; |  | ||||||
| const ButtonHTMLTypes = tuple('submit', 'button', 'reset'); | const ButtonHTMLTypes = tuple('submit', 'button', 'reset'); | ||||||
| export type ButtonHTMLType = typeof ButtonHTMLTypes[number]; | export type ButtonHTMLType = typeof ButtonHTMLTypes[number]; | ||||||
| 
 | 
 | ||||||
|  | export type LegacyButtonType = ButtonType | 'danger'; | ||||||
|  | export function convertLegacyProps(type?: LegacyButtonType): ButtonProps { | ||||||
|  |   if (type === 'danger') { | ||||||
|  |     return { danger: true }; | ||||||
|  |   } | ||||||
|  |   return { type }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const buttonProps = () => ({ | const buttonProps = () => ({ | ||||||
|   prefixCls: PropTypes.string, |   prefixCls: PropTypes.string, | ||||||
|   type: PropTypes.oneOf(ButtonTypes), |   type: PropTypes.oneOf(ButtonTypes), | ||||||
|   htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'), |   htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'), | ||||||
|   // icon: PropTypes.string,
 |  | ||||||
|   shape: PropTypes.oneOf(ButtonShapes), |   shape: PropTypes.oneOf(ButtonShapes), | ||||||
|   size: PropTypes.oneOf(ButtonSizes).def('default'), |   size: { | ||||||
|   loading: withUndefined(PropTypes.oneOfType([PropTypes.looseBool, PropTypes.object])), |     type: String as PropType<SizeType>, | ||||||
|  |   }, | ||||||
|  |   loading: { | ||||||
|  |     type: [Boolean, Object], | ||||||
|  |     default: (): boolean | { delay?: number } => false, | ||||||
|  |   }, | ||||||
|   disabled: PropTypes.looseBool, |   disabled: PropTypes.looseBool, | ||||||
|   ghost: PropTypes.looseBool, |   ghost: PropTypes.looseBool, | ||||||
|   block: PropTypes.looseBool, |   block: PropTypes.looseBool, | ||||||
|  |   danger: PropTypes.looseBool, | ||||||
|   icon: PropTypes.VNodeChild, |   icon: PropTypes.VNodeChild, | ||||||
|   href: PropTypes.string, |   href: PropTypes.string, | ||||||
|  |   target: PropTypes.string, | ||||||
|   title: PropTypes.string, |   title: PropTypes.string, | ||||||
|   onClick: PropTypes.func, |   onClick: { | ||||||
|  |     type: Function as PropType<(event: MouseEvent) => void>, | ||||||
|  |   }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export type ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof buttonProps>>>; | export type ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof buttonProps>>>; | ||||||
|  |  | ||||||
|  | @ -2,10 +2,16 @@ import { App, Plugin } from 'vue'; | ||||||
| import Button from './button'; | import Button from './button'; | ||||||
| import ButtonGroup from './button-group'; | import ButtonGroup from './button-group'; | ||||||
| 
 | 
 | ||||||
|  | import type { ButtonProps, ButtonShape, ButtonType } from './buttonTypes'; | ||||||
|  | import type { ButtonGroupProps } from './button-group'; | ||||||
|  | import type { SizeType as ButtonSize } from '../config-provider'; | ||||||
|  | 
 | ||||||
|  | export type { ButtonProps, ButtonShape, ButtonType, ButtonGroupProps, ButtonSize }; | ||||||
|  | 
 | ||||||
| Button.Group = ButtonGroup; | Button.Group = ButtonGroup; | ||||||
| 
 | 
 | ||||||
| /* istanbul ignore next */ | /* istanbul ignore next */ | ||||||
| Button.install = function(app: App) { | Button.install = function (app: App) { | ||||||
|   app.component(Button.name, Button); |   app.component(Button.name, Button); | ||||||
|   app.component(ButtonGroup.name, ButtonGroup); |   app.component(ButtonGroup.name, ButtonGroup); | ||||||
|   return app; |   return app; | ||||||
|  |  | ||||||
|  | @ -15,22 +15,18 @@ | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/12978 |   // Fixing https://github.com/ant-design/ant-design/issues/12978 | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/20058 |   // Fixing https://github.com/ant-design/ant-design/issues/20058 | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/19972 |   // Fixing https://github.com/ant-design/ant-design/issues/19972 | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/12978 |  | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/18107 |   // Fixing https://github.com/ant-design/ant-design/issues/18107 | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/13214 |   // Fixing https://github.com/ant-design/ant-design/issues/13214 | ||||||
|   // It is a render problem of chrome, which is only happened in the codesandbox demo |   // It is a render problem of chrome, which is only happened in the codesandbox demo | ||||||
|   // 0.001px solution works and I don't why |   // 0.001px solution works and I don't why | ||||||
|   line-height: @line-height-base; |   line-height: @btn-line-height; | ||||||
|   .btn(); |   .btn(); | ||||||
|   .btn-default(); |   .btn-default(); | ||||||
| 
 | 
 | ||||||
|   // Make sure that the target of Button's click event always be `button` |   // Fix loading button animation | ||||||
|   // Ref: https://github.com/ant-design/ant-design/issues/7034 |   // https://github.com/ant-design/ant-design/issues/24323 | ||||||
|   > i, |  | ||||||
|   > span { |   > span { | ||||||
|     display: inline-block; |     display: inline-block; | ||||||
|     transition: margin-left 0.3s @ease-in-out; |  | ||||||
|     pointer-events: none; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-primary { |   &-primary { | ||||||
|  | @ -48,6 +44,7 @@ | ||||||
|     .@{btn-prefix-cls}-group &:first-child { |     .@{btn-prefix-cls}-group &:first-child { | ||||||
|       &:not(:last-child) { |       &:not(:last-child) { | ||||||
|         border-right-color: @btn-group-border; |         border-right-color: @btn-group-border; | ||||||
|  | 
 | ||||||
|         &[disabled] { |         &[disabled] { | ||||||
|           border-right-color: @btn-default-border; |           border-right-color: @btn-default-border; | ||||||
|         } |         } | ||||||
|  | @ -57,6 +54,7 @@ | ||||||
|     .@{btn-prefix-cls}-group &:last-child:not(:first-child), |     .@{btn-prefix-cls}-group &:last-child:not(:first-child), | ||||||
|     .@{btn-prefix-cls}-group & + & { |     .@{btn-prefix-cls}-group & + & { | ||||||
|       border-left-color: @btn-group-border; |       border-left-color: @btn-group-border; | ||||||
|  | 
 | ||||||
|       &[disabled] { |       &[disabled] { | ||||||
|         border-left-color: @btn-default-border; |         border-left-color: @btn-default-border; | ||||||
|       } |       } | ||||||
|  | @ -71,6 +69,8 @@ | ||||||
|     .btn-dashed(); |     .btn-dashed(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // type="danger" will deprecated | ||||||
|  |   // use danger instead | ||||||
|   &-danger { |   &-danger { | ||||||
|     .btn-danger(); |     .btn-danger(); | ||||||
|   } |   } | ||||||
|  | @ -79,12 +79,29 @@ | ||||||
|     .btn-link(); |     .btn-link(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   &-text { | ||||||
|  |     .btn-text(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-dangerous { | ||||||
|  |     .btn-danger-default(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-dangerous&-primary { | ||||||
|  |     .btn-danger(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-dangerous&-link { | ||||||
|  |     .btn-danger-link(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-dangerous&-text { | ||||||
|  |     .btn-danger-text(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   &-icon-only { |   &-icon-only { | ||||||
|     .btn-square(@btn-prefix-cls); |     .btn-square(@btn-prefix-cls); | ||||||
| 
 |     vertical-align: -1px; | ||||||
|     > i { |  | ||||||
|       vertical-align: middle; |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-round { |   &-round { | ||||||
|  | @ -94,17 +111,16 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-circle, |   &-circle { | ||||||
|   &-circle-outline { |  | ||||||
|     .btn-circle(@btn-prefix-cls); |     .btn-circle(@btn-prefix-cls); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &::before { |   &::before { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|     top: -1px; |     top: -@btn-border-width; | ||||||
|     right: -1px; |     right: -@btn-border-width; | ||||||
|     bottom: -1px; |     bottom: -@btn-border-width; | ||||||
|     left: -1px; |     left: -@btn-border-width; | ||||||
|     z-index: 1; |     z-index: 1; | ||||||
|     display: none; |     display: none; | ||||||
|     background: @component-background; |     background: @component-background; | ||||||
|  | @ -133,10 +149,10 @@ | ||||||
|     &:not([disabled]) { |     &:not([disabled]) { | ||||||
|       pointer-events: none; |       pointer-events: none; | ||||||
|     } |     } | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   &&-loading::before { |     &::before { | ||||||
|     display: block; |       display: block; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) { |   &&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) { | ||||||
|  | @ -153,6 +169,25 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   // & > &-loading-icon { | ||||||
|  |   //   transition: all 0.3s @ease-in-out; | ||||||
|  | 
 | ||||||
|  |   //   .@{iconfont-css-prefix} { | ||||||
|  |   //     padding-right: @padding-xs; | ||||||
|  |   //     animation: none; | ||||||
|  |   //     // for smooth button padding transition | ||||||
|  |   //     svg { | ||||||
|  |   //       animation: loadingCircle 1s infinite linear; | ||||||
|  |   //     } | ||||||
|  |   //   } | ||||||
|  | 
 | ||||||
|  |   //   &:only-child { | ||||||
|  |   //     .@{iconfont-css-prefix} { | ||||||
|  |   //       padding-right: 0; | ||||||
|  |   //     } | ||||||
|  |   //   } | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|   &-group { |   &-group { | ||||||
|     .btn-group(@btn-prefix-cls); |     .btn-group(@btn-prefix-cls); | ||||||
|   } |   } | ||||||
|  | @ -166,7 +201,7 @@ | ||||||
|   // To ensure that a space will be placed between character and `Icon`. |   // To ensure that a space will be placed between character and `Icon`. | ||||||
|   > .@{iconfont-css-prefix} + span, |   > .@{iconfont-css-prefix} + span, | ||||||
|   > span + .@{iconfont-css-prefix} { |   > span + .@{iconfont-css-prefix} { | ||||||
|     margin-left: 8px; |     margin-left: @margin-xs; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-background-ghost { |   &-background-ghost { | ||||||
|  | @ -183,10 +218,12 @@ | ||||||
|     .button-variant-ghost(@btn-danger-border); |     .button-variant-ghost(@btn-danger-border); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-background-ghost&-link { |   &-background-ghost&-dangerous { | ||||||
|     .button-variant-ghost(@link-color; transparent); |     .button-variant-ghost(@btn-danger-border); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     color: @component-background; |   &-background-ghost&-dangerous&-link { | ||||||
|  |     .button-variant-ghost(@btn-danger-border, transparent); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &-two-chinese-chars::first-letter { |   &-two-chinese-chars::first-letter { | ||||||
|  | @ -203,16 +240,21 @@ | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // https://github.com/ant-design/ant-design/issues/12681 |   // https://github.com/ant-design/ant-design/issues/12681 | ||||||
|  |   // same method as Select | ||||||
|   &:empty { |   &:empty { | ||||||
|     vertical-align: top; |     display: inline-block; | ||||||
|  |     width: 0; | ||||||
|  |     visibility: hidden; | ||||||
|  |     content: '\a0'; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| a.@{btn-prefix-cls} { | a.@{btn-prefix-cls} { | ||||||
|   // Fixing https://github.com/ant-design/ant-design/issues/12978 |   // Fixing https://github.com/ant-design/ant-design/issues/12978 | ||||||
|  |   // https://github.com/ant-design/ant-design/issues/29978 | ||||||
|   // It is a render problem of chrome, which is only happened in the codesandbox demo |   // It is a render problem of chrome, which is only happened in the codesandbox demo | ||||||
|   // 0.1px for padding-top solution works and I don't why |   // 0.1px for padding-top solution works and I don't why | ||||||
|   padding-top: 0.1px; |   padding-top: 0.01px !important; | ||||||
|   line-height: @btn-height-base - 2px; |   line-height: @btn-height-base - 2px; | ||||||
| 
 | 
 | ||||||
|   &-lg { |   &-lg { | ||||||
|  | @ -222,3 +264,5 @@ a.@{btn-prefix-cls} { | ||||||
|     line-height: @btn-height-sm - 2px; |     line-height: @btn-height-sm - 2px; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | @import './rtl'; | ||||||
|  |  | ||||||
|  | @ -1,21 +1,22 @@ | ||||||
| // mixins for button | // mixins for button | ||||||
| // ------------------------ | // ------------------------ | ||||||
| .button-size(@height; @padding; @font-size; @border-radius) { | .button-size(@height; @padding-horizontal; @font-size; @border-radius) { | ||||||
|  |   @padding-vertical: max( | ||||||
|  |     (round(((@height - @font-size * @line-height-base) / 2) * 10) / 10) - @border-width-base, | ||||||
|  |     0 | ||||||
|  |   ); | ||||||
|   height: @height; |   height: @height; | ||||||
|   padding: @padding; |   padding: @padding-vertical @padding-horizontal; | ||||||
|   font-size: @font-size; |   font-size: @font-size; | ||||||
|   border-radius: @border-radius; |   border-radius: @border-radius; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) { | .button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) { | ||||||
|   &-disabled, |  | ||||||
|   &.disabled, |  | ||||||
|   &[disabled] { |   &[disabled] { | ||||||
|     &, |     &, | ||||||
|     &:hover, |     &:hover, | ||||||
|     &:focus, |     &:focus, | ||||||
|     &:active, |     &:active { | ||||||
|     &.active { |  | ||||||
|       .button-color(@color; @background; @border); |       .button-color(@color; @background; @border); | ||||||
| 
 | 
 | ||||||
|       text-shadow: none; |       text-shadow: none; | ||||||
|  | @ -44,8 +45,7 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &:active, |   &:active { | ||||||
|   &.active { |  | ||||||
|     & when (@theme = dark) { |     & when (@theme = dark) { | ||||||
|       .button-color( |       .button-color( | ||||||
|         @color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) ` |         @color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) ` | ||||||
|  | @ -76,8 +76,7 @@ | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &:active, |   &:active { | ||||||
|   &.active { |  | ||||||
|     & when (@theme = dark) { |     & when (@theme = dark) { | ||||||
|       .button-color(@primary-7; @background; @primary-7); |       .button-color(@primary-7; @background; @primary-7); | ||||||
|     } |     } | ||||||
|  | @ -103,7 +102,7 @@ | ||||||
|         .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); |         .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     & when not(@border = transparent) { |     & when not (@border = transparent) { | ||||||
|       & when (@theme = dark) { |       & when (@theme = dark) { | ||||||
|         .button-color( |         .button-color( | ||||||
|           ~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) ` |           ~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) ` | ||||||
|  | @ -116,8 +115,7 @@ | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &:active, |   &:active { | ||||||
|   &.active { |  | ||||||
|     & when (@border = transparent) { |     & when (@border = transparent) { | ||||||
|       & when (@theme = dark) { |       & when (@theme = dark) { | ||||||
|         .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); |         .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); | ||||||
|  | @ -143,9 +141,8 @@ | ||||||
| } | } | ||||||
| .button-color(@color; @background; @border) { | .button-color(@color; @background; @border) { | ||||||
|   color: @color; |   color: @color; | ||||||
|   background-color: @background; |   background: @background; | ||||||
|   border-color: @border; |   border-color: @border; // a inside Button which only work in Chrome | ||||||
|   // a inside Button which only work in Chrome |  | ||||||
|   // http://stackoverflow.com/a/17253457 |   // http://stackoverflow.com/a/17253457 | ||||||
|   > a:only-child { |   > a:only-child { | ||||||
|     color: currentColor; |     color: currentColor; | ||||||
|  | @ -168,37 +165,34 @@ | ||||||
|     position: relative; |     position: relative; | ||||||
|     &:hover, |     &:hover, | ||||||
|     &:focus, |     &:focus, | ||||||
|     &:active, |     &:active { | ||||||
|     &.active { |  | ||||||
|       z-index: 2; |       z-index: 2; | ||||||
|     } |     } | ||||||
|     &:disabled { |     &[disabled] { | ||||||
|       z-index: 0; |       z-index: 0; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   > .@{btnClassName}-icon-only { |   .@{btnClassName}-icon-only { | ||||||
|     font-size: @font-size-base; |     font-size: @font-size-base; | ||||||
|   } |   } | ||||||
|   // size |   // size | ||||||
|   &-lg > .@{btnClassName}, |   &-lg > .@{btnClassName}, | ||||||
|   &-lg > span > .@{btnClassName} { |   &-lg > span > .@{btnClassName} { | ||||||
|     .button-size(@btn-height-lg; @btn-padding-lg; @btn-font-size-lg; 0); |     .button-size(@btn-height-lg; @btn-padding-horizontal-lg; @btn-font-size-lg; 0); | ||||||
|     line-height: @btn-height-lg - 2px; |  | ||||||
|   } |   } | ||||||
|   &-lg > .@{btnClassName}.@{btnClassName}-icon-only { |   &-lg .@{btnClassName}.@{btnClassName}-icon-only { | ||||||
|     .square(@btn-height-lg); |     .square(@btn-height-lg); | ||||||
|     padding-right: 0; |     padding-right: 0; | ||||||
|     padding-left: 0; |     padding-left: 0; | ||||||
|   } |   } | ||||||
|   &-sm > .@{btnClassName}, |   &-sm > .@{btnClassName}, | ||||||
|   &-sm > span > .@{btnClassName} { |   &-sm > span > .@{btnClassName} { | ||||||
|     .button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; 0); |     .button-size(@btn-height-sm; @btn-padding-horizontal-sm; @font-size-base; 0); | ||||||
|     line-height: @btn-height-sm - 2px; |  | ||||||
|     > .@{iconfont-css-prefix} { |     > .@{iconfont-css-prefix} { | ||||||
|       font-size: @font-size-base; |       font-size: @font-size-base; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &-sm > .@{btnClassName}.@{btnClassName}-icon-only { |   &-sm .@{btnClassName}.@{btnClassName}-icon-only { | ||||||
|     .square(@btn-height-sm); |     .square(@btn-height-sm); | ||||||
|     padding-right: 0; |     padding-right: 0; | ||||||
|     padding-left: 0; |     padding-left: 0; | ||||||
|  | @ -219,7 +213,9 @@ | ||||||
|   transition: all 0.3s @ease-in-out; |   transition: all 0.3s @ease-in-out; | ||||||
|   user-select: none; |   user-select: none; | ||||||
|   touch-action: manipulation; |   touch-action: manipulation; | ||||||
|   .button-size(@btn-height-base; @btn-padding-base; @font-size-base; @btn-border-radius-base); |   .button-size( | ||||||
|  |     @btn-height-base; @btn-padding-horizontal-base; @font-size-base; @btn-border-radius-base | ||||||
|  |   ); | ||||||
|   > .@{iconfont-css-prefix} { |   > .@{iconfont-css-prefix} { | ||||||
|     line-height: 1; |     line-height: 1; | ||||||
|   } |   } | ||||||
|  | @ -235,7 +231,6 @@ | ||||||
|     outline: 0; |     outline: 0; | ||||||
|     box-shadow: none; |     box-shadow: none; | ||||||
|   } |   } | ||||||
|   &.disabled, |  | ||||||
|   &[disabled] { |   &[disabled] { | ||||||
|     cursor: not-allowed; |     cursor: not-allowed; | ||||||
|     > * { |     > * { | ||||||
|  | @ -243,10 +238,14 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   &-lg { |   &-lg { | ||||||
|     .button-size(@btn-height-lg; @btn-padding-lg; @btn-font-size-lg; @btn-border-radius-base); |     .button-size( | ||||||
|  |       @btn-height-lg; @btn-padding-horizontal-lg; @btn-font-size-lg; @btn-border-radius-base | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
|   &-sm { |   &-sm { | ||||||
|     .button-size(@btn-height-sm; @btn-padding-sm; @btn-font-size-sm; @btn-border-radius-sm); |     .button-size( | ||||||
|  |       @btn-height-sm; @btn-padding-horizontal-sm; @btn-font-size-sm; @btn-border-radius-sm | ||||||
|  |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| // primary button style | // primary button style | ||||||
|  | @ -258,8 +257,7 @@ | ||||||
|   .button-variant-other(@btn-default-color; @btn-default-bg; @btn-default-border); |   .button-variant-other(@btn-default-color; @btn-default-bg; @btn-default-border); | ||||||
|   &:hover, |   &:hover, | ||||||
|   &:focus, |   &:focus, | ||||||
|   &:active, |   &:active { | ||||||
|   &.active { |  | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|     background: @btn-default-bg; |     background: @btn-default-bg; | ||||||
|   } |   } | ||||||
|  | @ -277,10 +275,70 @@ | ||||||
| .btn-danger() { | .btn-danger() { | ||||||
|   .button-variant-primary(@btn-danger-color, @btn-danger-bg); |   .button-variant-primary(@btn-danger-color, @btn-danger-bg); | ||||||
| } | } | ||||||
|  | // danger default button style | ||||||
|  | .btn-danger-default() { | ||||||
|  |   .button-color(@error-color, @btn-default-bg, @error-color); | ||||||
|  |   &:hover, | ||||||
|  |   &:focus { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color( | ||||||
|  |         ~`colorPalette('@{error-color}', 7) `; @btn-default-bg; ~`colorPalette('@{error-color}', 7) | ||||||
|  |           ` | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color( | ||||||
|  |         ~`colorPalette('@{error-color}', 5) `; @btn-default-bg; ~`colorPalette('@{error-color}', 5) | ||||||
|  |           ` | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   &:active { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color( | ||||||
|  |         ~`colorPalette('@{error-color}', 5) `; @btn-default-bg; ~`colorPalette('@{error-color}', 5) | ||||||
|  |           ` | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color( | ||||||
|  |         ~`colorPalette('@{error-color}', 7) `; @btn-default-bg; ~`colorPalette('@{error-color}', 7) | ||||||
|  |           ` | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .button-disabled(); | ||||||
|  | } | ||||||
|  | // danger link button style | ||||||
|  | .btn-danger-link() { | ||||||
|  |   .button-variant-other(@error-color, transparent, transparent); | ||||||
|  |   box-shadow: none; | ||||||
|  |   &:hover, | ||||||
|  |   &:focus { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   &:active { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 5) `; transparent; transparent); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 7) `; transparent; transparent); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .button-disabled(@disabled-color; transparent; transparent); | ||||||
|  | } | ||||||
| // link button style | // link button style | ||||||
| .btn-link() { | .btn-link() { | ||||||
|   .button-variant-other(@link-color, transparent, transparent); |   .button-variant-other(@link-color, transparent, transparent); | ||||||
|   box-shadow: none; |   box-shadow: none; | ||||||
|  |   &:hover { | ||||||
|  |     background: @btn-link-hover-bg; | ||||||
|  |   } | ||||||
|   &:hover, |   &:hover, | ||||||
|   &:focus, |   &:focus, | ||||||
|   &:active { |   &:active { | ||||||
|  | @ -288,31 +346,82 @@ | ||||||
|   } |   } | ||||||
|   .button-disabled(@disabled-color; transparent; transparent); |   .button-disabled(@disabled-color; transparent; transparent); | ||||||
| } | } | ||||||
|  | // text button style | ||||||
|  | .btn-text() { | ||||||
|  |   .button-variant-other(@text-color, transparent, transparent); | ||||||
|  |   box-shadow: none; | ||||||
|  |   &:hover, | ||||||
|  |   &:focus { | ||||||
|  |     color: @text-color; | ||||||
|  |     background: @btn-text-hover-bg; | ||||||
|  |     border-color: transparent; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:active { | ||||||
|  |     color: @text-color; | ||||||
|  |     background: fadein(@btn-text-hover-bg, 1%); | ||||||
|  |     border-color: transparent; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .button-disabled(@disabled-color; transparent; transparent); | ||||||
|  | } | ||||||
|  | .btn-danger-text() { | ||||||
|  |   .button-variant-other(@error-color, transparent, transparent); | ||||||
|  |   box-shadow: none; | ||||||
|  |   &:hover, | ||||||
|  |   &:focus { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 7) `; @btn-text-hover-bg; transparent); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 5) `; @btn-text-hover-bg; transparent); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:active { | ||||||
|  |     & when (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 5) `; fadein(@btn-text-hover-bg, 1%); transparent); | ||||||
|  |     } | ||||||
|  |     & when not (@theme = dark) { | ||||||
|  |       .button-color(~`colorPalette('@{error-color}', 7) `; fadein(@btn-text-hover-bg, 1%); transparent); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .button-disabled(@disabled-color; transparent; transparent); | ||||||
|  | } | ||||||
| // round button | // round button | ||||||
| .btn-round(@btnClassName: btn) { | .btn-round(@btnClassName: btn) { | ||||||
|   .button-size(@btn-circle-size; 0 (@btn-circle-size / 2) ; @font-size-base; @btn-circle-size); |   .button-size(@btn-circle-size; (@btn-circle-size / 2); @font-size-base; @btn-circle-size); | ||||||
|   &.@{btnClassName}-lg { |   &.@{btnClassName}-lg { | ||||||
|     .button-size( |     .button-size( | ||||||
|       @btn-circle-size-lg; 0 (@btn-circle-size-lg / 2) ; @btn-font-size-lg; @btn-circle-size-lg |       @btn-circle-size-lg; (@btn-circle-size-lg / 2); @btn-font-size-lg; @btn-circle-size-lg | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   &.@{btnClassName}-sm { |   &.@{btnClassName}-sm { | ||||||
|     .button-size( |     .button-size( | ||||||
|       @btn-circle-size-sm; 0 (@btn-circle-size-sm / 2) ; @font-size-base; @btn-circle-size-sm |       @btn-circle-size-sm; (@btn-circle-size-sm / 2); @font-size-base; @btn-circle-size-sm | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| // square button: the content only contains icon | // square button: the content only contains icon | ||||||
| .btn-square(@btnClassName: btn) { | .btn-square(@btnClassName: btn) { | ||||||
|   .square(@btn-square-size); |   .square(@btn-square-size); | ||||||
|   .button-size(@btn-square-size; 0; @font-size-base + 2px; @btn-border-radius-base); |   .button-size(@btn-square-size; 0; @btn-square-only-icon-size; @btn-border-radius-base); | ||||||
|  |   & > * { | ||||||
|  |     font-size: @btn-square-only-icon-size; | ||||||
|  |   } | ||||||
|   &.@{btnClassName}-lg { |   &.@{btnClassName}-lg { | ||||||
|     .square(@btn-square-size-lg); |     .square(@btn-square-size-lg); | ||||||
|     .button-size(@btn-square-size-lg; 0; @btn-font-size-lg + 2px; @btn-border-radius-base); |     .button-size(@btn-square-size-lg; 0; @btn-square-only-icon-size-lg; @btn-border-radius-base); | ||||||
|  |     & > * { | ||||||
|  |       font-size: @btn-square-only-icon-size-lg; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   &.@{btnClassName}-sm { |   &.@{btnClassName}-sm { | ||||||
|     .square(@btn-square-size-sm); |     .square(@btn-square-size-sm); | ||||||
|     .button-size(@btn-square-size-sm; 0; @font-size-base; @btn-border-radius-base); |     .button-size(@btn-square-size-sm; 0; @btn-square-only-icon-size-sm; @btn-border-radius-base); | ||||||
|  |     & > * { | ||||||
|  |       font-size: @btn-square-only-icon-size-sm; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| // circle button: the content only contains icon | // circle button: the content only contains icon | ||||||
|  |  | ||||||
|  | @ -0,0 +1,108 @@ | ||||||
|  | .@{btn-prefix-cls} { | ||||||
|  |   &-rtl { | ||||||
|  |     direction: rtl; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-primary { | ||||||
|  |     .@{btn-prefix-cls}-group &:last-child:not(:first-child), | ||||||
|  |     .@{btn-prefix-cls}-group & + & { | ||||||
|  |       .@{btn-prefix-cls}-group-rtl& { | ||||||
|  |         border-right-color: @btn-group-border; | ||||||
|  |         border-left-color: @btn-default-border; | ||||||
|  |       } | ||||||
|  |       &[disabled] { | ||||||
|  |         .@{btn-prefix-cls}-group-rtl& { | ||||||
|  |           border-right-color: @btn-default-border; | ||||||
|  |           border-left-color: @btn-group-border; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   & > &-loading-icon { | ||||||
|  |     .@{iconfont-css-prefix} { | ||||||
|  |       .@{btn-prefix-cls}-rtl& { | ||||||
|  |         padding-right: 0; | ||||||
|  |         padding-left: @margin-xs; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &:only-child { | ||||||
|  |       .@{iconfont-css-prefix} { | ||||||
|  |         padding-right: 0; | ||||||
|  |         padding-left: 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   > .@{iconfont-css-prefix} + span, | ||||||
|  |   > span + .@{iconfont-css-prefix} { | ||||||
|  |     .@{btn-prefix-cls}-rtl& { | ||||||
|  |       margin-right: 8px; | ||||||
|  |       margin-left: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // mixin | ||||||
|  | .btn-group(@btnClassName: btn) { | ||||||
|  |   .@{btnClassName} + .@{btnClassName}, | ||||||
|  |   .@{btnClassName} + &, | ||||||
|  |   span + .@{btnClassName}, | ||||||
|  |   .@{btnClassName} + span, | ||||||
|  |   > span + span, | ||||||
|  |   & + .@{btnClassName}, | ||||||
|  |   & + & { | ||||||
|  |     .@{btnClassName}-rtl&, | ||||||
|  |     .@{btnClassName}-group-rtl& { | ||||||
|  |       margin-right: -1px; | ||||||
|  |       margin-left: auto; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &.@{btnClassName}-group-rtl { | ||||||
|  |     direction: rtl; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   > .@{btnClassName}:first-child:not(:last-child), | ||||||
|  |   > span:first-child:not(:last-child) > .@{btnClassName} { | ||||||
|  |     .@{btnClassName}-group-rtl& { | ||||||
|  |       border-top-left-radius: 0; | ||||||
|  |       border-top-right-radius: @btn-border-radius-base; | ||||||
|  |       border-bottom-right-radius: @btn-border-radius-base; | ||||||
|  |       border-bottom-left-radius: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   > .@{btnClassName}:last-child:not(:first-child), | ||||||
|  |   > span:last-child:not(:first-child) > .@{btnClassName} { | ||||||
|  |     .@{btnClassName}-group-rtl& { | ||||||
|  |       border-top-left-radius: @btn-border-radius-base; | ||||||
|  |       border-top-right-radius: 0; | ||||||
|  |       border-bottom-right-radius: 0; | ||||||
|  |       border-bottom-left-radius: @btn-border-radius-base; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &-sm { | ||||||
|  |     > .@{btnClassName}:first-child:not(:last-child), | ||||||
|  |     > span:first-child:not(:last-child) > .@{btnClassName} { | ||||||
|  |       .@{btnClassName}-group-rtl& { | ||||||
|  |         border-top-left-radius: 0; | ||||||
|  |         border-top-right-radius: @btn-border-radius-sm; | ||||||
|  |         border-bottom-right-radius: @btn-border-radius-sm; | ||||||
|  |         border-bottom-left-radius: 0; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     > .@{btnClassName}:last-child:not(:first-child), | ||||||
|  |     > span:last-child:not(:first-child) > .@{btnClassName} { | ||||||
|  |       .@{btnClassName}-group-rtl& { | ||||||
|  |         border-top-left-radius: @btn-border-radius-sm; | ||||||
|  |         border-top-right-radius: 0; | ||||||
|  |         border-bottom-right-radius: 0; | ||||||
|  |         border-bottom-left-radius: @btn-border-radius-sm; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -396,7 +396,7 @@ | ||||||
|   .@{calendar-prefix-cls}-ok-btn { |   .@{calendar-prefix-cls}-ok-btn { | ||||||
|     .btn(); |     .btn(); | ||||||
|     .btn-primary(); |     .btn-primary(); | ||||||
|     .button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; @border-radius-base); |     .button-size(@btn-height-sm; @btn-padding-horizontal-sm; @font-size-base; @border-radius-base); | ||||||
| 
 | 
 | ||||||
|     line-height: @btn-height-sm - 2px; |     line-height: @btn-height-sm - 2px; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,13 +1,14 @@ | ||||||
| import { defineComponent, ExtractPropTypes } from 'vue'; | import { defineComponent, ExtractPropTypes, PropType } from 'vue'; | ||||||
| import PropTypes from '../_util/vue-types'; | import PropTypes from '../_util/vue-types'; | ||||||
| import Button from '../button'; | import Button from '../button'; | ||||||
| import BaseMixin from '../_util/BaseMixin'; | import BaseMixin from '../_util/BaseMixin'; | ||||||
| import buttonTypes from '../button/buttonTypes'; | import { convertLegacyProps, LegacyButtonType } from '../button/buttonTypes'; | ||||||
| import { getSlot, findDOMNode } from '../_util/props-util'; | import { getSlot, findDOMNode } from '../_util/props-util'; | ||||||
| const ButtonType = buttonTypes().type; |  | ||||||
| 
 | 
 | ||||||
| const ActionButtonProps = { | const ActionButtonProps = { | ||||||
|   type: ButtonType, |   type: { | ||||||
|  |     type: String as PropType<LegacyButtonType>, | ||||||
|  |   }, | ||||||
|   actionFn: PropTypes.func, |   actionFn: PropTypes.func, | ||||||
|   closeModal: PropTypes.func, |   closeModal: PropTypes.func, | ||||||
|   autofocus: PropTypes.looseBool, |   autofocus: PropTypes.looseBool, | ||||||
|  | @ -76,7 +77,7 @@ export default defineComponent({ | ||||||
|   render() { |   render() { | ||||||
|     const { type, loading, buttonProps } = this; |     const { type, loading, buttonProps } = this; | ||||||
|     const props = { |     const props = { | ||||||
|       type, |       ...convertLegacyProps(type), | ||||||
|       onClick: this.onClick, |       onClick: this.onClick, | ||||||
|       loading, |       loading, | ||||||
|       ...buttonProps, |       ...buttonProps, | ||||||
|  |  | ||||||
|  | @ -13,15 +13,16 @@ import addEventListener from '../vc-util/Dom/addEventListener'; | ||||||
| import { getConfirmLocale } from './locale'; | import { getConfirmLocale } from './locale'; | ||||||
| import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; | import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; | ||||||
| import Button from '../button'; | import Button from '../button'; | ||||||
| import buttonTypes, { ButtonType, ButtonProps as ButtonPropsType } from '../button/buttonTypes'; | import buttonTypes, { | ||||||
|  |   ButtonProps as ButtonPropsType, | ||||||
|  |   convertLegacyProps, | ||||||
|  |   LegacyButtonType, | ||||||
|  | } from '../button/buttonTypes'; | ||||||
| import LocaleReceiver from '../locale-provider/LocaleReceiver'; | import LocaleReceiver from '../locale-provider/LocaleReceiver'; | ||||||
| import { getComponent, getSlot } from '../_util/props-util'; | import { getComponent, getSlot } from '../_util/props-util'; | ||||||
| import initDefaultProps from '../_util/props-util/initDefaultProps'; | import initDefaultProps from '../_util/props-util/initDefaultProps'; | ||||||
| import { defaultConfigProvider } from '../config-provider'; | import { defaultConfigProvider } from '../config-provider'; | ||||||
| 
 | 
 | ||||||
| const ButtonProps = buttonTypes(); |  | ||||||
| const ButtonType = ButtonProps.type; |  | ||||||
| 
 |  | ||||||
| let mousePosition: { x: number; y: number } | null = null; | let mousePosition: { x: number; y: number } | null = null; | ||||||
| // ref: https://github.com/ant-design/ant-design/issues/15795 | // ref: https://github.com/ant-design/ant-design/issues/15795 | ||||||
| const getClickPosition = (e: MouseEvent) => { | const getClickPosition = (e: MouseEvent) => { | ||||||
|  | @ -71,7 +72,9 @@ const modalProps = { | ||||||
|   /** 确认按钮文字*/ |   /** 确认按钮文字*/ | ||||||
|   okText: PropTypes.any, |   okText: PropTypes.any, | ||||||
|   /** 确认按钮类型*/ |   /** 确认按钮类型*/ | ||||||
|   okType: ButtonType, |   okType: { | ||||||
|  |     type: String as PropType<LegacyButtonType>, | ||||||
|  |   }, | ||||||
|   /** 取消按钮文字*/ |   /** 取消按钮文字*/ | ||||||
|   cancelText: PropTypes.any, |   cancelText: PropTypes.any, | ||||||
|   icon: PropTypes.any, |   icon: PropTypes.any, | ||||||
|  | @ -112,7 +115,7 @@ export interface ModalFuncProps { | ||||||
|   centered?: boolean; |   centered?: boolean; | ||||||
|   width?: string | number; |   width?: string | number; | ||||||
|   okText?: VNodeTypes; |   okText?: VNodeTypes; | ||||||
|   okType?: ButtonType; |   okType?: LegacyButtonType; | ||||||
|   cancelText?: VNodeTypes; |   cancelText?: VNodeTypes; | ||||||
|   icon?: VNodeTypes; |   icon?: VNodeTypes; | ||||||
|   /* Deprecated */ |   /* Deprecated */ | ||||||
|  | @ -133,9 +136,7 @@ export interface ModalFuncProps { | ||||||
| 
 | 
 | ||||||
| type getContainerFunc = () => HTMLElement; | type getContainerFunc = () => HTMLElement; | ||||||
| 
 | 
 | ||||||
| export type ModalFunc = ( | export type ModalFunc = (props: ModalFuncProps) => { | ||||||
|   props: ModalFuncProps, |  | ||||||
| ) => { |  | ||||||
|   destroy: () => void; |   destroy: () => void; | ||||||
|   update: (newConfig: ModalFuncProps) => void; |   update: (newConfig: ModalFuncProps) => void; | ||||||
| }; | }; | ||||||
|  | @ -190,7 +191,7 @@ export default defineComponent({ | ||||||
|       const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) }; |       const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) }; | ||||||
|       const okBtnProps = { |       const okBtnProps = { | ||||||
|         onClick: this.handleOk, |         onClick: this.handleOk, | ||||||
|         type: okType, |         ...convertLegacyProps(okType), | ||||||
|         loading: confirmLoading, |         loading: confirmLoading, | ||||||
|         ...(this.okButtonProps || {}), |         ...(this.okButtonProps || {}), | ||||||
|       }; |       }; | ||||||
|  |  | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import omit from 'omit.js'; | import omit from 'omit.js'; | ||||||
| import { defineComponent, inject } from 'vue'; | import { defineComponent, inject, PropType } from 'vue'; | ||||||
| import Tooltip from '../tooltip'; | import Tooltip from '../tooltip'; | ||||||
| import abstractTooltipProps from '../tooltip/abstractTooltipProps'; | import abstractTooltipProps from '../tooltip/abstractTooltipProps'; | ||||||
| import PropTypes from '../_util/vue-types'; | import PropTypes from '../_util/vue-types'; | ||||||
| import { getOptionProps, hasProp, getComponent, mergeProps } from '../_util/props-util'; | import { getOptionProps, hasProp, getComponent, mergeProps } from '../_util/props-util'; | ||||||
| import BaseMixin from '../_util/BaseMixin'; | import BaseMixin from '../_util/BaseMixin'; | ||||||
| import buttonTypes from '../button/buttonTypes'; | import { LegacyButtonType, convertLegacyProps } from '../button/buttonTypes'; | ||||||
| import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled'; | import ExclamationCircleFilled from '@ant-design/icons-vue/ExclamationCircleFilled'; | ||||||
| import Button from '../button'; | import Button from '../button'; | ||||||
| import LocaleReceiver from '../locale-provider/LocaleReceiver'; | import LocaleReceiver from '../locale-provider/LocaleReceiver'; | ||||||
|  | @ -14,7 +14,6 @@ import { defaultConfigProvider } from '../config-provider'; | ||||||
| import { withInstall } from '../_util/type'; | import { withInstall } from '../_util/type'; | ||||||
| 
 | 
 | ||||||
| const tooltipProps = abstractTooltipProps(); | const tooltipProps = abstractTooltipProps(); | ||||||
| const btnProps = buttonTypes(); |  | ||||||
| 
 | 
 | ||||||
| const Popconfirm = defineComponent({ | const Popconfirm = defineComponent({ | ||||||
|   name: 'APopconfirm', |   name: 'APopconfirm', | ||||||
|  | @ -26,7 +25,10 @@ const Popconfirm = defineComponent({ | ||||||
|     content: PropTypes.any, |     content: PropTypes.any, | ||||||
|     title: PropTypes.any, |     title: PropTypes.any, | ||||||
|     trigger: tooltipProps.trigger.def('click'), |     trigger: tooltipProps.trigger.def('click'), | ||||||
|     okType: btnProps.type.def('primary'), |     okType: { | ||||||
|  |       type: String as PropType<LegacyButtonType>, | ||||||
|  |       default: 'primary', | ||||||
|  |     }, | ||||||
|     disabled: PropTypes.looseBool.def(false), |     disabled: PropTypes.looseBool.def(false), | ||||||
|     okText: PropTypes.any, |     okText: PropTypes.any, | ||||||
|     cancelText: PropTypes.any, |     cancelText: PropTypes.any, | ||||||
|  | @ -97,7 +99,7 @@ const Popconfirm = defineComponent({ | ||||||
|         ...cancelButtonProps, |         ...cancelButtonProps, | ||||||
|       }); |       }); | ||||||
|       const okBtnProps = mergeProps({ |       const okBtnProps = mergeProps({ | ||||||
|         type: okType, |         ...convertLegacyProps(okType), | ||||||
|         size: 'small', |         size: 'small', | ||||||
|         onClick: this.onConfirmHandle, |         onClick: this.onConfirmHandle, | ||||||
|         ...okButtonProps, |         ...okButtonProps, | ||||||
|  | @ -137,7 +139,7 @@ const Popconfirm = defineComponent({ | ||||||
|       <LocaleReceiver |       <LocaleReceiver | ||||||
|         componentName="Popconfirm" |         componentName="Popconfirm" | ||||||
|         defaultLocale={defaultLocale.Popconfirm} |         defaultLocale={defaultLocale.Popconfirm} | ||||||
|         children={popconfirmLocale => this.renderOverlay(prefixCls, popconfirmLocale)} |         children={(popconfirmLocale) => this.renderOverlay(prefixCls, popconfirmLocale)} | ||||||
|       /> |       /> | ||||||
|     ); |     ); | ||||||
|     const tooltipProps = { |     const tooltipProps = { | ||||||
|  |  | ||||||
|  | @ -189,16 +189,18 @@ | ||||||
| @btn-default-ghost-bg: transparent; | @btn-default-ghost-bg: transparent; | ||||||
| @btn-default-ghost-border: @component-background; | @btn-default-ghost-border: @component-background; | ||||||
| 
 | 
 | ||||||
| @btn-padding-base: 0 @padding-md - 1px; |  | ||||||
| @btn-font-size-lg: @font-size-lg; | @btn-font-size-lg: @font-size-lg; | ||||||
| @btn-font-size-sm: @font-size-base; | @btn-font-size-sm: @font-size-base; | ||||||
| @btn-padding-lg: @btn-padding-base; | @btn-padding-horizontal-base: @padding-md - 1px; | ||||||
| @btn-padding-sm: 0 @padding-xs - 1px; | @btn-padding-horizontal-lg: @btn-padding-horizontal-base; | ||||||
|  | @btn-padding-horizontal-sm: @padding-xs - 1px; | ||||||
| 
 | 
 | ||||||
| @btn-height-base: 32px; | @btn-height-base: 32px; | ||||||
| @btn-height-lg: 40px; | @btn-height-lg: 40px; | ||||||
| @btn-height-sm: 24px; | @btn-height-sm: 24px; | ||||||
| 
 | 
 | ||||||
|  | @btn-line-height: @line-height-base; | ||||||
|  | 
 | ||||||
| @btn-circle-size: @btn-height-base; | @btn-circle-size: @btn-height-base; | ||||||
| @btn-circle-size-lg: @btn-height-lg; | @btn-circle-size-lg: @btn-height-lg; | ||||||
| @btn-circle-size-sm: @btn-height-sm; | @btn-circle-size-sm: @btn-height-sm; | ||||||
|  | @ -206,9 +208,13 @@ | ||||||
| @btn-square-size: @btn-height-base; | @btn-square-size: @btn-height-base; | ||||||
| @btn-square-size-lg: @btn-height-lg; | @btn-square-size-lg: @btn-height-lg; | ||||||
| @btn-square-size-sm: @btn-height-sm; | @btn-square-size-sm: @btn-height-sm; | ||||||
|  | @btn-square-only-icon-size: @font-size-base + 2px; | ||||||
|  | @btn-square-only-icon-size-sm: @font-size-base; | ||||||
|  | @btn-square-only-icon-size-lg: @btn-font-size-lg + 2px; | ||||||
| 
 | 
 | ||||||
| @btn-group-border: @primary-5; | @btn-group-border: @primary-5; | ||||||
| 
 | 
 | ||||||
|  | @btn-link-hover-bg: transparent; | ||||||
| @btn-text-hover-bg: rgba(0, 0, 0, 0.018); | @btn-text-hover-bg: rgba(0, 0, 0, 0.018); | ||||||
| 
 | 
 | ||||||
| // Checkbox | // Checkbox | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 John
						John