Merge branch 'v3-button' into next
commit
41324dc1e3
|
@ -4,7 +4,7 @@ const fs = require('fs');
|
|||
const assign = require('object-assign');
|
||||
const { getProjectPath } = require('./utils/projectHelper');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = function() {
|
||||
let my = {};
|
||||
if (fs.existsSync(getProjectPath('tsconfig.json'))) {
|
||||
my = require(getProjectPath('tsconfig.json'));
|
||||
|
|
|
@ -80,7 +80,7 @@ function compileTs(stream) {
|
|||
return stream
|
||||
.pipe(ts(tsConfig))
|
||||
.js.pipe(
|
||||
through2.obj(function (file, encoding, next) {
|
||||
through2.obj(function(file, encoding, next) {
|
||||
// console.log(file.path, file.base);
|
||||
file.path = file.path.replace(/\.[jt]sx$/, '.js');
|
||||
this.push(file);
|
||||
|
@ -146,7 +146,7 @@ function compile(modules) {
|
|||
const less = gulp
|
||||
.src(['components/**/*.less'])
|
||||
.pipe(
|
||||
through2.obj(function (file, encoding, next) {
|
||||
through2.obj(function(file, encoding, next) {
|
||||
this.push(file.clone());
|
||||
if (
|
||||
file.path.match(/\/style\/index\.less$/) ||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
const fs = require('fs');
|
||||
|
||||
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 startPattern = new RegExp(`^## ${version}`);
|
||||
const stopPattern = /^## /; // 前一个版本
|
||||
|
|
|
@ -11,7 +11,13 @@ module.exports = function getRunCmdEnv() {
|
|||
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin');
|
||||
|
||||
Object.entries(env)
|
||||
.filter(v => v.slice(0, 1).pop().toLowerCase() === 'path')
|
||||
.filter(
|
||||
v =>
|
||||
v
|
||||
.slice(0, 1)
|
||||
.pop()
|
||||
.toLowerCase() === 'path',
|
||||
)
|
||||
.forEach(v => {
|
||||
const key = v.slice(0, 1).pop();
|
||||
env[key] = env[key] ? `${nodeModulesBinDir}:${env[key]}` : nodeModulesBinDir;
|
||||
|
|
|
@ -20,7 +20,7 @@ function injectRequire() {
|
|||
const Module = require('module');
|
||||
|
||||
const oriRequire = Module.prototype.require;
|
||||
Module.prototype.require = function (...args) {
|
||||
Module.prototype.require = function(...args) {
|
||||
const moduleName = args[0];
|
||||
try {
|
||||
return oriRequire.apply(this, args);
|
||||
|
|
|
@ -18,6 +18,7 @@ export default (
|
|||
form?: ComputedRef<{
|
||||
requiredMark?: RequiredMark;
|
||||
}>;
|
||||
autoInsertSpaceInButton: ComputedRef<Boolean>;
|
||||
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
|
||||
} => {
|
||||
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
|
||||
|
@ -26,6 +27,7 @@ export default (
|
|||
);
|
||||
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
|
||||
const direction = computed(() => configProvider.direction);
|
||||
const autoInsertSpaceInButton = computed(() => configProvider.autoInsertSpaceInButton);
|
||||
const renderEmpty = computed(() => configProvider.renderEmpty);
|
||||
const space = computed(() => configProvider.space);
|
||||
const pageHeader = computed(() => configProvider.pageHeader);
|
||||
|
@ -41,6 +43,7 @@ export default (
|
|||
space,
|
||||
pageHeader,
|
||||
form,
|
||||
autoInsertSpaceInButton,
|
||||
renderEmpty,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -59,7 +59,7 @@ exports[`Button should not render as link button when href is undefined 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>
|
||||
</a>
|
||||
`;
|
||||
|
|
|
@ -2,9 +2,9 @@ import Button from '../index';
|
|||
import SearchOutlined from '@ant-design/icons-vue/SearchOutlined';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import { asyncExpect } from '@/tests/utils';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { asyncExpect, sleep } from '@/tests/utils';
|
||||
import mountTest from '@/tests/shared/mountTest';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
describe('Button', () => {
|
||||
mountTest(Button);
|
||||
|
@ -27,7 +27,7 @@ describe('Button', () => {
|
|||
expect(wrapper.find('.ant-btn-primary').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders Chinese characters correctly', done => {
|
||||
it('renders Chinese characters correctly', (done) => {
|
||||
const wrapper = mount({
|
||||
render() {
|
||||
return <Button>按钮</Button>;
|
||||
|
@ -247,4 +247,51 @@ describe('Button', () => {
|
|||
wrapper.unmount();
|
||||
}).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 { filterEmpty, getSlot } from '../_util/props-util';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { flattenChildren } from '../_util/props-util';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
import { tuple } from '../_util/type';
|
||||
import useConfigInject from '../_util/hooks/useConfigInject';
|
||||
|
||||
const ButtonGroupProps = {
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import type { SizeType } from '../config-provider';
|
||||
|
||||
const buttonGroupProps = {
|
||||
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({
|
||||
name: 'AButtonGroup',
|
||||
props: ButtonGroupProps,
|
||||
setup() {
|
||||
const configProvider = inject('configProvider', defaultConfigProvider);
|
||||
return {
|
||||
configProvider,
|
||||
props: buttonGroupProps,
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls, direction } = useConfigInject('btn-group', props);
|
||||
const classes = computed(() => {
|
||||
const { size } = props;
|
||||
// large => lg
|
||||
// small => sm
|
||||
let sizeCls = '';
|
||||
switch (size) {
|
||||
case 'large':
|
||||
sizeCls = 'lg';
|
||||
break;
|
||||
case 'small':
|
||||
sizeCls = 'sm';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {
|
||||
[`${prefixCls.value}`]: true,
|
||||
[`${prefixCls.value}-${sizeCls}`]: sizeCls,
|
||||
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
|
||||
};
|
||||
});
|
||||
return () => {
|
||||
return <div class={classes.value}>{flattenChildren(slots.default?.())}</div>;
|
||||
};
|
||||
},
|
||||
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
|
||||
// small => sm
|
||||
let sizeCls = '';
|
||||
switch (size) {
|
||||
case 'large':
|
||||
sizeCls = 'lg';
|
||||
break;
|
||||
case 'small':
|
||||
sizeCls = 'sm';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const classes = {
|
||||
[`${prefixCls}`]: true,
|
||||
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
||||
};
|
||||
return <div class={classes}>{filterEmpty(getSlot(this))}</div>;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,85 +1,81 @@
|
|||
import type { ExtractPropTypes, VNode } from 'vue';
|
||||
import { defineComponent, inject, Text } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
onUpdated,
|
||||
Ref,
|
||||
ref,
|
||||
Text,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import Wave from '../_util/wave';
|
||||
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
||||
import buttonTypes from './buttonTypes';
|
||||
import { getSlot, getComponent } from '../_util/props-util';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
// eslint-disable-next-line no-console
|
||||
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
|
||||
import { flattenChildren, getPropsSlot } from '../_util/props-util';
|
||||
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 isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
|
||||
const props = buttonTypes();
|
||||
|
||||
export type ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof buttonTypes>>>;
|
||||
function isUnborderedButtonType(type: ButtonType | undefined) {
|
||||
return type === 'text' || type === 'link';
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AButton',
|
||||
inheritAttrs: false,
|
||||
__ANT_BUTTON: true,
|
||||
props,
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
children: [],
|
||||
iconCom: undefined,
|
||||
delayTimeout: undefined,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sizeMap: {
|
||||
large: 'lg',
|
||||
small: 'sm',
|
||||
},
|
||||
sLoading: false,
|
||||
hasTwoCNChar: false,
|
||||
};
|
||||
},
|
||||
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;
|
||||
slots: ['icon'],
|
||||
emits: ['click'],
|
||||
setup(props, { slots, attrs, emit }) {
|
||||
const { prefixCls, autoInsertSpaceInButton, direction } = useConfigInject('btn', props);
|
||||
|
||||
const buttonNodeRef = ref<HTMLElement>(null);
|
||||
const delayTimeoutRef = ref(undefined);
|
||||
let isNeedInserted = false;
|
||||
|
||||
const innerLoading: Ref<Loading> = ref(false);
|
||||
const hasTwoCNChar = ref(false);
|
||||
|
||||
const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false);
|
||||
|
||||
// =============== Update Loading ===============
|
||||
const loadingOrDelay = computed(() =>
|
||||
typeof props.loading === 'object' && props.loading.delay
|
||||
? props.loading.delay || true
|
||||
: !!props.loading,
|
||||
);
|
||||
|
||||
watch(
|
||||
loadingOrDelay,
|
||||
val => {
|
||||
clearTimeout(delayTimeoutRef.value);
|
||||
if (typeof loadingOrDelay.value === 'number') {
|
||||
delayTimeoutRef.value = window.setTimeout(() => {
|
||||
innerLoading.value = val;
|
||||
}, loadingOrDelay.value);
|
||||
} else {
|
||||
innerLoading.value = val;
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
const classes = computed(() => {
|
||||
const { type, shape, size, ghost, block, danger } = props;
|
||||
const pre = prefixCls.value;
|
||||
// large => lg
|
||||
// small => sm
|
||||
let sizeCls = '';
|
||||
|
@ -93,43 +89,46 @@ export default defineComponent({
|
|||
default:
|
||||
break;
|
||||
}
|
||||
const iconType = sLoading ? 'loading' : this.iconCom;
|
||||
return {
|
||||
[$attrs.class as string]: $attrs.class,
|
||||
[`${prefixCls}`]: true,
|
||||
[`${prefixCls}-${type}`]: type,
|
||||
[`${prefixCls}-${shape}`]: shape,
|
||||
[`${prefixCls}-${sizeCls}`]: sizeCls,
|
||||
[`${prefixCls}-icon-only`]: this.children.length === 0 && iconType,
|
||||
[`${prefixCls}-loading`]: sLoading,
|
||||
[`${prefixCls}-background-ghost`]: ghost || type === 'ghost',
|
||||
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,
|
||||
[`${prefixCls}-block`]: block,
|
||||
[`${pre}`]: true,
|
||||
[`${pre}-${type}`]: type,
|
||||
[`${pre}-${shape}`]: shape,
|
||||
[`${pre}-${sizeCls}`]: sizeCls,
|
||||
[`${pre}-loading`]: innerLoading.value,
|
||||
[`${pre}-background-ghost`]: ghost && !isUnborderedButtonType(type),
|
||||
[`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value,
|
||||
[`${pre}-block`]: block,
|
||||
[`${pre}-dangerous`]: !!danger,
|
||||
[`${pre}-rtl`]: direction.value === 'rtl',
|
||||
};
|
||||
},
|
||||
fixTwoCNChar() {
|
||||
});
|
||||
|
||||
const fixTwoCNChar = () => {
|
||||
// Fix for HOC usage like <FormatMessage />
|
||||
const node = this.$refs.buttonNode as HTMLElement;
|
||||
if (!node) {
|
||||
const node = buttonNodeRef.value!;
|
||||
if (!node || autoInsertSpaceInButton.value === false) {
|
||||
return;
|
||||
}
|
||||
const buttonText = node.textContent;
|
||||
if (this.isNeedInserted() && isTwoCNChar(buttonText)) {
|
||||
if (!this.hasTwoCNChar) {
|
||||
this.hasTwoCNChar = true;
|
||||
|
||||
if (isNeedInserted && isTwoCNChar(buttonText)) {
|
||||
if (!hasTwoCNChar.value) {
|
||||
hasTwoCNChar.value = true;
|
||||
}
|
||||
} else if (this.hasTwoCNChar) {
|
||||
this.hasTwoCNChar = false;
|
||||
} else if (hasTwoCNChar.value) {
|
||||
hasTwoCNChar.value = false;
|
||||
}
|
||||
},
|
||||
handleClick(event: Event) {
|
||||
const { sLoading } = this.$data;
|
||||
if (sLoading) {
|
||||
};
|
||||
const handleClick = (event: Event) => {
|
||||
// https://github.com/ant-design/ant-design/issues/30207
|
||||
if (innerLoading.value || props.disabled) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
this.$emit('click', event);
|
||||
},
|
||||
insertSpace(child: VNode, needInserted: boolean) {
|
||||
emit('click', event);
|
||||
};
|
||||
|
||||
const insertSpace = (child: VNode, needInserted: boolean) => {
|
||||
const SPACE = needInserted ? ' ' : '';
|
||||
if (child.type === Text) {
|
||||
let text = (child.children as string).trim();
|
||||
|
@ -139,53 +138,78 @@ export default defineComponent({
|
|||
return <span>{text}</span>;
|
||||
}
|
||||
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 kids = children.map(child =>
|
||||
this.insertSpace(child, this.isNeedInserted() && autoInsertSpace),
|
||||
);
|
||||
watchEffect(() => {
|
||||
devWarning(
|
||||
!(props.ghost && isUnborderedButtonType(props.type)),
|
||||
'Button',
|
||||
"`link` or `text` button can't be a `ghost` button.",
|
||||
);
|
||||
});
|
||||
|
||||
if (href !== undefined) {
|
||||
return (
|
||||
<a {...buttonProps} href={href} ref="buttonNode">
|
||||
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 = {
|
||||
...attrs,
|
||||
title,
|
||||
disabled,
|
||||
class: [
|
||||
classes.value,
|
||||
attrs.class,
|
||||
{ [`${prefixCls.value}-icon-only`]: children.length === 0 && !!iconType },
|
||||
],
|
||||
onClick: handleClick,
|
||||
};
|
||||
|
||||
const iconNode = innerLoading.value ? (
|
||||
<span class={`${prefixCls.value}-loading-icon`}>
|
||||
<LoadingOutlined />
|
||||
</span>
|
||||
) : (
|
||||
icon
|
||||
);
|
||||
|
||||
const kids = children.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}
|
||||
{kids}
|
||||
</a>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
const buttonNode = (
|
||||
<button {...buttonProps} ref="buttonNode" type={htmlType || 'button'}>
|
||||
{iconNode}
|
||||
{kids}
|
||||
</button>
|
||||
);
|
||||
if (isUnborderedButtonType(type)) {
|
||||
return buttonNode;
|
||||
}
|
||||
|
||||
if (type === 'link') {
|
||||
return buttonNode;
|
||||
}
|
||||
|
||||
return <Wave ref="wave">{buttonNode}</Wave>;
|
||||
return <Wave ref="wave">{buttonNode}</Wave>;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,32 +1,48 @@
|
|||
import type { ExtractPropTypes } from 'vue';
|
||||
|
||||
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];
|
||||
const ButtonShapes = tuple('circle', 'circle-outline', 'round');
|
||||
const ButtonShapes = tuple('circle', 'round');
|
||||
export type ButtonShape = typeof ButtonShapes[number];
|
||||
const ButtonSizes = tuple('large', 'default', 'small');
|
||||
export type ButtonSize = typeof ButtonSizes[number];
|
||||
|
||||
const ButtonHTMLTypes = tuple('submit', 'button', 'reset');
|
||||
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 = () => ({
|
||||
prefixCls: PropTypes.string,
|
||||
type: PropTypes.oneOf(ButtonTypes),
|
||||
htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'),
|
||||
// icon: PropTypes.string,
|
||||
shape: PropTypes.oneOf(ButtonShapes),
|
||||
size: PropTypes.oneOf(ButtonSizes).def('default'),
|
||||
loading: withUndefined(PropTypes.oneOfType([PropTypes.looseBool, PropTypes.object])),
|
||||
size: {
|
||||
type: String as PropType<SizeType>,
|
||||
},
|
||||
loading: {
|
||||
type: [Boolean, Object],
|
||||
default: (): boolean | { delay?: number } => false,
|
||||
},
|
||||
disabled: PropTypes.looseBool,
|
||||
ghost: PropTypes.looseBool,
|
||||
block: PropTypes.looseBool,
|
||||
danger: PropTypes.looseBool,
|
||||
icon: PropTypes.VNodeChild,
|
||||
href: PropTypes.string,
|
||||
target: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
onClick: {
|
||||
type: Function as PropType<(event: MouseEvent) => void>,
|
||||
},
|
||||
});
|
||||
|
||||
export type ButtonProps = Partial<ExtractPropTypes<ReturnType<typeof buttonProps>>>;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import type { App, Plugin } from 'vue';
|
||||
import Button from './button';
|
||||
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;
|
||||
|
||||
|
@ -11,7 +16,9 @@ Button.install = function (app: App) {
|
|||
app.component(ButtonGroup.name, ButtonGroup);
|
||||
return app;
|
||||
};
|
||||
|
||||
export { ButtonGroup };
|
||||
|
||||
export default Button as typeof Button &
|
||||
Plugin & {
|
||||
readonly Group: typeof ButtonGroup;
|
||||
|
|
|
@ -15,22 +15,18 @@
|
|||
// 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/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/13214
|
||||
// It is a render problem of chrome, which is only happened in the codesandbox demo
|
||||
// 0.001px solution works and I don't why
|
||||
line-height: @line-height-base;
|
||||
line-height: @btn-line-height;
|
||||
.btn();
|
||||
.btn-default();
|
||||
|
||||
// Make sure that the target of Button's click event always be `button`
|
||||
// Ref: https://github.com/ant-design/ant-design/issues/7034
|
||||
> i,
|
||||
// Fix loading button animation
|
||||
// https://github.com/ant-design/ant-design/issues/24323
|
||||
> span {
|
||||
display: inline-block;
|
||||
transition: margin-left 0.3s @ease-in-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&-primary {
|
||||
|
@ -48,6 +44,7 @@
|
|||
.@{btn-prefix-cls}-group &:first-child {
|
||||
&:not(:last-child) {
|
||||
border-right-color: @btn-group-border;
|
||||
|
||||
&[disabled] {
|
||||
border-right-color: @btn-default-border;
|
||||
}
|
||||
|
@ -57,6 +54,7 @@
|
|||
.@{btn-prefix-cls}-group &:last-child:not(:first-child),
|
||||
.@{btn-prefix-cls}-group & + & {
|
||||
border-left-color: @btn-group-border;
|
||||
|
||||
&[disabled] {
|
||||
border-left-color: @btn-default-border;
|
||||
}
|
||||
|
@ -71,6 +69,8 @@
|
|||
.btn-dashed();
|
||||
}
|
||||
|
||||
// type="danger" will deprecated
|
||||
// use danger instead
|
||||
&-danger {
|
||||
.btn-danger();
|
||||
}
|
||||
|
@ -79,12 +79,29 @@
|
|||
.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 {
|
||||
.btn-square(@btn-prefix-cls);
|
||||
|
||||
> i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
vertical-align: -1px;
|
||||
}
|
||||
|
||||
&-round {
|
||||
|
@ -94,17 +111,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-circle,
|
||||
&-circle-outline {
|
||||
&-circle {
|
||||
.btn-circle(@btn-prefix-cls);
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
bottom: -1px;
|
||||
left: -1px;
|
||||
top: -@btn-border-width;
|
||||
right: -@btn-border-width;
|
||||
bottom: -@btn-border-width;
|
||||
left: -@btn-border-width;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
background: @component-background;
|
||||
|
@ -133,23 +149,28 @@
|
|||
&:not([disabled]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&&-loading::before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) {
|
||||
padding-left: 29px;
|
||||
.@{iconfont-css-prefix}:not(:last-child) {
|
||||
margin-left: -14px;
|
||||
&::before {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-sm&-loading:not(&-circle):not(&-circle-outline):not(&-icon-only) {
|
||||
padding-left: 24px;
|
||||
& > &-loading-icon {
|
||||
transition: all 0.3s @ease-in-out;
|
||||
|
||||
.@{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`.
|
||||
> .@{iconfont-css-prefix} + span,
|
||||
> span + .@{iconfont-css-prefix} {
|
||||
margin-left: 8px;
|
||||
margin-left: @margin-xs;
|
||||
}
|
||||
|
||||
&-background-ghost {
|
||||
|
@ -183,10 +204,12 @@
|
|||
.button-variant-ghost(@btn-danger-border);
|
||||
}
|
||||
|
||||
&-background-ghost&-link {
|
||||
.button-variant-ghost(@link-color; transparent);
|
||||
&-background-ghost&-dangerous {
|
||||
.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 {
|
||||
|
@ -203,16 +226,21 @@
|
|||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/12681
|
||||
// same method as Select
|
||||
&:empty {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
content: '\a0';
|
||||
}
|
||||
}
|
||||
|
||||
a.@{btn-prefix-cls} {
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
&-lg {
|
||||
|
@ -222,3 +250,5 @@ a.@{btn-prefix-cls} {
|
|||
line-height: @btn-height-sm - 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@import './rtl';
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
// 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;
|
||||
padding: @padding;
|
||||
padding: @padding-vertical @padding-horizontal;
|
||||
font-size: @font-size;
|
||||
border-radius: @border-radius;
|
||||
}
|
||||
|
||||
.button-disabled(@color: @btn-disable-color; @background: @btn-disable-bg; @border: @btn-disable-border) {
|
||||
&-disabled,
|
||||
&.disabled,
|
||||
&[disabled] {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
.button-color(@color; @background; @border);
|
||||
|
||||
text-shadow: none;
|
||||
|
@ -44,8 +45,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
& when (@theme = dark) {
|
||||
.button-color(
|
||||
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
|
||||
|
@ -76,8 +76,7 @@
|
|||
);
|
||||
}
|
||||
}
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
& when (@theme = dark) {
|
||||
.button-color(@primary-7; @background; @primary-7);
|
||||
}
|
||||
|
@ -103,7 +102,7 @@
|
|||
.button-color(~`colorPalette('@{color}', 5) `; transparent; transparent);
|
||||
}
|
||||
}
|
||||
& when not(@border = transparent) {
|
||||
& when not (@border = transparent) {
|
||||
& when (@theme = dark) {
|
||||
.button-color(
|
||||
~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) `
|
||||
|
@ -116,8 +115,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
& when (@border = transparent) {
|
||||
& when (@theme = dark) {
|
||||
.button-color(~`colorPalette('@{color}', 5) `; transparent; transparent);
|
||||
|
@ -143,9 +141,8 @@
|
|||
}
|
||||
.button-color(@color; @background; @border) {
|
||||
color: @color;
|
||||
background-color: @background;
|
||||
border-color: @border;
|
||||
// a inside Button which only work in Chrome
|
||||
background: @background;
|
||||
border-color: @border; // a inside Button which only work in Chrome
|
||||
// http://stackoverflow.com/a/17253457
|
||||
> a:only-child {
|
||||
color: currentColor;
|
||||
|
@ -168,37 +165,34 @@
|
|||
position: relative;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
z-index: 2;
|
||||
}
|
||||
&:disabled {
|
||||
&[disabled] {
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
> .@{btnClassName}-icon-only {
|
||||
.@{btnClassName}-icon-only {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
// size
|
||||
&-lg > .@{btnClassName},
|
||||
&-lg > span > .@{btnClassName} {
|
||||
.button-size(@btn-height-lg; @btn-padding-lg; @btn-font-size-lg; 0);
|
||||
line-height: @btn-height-lg - 2px;
|
||||
.button-size(@btn-height-lg; @btn-padding-horizontal-lg; @btn-font-size-lg; 0);
|
||||
}
|
||||
&-lg > .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
&-lg .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
.square(@btn-height-lg);
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
&-sm > .@{btnClassName},
|
||||
&-sm > span > .@{btnClassName} {
|
||||
.button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; 0);
|
||||
line-height: @btn-height-sm - 2px;
|
||||
.button-size(@btn-height-sm; @btn-padding-horizontal-sm; @font-size-base; 0);
|
||||
> .@{iconfont-css-prefix} {
|
||||
font-size: @font-size-base;
|
||||
}
|
||||
}
|
||||
&-sm > .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
&-sm .@{btnClassName}.@{btnClassName}-icon-only {
|
||||
.square(@btn-height-sm);
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
|
@ -219,7 +213,9 @@
|
|||
transition: all 0.3s @ease-in-out;
|
||||
user-select: none;
|
||||
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} {
|
||||
line-height: 1;
|
||||
}
|
||||
|
@ -235,7 +231,6 @@
|
|||
outline: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
&.disabled,
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
> * {
|
||||
|
@ -243,10 +238,14 @@
|
|||
}
|
||||
}
|
||||
&-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 {
|
||||
.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
|
||||
|
@ -258,8 +257,7 @@
|
|||
.button-variant-other(@btn-default-color; @btn-default-bg; @btn-default-border);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.active {
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
background: @btn-default-bg;
|
||||
}
|
||||
|
@ -277,10 +275,70 @@
|
|||
.btn-danger() {
|
||||
.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
|
||||
.btn-link() {
|
||||
.button-variant-other(@link-color, transparent, transparent);
|
||||
box-shadow: none;
|
||||
&:hover {
|
||||
background: @btn-link-hover-bg;
|
||||
}
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
|
@ -288,31 +346,82 @@
|
|||
}
|
||||
.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
|
||||
.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 {
|
||||
.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 {
|
||||
.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
|
||||
.btn-square(@btnClassName: btn) {
|
||||
.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 {
|
||||
.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 {
|
||||
.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
|
||||
|
|
|
@ -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 {
|
||||
.btn();
|
||||
.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;
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import type { ExtractPropTypes } from 'vue';
|
||||
import type { ExtractPropTypes, PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import Button from '../button';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import buttonTypes from '../button/buttonTypes';
|
||||
import { convertLegacyProps, LegacyButtonType } from '../button/buttonTypes';
|
||||
import { getSlot, findDOMNode } from '../_util/props-util';
|
||||
const ButtonType = buttonTypes().type;
|
||||
|
||||
const ActionButtonProps = {
|
||||
type: ButtonType,
|
||||
type: {
|
||||
type: String as PropType<LegacyButtonType>,
|
||||
},
|
||||
actionFn: PropTypes.func,
|
||||
closeModal: PropTypes.func,
|
||||
autofocus: PropTypes.looseBool,
|
||||
|
@ -77,7 +78,7 @@ export default defineComponent({
|
|||
render() {
|
||||
const { type, loading, buttonProps } = this;
|
||||
const props = {
|
||||
type,
|
||||
...convertLegacyProps(type),
|
||||
onClick: this.onClick,
|
||||
loading,
|
||||
...buttonProps,
|
||||
|
|
|
@ -7,8 +7,11 @@ import addEventListener from '../vc-util/Dom/addEventListener';
|
|||
import { getConfirmLocale } from './locale';
|
||||
import CloseOutlined from '@ant-design/icons-vue/CloseOutlined';
|
||||
import Button from '../button';
|
||||
import type { ButtonProps as ButtonPropsType, ButtonType } from '../button/buttonTypes';
|
||||
import buttonTypes from '../button/buttonTypes';
|
||||
import buttonTypes, {
|
||||
ButtonProps as ButtonPropsType,
|
||||
convertLegacyProps,
|
||||
LegacyButtonType,
|
||||
} from '../button/buttonTypes';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import { getComponent, getSlot } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
|
@ -63,7 +66,9 @@ const modalProps = {
|
|||
/** 确认按钮文字*/
|
||||
okText: PropTypes.any,
|
||||
/** 确认按钮类型*/
|
||||
okType: buttonTypes().type,
|
||||
okType: {
|
||||
type: String as PropType<LegacyButtonType>,
|
||||
},
|
||||
/** 取消按钮文字*/
|
||||
cancelText: PropTypes.any,
|
||||
icon: PropTypes.any,
|
||||
|
@ -104,7 +109,7 @@ export interface ModalFuncProps {
|
|||
centered?: boolean;
|
||||
width?: string | number;
|
||||
okText?: VNodeTypes;
|
||||
okType?: ButtonType;
|
||||
okType?: LegacyButtonType;
|
||||
cancelText?: VNodeTypes;
|
||||
icon?: VNodeTypes;
|
||||
/* Deprecated */
|
||||
|
@ -180,7 +185,7 @@ export default defineComponent({
|
|||
const cancelBtnProps = { onClick: this.handleCancel, ...(this.cancelButtonProps || {}) };
|
||||
const okBtnProps = {
|
||||
onClick: this.handleOk,
|
||||
type: okType,
|
||||
...convertLegacyProps(okType),
|
||||
loading: confirmLoading,
|
||||
...(this.okButtonProps || {}),
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import omit from 'omit.js';
|
||||
import { defineComponent, inject } from 'vue';
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import Tooltip from '../tooltip';
|
||||
import abstractTooltipProps from '../tooltip/abstractTooltipProps';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import { getOptionProps, hasProp, getComponent, mergeProps } from '../_util/props-util';
|
||||
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 Button from '../button';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
|
@ -14,7 +14,6 @@ import { defaultConfigProvider } from '../config-provider';
|
|||
import { withInstall } from '../_util/type';
|
||||
|
||||
const tooltipProps = abstractTooltipProps();
|
||||
const btnProps = buttonTypes();
|
||||
|
||||
const Popconfirm = defineComponent({
|
||||
name: 'APopconfirm',
|
||||
|
@ -26,7 +25,10 @@ const Popconfirm = defineComponent({
|
|||
content: PropTypes.any,
|
||||
title: PropTypes.any,
|
||||
trigger: tooltipProps.trigger.def('click'),
|
||||
okType: btnProps.type.def('primary'),
|
||||
okType: {
|
||||
type: String as PropType<LegacyButtonType>,
|
||||
default: 'primary',
|
||||
},
|
||||
disabled: PropTypes.looseBool.def(false),
|
||||
okText: PropTypes.any,
|
||||
cancelText: PropTypes.any,
|
||||
|
@ -97,7 +99,7 @@ const Popconfirm = defineComponent({
|
|||
...cancelButtonProps,
|
||||
});
|
||||
const okBtnProps = mergeProps({
|
||||
type: okType,
|
||||
...convertLegacyProps(okType),
|
||||
size: 'small',
|
||||
onClick: this.onConfirmHandle,
|
||||
...okButtonProps,
|
||||
|
|
|
@ -189,16 +189,18 @@
|
|||
@btn-default-ghost-bg: transparent;
|
||||
@btn-default-ghost-border: @component-background;
|
||||
|
||||
@btn-padding-base: 0 @padding-md - 1px;
|
||||
@btn-font-size-lg: @font-size-lg;
|
||||
@btn-font-size-sm: @font-size-base;
|
||||
@btn-padding-lg: @btn-padding-base;
|
||||
@btn-padding-sm: 0 @padding-xs - 1px;
|
||||
@btn-padding-horizontal-base: @padding-md - 1px;
|
||||
@btn-padding-horizontal-lg: @btn-padding-horizontal-base;
|
||||
@btn-padding-horizontal-sm: @padding-xs - 1px;
|
||||
|
||||
@btn-height-base: 32px;
|
||||
@btn-height-lg: 40px;
|
||||
@btn-height-sm: 24px;
|
||||
|
||||
@btn-line-height: @line-height-base;
|
||||
|
||||
@btn-circle-size: @btn-height-base;
|
||||
@btn-circle-size-lg: @btn-height-lg;
|
||||
@btn-circle-size-sm: @btn-height-sm;
|
||||
|
@ -206,9 +208,13 @@
|
|||
@btn-square-size: @btn-height-base;
|
||||
@btn-square-size-lg: @btn-height-lg;
|
||||
@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-link-hover-bg: transparent;
|
||||
@btn-text-hover-bg: rgba(0, 0, 0, 0.018);
|
||||
|
||||
// Checkbox
|
||||
|
|
2
v2-doc
2
v2-doc
|
@ -1 +1 @@
|
|||
Subproject commit f8c276329a6bfa295847dc4a11538863f9f4cced
|
||||
Subproject commit b6ab0fec2cfa378bab8dfe6c8ef6b6a8664b970e
|
Loading…
Reference in New Issue