button.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. import { computed, defineComponent, onBeforeUnmount, onMounted, onUpdated, shallowRef, Text, watch, watchEffect } from 'vue';
  5. import Wave from '../_util/wave';
  6. import buttonProps from './buttonTypes';
  7. import { flattenChildren, initDefaultProps } from '../_util/props-util';
  8. import useConfigInject from '../config-provider/hooks/useConfigInject';
  9. import { useInjectDisabled } from '../config-provider/DisabledContext';
  10. import devWarning from '../vc-util/devWarning';
  11. import LoadingIcon from './LoadingIcon';
  12. import useStyle from './style';
  13. import { GroupSizeContext } from './button-group';
  14. import { useCompactItemContext } from '../space/Compact';
  15. const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
  16. const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
  17. function isUnBorderedButtonType(type) {
  18. return type === 'text' || type === 'link';
  19. }
  20. export { buttonProps };
  21. export default defineComponent({
  22. compatConfig: {
  23. MODE: 3
  24. },
  25. name: 'AButton',
  26. inheritAttrs: false,
  27. __ANT_BUTTON: true,
  28. props: initDefaultProps(buttonProps(), {
  29. type: 'default'
  30. }),
  31. slots: Object,
  32. // emits: ['click', 'mousedown'],
  33. setup(props, _ref) {
  34. let {
  35. slots,
  36. attrs,
  37. emit,
  38. expose
  39. } = _ref;
  40. const {
  41. prefixCls,
  42. autoInsertSpaceInButton,
  43. direction,
  44. size
  45. } = useConfigInject('btn', props);
  46. const [wrapSSR, hashId] = useStyle(prefixCls);
  47. const groupSizeContext = GroupSizeContext.useInject();
  48. const disabledContext = useInjectDisabled();
  49. const mergedDisabled = computed(() => {
  50. var _a;
  51. return (_a = props.disabled) !== null && _a !== void 0 ? _a : disabledContext.value;
  52. });
  53. const buttonNodeRef = shallowRef(null);
  54. const delayTimeoutRef = shallowRef(undefined);
  55. let isNeedInserted = false;
  56. const innerLoading = shallowRef(false);
  57. const hasTwoCNChar = shallowRef(false);
  58. const autoInsertSpace = computed(() => autoInsertSpaceInButton.value !== false);
  59. const {
  60. compactSize,
  61. compactItemClassnames
  62. } = useCompactItemContext(prefixCls, direction);
  63. // =============== Update Loading ===============
  64. const loadingOrDelay = computed(() => typeof props.loading === 'object' && props.loading.delay ? props.loading.delay || true : !!props.loading);
  65. watch(loadingOrDelay, val => {
  66. clearTimeout(delayTimeoutRef.value);
  67. if (typeof loadingOrDelay.value === 'number') {
  68. delayTimeoutRef.value = setTimeout(() => {
  69. innerLoading.value = val;
  70. }, loadingOrDelay.value);
  71. } else {
  72. innerLoading.value = val;
  73. }
  74. }, {
  75. immediate: true
  76. });
  77. const classes = computed(() => {
  78. const {
  79. type,
  80. shape = 'default',
  81. ghost,
  82. block,
  83. danger
  84. } = props;
  85. const pre = prefixCls.value;
  86. const sizeClassNameMap = {
  87. large: 'lg',
  88. small: 'sm',
  89. middle: undefined
  90. };
  91. const sizeFullname = compactSize.value || (groupSizeContext === null || groupSizeContext === void 0 ? void 0 : groupSizeContext.size) || size.value;
  92. const sizeCls = sizeFullname ? sizeClassNameMap[sizeFullname] || '' : '';
  93. return [compactItemClassnames.value, {
  94. [hashId.value]: true,
  95. [`${pre}`]: true,
  96. [`${pre}-${shape}`]: shape !== 'default' && shape,
  97. [`${pre}-${type}`]: type,
  98. [`${pre}-${sizeCls}`]: sizeCls,
  99. [`${pre}-loading`]: innerLoading.value,
  100. [`${pre}-background-ghost`]: ghost && !isUnBorderedButtonType(type),
  101. [`${pre}-two-chinese-chars`]: hasTwoCNChar.value && autoInsertSpace.value,
  102. [`${pre}-block`]: block,
  103. [`${pre}-dangerous`]: !!danger,
  104. [`${pre}-rtl`]: direction.value === 'rtl'
  105. }];
  106. });
  107. const fixTwoCNChar = () => {
  108. // Fix for HOC usage like <FormatMessage />
  109. const node = buttonNodeRef.value;
  110. if (!node || autoInsertSpaceInButton.value === false) {
  111. return;
  112. }
  113. const buttonText = node.textContent;
  114. if (isNeedInserted && isTwoCNChar(buttonText)) {
  115. if (!hasTwoCNChar.value) {
  116. hasTwoCNChar.value = true;
  117. }
  118. } else if (hasTwoCNChar.value) {
  119. hasTwoCNChar.value = false;
  120. }
  121. };
  122. const handleClick = event => {
  123. // https://github.com/ant-design/ant-design/issues/30207
  124. if (innerLoading.value || mergedDisabled.value) {
  125. event.preventDefault();
  126. return;
  127. }
  128. emit('click', event);
  129. };
  130. const handleMousedown = event => {
  131. emit('mousedown', event);
  132. };
  133. const insertSpace = (child, needInserted) => {
  134. const SPACE = needInserted ? ' ' : '';
  135. if (child.type === Text) {
  136. let text = child.children.trim();
  137. if (isTwoCNChar(text)) {
  138. text = text.split('').join(SPACE);
  139. }
  140. return _createVNode("span", null, [text]);
  141. }
  142. return child;
  143. };
  144. watchEffect(() => {
  145. devWarning(!(props.ghost && isUnBorderedButtonType(props.type)), 'Button', "`link` or `text` button can't be a `ghost` button.");
  146. });
  147. onMounted(fixTwoCNChar);
  148. onUpdated(fixTwoCNChar);
  149. onBeforeUnmount(() => {
  150. delayTimeoutRef.value && clearTimeout(delayTimeoutRef.value);
  151. });
  152. const focus = () => {
  153. var _a;
  154. (_a = buttonNodeRef.value) === null || _a === void 0 ? void 0 : _a.focus();
  155. };
  156. const blur = () => {
  157. var _a;
  158. (_a = buttonNodeRef.value) === null || _a === void 0 ? void 0 : _a.blur();
  159. };
  160. expose({
  161. focus,
  162. blur
  163. });
  164. return () => {
  165. var _a, _b;
  166. const {
  167. icon = (_a = slots.icon) === null || _a === void 0 ? void 0 : _a.call(slots)
  168. } = props;
  169. const children = flattenChildren((_b = slots.default) === null || _b === void 0 ? void 0 : _b.call(slots));
  170. isNeedInserted = children.length === 1 && !icon && !isUnBorderedButtonType(props.type);
  171. const {
  172. type,
  173. htmlType,
  174. href,
  175. title,
  176. target
  177. } = props;
  178. const iconType = innerLoading.value ? 'loading' : icon;
  179. const buttonProps = _extends(_extends({}, attrs), {
  180. title,
  181. disabled: mergedDisabled.value,
  182. class: [classes.value, attrs.class, {
  183. [`${prefixCls.value}-icon-only`]: children.length === 0 && !!iconType
  184. }],
  185. onClick: handleClick,
  186. onMousedown: handleMousedown
  187. });
  188. // https://github.com/vueComponent/ant-design-vue/issues/4930
  189. if (!mergedDisabled.value) {
  190. delete buttonProps.disabled;
  191. }
  192. const iconNode = icon && !innerLoading.value ? icon : _createVNode(LoadingIcon, {
  193. "existIcon": !!icon,
  194. "prefixCls": prefixCls.value,
  195. "loading": !!innerLoading.value
  196. }, null);
  197. const kids = children.map(child => insertSpace(child, isNeedInserted && autoInsertSpace.value));
  198. if (href !== undefined) {
  199. return wrapSSR(_createVNode("a", _objectSpread(_objectSpread({}, buttonProps), {}, {
  200. "href": href,
  201. "target": target,
  202. "ref": buttonNodeRef
  203. }), [iconNode, kids]));
  204. }
  205. let buttonNode = _createVNode("button", _objectSpread(_objectSpread({}, buttonProps), {}, {
  206. "ref": buttonNodeRef,
  207. "type": htmlType
  208. }), [iconNode, kids]);
  209. if (!isUnBorderedButtonType(type)) {
  210. const _buttonNode = function () {
  211. return buttonNode;
  212. }();
  213. buttonNode = _createVNode(Wave, {
  214. "ref": "wave",
  215. "disabled": !!innerLoading.value
  216. }, {
  217. default: () => [_buttonNode]
  218. });
  219. }
  220. return wrapSSR(buttonNode);
  221. };
  222. }
  223. });