From f49437f002f8fe7c7b283e25bb74618ccb5ea814 Mon Sep 17 00:00:00 2001 From: lyn <76365499@qq.com> Date: Tue, 31 Jan 2023 22:13:12 +0800 Subject: [PATCH 1/6] fix: typo (#6218) * fix: typo * docs: docs update --- components/affix/index.tsx | 28 ++++++++++++++-------------- components/affix/utils.ts | 8 ++++---- components/upload/index.en-US.md | 14 +++++++------- components/upload/index.zh-CN.md | 14 +++++++------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/components/affix/index.tsx b/components/affix/index.tsx index e6ee0e9726..74bd14318c 100644 --- a/components/affix/index.tsx +++ b/components/affix/index.tsx @@ -110,30 +110,30 @@ const Affix = defineComponent({ status: AffixStatus.None, } as AffixState; const targetRect = getTargetRect(targetNode); - const placeholderReact = getTargetRect(placeholderNode.value as HTMLElement); - const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop.value); - const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom.value); + const placeholderRect = getTargetRect(placeholderNode.value as HTMLElement); + const fixedTop = getFixedTop(placeholderRect, targetRect, offsetTop.value); + const fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom.value); if (fixedTop !== undefined) { newState.affixStyle = { position: 'fixed', top: fixedTop, - width: placeholderReact.width + 'px', - height: placeholderReact.height + 'px', + width: placeholderRect.width + 'px', + height: placeholderRect.height + 'px', }; newState.placeholderStyle = { - width: placeholderReact.width + 'px', - height: placeholderReact.height + 'px', + width: placeholderRect.width + 'px', + height: placeholderRect.height + 'px', }; } else if (fixedBottom !== undefined) { newState.affixStyle = { position: 'fixed', bottom: fixedBottom, - width: placeholderReact.width + 'px', - height: placeholderReact.height + 'px', + width: placeholderRect.width + 'px', + height: placeholderRect.height + 'px', }; newState.placeholderStyle = { - width: placeholderReact.width + 'px', - height: placeholderReact.height + 'px', + width: placeholderRect.width + 'px', + height: placeholderRect.height + 'px', }; } @@ -169,9 +169,9 @@ const Affix = defineComponent({ const targetNode = target(); if (targetNode && placeholderNode.value) { const targetRect = getTargetRect(targetNode); - const placeholderReact = getTargetRect(placeholderNode.value as HTMLElement); - const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop.value); - const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom.value); + const placeholderRect = getTargetRect(placeholderNode.value as HTMLElement); + const fixedTop = getFixedTop(placeholderRect, targetRect, offsetTop.value); + const fixedBottom = getFixedBottom(placeholderRect, targetRect, offsetBottom.value); if ( (fixedTop !== undefined && affixStyle.top === fixedTop) || (fixedBottom !== undefined && affixStyle.bottom === fixedBottom) diff --git a/components/affix/utils.ts b/components/affix/utils.ts index 4ce8c11bb9..977c7c284f 100644 --- a/components/affix/utils.ts +++ b/components/affix/utils.ts @@ -10,19 +10,19 @@ export function getTargetRect(target: BindElement): DOMRect { : ({ top: 0, bottom: window.innerHeight } as DOMRect); } -export function getFixedTop(placeholderReact: DOMRect, targetRect: DOMRect, offsetTop: number) { - if (offsetTop !== undefined && targetRect.top > placeholderReact.top - offsetTop) { +export function getFixedTop(placeholderRect: DOMRect, targetRect: DOMRect, offsetTop: number) { + if (offsetTop !== undefined && targetRect.top > placeholderRect.top - offsetTop) { return `${offsetTop + targetRect.top}px`; } return undefined; } export function getFixedBottom( - placeholderReact: DOMRect, + placeholderRect: DOMRect, targetRect: DOMRect, offsetBottom: number, ) { - if (offsetBottom !== undefined && targetRect.bottom < placeholderReact.bottom + offsetBottom) { + if (offsetBottom !== undefined && targetRect.bottom < placeholderRect.bottom + offsetBottom) { const targetBottomOffset = window.innerHeight - targetRect.bottom; return `${offsetBottom + targetBottomOffset}px`; } diff --git a/components/upload/index.en-US.md b/components/upload/index.en-US.md index 910480abd3..e1eaccaa6a 100644 --- a/components/upload/index.en-US.md +++ b/components/upload/index.en-US.md @@ -22,7 +22,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - | | | | action | Uploading URL | string\|(file) => `Promise` | - | | | | beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. | (file, fileList) => `boolean` \| `Promise` | - | | -| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | Function | - | | | +| customRequest | override for the default xhr behavior allowing for additional customization and ability to implement your own XMLHttpRequest | function | - | | | | data | Uploading params or function which can return uploading params. | object\|function(file) | - | | | | directory | support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | | | | directory | Support upload whole directory([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | 3.0 | | @@ -32,7 +32,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | headers | Set request headers, valid above IE10. | object | - | | | | iconRender | Custom show icon | v-slot:iconRender="{file: UploadFile, listType?: UploadListType}" | - | 3.0 | | | isImageUrl | Customize if render <img /> in thumbnail | (file: UploadFile) => boolean | - | 3.0 | | -| itemRender | Custom item of uploadList | v-slot:itemRender="{originNode: ReactElement, file: UploadFile, fileList: object\[], actions: { download: function, preview: function, remove: function }" | - | 3.0 | | +| itemRender | Custom item of uploadList | v-slot:itemRender="{originNode: VNode, file: UploadFile, fileList: object\[], actions: { download: function, preview: function, remove: function }" | - | 3.0 | | | listType | Built-in stylesheets, support for three types: `text`, `picture` or `picture-card` | string | `text` | | | | maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | 3.0 | | | method | http method of upload request | string | `post` | 1.5.0 | | @@ -43,7 +43,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v | previewIcon | custom preview icon | v-slot:iconRender="{file: UploadFile}" | - | 3.0 | | | progress | Custom progress bar | [ProgressProps](/components/progress/#API) (support `type="line"` only) | { strokeWidth: 2, showInfo: false } | 3.0 | | | removeIcon | custom remove icon | v-slot:iconRender="{file: UploadFile}" | - | 3.0 | | -| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon`, `showRemoveIcon` and `showDownloadIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean } | true | showDownloadIcon(3.0) | | +| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon`, `showRemoveIcon` and `showDownloadIcon` individually | boolean \| { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean } | true | showDownloadIcon(3.0) | | | supportServerRender | Need to be turned on while the server side is rendering. | boolean | false | | | | withCredentials | ajax upload with cookie sent | boolean | false | | | @@ -51,11 +51,11 @@ Uploading is the process of publishing information (web pages, text, pictures, v | Events Name | Description | Arguments | Version | | | --- | --- | --- | --- | --- | -| change | A callback function, can be executed when uploading state is changing. See [change](#change) | Function | - | | -| download | Click the method to download the file, pass the method to perform the method logic, do not pass the default jump to the new TAB. | Function(file): void | Jump to new TAB | 1.5.0 | +| change | A callback function, can be executed when uploading state is changing. See [change](#change) | function | - | | +| download | Click the method to download the file, pass the method to perform the method logic, do not pass the default jump to the new TAB. | function(file): void | Jump to new TAB | 1.5.0 | | drop | A callback function executed when files are dragged and dropped into upload area | (event: DragEvent) => void | - | 3.0 | -| preview | A callback function, will be executed when file link or preview icon is clicked. | Function(file) | - | | -| reject | A callback function, will be executed when drop files is not accept. | Function(fileList) | - | | +| preview | A callback function, will be executed when file link or preview icon is clicked. | function(file) | - | | +| reject | A callback function, will be executed when drop files is not accept. | function(fileList) | - | | | remove   | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is false or a Promise which resolve(false) or reject | function(file): boolean \| Promise | -   | 3.0 | ### UploadFile diff --git a/components/upload/index.zh-CN.md b/components/upload/index.zh-CN.md index 5fea7076fc..5ea07a5a63 100644 --- a/components/upload/index.zh-CN.md +++ b/components/upload/index.zh-CN.md @@ -23,7 +23,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | accept | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | 无 | | | | action | 上传的地址 | string\|(file) => `Promise` | 无 | | | | beforeUpload | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传。支持返回一个 Promise 对象,Promise 对象 reject 时则停止上传,resolve 时开始上传( resolve 传入 `File` 或 `Blob` 对象则上传 resolve 传入对象)。 | (file, fileList) => `boolean` \| `Promise` | 无 | | -| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | Function | 无 | | | +| customRequest | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | function | 无 | | | | data | 上传所需参数或返回上传参数的方法 | object\|(file) => object | 无 | | | | directory | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false | 3.0 | | | disabled | 是否禁用 | boolean | false | | | @@ -32,7 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | headers | 设置上传的请求头部,IE10 以上有效 | object | 无 | | | | iconRender | 自定义显示 icon | v-slot:iconRender="{file: UploadFile, listType?: UploadListType}" | - | 3.0 | | | isImageUrl | 自定义缩略图是否使用 <img /> 标签进行显示 | (file: UploadFile) => boolean | - | 3.0 | | -| itemRender | 自定义上传列表项 | v-slot:itemRender="{originNode: ReactElement, file: UploadFile, fileList: object\[], actions: { download: function, preview: function, remove: function }" | - | 3.0 | | +| itemRender | 自定义上传列表项 | v-slot:itemRender="{originNode: VNode, file: UploadFile, fileList: object\[], actions: { download: function, preview: function, remove: function }" | - | 3.0 | | | listType | 上传列表的内建样式,支持三种基本样式 `text`, `picture` 和 `picture-card` | string | `text` | | | | maxCount | 限制上传数量。当为 1 时,始终用最新上传的文件代替当前文件 | number | - | 3.0 | | | method | 上传请求的 http method | string | `post` | 1.5.0 | | @@ -43,7 +43,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | previewIcon | 自定义预览 icon | v-slot:iconRender="{file: UploadFile}" | - | 3.0 | | | progress | 自定义进度条样式 | [ProgressProps](/components/progress/#API)(仅支持 `type="line"`) | { strokeWidth: 2, showInfo: false } | 3.0 | | | removeIcon | 自定义删除 icon | v-slot:iconRender="{file: UploadFile}" | - | 3.0 | | -| showUploadList | 是否展示 uploadList, 可设为一个对象,用于单独设定 showPreviewIcon, showRemoveIcon 和 showDownloadIcon | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean } | true | showDownloadIcon(3.0) | | +| showUploadList | 是否展示 uploadList, 可设为一个对象,用于单独设定 showPreviewIcon, showRemoveIcon 和 showDownloadIcon | boolean \| { showPreviewIcon?: boolean, showRemoveIcon?: boolean, showDownloadIcon?: boolean } | true | showDownloadIcon(3.0) | | | supportServerRender | 服务端渲染时需要打开这个 | boolean | false | | | | withCredentials | 上传请求时是否携带 cookie | boolean | false | | | @@ -51,11 +51,11 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QaeBt_ZMg/Upload.svg | 事件名称 | 说明 | 回调参数 | 版本 | | | --- | --- | --- | --- | --- | -| change | 上传文件改变时的状态,详见 [change](#change) | Function | 无 | | -| download | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页。 | Function(file): void | 跳转新标签页 | 1.5.0 | +| change | 上传文件改变时的状态,详见 [change](#change) | function | 无 | | +| download | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页。 | function(file): void | 跳转新标签页 | 1.5.0 | | drop | 当文件被拖入上传区域时执行的回调功能 | (event: DragEvent) => void | - | 3.0 | -| preview | 点击文件链接或预览图标时的回调 | Function(file) | 无 | | -| reject | 拖拽文件不符合 accept 类型时的回调 | Function(fileList) | 无 | | +| preview | 点击文件链接或预览图标时的回调 | function(file) | 无 | | +| reject | 拖拽文件不符合 accept 类型时的回调 | function(fileList) | 无 | | | remove   | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象,Promise 对象 resolve(false) 或 reject 时不移除 | function(file): boolean \| Promise | -   | 3.0 | ### UploadFile From 3a966a28a05b2321f904e041fd80193a3d886d00 Mon Sep 17 00:00:00 2001 From: aibayanyu20 Date: Wed, 1 Feb 2023 11:36:25 +0800 Subject: [PATCH 2/6] refactor: spin --- components/spin/Spin.tsx | 158 +++++++-------- components/spin/style/index.ts | 241 +++++++++++++++++++++++ components/theme/interface/components.ts | 4 +- 3 files changed, 315 insertions(+), 88 deletions(-) create mode 100644 components/spin/style/index.ts diff --git a/components/spin/Spin.tsx b/components/spin/Spin.tsx index f0500f9738..15614af3d1 100644 --- a/components/spin/Spin.tsx +++ b/components/spin/Spin.tsx @@ -1,10 +1,11 @@ import type { VNode, ExtractPropTypes, PropType } from 'vue'; -import { inject, cloneVNode, isVNode, defineComponent, nextTick } from 'vue'; +import { cloneVNode, isVNode, defineComponent, nextTick, shallowRef, computed } from 'vue'; import debounce from 'lodash-es/debounce'; import PropTypes from '../_util/vue-types'; -import { getComponent, getSlot } from '../_util/props-util'; +import { getPropsSlot } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; -import { defaultConfigProvider, configProviderKey } from '../config-provider/context'; +import useStyle from './style'; +import useConfigInject from 'ant-design-vue/es/config-provider/hooks/useConfigInject'; export type SpinSize = 'small' | 'default' | 'large'; export const spinProps = () => ({ @@ -40,10 +41,74 @@ export default defineComponent({ spinning: true, wrapperClassName: '', }), - setup() { - return { - originalUpdateSpinning: null, - configProvider: inject(configProviderKey, defaultConfigProvider), + setup(props, { attrs, slots }) { + const { prefixCls, size, direction } = useConfigInject('spin', props); + const [wrapSSR, hashId] = useStyle(prefixCls); + const children = slots.default?.(); + const shouldBeDelayed = computed(() => shouldDelay(props.spinning, props.delay)); + const sSpinning = shallowRef(props.spinning && shouldBeDelayed.value); + return () => { + const { class: cls, ...divProps } = attrs; + const { tip = slots.tip?.() } = props; + const spinClassName = { + [hashId.value]: true, + [prefixCls.value]: true, + [`${prefixCls.value}-sm`]: size.value === 'small', + [`${prefixCls.value}-lg`]: size.value === 'large', + [`${prefixCls.value}-spinning`]: sSpinning.value, + [`${prefixCls.value}-show-text`]: !!tip, + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + [cls as string]: !!cls, + }; + + function renderIndicator(prefixCls: string) { + const dotClassName = `${prefixCls}-dot`; + let indicator = getPropsSlot(slots, props, 'indicator'); + // should not be render default indicator when indicator value is null + if (indicator === null) { + return null; + } + if (Array.isArray(indicator)) { + indicator = indicator.length === 1 ? indicator[0] : indicator; + } + if (isVNode(indicator)) { + return cloneVNode(indicator, { class: dotClassName }); + } + + if (defaultIndicator && isVNode(defaultIndicator())) { + return cloneVNode(defaultIndicator(), { class: dotClassName }); + } + + return ( + + + + + + + ); + } + const spinElement = ( +
+ {renderIndicator(prefixCls.value)} + {tip ?
{tip}
: null} +
+ ); + if (children && children.length) { + const containerClassName = { + [`${prefixCls.value}-container`]: true, + [`${prefixCls.value}-blur`]: sSpinning.value, + }; + return wrapSSR( +
+ {sSpinning.value &&
{spinElement}
} +
+ {slots?.default?.()} +
+
, + ); + } + return wrapSSR(spinElement); }; }, data() { @@ -89,84 +154,5 @@ export default defineComponent({ (updateSpinning as any).cancel(); } }, - renderIndicator(prefixCls: string) { - const dotClassName = `${prefixCls}-dot`; - let indicator = getComponent(this, 'indicator'); - // should not be render default indicator when indicator value is null - if (indicator === null) { - return null; - } - if (Array.isArray(indicator)) { - indicator = indicator.length === 1 ? indicator[0] : indicator; - } - if (isVNode(indicator)) { - return cloneVNode(indicator, { class: dotClassName }); - } - - if (defaultIndicator && isVNode(defaultIndicator())) { - return cloneVNode(defaultIndicator(), { class: dotClassName }); - } - - return ( - - - - - - - ); - }, - }, - render() { - const { - size, - prefixCls: customizePrefixCls, - tip = this.$slots.tip?.(), - wrapperClassName, - } = this.$props; - const { class: cls, style, ...divProps } = this.$attrs; - const { getPrefixCls, direction } = this.configProvider; - const prefixCls = getPrefixCls('spin', customizePrefixCls); - - const { sSpinning } = this; - const spinClassName = { - [prefixCls]: true, - [`${prefixCls}-sm`]: size === 'small', - [`${prefixCls}-lg`]: size === 'large', - [`${prefixCls}-spinning`]: sSpinning, - [`${prefixCls}-show-text`]: !!tip, - [`${prefixCls}-rtl`]: direction === 'rtl', - [cls as string]: !!cls, - }; - - const spinElement = ( -
- {this.renderIndicator(prefixCls)} - {tip ?
{tip}
: null} -
- ); - const children = getSlot(this); - if (children && children.length) { - const containerClassName = { - [`${prefixCls}-container`]: true, - [`${prefixCls}-blur`]: sSpinning, - }; - - return ( -
- {sSpinning &&
{spinElement}
} -
- {children} -
-
- ); - } - return spinElement; }, }); diff --git a/components/spin/style/index.ts b/components/spin/style/index.ts new file mode 100644 index 0000000000..52187ca7d7 --- /dev/null +++ b/components/spin/style/index.ts @@ -0,0 +1,241 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import { Keyframes } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { resetComponent } from '../../_style'; + +export interface ComponentToken { + contentHeight: number; +} + +interface SpinToken extends FullToken<'Spin'> { + spinDotDefault: string; + spinDotSize: number; + spinDotSizeSM: number; + spinDotSizeLG: number; +} + +const antSpinMove = new Keyframes('antSpinMove', { + to: { opacity: 1 }, +}); + +const antRotate = new Keyframes('antRotate', { + to: { transform: 'rotate(405deg)' }, +}); + +const genSpinStyle: GenerateStyle = (token: SpinToken): CSSObject => ({ + [`${token.componentCls}`]: { + ...resetComponent(token), + position: 'absolute', + display: 'none', + color: token.colorPrimary, + textAlign: 'center', + verticalAlign: 'middle', + opacity: 0, + transition: `transform ${token.motionDurationSlow} ${token.motionEaseInOutCirc}`, + + '&-spinning': { + position: 'static', + display: 'inline-block', + opacity: 1, + }, + + '&-nested-loading': { + position: 'relative', + [`> div > ${token.componentCls}`]: { + position: 'absolute', + top: 0, + insetInlineStart: 0, + zIndex: 4, + display: 'block', + width: '100%', + height: '100%', + maxHeight: token.contentHeight, + + [`${token.componentCls}-dot`]: { + position: 'absolute', + top: '50%', + insetInlineStart: '50%', + margin: -token.spinDotSize / 2, + }, + + [`${token.componentCls}-text`]: { + position: 'absolute', + top: '50%', + width: '100%', + paddingTop: (token.spinDotSize - token.fontSize) / 2 + 2, + textShadow: `0 1px 2px ${token.colorBgContainer}`, // FIXME: shadow + }, + + [`&${token.componentCls}-show-text ${token.componentCls}-dot`]: { + marginTop: -(token.spinDotSize / 2) - 10, + }, + + '&-sm': { + [`${token.componentCls}-dot`]: { + margin: -token.spinDotSizeSM / 2, + }, + [`${token.componentCls}-text`]: { + paddingTop: (token.spinDotSizeSM - token.fontSize) / 2 + 2, + }, + [`&${token.componentCls}-show-text ${token.componentCls}-dot`]: { + marginTop: -(token.spinDotSizeSM / 2) - 10, + }, + }, + + '&-lg': { + [`${token.componentCls}-dot`]: { + margin: -(token.spinDotSizeLG / 2), + }, + [`${token.componentCls}-text`]: { + paddingTop: (token.spinDotSizeLG - token.fontSize) / 2 + 2, + }, + [`&${token.componentCls}-show-text ${token.componentCls}-dot`]: { + marginTop: -(token.spinDotSizeLG / 2) - 10, + }, + }, + }, + + [`${token.componentCls}-container`]: { + position: 'relative', + transition: `opacity ${token.motionDurationSlow}`, + + '&::after': { + position: 'absolute', + top: 0, + insetInlineEnd: 0, + bottom: 0, + insetInlineStart: 0, + zIndex: 10, + width: '100%', + height: '100%', + background: token.colorBgContainer, + opacity: 0, + transition: `all ${token.motionDurationSlow}`, + content: '""', + pointerEvents: 'none', + }, + }, + + [`${token.componentCls}-blur`]: { + clear: 'both', + opacity: 0.5, + userSelect: 'none', + pointerEvents: 'none', + + [`&::after`]: { + opacity: 0.4, + pointerEvents: 'auto', + }, + }, + }, + + // tip + // ------------------------------ + [`&-tip`]: { + color: token.spinDotDefault, + }, + + // dots + // ------------------------------ + [`${token.componentCls}-dot`]: { + position: 'relative', + display: 'inline-block', + fontSize: token.spinDotSize, + width: '1em', + height: '1em', + + '&-item': { + position: 'absolute', + display: 'block', + width: (token.spinDotSize - token.marginXXS / 2) / 2, + height: (token.spinDotSize - token.marginXXS / 2) / 2, + backgroundColor: token.colorPrimary, + borderRadius: '100%', + transform: 'scale(0.75)', + transformOrigin: '50% 50%', + opacity: 0.3, + animationName: antSpinMove, + animationDuration: '1s', + animationIterationCount: 'infinite', + animationTimingFunction: 'linear', + animationDirection: 'alternate', + + '&:nth-child(1)': { + top: 0, + insetInlineStart: 0, + }, + + '&:nth-child(2)': { + top: 0, + insetInlineEnd: 0, + animationDelay: '0.4s', + }, + + '&:nth-child(3)': { + insetInlineEnd: 0, + bottom: 0, + animationDelay: '0.8s', + }, + + '&:nth-child(4)': { + bottom: 0, + insetInlineStart: 0, + animationDelay: '1.2s', + }, + }, + + '&-spin': { + transform: 'rotate(45deg)', + animationName: antRotate, + animationDuration: '1.2s', + animationIterationCount: 'infinite', + animationTimingFunction: 'linear', + }, + }, + + // Sizes + // ------------------------------ + + // small + [`&-sm ${token.componentCls}-dot`]: { + fontSize: token.spinDotSizeSM, + + i: { + width: (token.spinDotSizeSM - token.marginXXS / 2) / 2, + height: (token.spinDotSizeSM - token.marginXXS / 2) / 2, + }, + }, + + // large + [`&-lg ${token.componentCls}-dot`]: { + fontSize: token.spinDotSizeLG, + + i: { + width: (token.spinDotSizeLG - token.marginXXS) / 2, + height: (token.spinDotSizeLG - token.marginXXS) / 2, + }, + }, + + [`&${token.componentCls}-show-text ${token.componentCls}-text`]: { + display: 'block', + }, + }, +}); + +// ============================== Export ============================== +export default genComponentStyleHook( + 'Spin', + token => { + const spinToken = mergeToken(token, { + spinDotDefault: token.colorTextDescription, + spinDotSize: token.controlHeightLG / 2, + spinDotSizeSM: token.controlHeightLG * 0.35, + spinDotSizeLG: token.controlHeight, + }); + return [genSpinStyle(spinToken)]; + }, + { + contentHeight: 400, + }, +); diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index f285f04dbd..cb19bb3792 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -35,7 +35,7 @@ import type { ComponentToken as NotificationComponentToken } from '../../notific // import type { ComponentToken as SkeletonComponentToken } from '../../skeleton/style'; // import type { ComponentToken as SliderComponentToken } from '../../slider/style'; // import type { ComponentToken as SpaceComponentToken } from '../../space/style'; -// import type { ComponentToken as SpinComponentToken } from '../../spin/style'; +import type { ComponentToken as SpinComponentToken } from '../../spin/style'; // import type { ComponentToken as StepsComponentToken } from '../../steps/style'; // import type { ComponentToken as TableComponentToken } from '../../table/style'; // import type { ComponentToken as TabsComponentToken } from '../../tabs/style'; @@ -90,7 +90,7 @@ export interface ComponentTokenMap { // Select?: SelectComponentToken; // Skeleton?: SkeletonComponentToken; // Slider?: SliderComponentToken; - // Spin?: SpinComponentToken; + Spin?: SpinComponentToken; Statistic?: {}; Switch?: {}; // Tag?: TagComponentToken; From c9f65ce135a680c85d09f47b3991adc6c919dbc9 Mon Sep 17 00:00:00 2001 From: aibayanyu20 Date: Wed, 1 Feb 2023 11:37:19 +0800 Subject: [PATCH 3/6] refactor: spin --- components/spin/Spin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/spin/Spin.tsx b/components/spin/Spin.tsx index 15614af3d1..e289a74d9a 100644 --- a/components/spin/Spin.tsx +++ b/components/spin/Spin.tsx @@ -5,7 +5,7 @@ import PropTypes from '../_util/vue-types'; import { getPropsSlot } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import useStyle from './style'; -import useConfigInject from 'ant-design-vue/es/config-provider/hooks/useConfigInject'; +import useConfigInject from '../config-provider/hooks/useConfigInject'; export type SpinSize = 'small' | 'default' | 'large'; export const spinProps = () => ({ From 4a186480fd8b90d372705c7f5ab1b2ecd7ebfb41 Mon Sep 17 00:00:00 2001 From: aibayanyu20 Date: Wed, 1 Feb 2023 13:58:56 +0800 Subject: [PATCH 4/6] refactor: spin --- components/spin/Spin.tsx | 77 +++++------ components/spin/style/index.less | 218 ------------------------------- components/spin/style/index.tsx | 2 - components/spin/style/rtl.less | 20 --- components/style.ts | 2 +- 5 files changed, 31 insertions(+), 288 deletions(-) delete mode 100644 components/spin/style/index.less delete mode 100644 components/spin/style/index.tsx delete mode 100644 components/spin/style/rtl.less diff --git a/components/spin/Spin.tsx b/components/spin/Spin.tsx index e289a74d9a..70222b180d 100644 --- a/components/spin/Spin.tsx +++ b/components/spin/Spin.tsx @@ -1,5 +1,5 @@ import type { VNode, ExtractPropTypes, PropType } from 'vue'; -import { cloneVNode, isVNode, defineComponent, nextTick, shallowRef, computed } from 'vue'; +import { cloneVNode, isVNode, defineComponent, shallowRef, watch } from 'vue'; import debounce from 'lodash-es/debounce'; import PropTypes from '../_util/vue-types'; import { getPropsSlot } from '../_util/props-util'; @@ -45,8 +45,35 @@ export default defineComponent({ const { prefixCls, size, direction } = useConfigInject('spin', props); const [wrapSSR, hashId] = useStyle(prefixCls); const children = slots.default?.(); - const shouldBeDelayed = computed(() => shouldDelay(props.spinning, props.delay)); - const sSpinning = shallowRef(props.spinning && shouldBeDelayed.value); + const sSpinning = shallowRef(props.spinning && shouldDelay(props.spinning, props.delay)); + let updateSpinning: any; + function originalUpdateSpinning() { + if (sSpinning.value !== props.spinning) { + sSpinning.value = props.spinning; + } + } + function cancelExistingSpin() { + if (updateSpinning && updateSpinning.cancel) { + updateSpinning.cancel(); + } + } + function debouncifyUpdateSpinning() { + const { delay } = props; + if (delay) { + cancelExistingSpin(); + updateSpinning = debounce(originalUpdateSpinning, delay); + } else { + updateSpinning = originalUpdateSpinning; + } + } + watch( + () => [props.spinning, props.delay], + () => { + debouncifyUpdateSpinning(); + updateSpinning?.(); + }, + { immediate: true }, + ); return () => { const { class: cls, ...divProps } = attrs; const { tip = slots.tip?.() } = props; @@ -111,48 +138,4 @@ export default defineComponent({ return wrapSSR(spinElement); }; }, - data() { - const { spinning, delay } = this; - const shouldBeDelayed = shouldDelay(spinning, delay); - return { - sSpinning: spinning && !shouldBeDelayed, - }; - }, - created() { - this.originalUpdateSpinning = this.updateSpinning; - this.debouncifyUpdateSpinning(this.$props); - }, - mounted() { - this.updateSpinning(); - }, - updated() { - nextTick(() => { - this.debouncifyUpdateSpinning(); - this.updateSpinning(); - }); - }, - beforeUnmount() { - this.cancelExistingSpin(); - }, - methods: { - debouncifyUpdateSpinning(props?: any) { - const { delay } = props || this.$props; - if (delay) { - this.cancelExistingSpin(); - this.updateSpinning = debounce(this.originalUpdateSpinning, delay); - } - }, - updateSpinning() { - const { spinning, sSpinning } = this; - if (sSpinning !== spinning) { - this.sSpinning = spinning; - } - }, - cancelExistingSpin() { - const { updateSpinning } = this; - if (updateSpinning && (updateSpinning as any).cancel) { - (updateSpinning as any).cancel(); - } - }, - }, }); diff --git a/components/spin/style/index.less b/components/spin/style/index.less deleted file mode 100644 index 2dee02e28e..0000000000 --- a/components/spin/style/index.less +++ /dev/null @@ -1,218 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@spin-prefix-cls: ~'@{ant-prefix}-spin'; -@spin-dot-default: @text-color-secondary; - -.@{spin-prefix-cls} { - .reset-component(); - - position: absolute; - display: none; - color: @primary-color; - text-align: center; - vertical-align: middle; - opacity: 0; - transition: transform 0.3s @ease-in-out-circ; - - &-spinning { - position: static; - display: inline-block; - opacity: 1; - } - - &-nested-loading { - position: relative; - > div > .@{spin-prefix-cls} { - position: absolute; - top: 0; - left: 0; - z-index: 4; - display: block; - width: 100%; - height: 100%; - max-height: 400px; - .@{spin-prefix-cls}-dot { - position: absolute; - top: 50%; - left: 50%; - margin: -(@spin-dot-size / 2); - } - .@{spin-prefix-cls}-text { - position: absolute; - top: 50%; - width: 100%; - padding-top: ((@spin-dot-size - @font-size-base) / 2) + 2px; - text-shadow: 0 1px 2px @shadow-color-inverse; - } - &.@{spin-prefix-cls}-show-text .@{spin-prefix-cls}-dot { - margin-top: -(@spin-dot-size / 2) - 10px; - } - } - - > div > .@{spin-prefix-cls}-sm { - .@{spin-prefix-cls}-dot { - margin: -(@spin-dot-size-sm / 2); - } - .@{spin-prefix-cls}-text { - padding-top: ((@spin-dot-size-sm - @font-size-base) / 2) + 2px; - } - &.@{spin-prefix-cls}-show-text .@{spin-prefix-cls}-dot { - margin-top: -(@spin-dot-size-sm / 2) - 10px; - } - } - - > div > .@{spin-prefix-cls}-lg { - .@{spin-prefix-cls}-dot { - margin: -(@spin-dot-size-lg / 2); - } - .@{spin-prefix-cls}-text { - padding-top: ((@spin-dot-size-lg - @font-size-base) / 2) + 2px; - } - &.@{spin-prefix-cls}-show-text .@{spin-prefix-cls}-dot { - margin-top: -(@spin-dot-size-lg / 2) - 10px; - } - } - } - - &-container { - position: relative; - transition: opacity 0.3s; - - &::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 10; - display: ~'none \9'; - width: 100%; - height: 100%; - background: @component-background; - opacity: 0; - transition: all 0.3s; - content: ''; - pointer-events: none; - } - } - - &-blur { - clear: both; - opacity: 0.5; - user-select: none; - pointer-events: none; - - &::after { - opacity: 0.4; - pointer-events: auto; - } - } - - // tip - // ------------------------------ - &-tip { - color: @spin-dot-default; - } - - // dots - // ------------------------------ - - &-dot { - position: relative; - display: inline-block; - font-size: @spin-dot-size; - - .square(1em); - - &-item { - position: absolute; - display: block; - width: 9px; - height: 9px; - background-color: @primary-color; - border-radius: 100%; - transform: scale(0.75); - transform-origin: 50% 50%; - opacity: 0.3; - animation: antSpinMove 1s infinite linear alternate; - - &:nth-child(1) { - top: 0; - left: 0; - } - - &:nth-child(2) { - top: 0; - right: 0; - animation-delay: 0.4s; - } - - &:nth-child(3) { - right: 0; - bottom: 0; - animation-delay: 0.8s; - } - - &:nth-child(4) { - bottom: 0; - left: 0; - animation-delay: 1.2s; - } - } - - &-spin { - transform: rotate(0deg); - animation: antRotate 1.2s infinite linear; - } - } - - // Sizes - // ------------------------------ - - // small - &-sm &-dot { - font-size: @spin-dot-size-sm; - - i { - width: 6px; - height: 6px; - } - } - - // large - &-lg &-dot { - font-size: @spin-dot-size-lg; - - i { - width: 14px; - height: 14px; - } - } - - &&-show-text &-text { - display: block; - } -} - -@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { - /* IE10+ */ - .@{spin-prefix-cls}-blur { - background: @component-background; - opacity: 0.5; - } -} - -@keyframes antSpinMove { - to { - opacity: 1; - } -} - -@keyframes antRotate { - to { - transform: rotate(360deg); - } -} - -@import './rtl'; diff --git a/components/spin/style/index.tsx b/components/spin/style/index.tsx deleted file mode 100644 index 3a3ab0de59..0000000000 --- a/components/spin/style/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import '../../style/index.less'; -import './index.less'; diff --git a/components/spin/style/rtl.less b/components/spin/style/rtl.less deleted file mode 100644 index 03fb9b257c..0000000000 --- a/components/spin/style/rtl.less +++ /dev/null @@ -1,20 +0,0 @@ -.@{spin-prefix-cls} { - &-rtl { - direction: rtl; - } - - &-dot { - &-spin { - .@{spin-prefix-cls}-rtl & { - transform: rotate(-45deg); - animation-name: antRotateRtl; - } - } - } -} - -@keyframes antRotateRtl { - to { - transform: rotate(-405deg); - } -} diff --git a/components/style.ts b/components/style.ts index 6bfc80e3c5..606436b77a 100644 --- a/components/style.ts +++ b/components/style.ts @@ -22,7 +22,7 @@ import './collapse/style'; import './carousel/style'; // import './notification/style'; // import './message/style'; -import './spin/style'; +// import './spin/style'; import './select/style'; import './switch/style'; import './auto-complete/style'; From 62ad42a9ded64e063d52e939e0f073190b2f5129 Mon Sep 17 00:00:00 2001 From: aibayanyu20 Date: Wed, 1 Feb 2023 16:02:29 +0800 Subject: [PATCH 5/6] refactor: spinnn --- components/spin/Spin.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/spin/Spin.tsx b/components/spin/Spin.tsx index 70222b180d..be5b2b88ce 100644 --- a/components/spin/Spin.tsx +++ b/components/spin/Spin.tsx @@ -2,7 +2,7 @@ import type { VNode, ExtractPropTypes, PropType } from 'vue'; import { cloneVNode, isVNode, defineComponent, shallowRef, watch } from 'vue'; import debounce from 'lodash-es/debounce'; import PropTypes from '../_util/vue-types'; -import { getPropsSlot } from '../_util/props-util'; +import { filterEmpty, getPropsSlot } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import useStyle from './style'; import useConfigInject from '../config-provider/hooks/useConfigInject'; @@ -44,7 +44,6 @@ export default defineComponent({ setup(props, { attrs, slots }) { const { prefixCls, size, direction } = useConfigInject('spin', props); const [wrapSSR, hashId] = useStyle(prefixCls); - const children = slots.default?.(); const sSpinning = shallowRef(props.spinning && shouldDelay(props.spinning, props.delay)); let updateSpinning: any; function originalUpdateSpinning() { @@ -77,6 +76,7 @@ export default defineComponent({ return () => { const { class: cls, ...divProps } = attrs; const { tip = slots.tip?.() } = props; + const children = slots.default?.(); const spinClassName = { [hashId.value]: true, [prefixCls.value]: true, @@ -121,7 +121,7 @@ export default defineComponent({ {tip ?
{tip}
: null} ); - if (children && children.length) { + if (children && filterEmpty(children).length) { const containerClassName = { [`${prefixCls.value}-container`]: true, [`${prefixCls.value}-blur`]: sSpinning.value, From ac222a34a4d57eca17513d2e3c9e09a7328855f5 Mon Sep 17 00:00:00 2001 From: aibayanyu20 Date: Wed, 1 Feb 2023 16:03:30 +0800 Subject: [PATCH 6/6] refactor: spin --- components/spin/Spin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/spin/Spin.tsx b/components/spin/Spin.tsx index be5b2b88ce..dd25059dd2 100644 --- a/components/spin/Spin.tsx +++ b/components/spin/Spin.tsx @@ -130,7 +130,7 @@ export default defineComponent({
{sSpinning.value &&
{spinElement}
}
- {slots?.default?.()} + {children}
, );