Merge pull request #17732 from ant-design/master

Merge master into feature
This commit is contained in:
偏右 2019-07-19 12:20:08 +08:00 committed by GitHub
commit 1147ae6949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 668 additions and 324 deletions

View File

@ -21,20 +21,18 @@ Please makes sure that these form are filled before submitting your pull request
- [ ] Branch merge
- [ ] Other (about what?)
### 👻 What's the background?
### 🔗 Related issue link
<!--
1. Describe the source of requirement, like related issue link.
2. Describe the problem and the scenario.
-->
### 💡 Solution
### 💡 Background and solution
<!--
1. How to fix the problem, and list final API implementation and usage sample if that is an new feature.
1. Describe the problem and the scenario.
2. GIF or snapshot should be provided if includes UI/interactive modification.
3. How to fix the problem, and list final API implementation and usage sample if that is an new feature.
-->
### 📝 Changelog

View File

@ -21,20 +21,18 @@
- [ ] 分支合并
- [ ] 其他改动(是关于什么的改动?)
### 👻 需求背景
### 🔗 相关 Issue
<!--
1. 描述相关需求的来源,如相关的 issue 讨论链接。
2. 要解决的具体问题。
-->
### 💡 解决方案和最终实现是?
### 💡 需求背景和解决方案
<!--
1. 列出最终的 API 实现和用法
2. 涉及UI/交互变动需要有截图或 GIF。
1. 要解决的具体问题
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->
### 📝 更新日志怎么写?

View File

