feat:Spinner组件功能升级 (#3427)

Co-authored-by: gongchengxiang <gongchengxiang@baidu.com>
This commit is contained in:
gongchengxiang 2022-03-18 20:58:04 +08:00 committed by GitHub
parent 28821fdb98
commit d961b42753
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 527 additions and 252 deletions

View File

@ -178,12 +178,6 @@ exports[`Renderer:Form initApi 1`] = `
style="display: none;"
type="submit"
/>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay"
/>
<div
class="cxd-Form-item cxd-Form-item--normal"
data-role="form-item"
@ -345,12 +339,6 @@ exports[`Renderer:Form sendOn:true 1`] = `
style="display: none;"
type="submit"
/>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay"
/>
<div
class="cxd-Form-item cxd-Form-item--normal"
data-role="form-item"
@ -865,12 +853,6 @@ exports[`Renderer:Form:remoteValidate 1`] = `
style="display: none;"
type="submit"
/>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay"
/>
<div
class="cxd-Form-item cxd-Form-item--normal is-error"
data-role="form-item"

View File

@ -104,11 +104,16 @@ exports[`Form:initData:remote 1`] = `
type="submit"
/>
<div
class="cxd-Spinner-overlay in"
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner in cxd-Spinner--overlay"
/>
class="cxd-Spinner cxd-Spinner--overlay"
>
<div
class="cxd-Spinner-icon cxd-Spinner-icon--default"
/>
</div>
<div
class="cxd-Form-item cxd-Form-item--normal"
data-role="form-item"
@ -253,12 +258,6 @@ exports[`Form:initData:remote 3`] = `
style="display: none;"
type="submit"
/>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay"
/>
<div
class="cxd-Form-item cxd-Form-item--normal"
data-role="form-item"

View File

@ -165,12 +165,6 @@ exports[`Renderer:service 1`] = `
</div>
</div>
</div>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
</div>
</form>
</div>

View File

@ -8,7 +8,12 @@ exports[`Renderer:markdown 1`] = `
<div>
<div
class="cxd-Spinner in"
/>
>
<div
class="cxd-Spinner-icon cxd-Spinner-icon--default"
/>
</div>
</div>
</div>
</div>

View File

@ -209,12 +209,6 @@ exports[`Renderer:Page handleAction actionType=ajax & feedback 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -357,12 +351,6 @@ exports[`Renderer:Page handleAction actionType=ajax 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -828,8 +816,13 @@ exports[`Renderer:Page handleAction actionType=drawer 1`] = `
class="cxd-Spinner-overlay in"
/>
<div
class="cxd-Spinner in cxd-Spinner--overlay cxd-Spinner--lg"
/>
class="cxd-Spinner cxd-Spinner--overlay in"
>
<div
class="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
/>
</div>
</div>
<div
class="cxd-Drawer-footer"
@ -1068,12 +1061,6 @@ exports[`Renderer:Page initApi 1`] = `
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
className="cxd-TplField"
>
@ -1101,12 +1088,6 @@ exports[`Renderer:Page initApi error show Message 1`] = `
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<div
className="cxd-Alert cxd-Alert--danger"
>
@ -1150,12 +1131,6 @@ exports[`Renderer:Page initApi reFetch when condition changes 1`] = `
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
className="cxd-TplField"
>
@ -1222,12 +1197,6 @@ exports[`Renderer:Page initApi reload by Dialog action 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -1442,12 +1411,6 @@ exports[`Renderer:Page initApi reload by Drawer action 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -1755,12 +1718,6 @@ exports[`Renderer:Page initApi reload by Form submit 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -1902,12 +1859,6 @@ exports[`Renderer:Page initApi reload by Form submit 2`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -1948,12 +1899,6 @@ exports[`Renderer:Page initApi reload by action 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -1994,12 +1939,6 @@ exports[`Renderer:Page initApi reload by action 2`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -2032,8 +1971,13 @@ exports[`Renderer:Page initApi show loading 1`] = `
className="cxd-Spinner-overlay in"
/>
<div
className="cxd-Spinner in cxd-Spinner--overlay cxd-Spinner--lg"
/>
className="cxd-Spinner cxd-Spinner--overlay in"
>
<div
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
/>
</div>
<span
className="cxd-TplField"
>
@ -2061,12 +2005,6 @@ exports[`Renderer:Page initApi show loading 2`] = `
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
className="cxd-TplField"
>
@ -2098,8 +2036,13 @@ exports[`Renderer:Page initApi silentPolling 1`] = `
className="cxd-Spinner-overlay in"
/>
<div
className="cxd-Spinner in cxd-Spinner--overlay cxd-Spinner--lg"
/>
className="cxd-Spinner cxd-Spinner--overlay in"
>
<div
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
/>
</div>
<span
className="cxd-TplField"
>
@ -2127,12 +2070,6 @@ exports[`Renderer:Page initApi silentPolling 2`] = `
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
className="cxd-TplField"
>
@ -2241,12 +2178,6 @@ exports[`Renderer:Page initFetchOn trigger initApi fetch when condition becomes
<div
className="cxd-Page-body"
>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
className="cxd-TplField"
>

View File

@ -1750,8 +1750,13 @@ exports[`Renderer:Wizard initApi show loading 1`] = `
className="cxd-Spinner-overlay in"
/>
<div
className="cxd-Spinner in cxd-Spinner--overlay cxd-Spinner--lg"
/>
className="cxd-Spinner cxd-Spinner--overlay in"
>
<div
className="cxd-Spinner-icon cxd-Spinner-icon--lg cxd-Spinner-icon--default"
/>
</div>
</div>
`;
@ -1939,12 +1944,6 @@ exports[`Renderer:Wizard initApi show loading 2`] = `
</button>
</div>
</div>
<div
className="cxd-Spinner-overlay"
/>
<div
className="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
</div>
`;

View File

@ -26,12 +26,6 @@ exports[`api:cache 1`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>
@ -72,12 +66,6 @@ exports[`api:cache 2`] = `
<div
class="cxd-Page-body"
>
<div
class="cxd-Spinner-overlay"
/>
<div
class="cxd-Spinner cxd-Spinner--overlay cxd-Spinner--lg"
/>
<span
class="cxd-TplField"
>

View File

@ -8,13 +8,191 @@ icon:
order: 64
---
一般用来做 `loading` 使用。
## 基本使用
`show` 属性控制 `spinner` 是否渲染。
```schema
{
"type": "page",
"body": {
"type": "spinner"
"type": "spinner",
"show": true,
}
}
```
`size` 属性控制 `spinner` 的大小,有三种配置:`sm`, `lg` 和 空值。
```schema
{
"type": "page",
"body": [
{
"type": "spinner",
"show": true,
"size": "sm",
"className": "mr-4"
},
{
"type": "spinner",
"show": true,
"size": "",
"className": "mr-4"
},
{
"type": "spinner",
"show": true,
"size": "lg"
}
]
}
```
`className` 属性、 `spinnerClassName`属性和 `spinnerWrapClassName`属性可以配置 spinner 自定义的 class`className`会添加到 spinner 组件的最外层标签上,`spinnerClassName`会添加到 icon 对应的标签上,`spinnerWrapClassName` 在作为组件容器使用时,会作用于整个 Spinner 组件的最外层元素上。
```schema
{
"type": "page",
"body": [
{
"type": "spinner",
"show": true,
"className": "my-spinner",
"spinnerClassName": "my-spinner-custom-icon",
"spinnerWrapClassName": "my-spinner-wrap",
}
]
}
```
`icon` 属性可以配置自定义的图标,可以是 `amis` 内置的图标名称(需要是在 icons.tsx 组件中注册过的); 可以是字体图标库的名称(需要引入对应的图标库),比如`fa fa-spinner`; 也可以是网络图片,比如 `/examples/static/logo.png`;
```schema
{
"type": "page",
"body": [
{
"type": "spinner",
"show": true,
"icon": "",
"className": "mr-4"
},
{
"type": "spinner",
"show": true,
"icon": "reload",
"className": "mr-4"
},
{
"type": "spinner",
"show": true,
"icon": "fa fa-spinner",
"className": "mr-4"
},
{
"type": "spinner",
"show": true,
"icon": "/examples/static/logo.png"
}
]
}
```
`tip` 属性可以配置 spinner 的文案,同时 `tipPlacement`可以配置 tip 的相对于 icon 的位置,有四种配置:`top`,`right`,`bottom`(默认),`left`;
```schema
{
"type": "page",
"body": [
{
"type": "spinner",
"show": true,
"tip": "加载中...",
"className": "mr-10"
},
{
"type": "spinner",
"show": true,
"tip": "加载中...",
"tipPlacement": "right",
"className": "mr-10"
},
{
"type": "spinner",
"show": true,
"tip": "加载中...",
"tipPlacement": "top",
"className": "mr-10"
},
{
"type": "spinner",
"show": true,
"tip": "加载中...",
"tipPlacement": "left"
}
]
}
```
`delay` 属性可以配置 spinner 的延迟显示时间,例如 delay=1000`show` 属性设为 `true`1000ms 后才会显示出来。
```schema
{
"type": "page",
"body": {
"type": "spinner",
"show": true,
"delay": 1000
}
}
```
## 作为容器使用
`spinner` 组件可以作为容器使用,被包裹的内容可通过 `body` 配置, 且作为容器使用的时候可以使用 `overlay` 属性来配置显示 spinner 的时候是否显示遮罩层(遮罩层背景颜色默认是透明的,可通过外层 className 自定义遮罩层颜色)。
```schema
{
"type": "page",
"body": {
"type": "spinner",
"show": true,
"overlay": true,
"body": {
"type": "form",
"body": [
{
"type": "input-text",
"name": "name",
"label": "姓名:"
},
{
"name": "email",
"type": "input-email",
"label": "邮箱:"
}
]
}
}
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| -------------------- | ----------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------- |
| type | `string` | `spinner` | 指定为 Spinner 渲染器 |
| show | `boolean` | `true` | 是否显示 spinner 组件 |
| className | `string` | | spinner 图标父级标签的自定义 class |
| spinnerClassName | `string` | | 组件中 icon 所在标签的自定义 class |
| spinnerWrapClassName | `string` | | 作为容器使用时组件最外层标签的自定义 class |
| size | `string` | | 组件大小 `sm` `lg` |
| icon | `string` | | 组件图标,可以是`amis`内置图标,也可以是字体图标或者网络图片链接,作为 ui 库使用时也可以是自定义组件 |
| tip | `string` | | 配置组件文案,例如`加载中...` |
| tipPlacement | `top`, `right`, `bottom`, `left` | `bottom` | 配置组件 `tip` 相对于 `icon` 的位置 |
| delay | `number` | `0` | 配置组件显示延迟的时间(毫秒) |
| overlay | `boolean` | `true` | 配置组件显示 spinner 时是否显示遮罩层 |
| body | [SchemaNode](../../docs/types/schemanode) | | 作为容器使用时,被包裹的内容 |

View File

@ -1238,15 +1238,16 @@
--Sparkline-line-color: var(--info);
--Sparkline-area-color: #{rgba($info, 0.1)};
--Spinner--lg-height: #{px2rem(50px)};
--Spinner--lg-width: #{px2rem(50px)};
--Spinner--lg-height: #{px2rem(48px)};
--Spinner--lg-width: #{px2rem(48px)};
--Spinner--sm-height: #{px2rem(16px)};
--Spinner--sm-width: #{px2rem(16px)};
// 修改自 https://github.com/SamHerbert/SVG-Loaders/blob/master/svg-loaders/tail-spin.svg
--Spinner-bg: url("data:image/svg+xml,%0A%3Csvg width='38' height='38' viewBox='0 0 38 38' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs%3E%3ClinearGradient x1='8.042%25' y1='0%25' x2='65.682%25' y2='23.865%25' id='a'%3E%3Cstop stop-color='%23eee' stop-opacity='0' offset='0%25'/%3E%3Cstop stop-color='%23999' stop-opacity='.631' offset='63.146%25'/%3E%3Cstop stop-color='%23666' offset='100%25'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg transform='translate(1 1)'%3E%3Cpath d='M36 18c0-9.94-8.06-18-18-18' id='Oval-2' stroke='url(%23a)' stroke-width='2'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 18 18' to='360 18 18' dur='0.9s' repeatCount='indefinite' /%3E%3C/path%3E%3Ccircle fill='%23eee' cx='36' cy='18' r='1'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 18 18' to='360 18 18' dur='0.9s' repeatCount='indefinite' /%3E%3C/circle%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A");
--Spinner-height: #{px2rem(26px)};
--Spinner-height: #{px2rem(32px)};
--Spinner-overlay-bg: rgba(255, 255, 255, 0.4);
--Spinner-width: #{px2rem(26px)};
--Spinner-width: #{px2rem(32px)};
--Spinner-color: #2468f2;
--Switch-bgColor: #{$gray600};
--Switch-borderColor: #{darken($gray600, 10%)};

View File

@ -18,8 +18,130 @@
}
}
.is-spin {
animation: spin 2s linear infinite;
.#{$ns}Spinner {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
opacity: 0;
transition: ease-out opacity var(--animation-duration);
&.in {
opacity: 1;
}
.#{$ns}Spinner-icon {
width: var(--Spinner-width);
height: var(--Spinner-height);
position: relative;
transform: translateZ(0);
animation: spin 2s linear infinite;
transition: ease-out all var(--animation-duration);
&--default {
background: var(--Spinner-bg);
background-size: 100%;
animation: none;
}
&--simple {
background: transparent;
display: flex;
align-items: center;
justify-content: center;
.icon {
width: 100%;
height: 100%;
color: var(--Spinner-color);
}
svg.icon {
top: 0;
path {
fill: var(--Spinner-color);
}
}
i.icon {
display: flex;
align-items: center;
justify-content: center;
font-size: var(--Spinner-width);
}
}
&--custom {
width: auto;
height: auto;
}
&--lg {
width: var(--Spinner--lg-width);
height: var(--Spinner--lg-height);
i.icon {
font-size: var(--Spinner--lg-height);
}
}
&--sm {
width: var(--Spinner--sm-width);
height: var(--Spinner--sm-height);
i.icon {
font-size: var(--Spinner--sm-height);
}
}
}
.#{$ns}Spinner-tip {
margin: px2rem(12px) 0 0 0;
word-break: keep-all;
white-space: nowrap;
color: var(--Spinner-color);
}
&.#{$ns}Spinner-tip--top {
flex-direction: column-reverse;
.#{$ns}Spinner-tip {
margin: 0 0 px2rem(12px) 0;
}
}
&.#{$ns}Spinner-tip--right {
flex-direction: row;
.#{$ns}Spinner-tip {
margin: 0 0 0 px2rem(12px);
}
}
&.#{$ns}Spinner-tip--bottom {
flex-direction: column;
.#{$ns}Spinner-tip {
margin: px2rem(12px) 0 0 0;
}
}
&.#{$ns}Spinner-tip--left {
flex-direction: row-reverse;
.#{$ns}Spinner-tip {
margin: 0 px2rem(12px) 0 0;
}
}
&--overlay {
position: absolute;
z-index: 11;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
}
.#{$ns}Spinner-overlay {
@ -31,7 +153,6 @@
bottom: 0;
background: var(--Spinner-overlay-bg);
transition: ease-out opacity var(--animation-duration);
opacity: 0;
&.in {
@ -39,60 +160,17 @@
}
}
.#{$ns}Spinner {
width: var(--Spinner-width);
height: var(--Spinner-height);
transform: translateZ(0);
display: inline-flex;
background: var(--Spinner-bg);
background-size: 100%;
// spinner渲染器scss
.#{$ns}Spinner-wrap {
position: relative;
width: 100%;
height: 100%;
&--lg {
width: var(--Spinner--lg-width);
height: var(--Spinner--lg-height);
}
&--sm {
width: var(--Spinner--sm-width);
height: var(--Spinner--sm-height);
}
transition: ease-out all var(--animation-duration);
&--icon {
background: transparent;
animation: spin 2s linear infinite;
width: 16px; // reload 那个 icon 必须用这个高宽
height: 16px;
svg.icon {
width: 16px;
height: 16px;
top: 0;
}
}
}
// 当启用 overlay 的时候应该是居中模式
.#{$ns}Spinner--overlay {
position: absolute;
z-index: 11;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
.#{$ns}Spinner--overlay.#{$ns}Spinner--lg {
width: var(--Spinner--lg-width);
height: var(--Spinner--lg-height);
line-height: var(--Spinner--lg-height);
}
@include media-breakpoint-up(md) {
.#{$ns}Layout .#{$ns}Page-body > .#{$ns}Spinner-overlay {
left: var(--Layout-aside-width);
}
.#{$ns}Layout--folded .#{$ns}Page-body > .#{$ns}Spinner-overlay {
left: var(--Layout-aside--folded-width);
.#{$ns}Spinner {
position: absolute;
z-index: 11;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
}

