diff --git a/components/__tests__/__snapshots__/index.test.js.snap b/components/__tests__/__snapshots__/index.test.js.snap
index 53f98628bc..072e985c04 100644
--- a/components/__tests__/__snapshots__/index.test.js.snap
+++ b/components/__tests__/__snapshots__/index.test.js.snap
@@ -41,6 +41,7 @@ Array [
"Rate",
"Row",
"Select",
+ "Skeleton",
"Slider",
"Spin",
"Steps",
diff --git a/components/_util/wave.tsx b/components/_util/wave.tsx
index 3642555fd5..7a00f4598f 100644
--- a/components/_util/wave.tsx
+++ b/components/_util/wave.tsx
@@ -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() {
diff --git a/components/button/__tests__/index.test.js b/components/button/__tests__/index.test.js
index d6e2542294..38930a6102 100644
--- a/components/button/__tests__/index.test.js
+++ b/components/button/__tests__/index.test.js
@@ -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(
+ Follow
+ );
+ if (process.env.REACT === '15') {
+ return;
+ }
+ expect(() => renderer.create(wrapper).toJSON()).not.toThrow();
+ });
+
it('renders Chinese characters correctly', () => {
const wrapper = render(
按钮
diff --git a/components/button/demo/ghost.md b/components/button/demo/ghost.md
index 9cff4953e9..7c7f77a522 100644
--- a/components/button/demo/ghost.md
+++ b/components/button/demo/ghost.md
@@ -7,7 +7,7 @@ title:
## zh-CN
-幽灵按钮将其他按钮的内容反色,背景变为透明,常用在有色背景上。
+幽灵按钮将按钮的内容反色,背景变为透明,常用在有色背景上。
## en-US
diff --git a/components/card/__tests__/__snapshots__/demo.test.js.snap b/components/card/__tests__/__snapshots__/demo.test.js.snap
index 02030679ec..74c318e496 100644
--- a/components/card/__tests__/__snapshots__/demo.test.js.snap
+++ b/components/card/__tests__/__snapshots__/demo.test.js.snap
@@ -372,174 +372,83 @@ exports[`renders ./components/card/demo/inner.md correctly 1`] = `
exports[`renders ./components/card/demo/loading.md correctly 1`] = `
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Toggle loading
-
-
`;
diff --git a/components/card/demo/loading.md b/components/card/demo/loading.md
index 408d44d240..c3e39deb0a 100644
--- a/components/card/demo/loading.md
+++ b/components/card/demo/loading.md
@@ -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 (
-
- Whatever content
+
+
+ , , ]}
+ >
+
+ }
+ title="Card title"
+ description="This is the description"
+ />
+
- Toggle loading
);
}
}
-ReactDOM.render(
- ,
- mountNode);
+ReactDOM.render( , mountNode);
````
diff --git a/components/date-picker/WeekPicker.tsx b/components/date-picker/WeekPicker.tsx
index 3a6ee610b5..9c71faacb9 100644
--- a/components/date-picker/WeekPicker.tsx
+++ b/components/date-picker/WeekPicker.tsx
@@ -87,7 +87,7 @@ class WeekPicker extends React.Component {
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 {
className={pickerInputClass}
onFocus={onFocus}
onBlur={onBlur}
- style={style}
/>
{clearIcon}
@@ -137,7 +136,11 @@ class WeekPicker extends React.Component {
);
};
return (
-
+
{
expect(wrapper).toMatchSnapshot();
});
+ it('render top drawer', () => {
+ const wrapper = render(
+
+ Here is content of Drawer
+
+ );
+ expect(wrapper).toMatchSnapshot();
+ });
+
it('have a title', () => {
const wrapper = render(
{
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
});
- it('render right MultiDrawer', () => {
+ it('render left MultiDrawer', () => {
const wrapper = mount( );
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( );
+ 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);
});
});
diff --git a/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap b/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap
index 6c8325225d..6ad726e1bb 100644
--- a/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap
+++ b/components/drawer/__tests__/__snapshots__/Drawer.test.js.snap
@@ -277,3 +277,43 @@ exports[`Drawer render correctly 1`] = `
`;
+
+exports[`Drawer render top drawer 1`] = `
+
+
+
+
+
+
+
+
+
+
+ Here is content of Drawer
+
+
+
+
+
+
+`;
diff --git a/components/drawer/demo/basic-left.md b/components/drawer/demo/basic-left.md
deleted file mode 100644
index 1caf745dea..0000000000
--- a/components/drawer/demo/basic-left.md
+++ /dev/null
@@ -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 (
-
-
- Open
-
-
- Some contents...
- Some contents...
- Some contents...
-
-
- );
- }
-}
-
-ReactDOM.render( , mountNode);
-```
diff --git a/components/drawer/demo/placement.md b/components/drawer/demo/placement.md
new file mode 100644
index 0000000000..a1a5907a0b
--- /dev/null
+++ b/components/drawer/demo/placement.md
@@ -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 (
+
+
+ top
+ right
+ bottom
+ left
+
+
+ Open
+
+
+ Some contents...
+ Some contents...
+ Some contents...
+
+
+ );
+ }
+}
+
+ReactDOM.render( , mountNode);
+```
diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md
index 2b7bfc60b5..eb9b33bac4 100644
--- a/components/drawer/index.en-US.md
+++ b/components/drawer/index.en-US.md
@@ -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) | - |
diff --git a/components/skeleton/demo/list.md b/components/skeleton/demo/list.md
new file mode 100644
index 0000000000..5c14a55554
--- /dev/null
+++ b/components/skeleton/demo/list.md
@@ -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 }) => (
+
+
+ {text}
+
+);
+
+class App extends React.Component {
+ state = {
+ loading: true,
+ }
+
+ onChange = (checked) => {
+ this.setState({ loading: !checked });
+ }
+
+ render() {
+ const { loading } = this.state;
+
+ return (
+
+
+
+
(
+ , , ]}
+ extra={!loading && }
+ >
+
+ }
+ title={{item.title} }
+ description={item.description}
+ />
+ {item.content}
+
+
+ )}
+ />
+
+ );
+ }
+}
+
+ReactDOM.render( , mountNode);
+````
+
+
diff --git a/components/skeleton/index.en-US.md b/components/skeleton/index.en-US.md
new file mode 100644
index 0000000000..607db7d533
--- /dev/null
+++ b/components/skeleton/index.en-US.md
@@ -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 | - |
diff --git a/components/skeleton/index.tsx b/components/skeleton/index.tsx
new file mode 100644
index 0000000000..bf2064b5f4
--- /dev/null
+++ b/components/skeleton/index.tsx
@@ -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(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 {
+ static defaultProps: Partial = {
+ 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 = (
+
+ );
+ }
+
+ let contentNode;
+ if (hasTitle || hasParagraph) {
+ // Title
+ let $title;
+ if (hasTitle) {
+ const titleProps: SkeletonTitleProps = {
+ ...getTitleBasicProps(hasAvatar, hasParagraph),
+ ...getComponentProps(title),
+ };
+
+ $title = (
+
+ );
+ }
+
+ // Paragraph
+ let paragraphNode;
+ if (hasParagraph) {
+ const paragraphProps: SkeletonParagraphProps = {
+ ...getParagraphBasicProps(hasAvatar, hasTitle),
+ ...getComponentProps(paragraph),
+ };
+
+ paragraphNode = (
+
+ );
+ }
+
+ contentNode = (
+
+ {$title}
+ {paragraphNode}
+
+ );
+ }
+
+ const cls = classNames(
+ prefixCls,
+ className, {
+ [`${prefixCls}-with-avatar`]: hasAvatar,
+ [`${prefixCls}-active`]: active,
+ },
+ );
+
+ return (
+
+ {avatarNode}
+ {contentNode}
+
+ );
+ }
+
+ return children;
+ }
+}
+
+export default Skeleton;
diff --git a/components/skeleton/index.zh-CN.md b/components/skeleton/index.zh-CN.md
new file mode 100644
index 0000000000..f515513827
--- /dev/null
+++ b/components/skeleton/index.zh-CN.md
@@ -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 | - |
diff --git a/components/skeleton/style/index.less b/components/skeleton/style/index.less
new file mode 100644
index 0000000000..b2e0ec93e6
--- /dev/null
+++ b/components/skeleton/style/index.less
@@ -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%;
+ }
+}
diff --git a/components/skeleton/style/index.tsx b/components/skeleton/style/index.tsx
new file mode 100644
index 0000000000..3a3ab0de59
--- /dev/null
+++ b/components/skeleton/style/index.tsx
@@ -0,0 +1,2 @@
+import '../../style/index.less';
+import './index.less';
diff --git a/components/spin/index.zh-CN.md b/components/spin/index.zh-CN.md
index d3f01600a0..95a434913f 100644
--- a/components/spin/index.zh-CN.md
+++ b/components/spin/index.zh-CN.md
@@ -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 | - |
diff --git a/components/steps/index.en-US.md b/components/steps/index.en-US.md
index 00823e19a8..b9acb4283a 100644
--- a/components/steps/index.en-US.md
+++ b/components/steps/index.en-US.md
@@ -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
diff --git a/components/steps/index.tsx b/components/steps/index.tsx
index bdbd71ea54..5a8f85fbcc 100644
--- a/components/steps/index.tsx
+++ b/components/steps/index.tsx
@@ -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';
diff --git a/components/steps/index.zh-CN.md b/components/steps/index.zh-CN.md
index 56fc397e91..c7c32466bc 100644
--- a/components/steps/index.zh-CN.md
+++ b/components/steps/index.zh-CN.md
@@ -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
diff --git a/components/style/core/motion/other.less b/components/style/core/motion/other.less
index 310fb4bc25..606529686d 100644
--- a/components/style/core/motion/other.less
+++ b/components/style/core/motion/other.less
@@ -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;
+ }
+}
diff --git a/components/style/themes/default.less b/components/style/themes/default.less
index 81625aa2f0..c65b0b65a1 100644
--- a/components/style/themes/default.less
+++ b/components/style/themes/default.less
@@ -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;
diff --git a/components/tooltip/style/index.less b/components/tooltip/style/index.less
index b54ed7c5b4..c272377532 100644
--- a/components/tooltip/style/index.less
+++ b/components/tooltip/style/index.less
@@ -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
diff --git a/components/tree-select/interface.tsx b/components/tree-select/interface.tsx
index 86a7438d9c..3f1dad9aca 100644
--- a/components/tree-select/interface.tsx
+++ b/components/tree-select/interface.tsx
@@ -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;
diff --git a/components/tree/Tree.tsx b/components/tree/Tree.tsx
index 3d067148cc..66a8907749 100644
--- a/components/tree/Tree.tsx
+++ b/components/tree/Tree.tsx
@@ -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 { }
diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx
index dc8841a5d3..bd82edd1bc 100755
--- a/components/upload/interface.tsx
+++ b/components/upload/interface.tsx
@@ -22,7 +22,6 @@ export interface UploadFile {
status?: UploadFileStatus;
percent?: number;
thumbUrl?: string;
- isNotImage?: boolean;
originFileObj?: File;
response?: any;
error?: any;
diff --git a/docs/react/customize-theme.en-US.md b/docs/react/customize-theme.en-US.md
index 8bbe66f454..b3131071b4 100644
--- a/docs/react/customize-theme.en-US.md
+++ b/docs/react/customize-theme.en-US.md
@@ -7,57 +7,106 @@ Ant Design allows you to customize some basic design aspects in order to meet th

-## 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/)
diff --git a/docs/react/customize-theme.zh-CN.md b/docs/react/customize-theme.zh-CN.md
index fe61ee9eb4..67efa1b878 100644
--- a/docs/react/customize-theme.zh-CN.md
+++ b/docs/react/customize-theme.zh-CN.md
@@ -7,24 +7,69 @@ Ant Design 设计规范上支持一定程度的样式定制,以满足业务和

-## 样式变量
+## 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))。
## 社区教程
diff --git a/site/theme/zh-CN.js b/site/theme/zh-CN.js
index dd1dcf85b3..7333ae851d 100644
--- a/site/theme/zh-CN.js
+++ b/site/theme/zh-CN.js
@@ -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': '知乎专栏',
diff --git a/tests/__snapshots__/index.test.js.snap b/tests/__snapshots__/index.test.js.snap
index 1f82a35b7c..d0acf60bcc 100644
--- a/tests/__snapshots__/index.test.js.snap
+++ b/tests/__snapshots__/index.test.js.snap
@@ -41,6 +41,7 @@ Array [
"Rate",
"Row",
"Select",
+ "Skeleton",
"Slider",
"Spin",
"Steps",