@ -162,29 +162,37 @@ describe('Affix Render', () => {
});
});
it('updatePosition when size changed', () => {
document.body.innerHTML = '<div id="mounter" />';
describe('updatePosition when size changed', () => {
function test(name, index) {
it(name, () => {
document.body.innerHTML = '<div id="mounter" />';
const updateCalled = jest.fn();
wrapper = mount(<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />, {
attachTo: document.getElementById('mounter'),
});
const updateCalled = jest.fn();
wrapper = mount(<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers();
jest.runAllTimers();
movePlaceholder(300);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
jest.runAllTimers();
wrapper.update();
movePlaceholder(300);
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
jest.runAllTimers();
wrapper.update();
// Mock trigger resize
updateCalled.mockReset();
wrapper
.find('ReactResizeObserver')
.instance()
.onResize();
jest.runAllTimers();
// Mock trigger resize
updateCalled.mockReset();
wrapper
.find('ReactResizeObserver')
.at(index)
.instance()
.onResize();
jest.runAllTimers();
expect(updateCalled).toHaveBeenCalled();
expect(updateCalled).toHaveBeenCalled();
});
}
test('inner', 0);
test('outer', 1);
});
});

View File

@ -34,6 +34,37 @@ exports[`renders ./components/affix/demo/basic.md correctly 1`] = `
</div>
`;
exports[`renders ./components/affix/demo/debug.md correctly 1`] = `
<div
style="height:10000px"
>
<div>
Top
</div>
<div>
<div
class=""
>
<div
style="background:red"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Affix top
</span>
</button>
</div>
</div>
</div>
<div>
Bottom
</div>
</div>
`;
exports[`renders ./components/affix/demo/on-change.md correctly 1`] = `
<div>
<div

View File

@ -0,0 +1,50 @@
---
order: 99
title:
zh-CN: 调试
en-US: Debug
debug: true
---
## zh-CN
DEBUG
## en-US
DEBUG
```jsx
import { Affix, Button } from 'antd';
class Demo extends React.Component {
state = {
top: 10,
};
render() {
return (
<div style={{ height: 10000 }}>
<div>Top</div>
<Affix offsetTop={this.state.top}>
<div style={{ background: 'red' }}>
<Button
type="primary"
onClick={() => {
this.setState({
top: this.state.top + 10,
});
}}
>
Affix top
</Button>
</div>
</Affix>
<div>Bottom</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
```

View File

@ -256,8 +256,8 @@ class Affix extends React.Component<AffixProps, AffixState> {
// =================== Render ===================
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
const { affixStyle, placeholderStyle, status } = this.state;
const { prefixCls, style, children } = this.props;
const { affixStyle, placeholderStyle } = this.state;
const { prefixCls, children } = this.props;
const className = classNames({
[getPrefixCls('affix', prefixCls)]: affixStyle,
});
@ -267,22 +267,26 @@ class Affix extends React.Component<AffixProps, AffixState> {
if (process.env.NODE_ENV === 'test') {
props = omit(props, ['onTestUpdatePosition']);
}
const mergedPlaceholderStyle = {
...(status === AffixStatus.None ? placeholderStyle : null),
...style,
};
return (
<div {...props} style={mergedPlaceholderStyle} ref={this.savePlaceholderNode}>
<div className={className} ref={this.saveFixedNode} style={this.state.affixStyle}>
<ResizeObserver
onResize={() => {
this.updatePosition();
}}
>
{children}
</ResizeObserver>
<ResizeObserver
onResize={() => {
this.updatePosition();
}}
>
<div {...props} ref={this.savePlaceholderNode}>
{affixStyle && <div style={placeholderStyle} aria-hidden="true" />}
<div className={className} ref={this.saveFixedNode} style={affixStyle}>
<ResizeObserver
onResize={() => {
this.updatePosition();
}}
>
{children}
</ResizeObserver>
</div>
</div>
</div>
</ResizeObserver>
);
};

View File

@ -145,6 +145,8 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
};
private inkNode: HTMLSpanElement;
// scroll scope's container
private scrollContainer: HTMLElement | Window;
private links: string[] = [];
private scrollEvent: any;
@ -174,7 +176,8 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
componentDidMount() {
const { getContainer } = this.props as AnchorDefaultProps;
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
this.scrollContainer = getContainer();
this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
this.handleScroll();
}
@ -185,6 +188,16 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
componentDidUpdate() {
if (this.scrollEvent) {
const { getContainer } = this.props as AnchorDefaultProps;
const currentContainer = getContainer();
if (this.scrollContainer !== currentContainer) {
this.scrollContainer = currentContainer;
this.scrollEvent.remove();
this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
this.handleScroll();
}
}
this.updateInk();
}

View File

@ -4,6 +4,8 @@ import Anchor from '..';
const { Link } = Anchor;
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
describe('Anchor Render', () => {
it('Anchor render perfectly', () => {
const wrapper = mount(
@ -62,7 +64,7 @@ describe('Anchor Render', () => {
wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
await new Promise(resolve => setTimeout(resolve, 1000));
await delay(1000);
expect(scrollToSpy).toHaveBeenCalled();
});
@ -130,4 +132,116 @@ describe('Anchor Render', () => {
expect(event).not.toBe(undefined);
expect(link).toEqual({ href, title });
});
it('Different function returns the same DOM', async () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(<div id="API">Hello</div>, { attachTo: root });
const getContainerA = () => {
return document.getElementById('API');
};
const getContainerB = () => {
return document.getElementById('API');
};
const wrapper = mount(
<Anchor getContainer={getContainerA}>
<Link href="#API" title="API" />
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
await delay(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).not.toHaveBeenCalled();
});
it('Different function returns different DOM', async () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(
<div>
<div id="API1">Hello</div>
<div id="API2">World</div>
</div>,
{ attachTo: root },
);
const getContainerA = () => {
return document.getElementById('API1');
};
const getContainerB = () => {
return document.getElementById('API2');
};
const wrapper = mount(
<Anchor getContainer={getContainerA}>
<Link href="#API1" title="API1" />
<Link href="#API2" title="API2" />
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).toHaveBeenCalled();
});
it('Same function returns the same DOM', () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(<div id="API">Hello</div>, { attachTo: root });
const getContainer = () => document.getElementById('API');
const wrapper = mount(
<Anchor getContainer={getContainer}>
<Link href="#API" title="API" />
</Anchor>,
);
wrapper.find('a[href="#API"]').simulate('click');
wrapper.instance().handleScroll();
expect(wrapper.instance().state).not.toBe(null);
});
it('Same function returns different DOM', async () => {
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(
<div>
<div id="API1">Hello</div>
<div id="API2">World</div>
</div>,
{ attachTo: root },
);
const holdContainer = {
container: document.getElementById('API1'),
};
const getContainer = () => {
return holdContainer.container;
};
const wrapper = mount(
<Anchor getContainer={getContainer}>
<Link href="#API1" title="API1" />
<Link href="#API2" title="API2" />
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
holdContainer.container = document.getElementById('API2');
wrapper.setProps({ 'data-only-trigger-re-render': true });
expect(removeListenerSpy).toHaveBeenCalled();
});
});

View File

@ -66,26 +66,6 @@
}
.button-disabled();
}
.button-variant-danger(@color; @background; @border) {
.button-color(@color; @background; @border);
&:hover {
.button-color(
@btn-primary-color; ~`colorPalette('@{color}', 5) `; ~`colorPalette('@{color}', 5) `
);
}
&:focus {
.button-color(
~`colorPalette('@{color}', 5) `; @component-background; ~`colorPalette('@{color}', 5) `
);
}
&:active,
&.active {
.button-color(
@btn-primary-color; ~`colorPalette('@{color}', 7) `; ~`colorPalette('@{color}', 7) `
);
}
.button-disabled();
}
.button-variant-ghost(@color; @border: @color) {
.button-color(@color; transparent; @border);
text-shadow: none;
@ -230,7 +210,7 @@
}
// danger button style
.btn-danger() {
.button-variant-danger(@btn-danger-color, @btn-danger-bg, @btn-danger-border);
.button-variant-primary(@btn-danger-color, @btn-danger-bg);
}
// link button style
.btn-link() {

View File

@ -40,14 +40,14 @@ cols: 1
### Card.Grid
| Property | Description | Type | Default | 版本 |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------- | ---------------------- | ------ | ------- | ---- |
| className | 网格容器类名 | string | - | |
| style | 定义网格容器类名的样式 | object | - | |
### Card.Meta
| Property | Description | Type | Default | 版本 |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------- | ------------------ | --------- | ------- | ---- |
| avatar | 头像/图标 | ReactNode | - | |
| className | 容器类名 | string | - | |

View File

@ -139,7 +139,7 @@
& > span {
position: relative;
display: inline-block;
display: block;
min-width: 32px;
font-size: 14px;
line-height: 22px;
@ -155,7 +155,8 @@
line-height: 22px;
}
a {
a,
i {
display: inline-block;
width: 100%;
color: @text-color-secondary;
@ -163,6 +164,7 @@
&:hover {
color: @primary-color;
transition: color 0.3s;
}
}
}

View File

@ -14,7 +14,7 @@ cols: 1
## API
| Property | Description | Type | Default | Version |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| actions | 在评论内容下面呈现的操作项列表 | Array<ReactNode> | - | 3.11.0 |
| author | 要显示为注释作者的元素 | string\|ReactNode | - | 3.11.0 |

View File

@ -9607,7 +9607,7 @@ exports[`ConfigProvider components Progress configProvider 1`] = `
>
<div
class="config-progress-bg"
style="width:0%;height:8px;border-radius:100px"
style="width:0%;height:8px;border-radius:"
/>
</div>
</div>
@ -9634,7 +9634,7 @@ exports[`ConfigProvider components Progress normal 1`] = `
>
<div
class="ant-progress-bg"
style="width:0%;height:8px;border-radius:100px"
style="width:0%;height:8px;border-radius:"
/>
</div>
</div>
@ -9661,7 +9661,7 @@ exports[`ConfigProvider components Progress prefixCls 1`] = `
>
<div
class="prefix-Progress-bg"
style="width:0%;height:8px;border-radius:100px"
style="width:0%;height:8px;border-radius:"
/>
</div>
</div>

View File

@ -354,7 +354,7 @@ describe('ConfigProvider', () => {
// Modal
testPair('Modal', props => (
<div>
<Modal {...props} visible>
<Modal {...props} visible getContainer={false}>
Bamboo is Little Light
</Modal>
</div>

View File

@ -4,11 +4,12 @@ import Drawer from '..';
import Button from '../../button';
class MultiDrawer extends React.Component {
state = { visible: false, childrenDrawer: false };
state = { visible: false, childrenDrawer: false, hasChildren: true };
showDrawer = () => {
this.setState({
visible: true,
hasChildren: true,
});
};
@ -21,6 +22,7 @@ class MultiDrawer extends React.Component {
showChildrenDrawer = () => {
this.setState({
childrenDrawer: true,
hasChildren: true,
});
};
@ -30,14 +32,23 @@ class MultiDrawer extends React.Component {
});
};
onRemoveChildDrawer = () => {
this.setState({
hasChildren: false,
});
};
render() {
const { childrenDrawer, visible } = this.state;
const { childrenDrawer, visible, hasChildren } = this.state;
const { placement } = this.props;
return (
<div>
<Button type="primary" id="open_drawer" onClick={this.showDrawer}>
Open drawer
</Button>
<Button type="primary" id="remove_drawer" onClick={this.onRemoveChildDrawer}>
rm child drawer
</Button>
<Drawer
title="Multi-level drawer"
className="test_drawer"
@ -50,17 +61,19 @@ class MultiDrawer extends React.Component {
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
Two-level drawer
</Button>
<Drawer
title="Two-level Drawer"
width={320}
className="Two-level"
getContainer={false}
placement={placement}
onClose={this.onChildrenDrawerClose}
visible={childrenDrawer}
>
<div id="two_drawer_text">This is two-level drawer</div>
</Drawer>
{hasChildren && (
<Drawer
title="Two-level Drawer"
width={320}
className="Two-level"
getContainer={false}
placement={placement}
onClose={this.onChildrenDrawerClose}
visible={childrenDrawer}
>
<div id="two_drawer_text">This is two-level drawer</div>
</Drawer>
)}
<div
style={{
position: 'absolute',
@ -121,4 +134,17 @@ describe('Drawer', () => {
expect(translateX).toEqual('translateY(180px)');
expect(wrapper.find('#two_drawer_text').exists()).toBe(true);
});
it('render MultiDrawer is child in unmount', () => {
const wrapper = mount(<MultiDrawer placement="top" mask={false} />);
wrapper.find('button#open_drawer').simulate('click');
wrapper.find('button#open_two_drawer').simulate('click');
wrapper.find('button#remove_drawer').simulate('click');
let translateX = wrapper.find('.ant-drawer.test_drawer').get(0).props.style.transform;
expect(translateX).toEqual(undefined);
wrapper.find('button#open_two_drawer').simulate('click');
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

@ -21,7 +21,7 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr
| --- | --- | --- | --- | --- |
| closable | Whether a close (x) button is visible on top right of the Drawer dialog or not. | boolean | true | 3.7.0 |
| destroyOnClose | Whether to unmount child components on closing drawer or not. | boolean | false | 3.7.0 |
| getContainer | Return the mounted node for Drawer. | HTMLElement \| `() => HTMLElement` \| Selectors | 'body' | 3.7.0 |
| getContainer | Return the mounted node for Drawer. | HTMLElement \| `() => HTMLElement` \| Selectors \| false | 'body' | 3.7.0 |
| mask | Whether to show mask or not. | Boolean | true | 3.7.0 |
| maskClosable | Clicking on the mask (area outside the Drawer) to close the Drawer or not. | boolean | true | 3.7.0 |
| maskStyle | Style for Drawer's mask element. | object | {} | 3.7.0 |

View File

@ -20,7 +20,7 @@ type placementType = (typeof PlacementTypes)[number];
export interface DrawerProps {
closable?: boolean;
destroyOnClose?: boolean;
getContainer?: string | HTMLElement | getContainerFunc;
getContainer?: string | HTMLElement | getContainerFunc | false;
maskClosable?: boolean;
mask?: boolean;
maskStyle?: React.CSSProperties;
@ -63,9 +63,19 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
push: false,
};
parentDrawer: Drawer;
parentDrawer: Drawer | null;
destroyClose: boolean;
public componentDidMount() {
// fix: delete drawer in child and re-render, no push started.
// <Drawer>{show && <Drawer />}</Drawer>
const { visible } = this.props;
if (visible && this.parentDrawer) {
this.parentDrawer.push();
}
}
public componentDidUpdate(preProps: DrawerProps) {
const { visible } = this.props;
if (preProps.visible !== visible && this.parentDrawer) {
@ -77,19 +87,13 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
}
}
close = (e: EventType) => {
const { visible, onClose } = this.props;
if (visible !== undefined && onClose) {
onClose(e);
public componentWillUnmount() {
// unmount drawer in child, clear push.
if (this.parentDrawer) {
this.parentDrawer.pull();
this.parentDrawer = null;
}
};
onMaskClick = (e: EventType) => {
if (!this.props.maskClosable && !(e.nativeEvent instanceof KeyboardEvent)) {
return;
}
this.close(e);
};
}
push = () => {
this.setState({
@ -116,7 +120,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
getDestroyOnClose = () => this.props.destroyOnClose && !this.props.visible;
// get drawar push width or height
// get drawer push width or height
getPushTransform = (placement?: placementType) => {
if (placement === 'left' || placement === 'right') {
return `translateX(${placement === 'left' ? 180 : -180}px)`;
@ -152,10 +156,10 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
}
renderCloseIcon() {
const { closable, prefixCls } = this.props;
const { closable, prefixCls, onClose } = this.props;
return (
closable && (
<button onClick={this.close} aria-label="Close" className={`${prefixCls}-close`}>
<button onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
<Icon type="close" />
</button>
)
@ -200,7 +204,7 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
);
};
// render Provider for Multi-level drawe
// render Provider for Multi-level drawer
renderProvider = (value: Drawer) => {
const {
prefixCls,
@ -214,11 +218,9 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
closable,
destroyOnClose,
mask,
maskClosable,
bodyStyle,
title,
push,
onClose,
visible,
// ConfigConsumerProps
getPopupContainer,
@ -250,7 +252,6 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
{...offsetStyle}
prefixCls={prefixCls}
open={this.props.visible}
onMaskClick={this.onMaskClick}
showMask={mask}
placement={placement}
style={this.getRcDrawerStyle()}

View File

@ -20,9 +20,9 @@ title: Drawer
| --- | --- | --- | --- | --- |
| closable | 是否显示右上角的关闭按钮 | boolean | true | 3.7.0 |
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | 3.7.0 |
| getContainer | 指定 Drawer 挂载的 HTML 节点 | HTMLElement \| `() => HTMLElement` \| Selectors | 'body' | 3.7.0 |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | 3.7.0 | 3.7.0 |
| mask | 是否展示遮罩 | Boolean | true |
| getContainer | 指定 Drawer 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| `() => HTMLElement` \| Selectors \| false | 'body' | 3.7.0 |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | 3.7.0 |
| mask | 是否展示遮罩 | Boolean | true | 3.7.0 |
| maskStyle | 遮罩样式 | object | {} | 3.7.0 |
| style | 可用于设置 Drawer 最外层容器的样式 | object | - | 3.7.0 |
| bodyStyle | 可用于设置 Drawer 的样式,调整浮层位置等 | object | - | 3.12.0 |

View File

@ -11,10 +11,10 @@
z-index: @zindex-modal;
width: 0%;
height: 100%;
transition: transform @animation-duration-slow @ease-base-out;
> * {
transition: transform @animation-duration-slow @ease-base-in,
box-shadow @animation-duration-slow @ease-base-in;
transition: transform @animation-duration-slow @ease-base-out,
box-shadow @animation-duration-slow @ease-base-out;
}
&-content-wrapper {
@ -197,7 +197,6 @@
transition: opacity @animation-duration-slow linear, height 0s ease @animation-duration-slow;
}
&-open {
transition: transform @animation-duration-slow @ease-base-out;
&-content {
box-shadow: @shadow-2;
}

View File

@ -531,6 +531,21 @@ exports[`renders ./components/empty/demo/customize.md correctly 1`] = `
</div>
`;
exports[`renders ./components/empty/demo/description.md correctly 1`] = `
<div
class="ant-empty"
>
<div
class="ant-empty-image"
>
<img
alt="empty"
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTg0IiBoZWlnaHQ9IjE1MiIgdmlld0JveD0iMCAwIDE4NCAxNTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0IDMxLjY3KSI+CiAgICAgIDxlbGxpcHNlIGZpbGwtb3BhY2l0eT0iLjgiIGZpbGw9IiNGNUY1RjciIGN4PSI2Ny43OTciIGN5PSIxMDYuODkiIHJ4PSI2Ny43OTciIHJ5PSIxMi42NjgiLz4KICAgICAgPHBhdGggZD0iTTEyMi4wMzQgNjkuNjc0TDk4LjEwOSA0MC4yMjljLTEuMTQ4LTEuMzg2LTIuODI2LTIuMjI1LTQuNTkzLTIuMjI1aC01MS40NGMtMS43NjYgMC0zLjQ0NC44MzktNC41OTIgMi4yMjVMMTMuNTYgNjkuNjc0djE1LjM4M2gxMDguNDc1VjY5LjY3NHoiIGZpbGw9IiNBRUI4QzIiLz4KICAgICAgPHBhdGggZD0iTTEwMS41MzcgODYuMjE0TDgwLjYzIDYxLjEwMmMtMS4wMDEtMS4yMDctMi41MDctMS44NjctNC4wNDgtMS44NjdIMzEuNzI0Yy0xLjU0IDAtMy4wNDcuNjYtNC4wNDggMS44NjdMNi43NjkgODYuMjE0djEzLjc5Mmg5NC43NjhWODYuMjE0eiIgZmlsbD0idXJsKCNsaW5lYXJHcmFkaWVudC0xKSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTMuNTYpIi8+CiAgICAgIDxwYXRoIGQ9Ik0zMy44MyAwaDY3LjkzM2E0IDQgMCAwIDEgNCA0djkzLjM0NGE0IDQgMCAwIDEtNCA0SDMzLjgzYTQgNCAwIDAgMS00LTRWNGE0IDQgMCAwIDEgNC00eiIgZmlsbD0iI0Y1RjVGNyIvPgogICAgICA8cGF0aCBkPSJNNDIuNjc4IDkuOTUzaDUwLjIzN2EyIDIgMCAwIDEgMiAyVjM2LjkxYTIgMiAwIDAgMS0yIDJINDIuNjc4YTIgMiAwIDAgMS0yLTJWMTEuOTUzYTIgMiAwIDAgMSAyLTJ6TTQyLjk0IDQ5Ljc2N2g0OS43MTNhMi4yNjIgMi4yNjIgMCAxIDEgMCA0LjUyNEg0Mi45NGEyLjI2MiAyLjI2MiAwIDAgMSAwLTQuNTI0ek00Mi45NCA2MS41M2g0OS43MTNhMi4yNjIgMi4yNjIgMCAxIDEgMCA0LjUyNUg0Mi45NGEyLjI2MiAyLjI2MiAwIDAgMSAwLTQuNTI1ek0xMjEuODEzIDEwNS4wMzJjLS43NzUgMy4wNzEtMy40OTcgNS4zNi02LjczNSA1LjM2SDIwLjUxNWMtMy4yMzggMC01Ljk2LTIuMjktNi43MzQtNS4zNmE3LjMwOSA3LjMwOSAwIDAgMS0uMjIyLTEuNzlWNjkuNjc1aDI2LjMxOGMyLjkwNyAwIDUuMjUgMi40NDggNS4yNSA1LjQydi4wNGMwIDIuOTcxIDIuMzcgNS4zNyA1LjI3NyA1LjM3aDM0Ljc4NWMyLjkwNyAwIDUuMjc3LTIuNDIxIDUuMjc3LTUuMzkzVjc1LjFjMC0yLjk3MiAyLjM0My01LjQyNiA1LjI1LTUuNDI2aDI2LjMxOHYzMy41NjljMCAuNjE3LS4wNzcgMS4yMTYtLjIyMSAxLjc4OXoiIGZpbGw9IiNEQ0UwRTYiLz4KICAgIDwvZz4KICAgIDxwYXRoIGQ9Ik0xNDkuMTIxIDMzLjI5MmwtNi44MyAyLjY1YTEgMSAwIDAgMS0xLjMxNy0xLjIzbDEuOTM3LTYuMjA3Yy0yLjU4OS0yLjk0NC00LjEwOS02LjUzNC00LjEwOS0xMC40MDhDMTM4LjgwMiA4LjEwMiAxNDguOTIgMCAxNjEuNDAyIDAgMTczLjg4MSAwIDE4NCA4LjEwMiAxODQgMTguMDk3YzAgOS45OTUtMTAuMTE4IDE4LjA5Ny0yMi41OTkgMTguMDk3LTQuNTI4IDAtOC43NDQtMS4wNjYtMTIuMjgtMi45MDJ6IiBmaWxsPSIjRENFMEU2Ii8+CiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDkuNjUgMTUuMzgzKSIgZmlsbD0iI0ZGRiI+CiAgICAgIDxlbGxpcHNlIGN4PSIyMC42NTQiIGN5PSIzLjE2NyIgcng9IjIuODQ5IiByeT0iMi44MTUiLz4KICAgICAgPHBhdGggZD0iTTUuNjk4IDUuNjNIMEwyLjg5OC43MDR6TTkuMjU5LjcwNGg0Ljk4NVY1LjYzSDkuMjU5eiIvPgogICAgPC9nPgogIDwvZz4KPC9zdmc+Cg=="
/>
</div>
</div>
`;
exports[`renders ./components/empty/demo/simple.md correctly 1`] = `
<div
class="ant-empty ant-empty-normal"

View File

@ -7,4 +7,9 @@ describe('Empty', () => {
const wrapper = mount(<Empty imageStyle={{ height: 20 }} />);
expect(wrapper.find('.ant-empty-image').props().style.height).toBe(20);
});
it('description can be false', () => {
const wrapper = mount(<Empty description={false} />);
expect(wrapper.find('.ant-empty-description').length).toBe(0);
});
});

View File

@ -0,0 +1,20 @@
---
order: 4
title:
zh-CN: 无描述
en-US: No description
---
## zh-CN
无描述展示。
## en-US
Simplest Usage with no description.
```jsx
import { Empty } from 'antd';
ReactDOM.render(<Empty description={false} />, mountNode);
```

View File

@ -39,7 +39,7 @@ const OriginEmpty: React.SFC<EmptyProps> = (props: EmptyProps) => (
<LocaleReceiver componentName="Empty">
{(locale: TransferLocale) => {
const prefixCls = getPrefixCls('empty', customizePrefixCls);
const des = description || locale.description;
const des = typeof description !== 'undefined' ? description : locale.description;
const alt = typeof des === 'string' ? des : 'empty';
let imageNode: React.ReactNode = null;
@ -64,7 +64,7 @@ const OriginEmpty: React.SFC<EmptyProps> = (props: EmptyProps) => (
<div className={`${prefixCls}-image`} style={imageStyle}>
{imageNode}
</div>
<p className={`${prefixCls}-description`}>{des}</p>
{des && <p className={`${prefixCls}-description`}>{des}</p>}
{children && <div className={`${prefixCls}-footer`}>{children}</div>}
</div>
);

View File

@ -141,7 +141,7 @@ export type WrappedFormUtils<V = any> = {
/** 获取一个输入控件的值 */
getFieldValue(fieldName: string): any;
/** 设置一组输入控件的值 */
setFieldsValue(obj: Object): void;
setFieldsValue(obj: Object, callback?: Function): void;
/** 设置一组输入控件的值 */
setFields(obj: Object): void;
/** 校验并获取一组输入域的值与 Error */

View File

@ -92,7 +92,7 @@ If the form has been decorated by `Form.create` then it has `this.props.form` pr
| isFieldValidating | Check if the specified field is being validated. | Function(name) | |
| resetFields | Reset the specified fields' value(to `initialValue`) and status. If you don't specify a parameter, all the fields will be reset. | Function(\[names: string\[]]) | |
| setFields | Set value and error state of fields. [Code Sample](https://github.com/react-component/form/blob/3b9959b57ab30b41d8890ff30c79a7e7c383cad3/examples/server-validate.js#L74-L79) | ({<br />&nbsp;&nbsp;\[fieldName\]: {value: any, errors: \[Error\] }<br />}) => void | |
| setFieldsValue | Set the value of a field. (Note: please don't use it in `componentWillReceiveProps`, otherwise, it will cause an endless loop, [reason](https://github.com/ant-design/ant-design/issues/2985)) | ({ \[fieldName\]&#x3A; value }) => void | |
| setFieldsValue | Set the value of a field. (Note: please don't use it in `componentWillReceiveProps`, otherwise, it will cause an endless loop, [reason](https://github.com/ant-design/ant-design/issues/2985)) | (<br />&nbsp;&nbsp;{ \[fieldName\]&#x3A; value },<br />&nbsp;&nbsp;callback: Function<br />) => void | |
| validateFields | Validate the specified fields and get theirs values and errors. If you don't specify the parameter of fieldNames, you will validate all fields. | (<br />&nbsp;&nbsp;\[fieldNames: string\[]],<br />&nbsp;&nbsp;\[options: object\],<br />&nbsp;&nbsp;callback(errors, values)<br />) => void | |
| validateFieldsAndScroll | This function is similar to `validateFields`, but after validation, if the target field is not in visible area of form, form will be automatically scrolled to the target field area. | same as `validateFields` | |

View File

@ -95,7 +95,7 @@ this.form // => The instance of CustomizedForm
| isFieldValidating | 判断一个输入控件是否在校验状态 | Function(name) | |
| resetFields | 重置一组输入控件的值(为 `initialValue`)与状态,如不传入参数,则重置所有组件 | Function(\[names: string\[]]) | |
| setFields | 设置一组输入控件的值与错误状态:[代码](https://github.com/react-component/form/blob/3b9959b57ab30b41d8890ff30c79a7e7c383cad3/examples/server-validate.js#L74-L79) | ({<br />&nbsp;&nbsp;\[fieldName\]: {value: any, errors: \[Error\] }<br />}) => void | |
| setFieldsValue | 设置一组输入控件的值(注意:不要在 `componentWillReceiveProps` 内使用,否则会导致死循环,[原因](https://github.com/ant-design/ant-design/issues/2985) | ({ \[fieldName\]&#x3A; value }) => void | |
| setFieldsValue | 设置一组输入控件的值(注意:不要在 `componentWillReceiveProps` 内使用,否则会导致死循环,[原因](https://github.com/ant-design/ant-design/issues/2985) | (<br />&nbsp;&nbsp;{ \[fieldName\]&#x3A; value },<br />&nbsp;&nbsp;callback: Function<br />) => void | |
| validateFields | 校验并获取一组输入域的值与 Error若 fieldNames 参数为空,则校验全部组件 | (<br />&nbsp;&nbsp;\[fieldNames: string\[]],<br />&nbsp;&nbsp;\[options: object\],<br />&nbsp;&nbsp;callback(errors, values)<br />) => void | |
| validateFieldsAndScroll | 与 `validateFields` 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 | 参考 `validateFields` | |
@ -205,7 +205,7 @@ validateFields(['field1', 'field2'], options, (errors, values) => {
| labelCol | label 标签布局,同 `<Col>` 组件,设置 `span` `offset` 值,如 `{span: 3, offset: 12}``sm: {span: 3, offset: 12}`。在 3.14.0 之后,你可以通过 Form 的 labelCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。 | [object](https://ant.design/components/grid/#Col) | | |
| required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean | false | |
| validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | | |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol。在 3.14.0 之后,你可以通过 Form 的 labelCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。 | [object](https://ant.design/components/grid/#Col) | | |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 labelCol。在 3.14.0 之后,你可以通过 Form 的 wrapperCol 进行统一设置。当和 Form 同时设置时,以 FormItem 为准。 | [object](https://ant.design/components/grid/#Col) | | |
### 校验规则

View File

@ -147,7 +147,7 @@ const App = () => (
<Transfer dataSource={[]} showSearch targetKeys={[]} render={item => item.title} />
<Calendar fullscreen={false} value={moment()} />
<Table dataSource={[]} columns={columns} />
<Modal title="Locale Modal" visible>
<Modal title="Locale Modal" visible getContainer={false}>
<p>Locale Modal</p>
</Modal>
</div>

View File

@ -569,7 +569,6 @@ describe('Menu', () => {
.instance()
.getMenuOpenAnimation(''),
).toBe('');
expect(wrapper.find('InternalMenu').state().switchingModeFromInline).toBe(false);
});
it('MenuItem should not render Tooltip when inlineCollapsed is false', () => {

View File

@ -276,10 +276,6 @@ class InternalMenu extends React.Component<InternalMenuProps, MenuState> {
// submenu should hide without animation
if (this.state.switchingModeFromInline) {
menuOpenAnimation = '';
this.setState({
switchingModeFromInline: false,
});
// this.switchingModeFromInline = false;
} else {
menuOpenAnimation = 'zoom-big';
}

View File

@ -68,7 +68,7 @@ export interface ModalProps {
maskTransitionName?: string;
transitionName?: string;
className?: string;
getContainer?: (instance: React.ReactInstance) => HTMLElement;
getContainer?: string | HTMLElement | getContainerFunc | false | null;
zIndex?: number;
bodyStyle?: React.CSSProperties;
maskStyle?: React.CSSProperties;
@ -78,6 +78,8 @@ export interface ModalProps {
prefixCls?: string;
}
type getContainerFunc = () => HTMLElement;
export interface ModalFuncProps {
prefixCls?: string;
className?: string;
@ -106,7 +108,7 @@ export interface ModalFuncProps {
maskStyle?: React.CSSProperties;
type?: string;
keyboard?: boolean;
getContainer?: (instance: React.ReactInstance) => HTMLElement;
getContainer?: string | HTMLElement | getContainerFunc | false | null;
autoFocusButton?: null | 'ok' | 'cancel';
transitionName?: string;
maskTransitionName?: string;
@ -221,7 +223,7 @@ export default class Modal extends React.Component<ModalProps, {}> {
return (
<Dialog
{...restProps}
getContainer={getContainer || getContextPopupContainer}
getContainer={getContainer === undefined ? getContextPopupContainer : getContainer}
prefixCls={prefixCls}
wrapClassName={classNames({ [`${prefixCls}-centered`]: !!centered }, wrapClassName)}
footer={footer === undefined ? defaultFooter : footer}

View File

@ -2,91 +2,94 @@
exports[`Modal render correctly 1`] = `
<div>
<div />
<div>
<div
class="ant-modal-mask fade-appear"
/>
<div
class="ant-modal-wrap "
role="dialog"
tabindex="-1"
>
<div
class="ant-modal zoom-appear"
role="document"
style="width: 520px;"
>
<div>
<div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
class="ant-modal-mask fade-appear"
/>
<div
class="ant-modal-content"
class="ant-modal-wrap "
role="dialog"
tabindex="-1"
>
<button
aria-label="Close"
class="ant-modal-close"
type="button"
<div
class="ant-modal zoom-appear"
role="document"
style="width: 520px;"
>
<span
class="ant-modal-close-x"
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
<div
class="ant-modal-content"
>
<i
aria-label="icon: close"
class="anticon anticon-close ant-modal-close-icon"
<button
aria-label="Close"
class="ant-modal-close"
type="button"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
class="ant-modal-close-x"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
</span>
</button>
<div
class="ant-modal-body"
>
Here is content of Modal
</div>
<div
class="ant-modal-footer"
>
<div>
<button
class="ant-btn"
type="button"
>
<span>
Cancel
<i
aria-label="icon: close"
class="anticon anticon-close ant-modal-close-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
<div
class="ant-modal-body"
>
<span>
OK
</span>
</button>
Here is content of Modal
</div>
<div
class="ant-modal-footer"
>
<div>
<button
class="ant-btn"
type="button"
>
<span>
Cancel
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
OK
</span>
</button>
</div>
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
</div>
</div>
</div>
@ -95,69 +98,72 @@ exports[`Modal render correctly 1`] = `
exports[`Modal render without footer 1`] = `
<div>
<div />
<div>
<div
class="ant-modal-mask fade-appear"
/>
<div
class="ant-modal-wrap "
role="dialog"
tabindex="-1"
>
<div
class="ant-modal zoom-appear"
role="document"
style="width: 520px;"
>
<div>
<div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
class="ant-modal-mask fade-appear"
/>
<div
class="ant-modal-content"
class="ant-modal-wrap "
role="dialog"
tabindex="-1"
>
<button
aria-label="Close"
class="ant-modal-close"
type="button"
>
<span
class="ant-modal-close-x"
>
<i
aria-label="icon: close"
class="anticon anticon-close ant-modal-close-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
</span>
</button>
<div
class="ant-modal-body"
class="ant-modal zoom-appear"
role="document"
style="width: 520px;"
>
Here is content of Modal
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
<div
class="ant-modal-content"
>
<button
aria-label="Close"
class="ant-modal-close"
type="button"
>
<span
class="ant-modal-close-x"
>
<i
aria-label="icon: close"
class="anticon anticon-close ant-modal-close-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="close"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
/>
</svg>
</i>
</span>
</button>
<div
class="ant-modal-body"
>
Here is content of Modal
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
</div>
</div>
<div
aria-hidden="true"
style="width: 0px; height: 0px; overflow: hidden;"
tabindex="0"
/>
</div>
</div>
</div>

View File

@ -84,8 +84,8 @@ describe('Modal.confirm triggers callbacks correctly', () => {
it('shows animation when close', () => {
jest.useFakeTimers();
open();
$$('.ant-btn')[0].click();
expect($$('.ant-modal-confirm')).toHaveLength(1);
$$('.ant-btn')[0].click();
jest.runAllTimers();
expect($$('.ant-modal-confirm')).toHaveLength(0);
jest.useRealTimers();

View File

@ -165,7 +165,7 @@ export default function confirm(config: ModalFuncProps) {
}
function render(props: any) {
ReactDOM.render(<ConfirmDialog {...props} />, div);
ReactDOM.render(<ConfirmDialog {...props} getContainer={false}/>, div);
}
render(currentConfig);

View File

@ -23,7 +23,7 @@ When requiring users to interact with the application, but without jumping to a
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | 3.1.0 |
| footer | Footer content, set as `footer={null}` when you don't need default buttons | string\|ReactNode | OK and Cancel buttons | |
| forceRender | Force render Modal | boolean | false | 3.12.0 |
| getContainer | Return the mount node for Modal | (instance): HTMLElement | () => document.body | |
| getContainer | Return the mount node for Modal | HTMLElement \| `() => HTMLElement` \| Selectors \| false | document.body | 3.20.2 |
| mask | Whether show mask or not. | Boolean | true | |
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true | |
| maskStyle | Style for modal's mask element. | object | {} | |

View File

@ -26,7 +26,7 @@ title: Modal
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | 3.1.0 |
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | string\|ReactNode | 确定取消按钮 | |
| forceRender | 强制渲染 Modal | boolean | false | 3.12.0 |
| getContainer | 指定 Modal 挂载的 HTML 节点 | (instance): HTMLElement | () => document.body | |
| getContainer | 指定 Modal 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| `() => HTMLElement` \| Selectors \| false | document.body | 3.20.2 |
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | 3.4.2 |
| mask | 是否展示遮罩 | Boolean | true | |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | |

View File

@ -53,6 +53,10 @@
padding: 0 6px;
color: @text-color;
transition: none;
&:hover {
text-decoration: none;
}
}
&:focus,

View File

@ -78,13 +78,13 @@ const Line: React.SFC<LineProps> = props => {
const percentStyle = {
width: `${validProgress(percent)}%`,
height: strokeWidth || (size === 'small' ? 6 : 8),
borderRadius: strokeLinecap === 'square' ? 0 : '100px',
borderRadius: strokeLinecap === 'square' ? 0 : '',
...backgroundProps,
};
const successPercentStyle = {
width: `${validProgress(successPercent)}%`,
height: strokeWidth || (size === 'small' ? 6 : 8),
borderRadius: strokeLinecap === 'square' ? 0 : '100px',
borderRadius: strokeLinecap === 'square' ? 0 : '',
};
const successSegment =
successPercent !== undefined ? (

View File

@ -491,7 +491,7 @@ exports[`renders ./components/progress/demo/dynamic.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:0%;height:8px;border-radius:100px"
style="width:0%;height:8px;border-radius:"
/>
</div>
</div>
@ -664,7 +664,7 @@ exports[`renders ./components/progress/demo/gradient-line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:99.9%;height:8px;border-radius:100px;background-image:linear-gradient(to right, #108ee9 0%, #87d068 100%)"
style="width:99.9%;height:8px;border-radius:;background-image:linear-gradient(to right, #108ee9 0%, #87d068 100%)"
/>
</div>
</div>
@ -688,7 +688,7 @@ exports[`renders ./components/progress/demo/gradient-line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:99.9%;height:8px;border-radius:100px;background-image:linear-gradient(to right, #108ee9, #87d068)"
style="width:99.9%;height:8px;border-radius:;background-image:linear-gradient(to right, #108ee9, #87d068)"
/>
</div>
</div>
@ -854,7 +854,7 @@ exports[`renders ./components/progress/demo/line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:30%;height:8px;border-radius:100px"
style="width:30%;height:8px;border-radius:"
/>
</div>
</div>
@ -878,7 +878,7 @@ exports[`renders ./components/progress/demo/line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:50%;height:8px;border-radius:100px"
style="width:50%;height:8px;border-radius:"
/>
</div>
</div>
@ -902,7 +902,7 @@ exports[`renders ./components/progress/demo/line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:70%;height:8px;border-radius:100px"
style="width:70%;height:8px;border-radius:"
/>
</div>
</div>
@ -943,7 +943,7 @@ exports[`renders ./components/progress/demo/line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:100%;height:8px;border-radius:100px"
style="width:100%;height:8px;border-radius:"
/>
</div>
</div>
@ -984,7 +984,7 @@ exports[`renders ./components/progress/demo/line.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:50%;height:8px;border-radius:100px"
style="width:50%;height:8px;border-radius:"
/>
</div>
</div>
@ -1009,7 +1009,7 @@ exports[`renders ./components/progress/demo/line-mini.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:30%;height:6px;border-radius:100px"
style="width:30%;height:6px;border-radius:"
/>
</div>
</div>
@ -1033,7 +1033,7 @@ exports[`renders ./components/progress/demo/line-mini.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:50%;height:6px;border-radius:100px"
style="width:50%;height:6px;border-radius:"
/>
</div>
</div>
@ -1057,7 +1057,7 @@ exports[`renders ./components/progress/demo/line-mini.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:70%;height:6px;border-radius:100px"
style="width:70%;height:6px;border-radius:"
/>
</div>
</div>
@ -1098,7 +1098,7 @@ exports[`renders ./components/progress/demo/line-mini.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:100%;height:6px;border-radius:100px"
style="width:100%;height:6px;border-radius:"
/>
</div>
</div>
@ -1257,11 +1257,11 @@ exports[`renders ./components/progress/demo/segment.md correctly 1`] = `
>
<div
class="ant-progress-bg"
style="width:60%;height:8px;border-radius:100px"
style="width:60%;height:8px;border-radius:"
/>
<div
class="ant-progress-success-bg"
style="width:30%;height:8px;border-radius:100px"
style="width:30%;height:8px;border-radius:"
/>
</div>
</div>

View File

@ -13,11 +13,11 @@ exports[`Progress render format 1`] = `
>
<div
class="ant-progress-bg"
style="width: 50%; height: 8px; border-radius: 100px;"
style="width: 50%; height: 8px;"
/>
<div
class="ant-progress-success-bg"
style="width: 10%; height: 8px; border-radius: 100px;"
style="width: 10%; height: 8px;"
/>
</div>
</div>
@ -44,7 +44,7 @@ exports[`Progress render negetive progress 1`] = `
>
<div
class="ant-progress-bg"
style="width: 0%; height: 8px; border-radius: 100px;"
style="width: 0%; height: 8px;"
/>
</div>
</div>
@ -71,11 +71,11 @@ exports[`Progress render negetive successPercent 1`] = `
>
<div
class="ant-progress-bg"
style="width: 50%; height: 8px; border-radius: 100px;"
style="width: 50%; height: 8px;"
/>
<div
class="ant-progress-success-bg"
style="width: 0%; height: 8px; border-radius: 100px;"
style="width: 0%; height: 8px;"
/>
</div>
</div>
@ -102,7 +102,7 @@ exports[`Progress render normal progress 1`] = `
>
<div
class="ant-progress-bg"
style="width: 0%; height: 8px; border-radius: 100px;"
style="width: 0%; height: 8px;"
/>
</div>
</div>
@ -129,7 +129,7 @@ exports[`Progress render out-of-range progress 1`] = `
>
<div
class="ant-progress-bg"
style="width: 100%; height: 8px; border-radius: 100px;"
style="width: 100%; height: 8px;"
/>
</div>
</div>
@ -173,7 +173,7 @@ exports[`Progress render out-of-range progress with info 1`] = `
>
<div
class="ant-progress-bg"
style="width: 100%; height: 8px; border-radius: 100px;"
style="width: 100%; height: 8px;"
/>
</div>
</div>
@ -296,7 +296,7 @@ exports[`Progress render strokeColor 2`] = `
style={
Object {
"backgroundImage": "linear-gradient(to right, #108ee9, #87d068)",
"borderRadius": "100px",
"borderRadius": "",
"height": 8,
"width": "50%",
}
@ -363,7 +363,7 @@ exports[`Progress render strokeColor 3`] = `
style={
Object {
"backgroundImage": "linear-gradient(to right, #108ee9 0%, #87d068 100%)",
"borderRadius": "100px",
"borderRadius": "",
"height": 8,
"width": "50%",
}

View File

@ -36,7 +36,7 @@
width: 100%;
vertical-align: middle;
background-color: @progress-remaining-color;
border-radius: 100px;
border-radius: @progress-radius;
}
&-circle-trail {
@ -52,6 +52,7 @@
&-bg {
position: relative;
background-color: @progress-default-color;
border-radius: @progress-radius;
transition: all 0.4s @ease-out-circ 0s;
}

View File

@ -24,7 +24,7 @@
width: 100%;
height: 4px;
background-color: @slider-rail-background-color;
border-radius: 2px;
border-radius: @border-radius-sm;
transition: background-color 0.3s;
}
@ -33,7 +33,7 @@
height: 4px;
background-color: @slider-track-background-color;
border-radius: @border-radius-base;
transition: background-color 0.3s ease;
transition: background-color 0.3s;
}
&-handle {
@ -42,10 +42,10 @@
height: 14px;
margin-top: -5px;
margin-left: -7px;
background-color: @component-background;
border: solid 2px @slider-handle-color;
background-color: @slider-handle-background-color;
border: solid @slider-handle-border-width @slider-handle-color;
border-radius: 50%;
box-shadow: 0;
box-shadow: @slider-handle-shadow;
cursor: pointer;
transition: border-color 0.3s, box-shadow 0.6s,
transform 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);

View File

@ -156,9 +156,9 @@
@btn-default-bg: @component-background;
@btn-default-border: @border-color-base;
@btn-danger-color: @error-color;
@btn-danger-bg: @background-color-base;
@btn-danger-border: @border-color-base;
@btn-danger-color: #fff;
@btn-danger-bg: color(~`colorPalette('@{error-color}', 5) `);
@btn-danger-border: color(~`colorPalette('@{error-color}', 5) `);
@btn-disable-color: @disabled-color;
@btn-disable-bg: @disabled-bg;
@ -370,6 +370,7 @@
@progress-default-color: @processing-color;
@progress-remaining-color: @background-color-base;
@progress-text-color: @text-color;
@progress-radius: 100px;
// Menu
// ---
@ -491,6 +492,8 @@
@tabs-highlight-color: @primary-color;
@tabs-hover-color: @primary-5;
@tabs-active-color: @primary-7;
@tabs-card-gutter: 2px;
@tabs-card-tab-active-border-top: 2px solid transparent;
// BackTop
// ---
@ -550,11 +553,14 @@
@slider-rail-background-color-hover: #e1e1e1;
@slider-track-background-color: @primary-3;
@slider-track-background-color-hover: @primary-4;
@slider-handle-border-width: 2px;
@slider-handle-background-color: @component-background;
@slider-handle-color: @primary-3;
@slider-handle-color-hover: @primary-4;
@slider-handle-color-focus: tint(@primary-color, 20%);
@slider-handle-color-focus-shadow: fade(@primary-color, 20%);
@slider-handle-color-tooltip-open: @primary-color;
@slider-handle-shadow: 0;
@slider-dot-border-color: @border-color-split;
@slider-dot-border-color-active: tint(@primary-color, 50%);
@slider-disabled-color: @disabled-color;
@ -629,3 +635,11 @@
// ---
@drawer-header-padding: 16px 24px;
@drawer-body-padding: 24px;
// Timeline
// ---
@timeline-width: 2px;
@timeline-color: @border-color-split;
@timeline-dot-border-width: 2px;
@timeline-dot-color: @primary-color;
@timeline-dot-bg: @component-background;

View File

@ -23,7 +23,7 @@ Ant Design has 3 types of Tabs for different situations.
| --- | --- | --- | --- | --- |
| activeKey | Current TabPane's key | string | - | |
| animated | Whether to change tabs with animation. Only works while `tabPosition="top"\|"bottom"` | boolean \| {inkBar:boolean, tabPane:boolean} | `true`, `false` when `type="card"` | |
| renderTabBar | replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ReactNode) => React.ReactNode | - | 3.9.0 |
| renderTabBar | replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - | 3.9.0 |
| defaultActiveKey | Initial active TabPane's key, if `activeKey` is not set. | string | - | |
| hideAdd | Hide plus icon or not. Only works while `type="editable-card"` | boolean | `false` | |
| size | preset tab bar size | `large` \| `default` \| `small` | `default` | |

View File

@ -31,7 +31,10 @@ export interface TabsProps {
className?: string;
animated?: boolean | { inkBar: boolean; tabPane: boolean };
tabBarGutter?: number;
renderTabBar?: (props: TabsProps, DefaultTabBar: React.ReactNode) => React.ReactElement<any>;
renderTabBar?: (
props: TabsProps,
DefaultTabBar: React.ComponentClass<any>,
) => React.ReactElement<any>;
destroyInactiveTabPane?: boolean;
}

View File

@ -26,7 +26,7 @@ Ant Design 依次提供了三级选项卡,分别用于不同的场景。
| --- | --- | --- | --- | --- |
| activeKey | 当前激活 tab 面板的 key | string | 无 | |
| animated | 是否使用动画切换 Tabs`tabPosition=top|bottom` 时有效 | boolean \| {inkBar:boolean, tabPane:boolean} | true, 当 type="card" 时为 false | |
| renderTabBar | 替换 TabBar用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ReactNode) => React.ReactNode | 无 | 3.9.0 |
| renderTabBar | 替换 TabBar用于二次封装标签头 | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | 无 | 3.9.0 |
| defaultActiveKey | 初始化选中面板的 key如果没有设置 activeKey | string | 第一个面板 | |
| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false | |
| size | 大小,提供 `large` `default``small` 三种大小 | string | 'default' | |

View File

@ -14,7 +14,7 @@
&&-card &-card-bar &-tab {
height: @tabs-card-height;
margin: 0;
margin-right: 2px;
margin-right: @tabs-card-gutter;
padding: 0 16px;
line-height: @tabs-card-height - 2px;
background: @tabs-card-head-background;
@ -28,6 +28,10 @@
background: @component-background;
border-color: @border-color-split;
border-bottom: @border-width-base solid @component-background;
&::before {
border-top: @tabs-card-tab-active-border-top;
}
}
&&-card &-card-bar &-tab-disabled {
color: @tabs-card-active-color;

View File

@ -181,6 +181,18 @@
cursor: pointer;
transition: color 0.3s @ease-in-out;
&::before {
position: absolute;
top: -1px;
left: 0;
width: 100%;
border-top: 2px solid transparent;
border-radius: @border-radius-base @border-radius-base 0 0;
transition: all 0.3s;
content: '';
pointer-events: none;
}
&:last-child {
margin-right: 0;
}

View File

@ -2,7 +2,6 @@
@import '../../style/mixins/index';
@timeline-prefix-cls: ~'@{ant-prefix}-timeline';
@timeline-color: @border-color-split;
.@{timeline-prefix-cls} {
.reset-component;
@ -23,7 +22,7 @@
top: 0.75em;
left: 4px;
height: 100%;
border-left: 2px solid @timeline-color;
border-left: @timeline-width solid @timeline-color;
}
&-pending &-head {
@ -38,18 +37,20 @@
position: absolute;
width: 10px;
height: 10px;
background-color: @component-background;
border: 2px solid transparent;
background-color: @timeline-dot-bg;
border: @timeline-dot-border-width solid transparent;
border-radius: 100px;
&-blue {
color: @primary-color;
border-color: @primary-color;
}
&-red {
color: @error-color;
border-color: @error-color;
}
&-green {
color: @success-color;
border-color: @success-color;

View File

@ -36,7 +36,7 @@
padding: 3px 5px;
color: @text-color;
text-decoration: none;
border-radius: 2px;
border-radius: @border-radius-sm;
cursor: pointer;
transition: all 0.3s;
&:hover {

View File

@ -97,7 +97,7 @@ exports[`Upload List handle error 1`] = `
>
<div
class="ant-progress-bg"
style="width: 0%; height: 2px; border-radius: 100px;"
style="width: 0%; height: 2px;"
/>
</div>
</div>
@ -206,7 +206,7 @@ exports[`Upload List should be uploading when upload a file 1`] = `
>
<div
class="ant-progress-bg"
style="width: 0%; height: 2px; border-radius: 100px;"
style="width: 0%; height: 2px;"
/>
</div>
</div>
@ -315,7 +315,7 @@ exports[`Upload List should be uploading when upload a file 2`] = `
>
<div
class="ant-progress-bg"
style="width: 0%; height: 2px; border-radius: 100px;"
style="width: 0%; height: 2px;"
/>
</div>
</div>

View File

@ -201,7 +201,7 @@ We use `modifyVars` option of [less-loader](https://github.com/webpack/less-load
## eject
You can also could try [yarn run eject](https://github.com/facebookincubator/create-react-app#converting-to-a-custom-setup) for a custom setup of create-react-app, although you should dig into it by yourself.
You can also eject your application using [yarn run eject](https://github.com/facebookincubator/create-react-app#converting-to-a-custom-setup) for a custom setup of create-react-app, although you should dig into it by yourself.
## Source code and other boilerplates

View File

@ -63,8 +63,8 @@
"rc-cascader": "~0.17.4",
"rc-checkbox": "~2.1.6",
"rc-collapse": "~1.11.3",
"rc-dialog": "~7.4.0",
"rc-drawer": "~1.10.1",
"rc-dialog": "~7.5.2",
"rc-drawer": "~2.0.1",
"rc-dropdown": "~2.4.1",
"rc-editor-mention": "^1.1.13",
"rc-form": "^2.4.5",
@ -75,7 +75,7 @@
"rc-pagination": "~1.20.1",
"rc-progress": "~2.5.0",
"rc-rate": "~2.5.0",
"rc-select": "~9.1.4",
"rc-select": "~9.2.0",
"rc-slider": "~8.6.11",
"rc-steps": "~3.4.1",
"rc-switch": "~1.9.0",

View File

@ -45,6 +45,7 @@ module.exports = {
其他: 6,
Other: 6,
Components: 100,
组件: 100,
},
typeOrder: {
General: 0,

View File

@ -100,13 +100,21 @@ a {
}
// reset menu text color
.menu-site .ant-menu-item > a {
color: @site-text-color;
}
.menu-site {
.ant-menu-item > a {
color: @site-text-color;
}
.menu-site .ant-menu-item-selected > a,
.menu-site .ant-menu-item > a:hover {
color: @primary-color;
.ant-menu-item-selected > a,
.ant-menu-item > a:hover {
color: @primary-color;
}
.menu-antd-components-count {
margin-left: .5em;
color: @disabled-color;
font-size: 12px;
}
}
#react-content {

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'bisheng/router';
import { Row, Col, Menu, Icon, Affix } from 'antd';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import get from 'lodash/get';
import MobileMenu from 'rc-drawer';
@ -56,6 +57,26 @@ const getSideBarOpenKeys = nextProps => {
return shouldOpenKeys;
};
const getSubMenuTitle = (menuItem) => {
if (menuItem.title !== 'Components') {
return menuItem.title;
}
let count = 0;
menuItem.children.forEach(item => {
if (item.children) {
count += item.children.length;
}
});
return (
<h4>
{menuItem.title === 'Components' ? (
<FormattedMessage id="app.header.menu.components" />
) : menuItem.title}
<span className="menu-antd-components-count">{count}</span>
</h4>
);
};
export default class MainContent extends Component {
static contextTypes = {
intl: PropTypes.object.isRequired,
@ -117,7 +138,10 @@ export default class MainContent extends Component {
return menuItems.map(menuItem => {
if (menuItem.children) {
return (
<SubMenu title={<h4>{menuItem.title}</h4>} key={menuItem.title}>
<SubMenu
title={getSubMenuTitle(menuItem)}
key={menuItem.title}
>
{menuItem.children.map(child => {
if (child.type === 'type') {
return (

View File

@ -7,7 +7,12 @@ $('lib')
.hasFile('index.d.ts');
$('lib/*')
.filter(filename => !filename.endsWith('index.js') && !filename.endsWith('index.d.ts'))
.filter(
filename =>
!filename.endsWith('index.js') &&
!filename.endsWith('index.d.ts') &&
!filename.endsWith('.map'),
)
.isDirectory()
.filter(filename => !filename.endsWith('style') && !filename.endsWith('_util'))
.hasFile('index.js')