View File

@ -58,6 +58,7 @@ import {PaginationSchema} from './renderers/Pagination';
import {AnchorNavSchema} from './renderers/AnchorNav';
import {AvatarSchema} from './renderers/Avatar';
import {StepsSchema} from './renderers/Steps';
import {SpinnerSchema} from './renderers/Spinner';
import {TimelineSchema} from './renderers/Timeline';
import {ArrayControlSchema} from './renderers/Form/InputArray';
import {ButtonGroupControlSchema} from './renderers/Form/ButtonGroupSelect';
@ -150,6 +151,7 @@ export type SchemaType =
| 'month'
| 'static-month' // 这个几个跟表单项同名再form下面用必须带前缀 static-
| 'dialog'
| 'spinner'
| 'divider'
| 'dropdown-button'
| 'drawer'
@ -384,6 +386,7 @@ export type SchemaObject =
| ServiceSchema
| SparkLineSchema
| StatusSchema
| SpinnerSchema
| TableSchema
| TabsSchema
| TasksSchema

View File

@ -7,77 +7,112 @@
import React from 'react';
import {themeable, ThemeProps} from '../theme';
import Transition, {ENTERED, ENTERING} from 'react-transition-group/Transition';
import {Icon} from './icons';
import Transition, {ENTERED} from 'react-transition-group/Transition';
import {Icon, hasIcon} from './icons';
import {generateIcon} from '../utils/icon';
const fadeStyles: {
[propName: string]: string;
} = {
[ENTERING]: 'in',
[ENTERED]: 'in'
};
interface SpinnerProps extends ThemeProps {
overlay: boolean;
spinnerClassName: string;
mode: string;
size: 'sm' | 'lg' | '';
show: boolean;
icon?: string;
// Spinner Props
export interface SpinnerProps extends ThemeProps {
show: boolean; // 控制Spinner显示与隐藏
className?: string; // 自定义最外层元素class
spinnerClassName?: string; // spin图标位置包裹元素的自定义class
/**
* @deprecated
*/
mode?: string;
size?: 'sm' | 'lg' | ''; // spinner Icon 大小
icon?: string | React.ReactNode; // 自定义icon
tip?: string; // spinner文案
tipPlacement?: 'top' | 'right' | 'bottom' | 'left'; // spinner文案位置
delay?: number; // 延迟显示
overlay?: boolean; // 是否显示遮罩层有children属性才生效
}
export class Spinner extends React.Component<SpinnerProps, object> {
export class Spinner extends React.Component<SpinnerProps> {
static defaultProps = {
overlay: false,
show: true,
className: '',
spinnerClassName: '',
mode: '',
size: '' as '',
show: true
icon: '',
tip: '',
tipPlacement: 'bottom' as 'bottom',
delay: 0,
overlay: false
};
div: React.RefObject<HTMLDivElement> = React.createRef();
overlay: React.RefObject<HTMLDivElement> = React.createRef();
render() {
const {
show,
classnames: cx,
show,
className,
spinnerClassName,
mode,
size,
size = '',
overlay,
icon
delay,
icon,
tip,
tipPlacement = ''
} = this.props;
return (
<Transition mountOnEnter unmountOnExit in={show} timeout={350}>
{(status: string) => {
if (status === ENTERING) {
// force reflow
// 由于从 mount 进来到加上 in 这个 class 估计是时间太短上次的样式还没应用进去所以这里强制reflow一把。
// 否则看不到动画。
// this.div.current!.offsetWidth;
this.overlay.current && this.overlay.current.offsetWidth;
}
const isCustomIcon = icon && React.isValidElement(icon);
const timeout = {enter: delay, exit: 0};
return (
<Transition mountOnEnter unmountOnExit in={show} timeout={timeout}>
{(status: string) => {
return (
<>
{/* 遮罩层 */}
{overlay ? (
<div
ref={this.overlay}
className={cx(`Spinner-overlay`, fadeStyles[status])}
/>
<div className={cx(`Spinner-overlay`, fadeStyles[status])} />
) : null}
{/* spinner图标和文案 */}
<div
ref={this.div}
className={cx(`Spinner`, spinnerClassName, fadeStyles[status], {
[`Spinner--${mode}`]: mode,
[`Spinner--overlay`]: overlay,
[`Spinner--${size}`]: size,
[`Spinner--icon`]: icon
})}
className={cx(
`Spinner`,
tip && {
[`Spinner-tip--${tipPlacement}`]: [
'top',
'right',
'bottom',
'left'
].includes(tipPlacement)
},
{[`Spinner--overlay`]: overlay},
fadeStyles[status],
className
)}
>
{icon ? <Icon icon={icon} className="icon" /> : null}
<div
className={cx(
`Spinner-icon`,
{
[`Spinner-icon--${size}`]: ['lg', 'sm'].includes(size),
[`Spinner-icon--default`]: !icon,
[`Spinner-icon--simple`]: !isCustomIcon && icon,
[`Spinner-icon--custom`]: isCustomIcon
},
spinnerClassName
)}
>
{icon ? (
isCustomIcon ? (
icon
) : hasIcon(icon as string) ? (
<Icon icon={icon} className="icon" />
) : (
generateIcon(cx, icon as string, 'icon')
)
) : null}
</div>
{tip ? <span className={cx(`Spinner-tip`)}>{tip}</span> : ''}
</div>
</>
);

View File

@ -1,14 +1,96 @@
import Spinner from '../components/Spinner';
import {Renderer, RendererProps} from '../factory';
import React from 'react';
import {BaseSchema, SchemaCollection} from '../Schema';
interface SpinnerProps extends RendererProps {}
export interface SpinnerSchema extends BaseSchema {
/**
*
*/
type: 'spinner';
/**
* Spinner显示与隐藏
*/
show: boolean;
/**
* spinner的class
*/
className?: string;
/**
* spin图标位置包裹元素的自定义class
*/
spinnerClassName?: string;
/**
* 使class
*/
spinnerWrapClassName?: string;
/**
* @deprecated
*/
mode?: string;
/**
* spinner Icon
*/
size?: 'sm' | 'lg' | '';
/**
* icon
*/
icon?: string;
/**
* spinner文案
*/
tip?: string;
/**
* spinner文案位置
*/
tipPlacement?: 'top' | 'right' | 'bottom' | 'left';
/**
*
*/
delay?: number;
/**
*
*/
overlay?: boolean;
/**
* 使
*/
body?: SchemaCollection;
}
interface SpinnerRenderProps
extends RendererProps,
Omit<SpinnerSchema, 'className'> {}
@Renderer({
type: 'spinner'
})
export class SpinnerRenderer extends React.Component<SpinnerProps> {
export class SpinnerRenderer extends React.Component<SpinnerRenderProps> {
renderBody() {
const {body, render} = this.props;
return body ? render('body', body) : null;
}
render() {
return <Spinner {...this.props} />;
const {classnames: cx, spinnerWrapClassName, body, ...rest} = this.props;
return body ? (
<div className={cx(`Spinner-wrap`, spinnerWrapClassName)}>
<Spinner {...rest}></Spinner>
{this.renderBody()}
</div>
) : (
<Spinner {...rest}></Spinner>
);
}
}