Merge branch 'v3-button' into next

pull/4301/head
tangjinzhou 2021-06-30 14:42:02 +08:00
commit 41324dc1e3
22 changed files with 738 additions and 296 deletions

View File

@ -4,7 +4,7 @@ const fs = require('fs');
const assign = require('object-assign'); const assign = require('object-assign');
const { getProjectPath } = require('./utils/projectHelper'); const { getProjectPath } = require('./utils/projectHelper');
module.exports = function () { module.exports = function() {
let my = {}; let my = {};
if (fs.existsSync(getProjectPath('tsconfig.json'))) { if (fs.existsSync(getProjectPath('tsconfig.json'))) {
my = require(getProjectPath('tsconfig.json')); my = require(getProjectPath('tsconfig.json'));

View File

@ -80,7 +80,7 @@ function compileTs(stream) {
return stream return stream
.pipe(ts(tsConfig)) .pipe(ts(tsConfig))
.js.pipe( .js.pipe(
through2.obj(function (file, encoding, next) { through2.obj(function(file, encoding, next) {
// console.log(file.path, file.base); // console.log(file.path, file.base);
file.path = file.path.replace(/\.[jt]sx$/, '.js'); file.path = file.path.replace(/\.[jt]sx$/, '.js');
this.push(file); this.push(file);
@ -146,7 +146,7 @@ function compile(modules) {
const less = gulp const less = gulp
.src(['components/**/*.less']) .src(['components/**/*.less'])
.pipe( .pipe(
through2.obj(function (file, encoding, next) { through2.obj(function(file, encoding, next) {
this.push(file.clone()); this.push(file.clone());
if ( if (
file.path.match(/\/style\/index\.less$/) || file.path.match(/\/style\/index\.less$/) ||

View File

@ -1,7 +1,10 @@
const fs = require('fs'); const fs = require('fs');
module.exports = function getChangelog(file, version) { module.exports = function getChangelog(file, version) {
const lines = fs.readFileSync(file).toString().split('\n'); const lines = fs
.readFileSync(file)
.toString()
.split('\n');
const changeLog = []; const changeLog = [];
const startPattern = new RegExp(`^## ${version}`); const startPattern = new RegExp(`^## ${version}`);
const stopPattern = /^## /; // 前一个版本 const stopPattern = /^## /; // 前一个版本

View File

@ -11,7 +11,13 @@ module.exports = function getRunCmdEnv() {
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin'); const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin');
Object.entries(env) Object.entries(env)
.filter(v => v.slice(0, 1).pop().toLowerCase() === 'path') .filter(
v =>
v
.slice(0, 1)
.pop()
.toLowerCase() === 'path',
)
.forEach(v => { .forEach(v => {
const key = v.slice(0, 1).pop(); const key = v.slice(0, 1).pop();
env[key] = env[key] ? `${nodeModulesBinDir}:${env[key]}` : nodeModulesBinDir; env[key] = env[key] ? `${nodeModulesBinDir}:${env[key]}` : nodeModulesBinDir;

View File

@ -20,7 +20,7 @@ function injectRequire() {
const Module = require('module'); const Module = require('module');
const oriRequire = Module.prototype.require; const oriRequire = Module.prototype.require;
Module.prototype.require = function (...args) { Module.prototype.require = function(...args) {
const moduleName = args[0]; const moduleName = args[0];
try { try {
return oriRequire.apply(this, args); return oriRequire.apply(this, args);

View File

@ -18,6 +18,7 @@ export default (
form?: ComputedRef<{ form?: ComputedRef<{
requiredMark?: RequiredMark; requiredMark?: RequiredMark;
}>; }>;
autoInsertSpaceInButton: ComputedRef<Boolean>;
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>; renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
} => { } => {
const configProvider = inject<UnwrapRef<ConfigProviderProps>>( const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
@ -26,6 +27,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 renderEmpty = computed(() => configProvider.renderEmpty); const renderEmpty = computed(() => configProvider.renderEmpty);
const space = computed(() => configProvider.space); const space = computed(() => configProvider.space);
const pageHeader = computed(() => configProvider.pageHeader); const pageHeader = computed(() => configProvider.pageHeader);
@ -41,6 +43,7 @@ export default (
space, space,
pageHeader, pageHeader,
form, form,
autoInsertSpaceInButton,
renderEmpty, renderEmpty,
}; };
}; };

View File

@ -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>
`; `;

View File

@ -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();
});
});
}); });

View File

@ -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'));
});
});

View File

@ -1,36 +1,28 @@
import { defineComponent, inject } from 'vue'; import { computed, 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 { const classes = computed(() => {
configProvider, const { size } = props;
};
},
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 // large => lg
// small => sm // small => sm
let sizeCls = ''; let sizeCls = '';
@ -44,10 +36,14 @@ export default defineComponent({
default: default:
break; break;
} }
const classes = { return {
[`${prefixCls}`]: true, [`${prefixCls.value}`]: true,
[`${prefixCls}-${sizeCls}`]: sizeCls, [`${prefixCls.value}-${sizeCls}`]: sizeCls,
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
};
});
return () => {
return <div class={classes.value}>{flattenChildren(slots.default?.())}</div>;
}; };
return <div class={classes}>{filterEmpty(getSlot(this))}</div>;
}, },
}); });

View File

@ -1,85 +1,81 @@
import type { ExtractPropTypes, VNode } from 'vue'; import {
import { defineComponent, inject, Text } from 'vue'; computed,
defineComponent,
onBeforeUnmount,
onMounted,
onUpdated,
Ref,
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';
type Loading = boolean | number;
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();
export type ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof 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 delayTimeoutRef = ref(undefined);
}, let isNeedInserted = false;
data() {
return { const innerLoading: Ref<Loading> = ref(false);
sizeMap: { const hasTwoCNChar = ref(false);
large: 'lg',
small: 'sm', const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false);
},
sLoading: false, // =============== Update Loading ===============
hasTwoCNChar: false, const loadingOrDelay = computed(() =>
}; typeof props.loading === 'object' && props.loading.delay
}, ? props.loading.delay || true
watch: { : !!props.loading,
loading: { );
handler(val, preVal) {
if (preVal && typeof preVal !== 'boolean') { watch(
clearTimeout(this.delayTimeout); loadingOrDelay,
} val => {
if (val && typeof val !== 'boolean' && val.delay) { clearTimeout(delayTimeoutRef.value);
this.delayTimeout = setTimeout(() => { if (typeof loadingOrDelay.value === 'number') {
this.sLoading = !!val; delayTimeoutRef.value = window.setTimeout(() => {
}, val.delay); innerLoading.value = val;
}, loadingOrDelay.value);
} else { } else {
this.sLoading = !!val; innerLoading.value = val;
} }
}, },
{
immediate: true, 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;
const classes = computed(() => {
const { type, shape, size, ghost, block, danger } = props;
const pre = prefixCls.value;
// large => lg // large => lg
// small => sm // small => sm
let sizeCls = ''; let sizeCls = '';
@ -93,43 +89,46 @@ export default defineComponent({
default: default:
break; break;
} }
const iconType = sLoading ? 'loading' : this.iconCom;
return { return {
[$attrs.class as string]: $attrs.class, [`${pre}`]: true,
[`${prefixCls}`]: true, [`${pre}-${type}`]: type,
[`${prefixCls}-${type}`]: type, [`${pre}-${shape}`]: shape,
[`${prefixCls}-${shape}`]: shape, [`${pre}-${sizeCls}`]: sizeCls,
[`${prefixCls}-${sizeCls}`]: sizeCls, [`${pre}-loading`]: innerLoading.value,
[`${prefixCls}-icon-only`]: this.children.length === 0 && iconType, [`${pre}-background-ghost`]: ghost && !isUnborderedButtonType(type),
[`${prefixCls}-loading`]: sLoading, [`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value,
[`${prefixCls}-background-ghost`]: ghost || type === 'ghost', [`${pre}-block`]: block,
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace, [`${pre}-dangerous`]: !!danger,
[`${prefixCls}-block`]: block, [`${pre}-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 (innerLoading.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();
@ -139,36 +138,60 @@ 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();
watchEffect(() => {
devWarning(
!(props.ghost && isUnborderedButtonType(props.type)),
'Button',
"`link` or `text` button can't be a `ghost` button.",
);
});
onMounted(fixTwoCNChar);
onUpdated(fixTwoCNChar);
onBeforeUnmount(() => {
delayTimeoutRef.value && clearTimeout(delayTimeoutRef.value);
});
return () => {
const children = flattenChildren(getPropsSlot(slots, props));
const icon = getPropsSlot(slots, props, 'icon');
isNeedInserted = children.length === 1 && !icon && !isUnborderedButtonType(props.type);
const { type, htmlType, disabled, href, title, target } = props;
const iconType = innerLoading.value ? 'loading' : icon;
const buttonProps = { const buttonProps = {
...$attrs, ...attrs,
title, title,
disabled, disabled,
class: classes, class: [
classes.value,
attrs.class,
{ [`${prefixCls.value}-icon-only`]: children.length === 0 && !!iconType },
],
onClick: handleClick, onClick: handleClick,
}; };
const iconNode = sLoading ? <LoadingOutlined /> : iconCom;
const autoInsertSpace = this.configProvider.autoInsertSpaceInButton !== false; const iconNode = innerLoading.value ? (
<span class={`${prefixCls.value}-loading-icon`}>
<LoadingOutlined />
</span>
) : (
icon
);
const kids = children.map(child => const kids = children.map(child =>
this.insertSpace(child, this.isNeedInserted() && autoInsertSpace), insertSpace(child, isNeedInserted && autoInsertSpace.value),
); );
if (href !== undefined) { if (href !== undefined) {
return ( return (
<a {...buttonProps} href={href} ref="buttonNode"> <a {...buttonProps} href={href} target={target} ref={buttonNodeRef}>
{iconNode} {iconNode}
{kids} {kids}
</a> </a>
@ -176,16 +199,17 @@ export default defineComponent({
} }
const buttonNode = ( const buttonNode = (
<button {...buttonProps} ref="buttonNode" type={htmlType || 'button'}> <button {...buttonProps} ref={buttonNodeRef} type={htmlType}>
{iconNode} {iconNode}
{kids} {kids}
</button> </button>
); );
if (type === 'link') { if (isUnborderedButtonType(type)) {
return buttonNode; return buttonNode;
} }
return <Wave ref="wave">{buttonNode}</Wave>; return <Wave ref="wave">{buttonNode}</Wave>;
};
}, },
}); });

View File

@ -1,32 +1,48 @@
import type { 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', '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>>>;

View File

@ -1,7 +1,12 @@
import type { App, Plugin } from 'vue'; import type { App, Plugin } from 'vue';
import Button from './button'; import Button from './button';
import ButtonGroup from './button-group'; import ButtonGroup from './button-group';
export type { ButtonProps } from './button';
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;
@ -11,7 +16,9 @@ Button.install = function (app: App) {
app.component(ButtonGroup.name, ButtonGroup); app.component(ButtonGroup.name, ButtonGroup);
return app; return app;
}; };
export { ButtonGroup }; export { ButtonGroup };
export default Button as typeof Button & export default Button as typeof Button &
Plugin & { Plugin & {
readonly Group: typeof ButtonGroup; readonly Group: typeof ButtonGroup;

View File

@ -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,23 +149,28 @@
&: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) {
padding-left: 29px;
.@{iconfont-css-prefix}:not(:last-child) {
margin-left: -14px;
}
} }
&-sm&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) { & > &-loading-icon {
padding-left: 24px; transition: all 0.3s @ease-in-out;
.@{iconfont-css-prefix} { .@{iconfont-css-prefix} {
margin-left: -17px; 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;
}
} }
} }
@ -166,7 +187,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 +204,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 +226,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 +250,5 @@ a.@{btn-prefix-cls} {
line-height: @btn-height-sm - 2px; line-height: @btn-height-sm - 2px;
} }
} }
@import './rtl';

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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;

View File

@ -1,14 +1,15 @@
import type { ExtractPropTypes } from 'vue'; import type { ExtractPropTypes, PropType } from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } 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,
@ -77,7 +78,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,

View File

@ -7,8 +7,11 @@ 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 type { ButtonProps as ButtonPropsType, ButtonType } from '../button/buttonTypes'; import buttonTypes, {
import buttonTypes from '../button/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';
@ -63,7 +66,9 @@ const modalProps = {
/** 确认按钮文字*/ /** 确认按钮文字*/
okText: PropTypes.any, okText: PropTypes.any,
/** 确认按钮类型*/ /** 确认按钮类型*/
okType: buttonTypes().type, okType: {
type: String as PropType<LegacyButtonType>,
},
/** 取消按钮文字*/ /** 取消按钮文字*/
cancelText: PropTypes.any, cancelText: PropTypes.any,
icon: PropTypes.any, icon: PropTypes.any,
@ -104,7 +109,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 */
@ -180,7 +185,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 || {}),
}; };

View File

@ -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,

View File

@ -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

2
v2-doc

@ -1 +1 @@
Subproject commit f8c276329a6bfa295847dc4a11538863f9f4cced Subproject commit b6ab0fec2cfa378bab8dfe6c8ef6b6a8664b970e