Merge branch 'feature-3.9.0' of https://github.com/ant-design/ant-design into feature/svg-icon

# Conflicts:
#	package.json
This commit is contained in:
HeskeyBaozi 2018-08-25 13:24:44 +08:00
commit d9b38aee4b
53 changed files with 1783 additions and 369 deletions

View File

@ -41,6 +41,7 @@ Array [
"Rate",
"Row",
"Select",
"Skeleton",
"Slider",
"Spin",
"Steps",

View File

@ -7,6 +7,8 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
cancel: () => void;
};
private extraNode: HTMLDivElement;
private clickWaveTimeoutId: number;
private styleForPesudo: HTMLStyleElement | null;
isNotGrey(color: string) {
@ -17,22 +19,17 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
return true;
}
onClick = (node: HTMLElement) => {
onClick = (node: HTMLElement, waveColor: string) => {
if (node.className.indexOf('-leave') >= 0) {
return;
}
this.removeExtraStyleNode();
const { insertExtraNode } = this.props;
const extraNode = document.createElement('div');
this.extraNode = document.createElement('div');
const extraNode = this.extraNode;
extraNode.className = 'ant-click-animating-node';
const attributeName = insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
const attributeName = this.getAttributeName();
node.removeAttribute(attributeName);
node.setAttribute(attributeName, 'true');
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
// Not white or transparnt or grey
if (waveColor &&
waveColor !== '#ffffff' &&
@ -49,19 +46,13 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
if (insertExtraNode) {
node.appendChild(extraNode);
}
const transitionEnd = () => {
node.removeAttribute(attributeName);
this.removeExtraStyleNode();
if (insertExtraNode) {
node.removeChild(extraNode);
}
TransitionEvents.removeEndEventListener(node, transitionEnd);
};
TransitionEvents.addEndEventListener(node, transitionEnd);
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
}
bindAnimationEvent = (node: HTMLElement) => {
if (node.getAttribute('disabled') ||
if (!node ||
!node.getAttribute ||
node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0) {
return;
}
@ -70,7 +61,13 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
if ((e.target as HTMLElement).tagName === 'INPUT') {
return;
}
setTimeout(() => this.onClick(node), 0);
this.resetEffect(node);
// Get wave color from target
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
this.clickWaveTimeoutId = window.setTimeout(() => this.onClick(node, waveColor), 0);
};
node.addEventListener('click', onClick, true);
return {
@ -80,6 +77,29 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
};
}
getAttributeName() {
const { insertExtraNode } = this.props;
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
}
resetEffect(node: HTMLElement) {
const { insertExtraNode } = this.props;
const attributeName = this.getAttributeName();
node.removeAttribute(attributeName);
this.removeExtraStyleNode();
if (insertExtraNode) {
node.removeChild(this.extraNode);
}
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
}
onTransitionEnd = (e: AnimationEvent) => {
if (!e || e.animationName !== 'fadeEffect') {
return;
}
this.resetEffect(e.target as HTMLElement);
}
removeExtraStyleNode() {
if (this.styleForPesudo && document.body.contains(this.styleForPesudo)) {
document.body.removeChild(this.styleForPesudo);
@ -95,6 +115,9 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
if (this.instance) {
this.instance.cancel();
}
if (this.clickWaveTimeoutId) {
clearTimeout(this.clickWaveTimeoutId);
}
}
render() {

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { render, mount } from 'enzyme';
import renderer from 'react-test-renderer';
import Button from '..';
import Icon from '../../icon';
@ -11,6 +12,16 @@ describe('Button', () => {
expect(wrapper).toMatchSnapshot();
});
it('mount correctly', () => {
const wrapper = mount(
<Button>Follow</Button>
);
if (process.env.REACT === '15') {
return;
}
expect(() => renderer.create(wrapper).toJSON()).not.toThrow();
});
it('renders Chinese characters correctly', () => {
const wrapper = render(
<Button>按钮</Button>

View File

@ -7,7 +7,7 @@ title:
## zh-CN
幽灵按钮将其他按钮的内容反色,背景变为透明,常用在有色背景上。
幽灵按钮将按钮的内容反色,背景变为透明,常用在有色背景上。
## en-US

View File

@ -372,174 +372,83 @@ exports[`renders ./components/card/demo/inner.md correctly 1`] = `
exports[`renders ./components/card/demo/loading.md correctly 1`] = `
<div>
<div
class="ant-card ant-card-loading ant-card-bordered"
<span
class="ant-switch"
tabindex="0"
>
<span
class="ant-switch-inner"
/>
</span>
<div
class="ant-card ant-card-bordered"
style="width:300px;margin-top:16px"
>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div
class="ant-card-loading-content"
class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active"
>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
class="ant-skeleton-header"
>
<div
class="ant-col-22"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
class="ant-skeleton-content"
>
<div
class="ant-col-8"
style="padding-left:4px;padding-right:4px"
<h3
class="ant-skeleton-title"
style="width:50%"
/>
<ul
class="ant-skeleton-paragraph"
>
<div
class="ant-card-loading-block"
<li
style="width:100%"
/>
</div>
<div
class="ant-col-15"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
<li
style="width:100%"
/>
</div>
</div>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
>
<div
class="ant-col-6"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-18"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
</div>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
>
<div
class="ant-col-13"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-9"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
</div>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
>
<div
class="ant-col-4"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-3"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-16"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
</div>
<div
class="ant-row"
style="margin-left:-4px;margin-right:-4px"
>
<div
class="ant-col-8"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-6"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
<div
class="ant-col-8"
style="padding-left:4px;padding-right:4px"
>
<div
class="ant-card-loading-block"
/>
</div>
</ul>
</div>
</div>
</div>
<ul
class="ant-card-actions"
>
<li
style="width:33.333333333333336%"
>
<span>
<i
class="anticon anticon-setting"
/>
</span>
</li>
<li
style="width:33.333333333333336%"
>
<span>
<i
class="anticon anticon-edit"
/>
</span>
</li>
<li
style="width:33.333333333333336%"
>
<span>
<i
class="anticon anticon-ellipsis"
/>
</span>
</li>
</ul>
</div>
<button
class="ant-btn"
style="margin-top:16px"
type="button"
>
<span>
Toggle loading
</span>
</button>
</div>
`;

View File

@ -14,30 +14,42 @@ title:
Shows a loading indicator while the contents of the card is being fetched.
````jsx
import { Card, Button } from 'antd';
import { Skeleton, Switch, Card, Icon, Avatar } from 'antd';
class LoadingCard extends React.Component {
const { Meta } = Card;
class App extends React.Component {
state = {
loading: true,
}
handleClick = () => {
this.setState({ loading: !this.state.loading });
onChange = (checked) => {
this.setState({ loading: !checked });
}
render() {
const { loading } = this.state;
return (
<div>
<Card loading={this.state.loading} title="Card title">
Whatever content
<Switch checked={!loading} onChange={this.onChange} />
<Card
style={{ width: 300, marginTop: 16 }}
actions={[<Icon type="setting" />, <Icon type="edit" />, <Icon type="ellipsis" />]}
>
<Skeleton loading={loading} avatar active>
<Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title="Card title"
description="This is the description"
/>
</Skeleton>
</Card>
<Button onClick={this.handleClick} style={{ marginTop: 16 }}>Toggle loading</Button>
</div>
);
}
}
ReactDOM.render(
<LoadingCard />,
mountNode);
ReactDOM.render(<App />, mountNode);
````

View File

@ -87,7 +87,7 @@ class WeekPicker extends React.Component<any, any> {
const {
prefixCls, className, disabled, pickerClass, popupStyle,
pickerInputClass, format, allowClear, locale, localeCode, disabledDate,
style, onFocus, onBlur,
style, onFocus, onBlur, id,
} = this.props;
const pickerValue = this.state.value;
@ -129,7 +129,6 @@ class WeekPicker extends React.Component<any, any> {
className={pickerInputClass}
onFocus={onFocus}
onBlur={onBlur}
style={style}
/>
{clearIcon}
<Icon type="calendar" className={`${prefixCls}-picker-icon`}/>
@ -137,7 +136,11 @@ class WeekPicker extends React.Component<any, any> {
);
};
return (
<span className={classNames(className, pickerClass)} id={this.props.id}>
<span
className={classNames(className, pickerClass)}
style={style}
id={id}
>
<RcDatePicker
{...this.props}
calendar={calendar}

View File

@ -3,13 +3,13 @@
exports[`WeekPicker should support style prop 1`] = `
<span
class="ant-calendar-picker"
style="width: 400px;"
>
<span>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
style="width: 400px;"
value=""
/>
<i

View File

@ -16,6 +16,20 @@ describe('Drawer', () => {
expect(wrapper).toMatchSnapshot();
});
it('render top drawer', () => {
const wrapper = render(
<Drawer
visible
height={400}
placement="top"
getContainer={false}
>
Here is content of Drawer
</Drawer>
);
expect(wrapper).toMatchSnapshot();
});
it('have a title', () => {
const wrapper = render(
<Drawer

View File

@ -42,7 +42,6 @@ class MultiDrawer extends React.Component {
title="Multi-level drawer"
className="test_drawer"
width={520}
closable={false}
onClose={this.onClose}
getContainer={false}
placement={placement}
@ -54,7 +53,7 @@ class MultiDrawer extends React.Component {
<Drawer
title="Two-level Drawer"
width={320}
closable={false}
className="Two-level"
getContainer={false}
placement={placement}
onClose={this.onChildrenDrawerClose}
@ -105,12 +104,23 @@ describe('Drawer', () => {
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
});
it('render right MultiDrawer', () => {
it('render left MultiDrawer', () => {
const wrapper = mount(<MultiDrawer placement="left" />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
expect(translateX).toEqual('translateX(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
wrapper.find('.Two-level .ant-drawer-close').simulate('click');
expect(wrapper.state().childrenDrawer).toBe(false);
});
it('render left MultiDrawer', () => {
const wrapper = mount(<MultiDrawer placement="top" />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
const translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
expect(translateX).toEqual('translateY(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
});
});

View File

@ -277,3 +277,43 @@ exports[`Drawer render correctly 1`] = `
</div>
</div>
`;
exports[`Drawer render top drawer 1`] = `
<div
class=""
>
<div
class="ant-drawer ant-drawer-top"
>
<div
class="ant-drawer-mask"
/>
<div
class="ant-drawer-content-wrapper"
style="transform:translateY(-100%);-ms-transform:translateY(-100%);height:400px"
>
<div
class="ant-drawer-content"
>
<div
class="ant-drawer-wrapper-body"
>
<button
aria-label="Close"
class="ant-drawer-close"
>
<span
class="ant-drawer-close-x"
/>
</button>
<div
class="ant-drawer-body"
>
Here is content of Drawer
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -1,57 +0,0 @@
---
order: 1
title:
zh-CN: 左侧滑出
en-US: Left Slider
---
## zh-CN
基础抽屉,点击触发按钮抽屉从左滑出,点击遮罩区关闭
## en-US
Basic drawer.
```jsx
import { Drawer, Button } from 'antd';
class App extends React.Component {
state = { visible: false };
showDrawer = () => {
this.setState({
visible: true,
});
};
onClose = () => {
this.setState({
visible: false,
});
};
render() {
return (
<div>
<Button type="primary" onClick={this.showDrawer}>
Open
</Button>
<Drawer
title="Basic Drawer"
placement="left"
closable={false}
onClose={this.onClose}
visible={this.state.visible}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
```

View File

@ -0,0 +1,75 @@
---
order: 1
title:
zh-CN: 自定义位置
en-US: Custom Placement
---
## zh-CN
自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭
## en-US
Basic drawer.
```jsx
import { Drawer, Button, Radio } from 'antd';
const RadioGroup = Radio.Group;
class App extends React.Component {
state = { visible: false, placement: 'left' };
showDrawer = () => {
this.setState({
visible: true,
});
};
onClose = () => {
this.setState({
visible: false,
});
};
onChange = (e) => {
this.setState({
placement: e.target.value,
});
}
render() {
return (
<div>
<RadioGroup
style={{ marginRight: 8 }}
defaultValue={this.state.placement}
onChange={this.onChange}
>
<Radio value="top">top</Radio>
<Radio value="right">right</Radio>
<Radio value="bottom">bottom</Radio>
<Radio value="left">left</Radio>
</RadioGroup>
<Button type="primary" onClick={this.showDrawer}>
Open
</Button>
<Drawer
title="Basic Drawer"
placement={this.state.placement}
closable={false}
onClose={this.onClose}
visible={this.state.visible}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
```

View File

@ -27,9 +27,10 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
| title | The title for Drawer. | string\|ReactNode | - |
| visible | Whether the Drawer dialog is visible or not. | boolean | false |
| width | Width of the Drawer dialog. | string\|number | 256 |
| height | placement is `top` or `bottom`, height of the Drawer dialog. | string\|number | - |
| className | The class name of the container of the Drawer dialog. | string | - |
| zIndex | The `z-index` of the Drawer. | Number | 1000 |
| placement | The placement of the Drawer. | 'left' \| 'right' | 'right'
| placement | The placement of the Drawer. | 'top' \| 'right' \| 'bottom' \| 'left' | 'right' |
| onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button. | function(e) | - |
<style>

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import RcDrawer from 'rc-drawer';
import * as PropTypes from 'prop-types';
import RcDrawer from 'rc-drawer';
import createReactContext, { Context } from 'create-react-context';
import warning from 'warning';
import classNames from 'classnames';
@ -14,6 +14,7 @@ type EventType =
type getContainerfunc = () => HTMLElement;
type placementType = 'top' | 'right' | 'bottom' | 'left';
export interface DrawerProps {
closable?: boolean;
destroyOnClose?: boolean;
@ -25,12 +26,13 @@ export interface DrawerProps {
title?: React.ReactNode;
visible?: boolean;
width?: number | string;
height?: number | string;
/* deprecated, use className instead */
wrapClassName?: string;
zIndex?: number;
prefixCls?: string;
push?: boolean;
placement?: 'left' | 'right';
placement?: placementType;
onClose?: (e: EventType) => void;
className?: string;
}
@ -66,6 +68,7 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
static defaultProps = {
prefixCls: 'ant-drawer',
width: 256,
height: 256,
closable: true,
placement: 'right',
maskClosable: true,
@ -76,14 +79,14 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
push: false,
};
praentDrawer: Drawer;
parentDrawer: Drawer;
destoryClose: boolean;
public componentDidUpdate(preProps: DrawerProps) {
if (preProps.visible !== this.props.visible && this.praentDrawer) {
if (preProps.visible !== this.props.visible && this.parentDrawer) {
if (this.props.visible) {
this.praentDrawer.push();
this.parentDrawer.push();
} else {
this.praentDrawer.pull();
this.parentDrawer.pull();
}
}
}
@ -124,6 +127,16 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
getDestoryOnClose = () => (this.props.destroyOnClose && !this.props.visible);
// get drawar push width or height
getPushTransform = (placement?: placementType) => {
if (placement === 'left' || placement === 'right') {
return `translateX(${placement === 'left' ? 180 : -180}px)`;
}
if (placement === 'top' || placement === 'bottom') {
return `translateY(${placement === 'top' ? 180 : -180}px)`;
}
}
// render drawer body dom
renderBody = () => {
if (this.destoryClose && !this.props.visible) {
return null;
@ -138,12 +151,15 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
} : {};
const isDestroyOnClose = this.getDestoryOnClose();
if (isDestroyOnClose) {
// Increase the opacity transition, delete children after closing.
containerStyle.opacity = 0;
containerStyle.transition = 'opacity .3s';
}
const { prefixCls, title, closable } = this.props;
// is have header dom
let header;
if (title) {
header = (
@ -152,6 +168,7 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
</div>
);
}
// is have closer button
let closer;
if (closable) {
closer = (
@ -181,26 +198,40 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
</div>
);
}
getRcDrawerStyle = () => {
const { zIndex, placement } = this.props;
return this.state.push
? {
zIndex,
transform: this.getPushTransform(placement),
}
: { zIndex };
}
// render Provider for Multi-level drawe
renderProvider = (value: Drawer) => {
let { zIndex, style, placement, className, wrapClassName, ...rest } = this.props;
let { zIndex, style, placement, className, wrapClassName, width, height, ...rest } = this.props;
warning(wrapClassName === undefined, 'wrapClassName is deprecated, please use className instead.');
const RcDrawerStyle = this.state.push
? {
zIndex,
transform: `translateX(${placement === 'left' ? 180 : -180}px)`,
}
: { zIndex };
this.praentDrawer = value;
this.parentDrawer = value;
const offsetStyle: any = {};
if (placement === 'left' || placement === 'right') {
offsetStyle.width = width;
} else {
offsetStyle.height = height;
}
return (
<DrawerContext.Provider value={this}>
<RcDrawer
{...rest}
handler={false}
{...rest}
{...offsetStyle}
open={this.props.visible}
onMaskClick={this.onMaskClick}
showMask={this.props.mask}
placement={placement}
style={RcDrawerStyle}
style={this.getRcDrawerStyle()}
className={classNames(wrapClassName, className)}
>
{this.renderBody()}
@ -208,6 +239,7 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
</DrawerContext.Provider>
);
}
render() {
return (
<DrawerContext.Consumer>{this.renderProvider}</DrawerContext.Consumer>

View File

@ -28,9 +28,10 @@ title: Drawer
| title | 标题 | string \| ReactNode | - |
| visible | Drawer 是否可见 | boolean | - |
| width | 宽度 | string \| number | 256 |
| height | 高度, 在 `placement``top``bottom` 时使用 | string \| number | 256 |
| className | 对话框外层容器的类名 | string | - |
| zIndex | 设置 Drawer 的 `z-index` | Number | 1000 |
| placement | 抽屉的方向 | 'left' \| 'right' | 'right'
| placement | 抽屉的方向 | 'top' \| 'right' \| 'bottom' \| 'left' | 'right'
| onClose | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | 无 |
<style>

View File

@ -6,8 +6,8 @@
position: fixed;
top: 0;
width: 0%;
height: 100%;
z-index: @zindex-modal;
transition: transform @animation-duration-slow @ease-base-in;
> * {
transition: transform @animation-duration-slow @ease-base-in;
}
@ -18,8 +18,6 @@
.@{dawer-prefix-cls}-content {
width: 100%;
height: 100%;
opacity: 0;
transition: opacity @animation-duration-slow linear;
}
&-left,
&-right {
@ -54,10 +52,14 @@
&-top,
&-bottom {
.@{dawer-prefix-cls}-content-wrapper,
.@{dawer-prefix-cls}-content {
width: 100%;
height: 0%;
.@{dawer-prefix-cls}-content-wrapper {
width: 100%;
}
&.@{dawer-prefix-cls}-open {
height: 100%;
}
}
&-top {
&.@{dawer-prefix-cls}-open {
@ -81,13 +83,10 @@
&.@{dawer-prefix-cls}-open {
.@{dawer-prefix-cls} {
&-content {
opacity: 1;
}
&-mask {
opacity: 0.3;
height: 100%;
animation: antdDrawerFadeIn @animation-duration-slow @ease-base-out;
animation: antdDrawerFadeIn @animation-duration-slow @ease-base-out;
transition: none;
}
}
@ -170,9 +169,6 @@
}
&-open {
transition: transform @animation-duration-slow @ease-base-out;
> * {
transition: transform @animation-duration-slow @ease-base-out;
}
&-content {
box-shadow: @shadow-2;
}

View File

@ -117,8 +117,8 @@ export type WrappedFormUtils = {
isFieldsTouched(names?: Array<string>): boolean;
/** 重置一组输入控件的值与状态,如不传入参数,则重置所有组件 */
resetFields(names?: Array<string>): void;
getFieldDecorator(id: string, options?: GetFieldDecoratorOptions): (node: React.ReactNode) => React.ReactNode;
// tslint:disable-next-line:max-line-length
getFieldDecorator<T extends Object = {}>(id: keyof T, options?: GetFieldDecoratorOptions): (node: React.ReactNode) => React.ReactNode;
};
export interface FormComponentProps {

View File

@ -91,6 +91,8 @@ export { default as Row } from './row';
export { default as Select } from './select';
export { default as Skeleton } from './skeleton';
export { default as Slider } from './slider';
export { default as Spin } from './spin';

View File

@ -99,7 +99,6 @@ The sidebar.
| trigger | specify the customized trigger, set to null to hide the trigger | string\|ReactNode | - |
| width | width of the sidebar | number\|string | 200 |
| onCollapse | the callback function, executed by clicking the trigger or activating the responsive layout | (collapsed, type) => {} | - |
| theme | color theme of the sidebar | string: `light` `dark` | `dark` |
| onBreakpoint | the callback function, executed when [breakpoints](/components/grid#api) changed | (broken) => {} | - |
#### breakpoint width

View File

@ -365,7 +365,7 @@ exports[`renders ./components/list/demo/infinite-virtualized-load.md correctly 1
exports[`renders ./components/list/demo/loadmore.md correctly 1`] = `
<div
class="ant-list demo-loadmore-list ant-list-split ant-list-loading ant-list-something-after-last-item"
class="ant-list demo-loadmore-list ant-list-split ant-list-loading"
>
<div
class="ant-spin-nested-loading"
@ -392,18 +392,6 @@ exports[`renders ./components/list/demo/loadmore.md correctly 1`] = `
/>
</div>
</div>
<div
style="text-align:center;margin-top:12px;height:32px;line-height:32px"
>
<button
class="ant-btn"
type="button"
>
<span>
loading more
</span>
</button>
</div>
</div>
`;

View File

@ -14,25 +14,27 @@ title:
Load more list with `loadMore` property.
````jsx
import { List, Avatar, Button, Spin } from 'antd';
import { List, Avatar, Button, Skeleton } from 'antd';
import reqwest from 'reqwest';
const fakeDataUrl = 'https://randomuser.me/api/?results=5&inc=name,gender,email,nat&noinfo';
const count = 3;
const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat&noinfo`;
class LoadMoreList extends React.Component {
state = {
loading: true,
loadingMore: false,
showLoadingMore: true,
initLoading: true,
loading: false,
data: [],
list: [],
}
componentDidMount() {
this.getData((res) => {
this.setState({
loading: false,
initLoading: false,
data: res.results,
list: res.results,
});
});
}
@ -51,13 +53,15 @@ class LoadMoreList extends React.Component {
onLoadMore = () => {
this.setState({
loadingMore: true,
loading: true,
list: this.state.data.concat([...new Array(count)].map(() => ({ loading: true, name: {} }))),
});
this.getData((res) => {
const data = this.state.data.concat(res.results);
this.setState({
data,
loadingMore: false,
list: data,
loading: false,
}, () => {
// Resetting window's offsetTop so as to display react-virtualized demo underfloor.
// In real scene, you can using public method of react-virtualized:
@ -68,28 +72,30 @@ class LoadMoreList extends React.Component {
}
render() {
const { loading, loadingMore, showLoadingMore, data } = this.state;
const loadMore = showLoadingMore ? (
const { initLoading, loading, list } = this.state;
const loadMore = !initLoading && !loading ? (
<div style={{ textAlign: 'center', marginTop: 12, height: 32, lineHeight: '32px' }}>
{loadingMore && <Spin />}
{!loadingMore && <Button onClick={this.onLoadMore}>loading more</Button>}
<Button onClick={this.onLoadMore}>loading more</Button>
</div>
) : null;
return (
<List
className="demo-loadmore-list"
loading={loading}
loading={initLoading}
itemLayout="horizontal"
loadMore={loadMore}
dataSource={data}
dataSource={list}
renderItem={item => (
<List.Item actions={[<a>edit</a>, <a>more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.name.last}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div>content</div>
<Skeleton avatar title={false} loading={item.loading} active>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.name.last}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div>content</div>
</Skeleton>
</List.Item>
)}
/>

View File

@ -179,6 +179,16 @@
position: absolute;
border-radius: @border-radius-base;
z-index: @zindex-dropdown;
&:before {
position: absolute;
top: -7px;
left: -6px;
right: -6px;
bottom: 0;
content: ' ';
opacity: .0001;
}
}
> .@{menu-prefix-cls} {
@ -375,6 +385,7 @@
width: @menu-collapsed-width;
> .@{menu-prefix-cls}-item,
> .@{menu-prefix-cls}-item-group > .@{menu-prefix-cls}-item-group-list > .@{menu-prefix-cls}-item,
> .@{menu-prefix-cls}-item-group > .@{menu-prefix-cls}-item-group-list > .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title,
> .@{menu-prefix-cls}-submenu > .@{menu-prefix-cls}-submenu-title {
left: 0;
text-overflow: clip;

View File

@ -0,0 +1,37 @@
import * as React from 'react';
import classNames from 'classnames';
export interface SkeletonAvatarProps {
prefixCls?: string;
className?: string;
style?: object;
size?: 'large' | 'small' | 'default';
shape?: 'circle'| 'square';
}
class Title extends React.Component<SkeletonAvatarProps, any> {
static defaultProps: Partial<SkeletonAvatarProps> = {
prefixCls: 'ant-skeleton-avatar',
size: 'large',
};
render() {
const { prefixCls, className, style, size, shape } = this.props;
const sizeCls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
});
const shapeCls = classNames({
[`${prefixCls}-circle`]: shape === 'circle',
[`${prefixCls}-square`]: shape === 'square',
});
return (
<span className={classNames(prefixCls, className, sizeCls, shapeCls)} style={style} />
);
}
}
export default Title;

View File

@ -0,0 +1,78 @@
import * as React from 'react';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
type widthUnit = number | string;
export interface SkeletonParagraphProps {
prefixCls?: string;
className?: string;
style?: object;
width?: widthUnit | Array<widthUnit>;
rows?: number;
}
interface SkeletonParagraphState {
prevProps: SkeletonParagraphProps;
widthList: Array<widthUnit>;
}
class Paragraph extends React.Component<SkeletonParagraphProps, SkeletonParagraphState> {
static defaultProps: Partial<SkeletonParagraphProps> = {
prefixCls: 'ant-skeleton-paragraph',
};
static getDerivedStateFromProps(
props: SkeletonParagraphProps,
state: SkeletonParagraphState,
): Partial<SkeletonParagraphState> {
const { prevProps } = state;
const { width, rows = 2 } = props;
const newState: Partial<SkeletonParagraphState> = {
prevProps: props,
};
if (rows !== prevProps.rows || width !== prevProps.width) {
// Parse width list
let widthList = [];
if (width && Array.isArray(width)) {
widthList = width;
} else if (width && !Array.isArray(width)) {
widthList = [];
widthList[rows - 1] = width;
}
newState.widthList = widthList;
}
return newState;
}
state: SkeletonParagraphState = {
prevProps: {},
widthList: [],
};
render() {
const { widthList } = this.state;
const { prefixCls, className, style, rows } = this.props;
const rowList = [...Array(rows)].map((_, index) => (
<li key={index} style={{ width: widthList[index] || '100%' }} />
));
return (
<ul
className={classNames(prefixCls, className)}
style={style}
>
{rowList}
</ul>
);
}
}
polyfill(Paragraph);
export default Paragraph;

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import classNames from 'classnames';
export interface SkeletonTitleProps {
prefixCls?: string;
className?: string;
style?: object;
width?: number | string;
}
class Title extends React.Component<SkeletonTitleProps, any> {
static defaultProps: Partial<SkeletonTitleProps> = {
prefixCls: 'ant-skeleton-title',
};
render() {
const { prefixCls, className, width, style } = this.props;
return (
<h3
className={classNames(prefixCls, className)}
style={{ width, ...style }}
/>
);
}
}
export default Title;

View File

@ -0,0 +1,204 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/skeleton/demo/active.md correctly 1`] = `
<div
class="ant-skeleton ant-skeleton-active"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:38%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width:100%"
/>
<li
style="width:100%"
/>
<li
style="width:61%"
/>
</ul>
</div>
</div>
`;
exports[`renders ./components/skeleton/demo/basic.md correctly 1`] = `
<div
class="ant-skeleton"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:38%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width:100%"
/>
<li
style="width:100%"
/>
<li
style="width:61%"
/>
</ul>
</div>
</div>
`;
exports[`renders ./components/skeleton/demo/children.md correctly 1`] = `
<div
class="article"
>
<div>
<h4>
Ant Design, a design language
</h4>
<p>
We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.
</p>
</div>
<button
class="ant-btn"
type="button"
>
<span>
Show Skeleton
</span>
</button>
</div>
`;
exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
<div>
<span
class="ant-switch"
tabindex="0"
>
<span
class="ant-switch-inner"
/>
</span>
<div
class="ant-list ant-list-vertical ant-list-lg ant-list-split"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-skeleton ant-skeleton-active"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:38%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width:100%"
/>
<li
style="width:100%"
/>
<li
style="width:61%"
/>
</ul>
</div>
</div>
</div>
</div>
<div
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-skeleton ant-skeleton-active"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:38%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width:100%"
/>
<li
style="width:100%"
/>
<li
style="width:61%"
/>
</ul>
</div>
</div>
</div>
</div>
<div
class="ant-list-item"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-skeleton ant-skeleton-active"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width:38%"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width:100%"
/>
<li
style="width:100%"
/>
<li
style="width:61%"
/>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,279 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Skeleton avatar shape 1`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 50%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar shape 2`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-square"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 50%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 1`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-sm ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 50%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 2`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 50%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 3`] = `
<div
class="ant-skeleton ant-skeleton-with-avatar"
>
<div
class="ant-skeleton-header"
>
<span
class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"
/>
</div>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 50%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph rows 1`] = `
<div
class="ant-skeleton"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 38%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
<li
style="width: 61%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph width 1`] = `
<div
class="ant-skeleton"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 38%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
<li
style="width: 93%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph width 2`] = `
<div
class="ant-skeleton"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 38%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 28%;"
/>
<li
style="width: 93%;"
/>
<li
style="width: 100%;"
/>
</ul>
</div>
</div>
`;
exports[`Skeleton title width 1`] = `
<div
class="ant-skeleton"
>
<div
class="ant-skeleton-content"
>
<h3
class="ant-skeleton-title"
style="width: 93%;"
/>
<ul
class="ant-skeleton-paragraph"
>
<li
style="width: 100%;"
/>
<li
style="width: 100%;"
/>
<li
style="width: 61%;"
/>
</ul>
</div>
</div>
`;

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('skeleton');

View File

@ -0,0 +1,50 @@
import React from 'react';
import { mount } from 'enzyme';
import Skeleton from '..';
describe('Skeleton', () => {
const genSkeleton = props => mount(
<Skeleton loading {...props}>
Bamboo
</Skeleton>
);
describe('avatar', () => {
it('size', () => {
const wrapperSmall = genSkeleton({ avatar: { size: 'small' } });
expect(wrapperSmall.render()).toMatchSnapshot();
const wrapperDefault = genSkeleton({ avatar: { size: 'default' } });
expect(wrapperDefault.render()).toMatchSnapshot();
const wrapperLarge = genSkeleton({ avatar: { size: 'large' } });
expect(wrapperLarge.render()).toMatchSnapshot();
});
it('shape', () => {
const wrapperCircle = genSkeleton({ avatar: { shape: 'circle' } });
expect(wrapperCircle.render()).toMatchSnapshot();
const wrapperSquare = genSkeleton({ avatar: { shape: 'square' } });
expect(wrapperSquare.render()).toMatchSnapshot();
});
});
describe('title', () => {
it('width', () => {
const wrapper = genSkeleton({ title: { width: '93%' } });
expect(wrapper.render()).toMatchSnapshot();
});
});
describe('paragraph', () => {
it('rows', () => {
const wrapper = genSkeleton({ paragraph: { rows: 5 } });
expect(wrapper.render()).toMatchSnapshot();
});
it('width', () => {
const wrapperPure = genSkeleton({ paragraph: { width: '93%' } });
expect(wrapperPure.render()).toMatchSnapshot();
const wrapperList = genSkeleton({ paragraph: { width: ['28%', '93%'] } });
expect(wrapperList.render()).toMatchSnapshot();
});
});
});

View File

@ -0,0 +1,22 @@
---
order: 1
title:
zh-CN: 动画效果
en-US: Active Animation
---
## zh-CN
显示动画效果。
## en-US
Display active animation.
````jsx
import { Skeleton } from 'antd';
ReactDOM.render(
<Skeleton active />,
mountNode);
````

View File

@ -0,0 +1,22 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
最简单的用法。
## en-US
Basic usage.
````jsx
import { Skeleton } from 'antd';
ReactDOM.render(
<Skeleton />,
mountNode);
````

View File

@ -0,0 +1,58 @@
---
order: 2
title:
zh-CN: 包含子组件
en-US: Contains sub component
---
## zh-CN
加载占位图包含子组件。
## en-US
Skeleton contains sub component.
````jsx
import { Skeleton, Button } from 'antd';
class Demo extends React.Component {
state = {
loading: false,
};
showSkeleton = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ loading: false });
}, 3000);
};
render() {
return (
<div className="article">
<Skeleton loading={this.state.loading}>
<div>
<h4>Ant Design, a design language</h4>
<p>We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.</p>
</div>
</Skeleton>
<Button onClick={this.showSkeleton} disabled={this.state.loading}>
Show Skeleton
</Button>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
````
<style>
.article h4 {
margin-bottom: 16px;
}
.article button {
margin-top: 16px;
}
</style>

View File

@ -0,0 +1,86 @@
---
order: 3
title:
zh-CN: 列表
en-US: List
---
## zh-CN
在列表组件中使用加载占位符。
## en-US
Use skeleton in list component.
````jsx
import { Skeleton, Switch, List, Avatar, Icon } from 'antd';
const listData = [];
for (let i = 0; i < 3; i++) {
listData.push({
href: 'http://ant.design',
title: `ant design part ${i}`,
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.',
});
}
const IconText = ({ type, text }) => (
<span>
<Icon type={type} style={{ marginRight: 8 }} />
{text}
</span>
);
class App extends React.Component {
state = {
loading: true,
}
onChange = (checked) => {
this.setState({ loading: !checked });
}
render() {
const { loading } = this.state;
return (
<div>
<Switch checked={!loading} onChange={this.onChange} />
<List
itemLayout="vertical"
size="large"
dataSource={listData}
renderItem={item => (
<List.Item
key={item.title}
actions={!loading && [<IconText type="star-o" text="156" />, <IconText type="like-o" text="156" />, <IconText type="message" text="2" />]}
extra={!loading && <img width={272} alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png" />}
>
<Skeleton loading={loading} active>
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={<a href={item.href}>{item.title}</a>}
description={item.description}
/>
{item.content}
</Skeleton>
</List.Item>
)}
/>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
````
<style>
.skeleton-demo {
border: 1px solid #f4f4f4;
}
</style>

View File

@ -0,0 +1,44 @@
---
category: Components
type: Data Entry
title: Skeleton
cols: 1
---
Provide a placeholder at the place which need waiting for loading.
## When To Use
- When resource needs long time to load, like low network speed.
- The component contains much information. Such as List or Card.
## API
### Skeleton
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| active | Show animation effect | boolean | false |
| avatar | Show avatar placeholder | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
| loading | Display the skeleton when `true` | boolean | - |
| paragraph | Show paragraph placeholder | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | Show title placeholder | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
### SkeletonAvatarProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| size | Set the size of avatar | Enum{ 'large', 'small', 'default' } | - |
| shape | Set the shape of avatar | Enum{ 'circle', 'square' } | - |
### SkeletonTitleProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| width | Set the width of title | number \| string | - |
### SkeletonParagraphProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| rows | Set the row count of paragraph | number | - |
| width | Set the width of paragraph. When width is an Array, it can set the width of each row. Otherwise only set the last row width | number \| string \| Array<number \| string> | - |

View File

@ -0,0 +1,155 @@
import * as React from 'react';
import classNames from 'classnames';
import Avatar, { SkeletonAvatarProps } from './Avatar';
import Title, { SkeletonTitleProps } from './Title';
import Paragraph, { SkeletonParagraphProps } from './Paragraph';
export interface SkeletonProps {
active?: boolean;
loading?: boolean;
prefixCls?: string;
className?: string;
children?: React.ReactNode;
avatar?: SkeletonAvatarProps | boolean;
title?: SkeletonTitleProps | boolean;
paragraph?: SkeletonParagraphProps | boolean;
}
function getComponentProps<T>(prop: T | boolean | undefined): T | {} {
if (prop && typeof prop === 'object') {
return prop;
}
return {};
}
function getAvatarBasicProps(hasTitle: boolean, hasParagraph: boolean): SkeletonAvatarProps {
if (hasTitle && !hasParagraph) {
return { shape: 'square' };
}
return { shape: 'circle' };
}
function getTitleBasicProps(hasAvatar: boolean, hasParagraph: boolean): SkeletonTitleProps {
if (!hasAvatar && hasParagraph) {
return { width: '38%' };
}
if (hasAvatar && hasParagraph) {
return { width: '50%' };
}
return { width: '100%' };
}
function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): SkeletonParagraphProps {
const basicProps: SkeletonParagraphProps = {};
// Width
if (hasAvatar && hasTitle) {
basicProps.width = '100%';
} else {
basicProps.width = '61%';
}
// Rows
if (!hasAvatar && hasTitle) {
basicProps.rows = 3;
} else {
basicProps.rows = 2;
}
return basicProps;
}
class Skeleton extends React.Component<SkeletonProps, any> {
static defaultProps: Partial<SkeletonProps> = {
prefixCls: 'ant-skeleton',
avatar: false,
title: true,
paragraph: true,
};
render() {
const {
loading, prefixCls, className, children,
avatar, title, paragraph, active,
} = this.props;
if (loading || !('loading' in this.props)) {
const hasAvatar = !!avatar;
const hasTitle = !!title;
const hasParagraph = !!paragraph;
// Avatar
let avatarNode;
if (hasAvatar) {
const avatarProps: SkeletonAvatarProps = {
...getAvatarBasicProps(hasTitle, hasParagraph),
...getComponentProps(avatar),
};
avatarNode = (
<div className={`${prefixCls}-header`}>
<Avatar {...avatarProps} />
</div>
);
}
let contentNode;
if (hasTitle || hasParagraph) {
// Title
let $title;
if (hasTitle) {
const titleProps: SkeletonTitleProps = {
...getTitleBasicProps(hasAvatar, hasParagraph),
...getComponentProps(title),
};
$title = (
<Title {...titleProps} />
);
}
// Paragraph
let paragraphNode;
if (hasParagraph) {
const paragraphProps: SkeletonParagraphProps = {
...getParagraphBasicProps(hasAvatar, hasTitle),
...getComponentProps(paragraph),
};
paragraphNode = (
<Paragraph {...paragraphProps} />
);
}
contentNode = (
<div className={`${prefixCls}-content`}>
{$title}
{paragraphNode}
</div>
);
}
const cls = classNames(
prefixCls,
className, {
[`${prefixCls}-with-avatar`]: hasAvatar,
[`${prefixCls}-active`]: active,
},
);
return (
<div className={cls}>
{avatarNode}
{contentNode}
</div>
);
}
return children;
}
}
export default Skeleton;

View File

@ -0,0 +1,46 @@
---
category: Components
subtitle: 加载占位图
type: Data Entry
title: Skeleton
cols: 1
---
在需要等待加载内容的位置提供一个占位图。
## 何时使用
- 网络较慢,需要长时间等待加载处理的情况下。
- 图文信息内容较多的列表/卡片中。
## API
### Skeleton
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| active | 是否展示动画效果 | boolean | false |
| avatar | 是否显示头像占位图 | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
| loading | 为 `true` 时,显示占位图。反之则直接展示子组件 | boolean | - |
| paragraph | 是否显示段落占位图 | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | 是否显示标题占位图 | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
### SkeletonAvatarProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| size | 设置头像占位图的大小 | Enum{ 'large', 'small', 'default' } | - |
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | - |
### SkeletonTitleProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| width | 设置标题占位图的宽度 | number \| string | - |
### SkeletonParagraphProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| rows | 设置段落占位图的行数 | number | - |
| width | 设置标题占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度 | number \| string \| Array<number \| string> | - |

View File

@ -0,0 +1,113 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@skeleton-prefix-cls: ~"@{ant-prefix}-skeleton";
@skeleton-avatar-prefix-cls: ~"@{skeleton-prefix-cls}-avatar";
@skeleton-title-prefix-cls: ~"@{skeleton-prefix-cls}-title";
@skeleton-paragraph-prefix-cls: ~"@{skeleton-prefix-cls}-paragraph";
@skeleton-to-color: shade(@skeleton-color, 5%);
.@{skeleton-prefix-cls} {
display: table;
width: 100%;
&-header {
display: table-cell;
vertical-align: top;
padding-right: 16px;
// Avatar
.@{skeleton-avatar-prefix-cls} {
display: inline-block;
vertical-align: top;
background: @skeleton-color;
.avatar-size(@avatar-size-base);
&-lg {
.avatar-size(@avatar-size-lg);
}
&-sm {
.avatar-size(@avatar-size-sm);
}
}
}
&-content {
display: table-cell;
vertical-align: top;
width: 100%;
// Title
.@{skeleton-title-prefix-cls} {
margin-top: 16px;
height: 16px;
width: 100%;
background: @skeleton-color;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 24px;
}
}
// paragraph
.@{skeleton-paragraph-prefix-cls} {
> li {
height: 16px;
background: @skeleton-color;
+ li {
margin-top: 16px;
}
}
}
}
&-with-avatar &-content {
// Title
.@{skeleton-title-prefix-cls} {
margin-top: 12px;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 28px;
}
}
}
// With active animation
&.@{skeleton-prefix-cls}-active {
& .@{skeleton-prefix-cls}-content {
.@{skeleton-title-prefix-cls},
.@{skeleton-paragraph-prefix-cls} > li {
.skeleton-color();
}
}
}
}
.avatar-size(@size) {
width: @size;
height: @size;
line-height: @size;
&.@{skeleton-avatar-prefix-cls}-circle {
border-radius: 50%;
}
}
.skeleton-color() {
background: linear-gradient(90deg, @skeleton-color 25%, @skeleton-to-color 37%, @skeleton-color 63%);
animation: ~"@{skeleton-prefix-cls}-loading" 1.4s ease infinite;
background-size: 400% 100%;
}
@keyframes ~"@{skeleton-prefix-cls}-loading" {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}

View File

@ -0,0 +1,2 @@
import '../../style/index.less';
import './index.less';

View File

@ -18,7 +18,7 @@ subtitle: 加载中
| delay | 延迟显示加载效果的时间(防止闪烁) | number (毫秒) | - |
| indicator | 加载指示符 | ReactElement | - |
| size | 组件大小,可选值为 `small` `default` `large` | string | 'default' |
| spinning | 是否旋转 | boolean | true |
| spinning | 是否为加载中状态 | boolean | true |
| tip | 当作为包裹元素时,可以自定义描述文案 | string | - |
| wrapperClassName | 包装器的类属性 | string | - |

View File

@ -33,6 +33,7 @@ The whole of the step bar.
| progressDot | Steps with progress dot style, customize the progress dot by setting it to a function. labelPlacement will be `vertical` | Boolean or (iconDot, {index, status, title, description}) => ReactNode | false |
| size | to specify the size of the step bar, `default` and `small` are currently supported | string | `default` |
| status | to specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` |
| initial | set the initial step, counting from 0 | number | 0 |
### Steps.Step

View File

@ -6,6 +6,7 @@ export interface StepsProps {
prefixCls?: string;
iconPrefix?: string;
current?: number;
initial?: number;
status?: 'wait' | 'process' | 'finish' | 'error';
size?: 'default' | 'small';
direction?: 'horizontal' | 'vertical';

View File

@ -34,6 +34,7 @@ title: Steps
| progressDot | 点状步骤条,可以设置为一个 function,labelPlacement 将强制为`vertical` | Boolean or (iconDot, {index, status, title, description}) => ReactNode | false |
| size | 指定大小,目前支持普通(`default`)和迷你(`small` | string | default |
| status | 指定当前步骤的状态,可选 `wait` `process` `finish` `error` | string | process |
| initial | 起始序号,从 0 开始记数 | number | 0 |
### Steps.Step

View File

@ -19,14 +19,14 @@
right: -1px;
border-radius: inherit;
border: 0 solid @primary-color;
opacity: 0.4;
animation: waveEffect .4s cubic-bezier(.25, .8, .25, 1);
opacity: 0.2;
animation: fadeEffect 2.4s @ease-out-circ, waveEffect .48s @ease-out-circ;
animation-fill-mode: forwards;
display: block;
}
@keyframes waveEffect {
to {
opacity: 0;
100% {
top: -@wave-animation-width;
left: -@wave-animation-width;
bottom: -@wave-animation-width;
@ -34,3 +34,9 @@
border-width: @wave-animation-width;
}
}
@keyframes fadeEffect {
100% {
opacity: 0;
}
}

View File

@ -484,6 +484,10 @@
@collapse-content-padding: @padding-md;
@collapse-content-bg: @component-background;
// Skeleton
// ---
@skeleton-color: #f2f2f2;
// Message
// ---
@message-notice-content-padding: 10px 16px;

View File

@ -47,7 +47,7 @@
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
min-height: 32px;
word-break: break-all;
word-wrap: break-word;
}
// Arrows

View File

@ -3,7 +3,7 @@ import { AbstractSelectProps } from '../select';
export type TreeNode = TreeNodeNormal | TreeNodeSimpleMode;
interface TreeNodeNormal {
export interface TreeNodeNormal {
value: string;
/**
* @deprecated Please use `title` instead.
@ -18,12 +18,12 @@ interface TreeNodeNormal {
children?: TreeNodeNormal[];
}
interface TreeNodeSimpleMode {
export interface TreeNodeSimpleMode {
/* It is possible to change `id` and `pId` prop keys using TreeDataSimpleMode so those keys can be anything */
[key: string]: string | boolean | React.ReactNode;
}
interface TreeDataSimpleMode {
export interface TreeDataSimpleMode {
id?: string;
pId?: string;
rootPId?: string;

View File

@ -39,6 +39,8 @@ export interface AntTreeNodeProps {
selectable?: boolean;
icon?: ((treeNode: AntdTreeNodeAttribute) => React.ReactNode) | React.ReactNode;
children?: React.ReactNode;
[customProp: string]: any;
}
export interface AntTreeNode extends React.Component<AntTreeNodeProps, {}> { }

View File

@ -22,7 +22,6 @@ export interface UploadFile {
status?: UploadFileStatus;
percent?: number;
thumbUrl?: string;
isNotImage?: boolean;
originFileObj?: File;
response?: any;
error?: any;

View File

@ -7,57 +7,106 @@ Ant Design allows you to customize some basic design aspects in order to meet th
![customized themes](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png)
## Less variables
## Ant Design Less variables
We are using [Less](http://lesscss.org/) as the development language for styling. A set of less variables are defined for each design aspect that can be customized to your needs.
- [Default Variables](https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less)
There are some major variables below, all less variables could be found in [Default Variables](https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less).
```less
@primary-color: #1890ff; // primary color for all components
@link-color: #1890ff; // link color
@success-color: #52c41a; // success state color
@warning-color: #faad14; // warning state color
@error-color: #f5222d; // error state color
@font-size-base: 14px; // major text font size
@heading-color: rgba(0, 0, 0, .85); // heading text color
@text-color: rgba(0, 0, 0, .65); // major text color
@text-color-secondary : rgba(0, 0, 0, .45); // secondary text color
@disabled-color : rgba(0, 0, 0, .25); // disable state color
@border-radius-base: 4px; // major border radius
@border-color-base: #d9d9d9; // major border color
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, .15); // major shadow for layers
```
Please report an issue if the existing list of variables is not enough for you.
## How to do it
We recommend [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) to override the default values of the variables. There are two ways to achieve it in practice.
We will use [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) provided by less.js to override the default values of the variables, You can use this [example](https://github.com/ant-design/create-react-app-antd) as a live playground. We now introduce some popular way to do it depends on different workflow.
You can use this [example](https://github.com/ant-design/antd-init/tree/master/examples/customize-antd-theme) as a playground.
### Customize in webpack
### 1) Using `theme` property (recommended way)
We take a typical `webpack.config.js` file as example to customize it's [less-loader](https://github.com/webpack-contrib/less-loader) options.
```diff
// webpack.config.js
module.exports = {
rules: [{
test: /\.less$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader', // translates CSS into CommonJS
}, {
loader: 'less-loader', // compiles Less to CSS
+ options: {
+ modifyVars: {
+ 'primary-color': '#1DA57A',
+ 'link-color': '#1DA57A',
+ 'border-radius-base': '2px',
+ },
+ javascriptEnabled: true,
+ },
}],
// ...other rules
}],
// ...other config
}
```
Note that do not exclude antd package in node_modules when using less-loader.
### Customize in roadhog or Umi
You can easily use `theme` field in `.webpackrc` file of your project root directory if you are using [roadhog](https://github.com/sorrycc/roadhog) or [Umi](http://umijs.org/)which could be a object or a javascript file path.
Specify the `theme` property in the `package.json` or `.webpackrc` file, whose value can be either an object or the path to a JS file that contains the custom values of specific variables:
- example of directly specifying the custom values as an object:
```js
"theme": {
"primary-color": "#1DA57A",
},
```
- example of specifying a [file path](https://github.com/ant-design/antd-init/blob/master/examples/customize-antd-theme/theme.js) to a JS file:
Or just [a javascript file path](https://github.com/ant-design/ant-design-pro/blob/3c2a056ef0dac06ce3b4389192691bb1f5c448e2/.webpackrc.js#L19):
```js
"theme": "./theme.js",
```
This approach is available only when using [antd-init](https://github.com/ant-design/antd-init) or [dva-cli](https://github.com/dvajs/dva-cli). If you choose other boilerplates, you can write a webpack config about [less-loader modifyVars](https://github.com/webpack/less-loader#less-options) like [atool-build ](https://github.com/ant-tool/atool-build/blob/a4b3e3eec4ffc09b0e2352d7f9d279c4c28fdb99/src/getWebpackCommonConfig.js#L131-L138) does.
### Customize in create-react-app
Note:
Follow [Use in create-react-app](/docs/react/create-react-app).
- Importing styles from less files is necessary.
- If you import styles by specifying the `style` option of [babel-plugin-import](https://github.com/ant-design/babel-plugin-import), change it from `'css'` to `true`, which will import the `less` version of antd.
- If you import styles from `'antd/dist/antd.css'`, change it to `antd/dist/antd.less`.
- When using `dva-cli@0.7.0+`, you should add the `theme` block to [.roadhogrc](https://github.com/dvajs/dva-example-user-dashboard/commit/d6da33b3a6e18eb7f003752a4b00b5a660747c31) instead of `package.json`.
- If you want to override `@icon-url`, the value must be contained in quotes like `"@icon-url": "'your-icon-font-path'"` ([A fix sample](https://github.com/visvadw/dvajs-user-dashboard/pull/2)).
### Customize in less file
### 2) Overriding Less variables (alternative way)
Another approach to customize theme is creating a `less` file within variables to override `antd.less`.
Override variables via less definition files.
Create a standalone less file like the one below, and import it in your project.
```css
@import "~antd/dist/antd.less"; // import official less entry file
@import "your-theme-file.less"; // override variables here
```
```css
@import "~antd/dist/antd.less"; // Import Ant Design styles by less entry
@import "your-theme-file.less"; // variables to override above
```
Note: This way will load the styles of all components, regardless of your demand, which cause `style` option of `babel-plugin-import` not working.
## Not working?
You must import styles as less format. A common mistake would be importing multiple copied of styles that some of them are css format to override the less styles.
- If you import styles by specifying the `style` option of [babel-plugin-import](https://github.com/ant-design/babel-plugin-import), change it from `'css'` to `true`, which will import the `less` version of antd.
- If you import styles from `'antd/dist/antd.css'`, change it to `antd/dist/antd.less`.
If you want to override `@icon-url`, the value must be contained in quotes like `"@icon-url": "'your-icon-font-path'"` ([A fix sample](https://github.com/visvadw/dvajs-user-dashboard/pull/2)).
## Related Articles
- [Using Ant Design in Sass-Styled Webpack Projects with `antd-scss-theme-plugin`](https://intoli.com/blog/antd-scss-theme-plugin/)

View File

@ -7,24 +7,69 @@ Ant Design 设计规范上支持一定程度的样式定制,以满足业务和
![一些配置好的主题](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png)
## 样式变量
## Ant Design 的样式变量
antd 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整。
- [默认样式变量](https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less)
以下是一些最常用的通用变量,所有样式变量可以在 [这里](https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less) 找到。
```less
@primary-color: #1890ff; // 全局主色
@link-color: #1890ff; // 链接色
@success-color: #52c41a; // 成功色
@warning-color: #faad14; // 警告色
@error-color: #f5222d; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: rgba(0, 0, 0, .85); // 标题色
@text-color: rgba(0, 0, 0, .65); // 主文本色
@text-color-secondary : rgba(0, 0, 0, .45); // 次文本色
@disabled-color : rgba(0, 0, 0, .25); // 失效色
@border-radius-base: 4px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, .15); // 浮层阴影
```
如果以上变量不能满足你的定制需求,可以给我们提 issue。
## 定制方式
我们使用 [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) 的方式来覆盖变量。
在具体工程实践中,有 `package.theme``less` 两种方案,选择一种即可。
原理上是使用 less 提供的 [modifyVars](http://lesscss.org/usage/#using-less-in-the-browser-modify-variables) 的方式进行覆盖变量,可以在本地运行 [例子](https://github.com/ant-design/create-react-app-antd) 查看定制效果。下面将针对不同的场景提供一些常用的定制方式。
可以在本地运行 [例子](https://github.com/ant-design/antd-init/tree/master/examples/customize-antd-theme) 查看定制效果。
### 在 webpack 中定制主题
### 1) theme 属性(推荐)
我们以 webpack@4 为例进行说明,以下是一个 `webpack.config.js` 的典型例子,对 [less-loader](https://github.com/webpack-contrib/less-loader) 的 options 属性进行相应配置。
配置在 `package.json``.webpackrc` 下的 `theme` 字段。theme 可以配置为一个对象或文件路径。
```diff
// webpack.config.js
module.exports = {
rules: [{
test: /\.less$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader', // translates CSS into CommonJS
}, {
loader: 'less-loader', // compiles Less to CSS
+ options: {
+ modifyVars: {
+ 'primary-color': '#1DA57A',
+ 'link-color': '#1DA57A',
+ 'border-radius-base': '2px',
+ },
+ javascriptEnabled: true,
+ },
}],
// ...other rules
}],
// ...other config
}
```
注意 less-loader 的处理范围不要过滤掉 `node_modules` 下的 antd 包。
### 在 roadhog 或 Umi 里配置主题
如果你在使用 [roadhog](https://github.com/sorrycc/roadhog) 或者 [Umi](http://umijs.org/),那么可以很方便地在项目根目录的 `.webpackrc` 文件中 `theme` 字段进行主题配置。`theme` 可以配置为一个对象或文件路径。
```js
"theme": {
@ -32,34 +77,35 @@ antd 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定
},
```
或者 [一个 js 文件](https://github.com/ant-design/antd-init/blob/master/examples/customize-antd-theme/theme.js)
或者 [一个 js 文件](https://github.com/ant-design/ant-design-pro/blob/3c2a056ef0dac06ce3b4389192691bb1f5c448e2/.webpackrc.js#L19)
```js
"theme": "./theme.js",
```
定义 `theme` 属性时,需要配合使用([antd-init](https://github.com/ant-design/antd-init) 或 [dva-cli](https://github.com/dvajs/dva-cli)。如果你使用的是其他脚手架,可以参考 [atool-build 中 less-loader 的 webpack 相关配置 ](https://github.com/ant-tool/atool-build/blob/a4b3e3eec4ffc09b0e2352d7f9d279c4c28fdb99/src/getWebpackCommonConfig.js#L131-L138),利用 [less-loader](https://github.com/webpack/less-loader#less-options) 的 `modifyVars` 配置来覆盖原来的样式变量。
### 在 create-react-app 中定制主题
注意:
参考 [在 create-react-app 中使用](/docs/react/create-react-app) 进行配置即可。
- 样式必须加载 less 格式。
- 如果你在使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 的 `style` 配置来引入样式,需要将配置值从 `'css'` 改为 `true`,这样会引入 less 文件。
- 如果你是通过 `'antd/dist/antd.css'` 引入样式的,改为 `antd/dist/antd.less`
- `dva-cli@0.7.0+``theme` 属性需要写在 [.roadhogrc](https://github.com/dvajs/dva-example-user-dashboard/commit/d6da33b3a6e18eb7f003752a4b00b5a660747c31) 文件里。
- 如果要覆盖 `@icon-url` 变量,内容需要包括引号 `"@icon-url": "'your-icon-font-path'"`[修正示例](https://github.com/visvadw/dvajs-user-dashboard/pull/2))。
### 配置 less 变量文件
### 2) less
另外一种方式是建立一个单独的 `less` 变量文件,引入这个文件覆盖 `antd.less` 里的变量。
用 less 文件进行变量覆盖。
```css
@import "~antd/dist/antd.less"; // 引入官方提供的 less 样式入口文件
@import "your-theme-file.less"; // 用于覆盖上面定义的变量
```
建立一个单独的 `less` 文件如下,再引入这个文件。
注意,这种方式已经载入了所有组件的样式,不需要也无法和按需加载插件 `babel-plugin-import``style` 属性一起使用
```css
@import "~antd/dist/antd.less"; // 引入官方提供的 less 样式入口文件
@import "your-theme-file.less"; // 用于覆盖上面定义的变量
```
## 没有生效?
注意:这种方式已经载入了所有组件的样式,不需要也无法和按需加载插件 `babel-plugin-import``style` 属性一起使用。
注意样式必须加载 less 格式一个常见的问题就是引入了多份样式less 的样式被 css 的样式覆盖了。
- 如果你在使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 的 `style` 配置来引入样式,需要将配置值从 `'css'` 改为 `true`,这样会引入 less 文件。
- 如果你是通过 `'antd/dist/antd.css'` 引入样式的,改为 `antd/dist/antd.less`
如果要覆盖 `@icon-url` 变量,内容需要包括引号 `"@icon-url": "'your-icon-font-path'"`[修正示例](https://github.com/visvadw/dvajs-user-dashboard/pull/2))。
## 社区教程

View File

@ -75,7 +75,7 @@ module.exports = {
'app.footer.company': 'AFX',
'app.footer.ant-design': '蚂蚁 UI 体系',
'app.footer.yuque': '语雀',
'app.footer.yuque.slogan': '企业文档管理平台',
'app.footer.yuque.slogan': '知识创作·协作平台',
'app.footer.fengdie': '云凤蝶',
'app.footer.fengdie.slogan': '移动建站平台',
'app.footer.zhihu': '知乎专栏',

View File

@ -41,6 +41,7 @@ Array [
"Rate",
"Row",
"Select",
"Skeleton",
"Slider",
"Spin",
"Steps",