mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 10:59:19 +08:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea914721d | ||
|
|
0a962b19ad | ||
|
|
3add760d42 | ||
|
|
c05df5ffd1 | ||
|
|
393e51cab6 | ||
|
|
836a058c57 | ||
|
|
842b6636a3 | ||
|
|
a3ec948dcd | ||
|
|
d70ff98f9f | ||
|
|
6bd75f2666 | ||
|
|
f8d2aeefc3 | ||
|
|
52e3511192 | ||
|
|
9d218860b5 | ||
|
|
d3bb55b825 | ||
|
|
d57827f87c | ||
|
|
eeb6ab5a01 | ||
|
|
f7e480ad53 | ||
|
|
b240494dd5 | ||
|
|
55e9e83be0 | ||
|
|
ca2e9c6dc2 | ||
|
|
7860d74725 | ||
|
|
0e3d40e6d9 | ||
|
|
55c22a7232 | ||
|
|
835dc2fb52 | ||
|
|
2026449976 | ||
|
|
6bf99c1819 | ||
|
|
6cd841ef31 | ||
|
|
90fbd4da51 | ||
|
|
a62c958a15 | ||
|
|
7b76906924 | ||
|
|
3c49a90980 | ||
|
|
0f503101af | ||
|
|
527949df04 | ||
|
|
f599a24228 | ||
|
|
55efef3220 | ||
|
|
75423ed4b1 | ||
|
|
818deacfee | ||
|
|
fed19bbcb5 | ||
|
|
84c4139023 | ||
|
|
d051d94af4 | ||
|
|
a3388aa840 | ||
|
|
9d2b48b511 | ||
|
|
b8fb60079c | ||
|
|
e9111a855f | ||
|
|
08e21e0a2f | ||
|
|
4878258f6c | ||
|
|
1da5490ab8 | ||
|
|
ba227bbe54 | ||
|
|
12e1735255 | ||
|
|
84b70ac140 | ||
|
|
8a7d59241d | ||
|
|
831533d139 | ||
|
|
23331e8594 | ||
|
|
915f343de3 | ||
|
|
49256c59c9 | ||
|
|
3bb4b6ea7c | ||
|
|
66f6de79ce | ||
|
|
8268bfdd9e | ||
|
|
0c848a5cd2 | ||
|
|
0257e02e65 | ||
|
|
355f82231c | ||
|
|
6343e6b763 | ||
|
|
1ed434a695 | ||
|
|
af3c1aac96 | ||
|
|
7a115c6c4b | ||
|
|
9e322ea445 | ||
|
|
1700ae6078 | ||
|
|
1be129fa50 | ||
|
|
a0c96783c0 | ||
|
|
0aca663092 | ||
|
|
7483254df6 | ||
|
|
ea6a84d9cf | ||
|
|
1e5264bad7 | ||
|
|
f262f1ab99 | ||
|
|
d12cb17804 | ||
|
|
4fe28ec298 | ||
|
|
9178108906 | ||
|
|
c2d3f16dfb | ||
|
|
3112b0d7ca | ||
|
|
d5edcb9ef0 | ||
|
|
a9fff1be5b | ||
|
|
5d4d9f5cea | ||
|
|
97bc13426f | ||
|
|
05c6d09eab | ||
|
|
58d98a7802 | ||
|
|
7a6b21d02b | ||
|
|
d38c5a43da | ||
|
|
dc78317688 | ||
|
|
3c99097bc8 | ||
|
|
2482b4b760 | ||
|
|
0b29ee6fee | ||
|
|
8afe3965c1 | ||
|
|
5179ffbbe1 | ||
|
|
140997b033 | ||
|
|
ff5d9f6029 | ||
|
|
6ceef46129 | ||
|
|
f747f10676 | ||
|
|
f8b46ad06a | ||
|
|
9fe859a31f | ||
|
|
39cf0899f5 | ||
|
|
f52dcbfb37 | ||
|
|
be02817433 | ||
|
|
55c2223366 | ||
|
|
a223c7bdba | ||
|
|
d0a6fa47a9 | ||
|
|
6f8f7de69e | ||
|
|
6bcd489c50 | ||
|
|
9ac21d010b | ||
|
|
58e0228e86 | ||
|
|
13d48aa1e5 | ||
|
|
23dd825afd | ||
|
|
c082e1b1d1 | ||
|
|
9ed9fb053a | ||
|
|
2fe4590887 | ||
|
|
512796b7fe | ||
|
|
806df6e912 | ||
|
|
b38d1afe5f | ||
|
|
71772260da | ||
|
|
62d2687d6d | ||
|
|
2a12545694 | ||
|
|
6851c8ce3e | ||
|
|
0920efb87f |
18
.github/ISSUE_TEMPLATE.md
vendored
18
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,6 +1,14 @@
|
||||
<!-- Issue Template -->
|
||||
|
||||
<!-- 请按照下列格式报告问题,务必提供复现步骤,否则恕难解决,感谢您的支持。-->
|
||||
<!--
|
||||
antd 的用法咨询,建议通过以下渠道,官方 issues 目前没有足够精力提供此类咨询服务:
|
||||
|
||||
1. [Stack Overflow](http://stackoverflow.com/questions/tagged/antd)
|
||||
2. [Segment Fault](https://segmentfault.com/t/antd)(中文)
|
||||
3. [Gitter](https://gitter.im/ant-design/ant-design)
|
||||
|
||||
如果是报告 bug,请按照下列格式书写,并务必提供复现步骤,否则恕难解决,感谢您的支持。
|
||||
-->
|
||||
|
||||
#### 发生问题的环境是:
|
||||
|
||||
@@ -10,17 +18,17 @@
|
||||
- 操作系统及其版本:
|
||||
- 浏览器及其版本:
|
||||
|
||||
#### 您做了什么?
|
||||
#### 您做了什么?请提供尽可能详细的重现步骤。
|
||||
|
||||
<!-- 如:引入 antd 了 Button -->
|
||||
|
||||
#### 您期待的结果是:
|
||||
#### 您期待的结果是:
|
||||
|
||||
<!-- 如:像官网一样正常显示 -->
|
||||
|
||||
#### 实际上的结果是:
|
||||
#### 实际上的结果是:
|
||||
|
||||
<!-- 如:样式错位了 -->
|
||||
<!-- 如:样式错位了,最好提供截图 -->
|
||||
|
||||
#### 可重现的在线演示
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@ sudo: false
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "5"
|
||||
- "6"
|
||||
|
||||
@@ -9,6 +9,48 @@ If you want to read change logs before `2.0.0`, please visit [GitHub](https://gi
|
||||
|
||||
---
|
||||
|
||||
## 2.2.0
|
||||
|
||||
`2016-10-28`
|
||||
|
||||
* Supports TypeScript@2.0. [@AlbertZheng](https://github.com/AlbertZheng) [#3358](https://github.com/ant-design/ant-design/issues/3358)
|
||||
* Not rely on specific version of React now. [#3627](https://github.com/ant-design/ant-design/pull/3627)
|
||||
* Alert supports `className` `style`.
|
||||
* DatePicker & MonthPicker & RangePicker allow developers to set whether to show the clear button. [#3618](https://github.com/ant-design/ant-design/issues/3618)
|
||||
* Form.Item can generate `validateStatus` & `help` for nested form control automatically. [#3212](https://github.com/ant-design/ant-design/issues/3212)
|
||||
* RangePicker can set some hours or minutes or seconds to be not selectable. [#](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date)
|
||||
* Switch
|
||||
* The width of Switch will resize automatically, according to `checkedChildren/unCheckedChildren`. [#3380](https://github.com/ant-design/ant-design/issues/3380)
|
||||
* Improve the switch animation.
|
||||
* Upload can [customized request](https://github.com/react-component/upload#customrequest) now. [@edgji](https://github.com/edgji)
|
||||
* Icon
|
||||
* New icons `bulb` `select` `like-o` `dislike-o`.
|
||||
* Adjust existing icons `loading` `like` `dislike`.
|
||||
* Improve the TypeScript definition of Card & DatePicker & Icon & Table. [@infeng](https://github.com/infeng) [3468](https://github.com/ant-design/ant-design/pull/3468) [#3603](https://github.com/ant-design/ant-design/pull/3603) [#3531](https://github.com/ant-design/ant-design/pull/3531)
|
||||
|
||||
* Fix Cascader `defaultValue` should work. [#3470](https://github.com/ant-design/ant-design/issues/3470)
|
||||
* Fix the alignment of Button & Input & DatePicker & Select. [#3481](https://github.com/ant-design/ant-design/issues/3481)
|
||||
* DatePicker
|
||||
* Fix wrong timing of triggering `onChange` while `DatePicker[showTime]` is set. [#3523](https://github.com/ant-design/ant-design/issues/3523)
|
||||
* Fix `Dropdown.Button[disabled]` doesn't works for behaviour. [#3535](https://github.com/ant-design/ant-design/issues/3535)
|
||||
* Menu
|
||||
* Fix errors in SSR, thanks to [@xpcode](https://github.com/xpcode) to find the solution. [#2061](https://github.com/ant-design/ant-design/issues/2061) [#2406](https://github.com/ant-design/ant-design/issues/2406) [#3293](https://github.com/ant-design/ant-design/issues/3293)
|
||||
* Fix children don't support `null`. [#3599](https://github.com/ant-design/ant-design/issues/3599)
|
||||
* Fix loading status animation for message.[#3536](https://github.com/ant-design/ant-design/issues/3536)
|
||||
* Form
|
||||
* Fix style issue while using `Form[inline]` and `Input[addonBefore|addonAfter]` together. [#3524](https://github.com/ant-design/ant-design/issues/3524)
|
||||
* Fix style issue for Radio.Button in Form.Item.
|
||||
* Fix style issue for search button in Form.Item. [#3630](https://github.com/ant-design/ant-design/issues/3630)
|
||||
* Fix Form.Item should not treat no user input as validate success. [#3613](https://github.com/ant-design/ant-design/issues/3613)
|
||||
* Should not limit the min width of Popover while `Popover[title]` is not set.
|
||||
* Table
|
||||
* Fix style of fixed header of Table while `dataSource` is empty.[#3567](https://github.com/ant-design/ant-design/issues/3567)
|
||||
* Fix Table will overlap SubMenu while `dataSource` is empty. [#3521](https://github.com/ant-design/ant-design/issues/3521)
|
||||
* Tabs
|
||||
* Height of header of `Tabs[type="card|editable-card"]` should follow design.
|
||||
* Fix height of TabPane should follow height of its content. [#3304](https://github.com/ant-design/ant-design/issues/3304)
|
||||
* Fix style of `TreeSelect[showSearch]`. [#3520](https://github.com/ant-design/ant-design/issues/3520)
|
||||
|
||||
## 2.1.0
|
||||
|
||||
`2016-10-16`
|
||||
@@ -108,7 +150,7 @@ There are some breaking changes in `antd@2.0.0`, and you need to modify your cod
|
||||
// send data to server
|
||||
}
|
||||
```
|
||||
* For the value of time-related components becomes an instance of `moment`, you should replace `type='date'` with `type='object'` in form validation.
|
||||
* For the value of time-related components becomes an instance of `moment`, you should replace `type: 'date'` with `type: 'object'` in form validation.
|
||||
* The `format` of time-related components is changed from [gregorian-calendar-format](https://github.com/yiminghe/gregorian-calendar-format#api) to [moment format](http://momentjs.com/docs/#/parsing/string-format/) now, for instance the format `yyyy-MM-dd` should change to `YYYY-MM-DD`.
|
||||
* `linkRender` and `nameRender` of Breadcrumb are removed, please use `itemRender`.
|
||||
* `onClose` and `onOpen` of Menu are removed, please use `onOpenChange`. As being totally different, please check [this demo](http://beta.ant.design/components/menu/#components-menu-demo-sider-current) first.
|
||||
|
||||
@@ -9,12 +9,54 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 2.2.0
|
||||
|
||||
`2016-10-28`
|
||||
|
||||
* 支持 TypeScript@2.0。[@AlbertZheng](https://github.com/AlbertZheng) [#3358](https://github.com/ant-design/ant-design/issues/3358)
|
||||
* 不再强依赖于 React 特定版本。[#3627](https://github.com/ant-design/ant-design/pull/3627)
|
||||
* Alert 支持 `className` `style` 属性。
|
||||
* DatePicker MonthPicker RangePicker 现在允许设置是否显示清除按钮。[#3618](https://github.com/ant-design/ant-design/issues/3618)
|
||||
* Form.Item 现在可以感知深层嵌套的表单域,以自动为其生成错误信息和状态。[#3212](https://github.com/ant-design/ant-design/issues/3212)
|
||||
* RangePicker 现在可以设置不可选的时间。[#](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date)
|
||||
* Switch
|
||||
* 宽度现在会随着 `checkedChildren/unCheckedChildren` 自动调整。[#3380](https://github.com/ant-design/ant-design/issues/3380)
|
||||
* 优化切换动画。
|
||||
* Upload 现在可以 [自定义上传方式](https://github.com/react-component/upload#customrequest)。[@edgji](https://github.com/edgji)
|
||||
* Icon
|
||||
* 新增 `bulb` `select` `like-o` `dislike-o`。
|
||||
* 调整 `loading` `like` `dislike`。
|
||||
* 优化 Card DatePicker Icon Table 的 TypeScript 定义。[@infeng](https://github.com/infeng) [3468](https://github.com/ant-design/ant-design/pull/3468) [#3603](https://github.com/ant-design/ant-design/pull/3603) [#3531](https://github.com/ant-design/ant-design/pull/3531)
|
||||
|
||||
* 修复 Cascader `defaultValue` 失效的问题。[#3470](https://github.com/ant-design/ant-design/issues/3470)
|
||||
* 修复在一行内同时使用 Button Input DatePicker Select 时对齐的问题。[#3481](https://github.com/ant-design/ant-design/issues/3481)
|
||||
* DatePicker
|
||||
* 修复设置 `DatePicker[showTime]` 后 `onChange` 事件触发时机问题。[#3523](https://github.com/ant-design/ant-design/issues/3523)
|
||||
* 修复 Dropdown.Button disabled 后仍然响应操作的问题。[#3535](https://github.com/ant-design/ant-design/issues/3535)
|
||||
* Menu
|
||||
* 修复服务端渲染问题,感谢 [@xpcode](https://github.com/xpcode) 定位问题。[#2061](https://github.com/ant-design/ant-design/issues/2061) [#2406](https://github.com/ant-design/ant-design/issues/2406) [#3293](https://github.com/ant-design/ant-design/issues/3293)
|
||||
* 修复 children 不能为 `null` 的问题。[#3599](https://github.com/ant-design/ant-design/issues/3599)
|
||||
* 修复 message 加载状态无动画的问题。[#3536](https://github.com/ant-design/ant-design/issues/3536)
|
||||
* Form
|
||||
* 修复 `Form[inline]` 与 `Input[addonBefore|addonAfter]` 一起使用时的样式问题。[#3524](https://github.com/ant-design/ant-design/issues/3524)
|
||||
* 修复 Form.Item 内 Radio.Button 样式问题。
|
||||
* 修复 Form.Item 内搜索按钮的样式问题。[#3630](https://github.com/ant-design/ant-design/issues/3630)
|
||||
* 修复用户无输入时 Form.Item 识别为校验成功的问题。[#3613](https://github.com/ant-design/ant-design/issues/3613)
|
||||
* 当 `Popover[title]` 没有设置时,不再限制 Popover 的最小宽度。
|
||||
* Table
|
||||
* 修复固定表头在没有数据情况下的样式问题。[#3567](https://github.com/ant-design/ant-design/issues/3567)
|
||||
* 修复无数据时会覆盖 SubMenu 的问题。[#3521](https://github.com/ant-design/ant-design/issues/3521)
|
||||
* Tabs
|
||||
* 修复卡片叶签头部高度与设计稿不一致的问题。
|
||||
* 修复 TabPane 的高度会被同级 TabPane 撑高的问题。[#3304](https://github.com/ant-design/ant-design/issues/3304)
|
||||
* 修复 `TreeSelect[showSearch]` 样式问题。[#3520](https://github.com/ant-design/ant-design/issues/3520)
|
||||
|
||||
## 2.1.0
|
||||
|
||||
`2016-10-16`
|
||||
|
||||
- Icon 现在支持旋转动画。
|
||||
- Tabs 现在可以禁用切换动画。
|
||||
- Tabs 现在可以禁用切换动画。[#3324](https://github.com/ant-design/ant-design/issues/3324)
|
||||
- 新增西班牙语的 localization 支持。@Danjavia
|
||||
- 更新俄语的 localization 文案。@plandem
|
||||
- 新增 AutoComplete[onSelect] 回调。
|
||||
@@ -37,12 +79,12 @@ timeline: true
|
||||
- 修复 Popover 箭头样式问题。
|
||||
- 修复 Popover 和 Popconfirm 的 `arrowPointAtCenter` 无效的问题。
|
||||
- Select
|
||||
- 修复样式重复引入的问题。
|
||||
- 修复样式重复引入的问题。[#3376](https://github.com/ant-design/ant-design/issues/3376)
|
||||
- 修复 `notFoundContent` 无法置空的问题。[#3345](https://github.com/ant-design/ant-design/issues/3345)
|
||||
- 修复 Table 内使用 Select[showSearch] 后宽度会跳动的问题。[#3413](https://github.com/ant-design/ant-design/issues/3413)
|
||||
- 修复 Table 边框线与页头页脚冲突的问题。[#3301](https://github.com/ant-design/ant-design/issues/3301)
|
||||
- 修复 TabPane 高度不随内容变化的问题。[#3377](https://github.com/ant-design/ant-design/issues/3377)
|
||||
- 修复 Transfer[titles] 不受 LocaleProvider 控制的问题。
|
||||
- 修复 Transfer[titles] 不受 LocaleProvider 控制的问题。[#3264](https://github.com/ant-design/ant-design/pull/3264)
|
||||
- Upload
|
||||
- 修复用户自定义 `onRemove` 事件会覆盖默认行为的问题。[#3317](https://github.com/ant-design/ant-design/issues/3317)
|
||||
- 修复图片卡片样式问题。[#3316](https://github.com/ant-design/ant-design/issues/3316)
|
||||
@@ -107,7 +149,7 @@ timeline: true
|
||||
}
|
||||
```
|
||||
|
||||
* 时间类组件与表单校验一起使用时,`type='date'` 改为 `type='object'`。
|
||||
* 时间类组件与表单校验一起使用时,`type: 'date'` 改为 `type: 'object'`。
|
||||
* 时间类组件的 `format` 属性也发生了变化,从 [gregorian-calendar-format 的格式](https://github.com/yiminghe/gregorian-calendar-format#api) 变化为与 [moment 的格式](http://momentjs.com/docs/#/parsing/string-format/),例如原来的 `yyyy-MM-dd` 将变为 `YYYY-MM-DD`。
|
||||
* Breadcrumb 移除 `linkRender` 和 `nameRender`,请使用 `itemRender`。
|
||||
* Menu 移除 `onClose` `onOpen`,请使用 `onOpenChange`。API 差异较大,请先研究 [demo](http://beta.ant.design/components/menu/#components-menu-demo-sider-current)。
|
||||
|
||||
@@ -4,7 +4,13 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Ant Design [](https://travis-ci.org/ant-design/ant-design) [](https://www.npmjs.org/package/antd) [](https://npmjs.org/package/antd) [](https://david-dm.org/ant-design/ant-design) [](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
# Ant Design
|
||||
[](https://travis-ci.org/ant-design/ant-design)
|
||||
[](https://www.npmjs.org/package/antd)
|
||||
[](https://npmjs.org/package/antd)
|
||||
[](https://cdnjs.com/libraries/antd)
|
||||
[](https://david-dm.org/ant-design/ant-design)
|
||||
[](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
一套企业级的 UI 设计语言和 React 实现。
|
||||
|
||||
@@ -13,7 +19,7 @@
|
||||
- 提炼和服务企业级中后台产品的交互语言和视觉风格。
|
||||
- [React Component](http://react-component.github.io/badgeboard/) 基础上精心封装的高质量 UI 组件。
|
||||
- 使用 TypeScript 构建,提供完整的类型定义文件。
|
||||
- 基于 npm + webpack + babel + dora + [dva](https://github.com/dvajs/dva) 的企业级业务开发框架。
|
||||
- 基于 npm + webpack + babel + [dora](https://github.com/dora-js/dora) + [dva](https://github.com/dvajs/dva) 的企业级业务开发框架。
|
||||
|
||||
## 安装
|
||||
|
||||
|
||||
10
README.md
10
README.md
@@ -4,7 +4,13 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# Ant Design [](https://travis-ci.org/ant-design/ant-design) [](https://www.npmjs.org/package/antd) [](https://npmjs.org/package/antd) [](https://david-dm.org/ant-design/ant-design) [](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
# Ant Design
|
||||
[](https://travis-ci.org/ant-design/ant-design)
|
||||
[](https://www.npmjs.org/package/antd)
|
||||
[](https://npmjs.org/package/antd)
|
||||
[](https://cdnjs.com/libraries/antd)
|
||||
[](https://david-dm.org/ant-design/ant-design)
|
||||
[](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
An enterprise-class UI design language and React-based implementation.
|
||||
|
||||
@@ -13,7 +19,7 @@ An enterprise-class UI design language and React-based implementation.
|
||||
- An enterprise-class design language and high quality UI.
|
||||
- Graceful UI components out of the box, base on [React Component](http://react-component.github.io/badgeboard/).
|
||||
- Writen in TypeScript with complete define types.
|
||||
- A npm + webpack + babel + dora + [dva](https://github.com/dvajs/dva) development framework.
|
||||
- A npm + webpack + babel + [dora](https://github.com/dora-js/dora) + [dva](https://github.com/dvajs/dva) development framework.
|
||||
|
||||
## Install
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import assign from 'object-assign';
|
||||
|
||||
export default function getLocale(props, context, component, getDefaultLocale) {
|
||||
let locale = null;
|
||||
if (context && context.antLocale && context.antLocale[component]) {
|
||||
locale = context.antLocale[component];
|
||||
} else {
|
||||
locale = getDefaultLocale();
|
||||
}
|
||||
// 统一合并为完整的 Locale
|
||||
export default function getLocale(props, context, componentName, getDefaultLocale) {
|
||||
const locale = context && context.antLocale && context.antLocale[componentName] ?
|
||||
context.antLocale[componentName] : getDefaultLocale();
|
||||
|
||||
const result = assign({}, locale, props.locale);
|
||||
result.lang = assign({}, locale.lang, props.locale.lang);
|
||||
return result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function getScroll(target, top) {
|
||||
export default function getScroll(target, top): number {
|
||||
if (typeof window === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default function splitObject(obj, parts): Array<any> {
|
||||
let left = {};
|
||||
let right = {};
|
||||
const left = {};
|
||||
const right = {};
|
||||
Object.keys(obj).forEach((k) => {
|
||||
if (parts.indexOf(k) !== -1) {
|
||||
left[k] = obj[k];
|
||||
|
||||
@@ -6,13 +6,13 @@ import shallowequal from 'shallowequal';
|
||||
import omit from 'omit.js';
|
||||
import getScroll from '../_util/getScroll';
|
||||
|
||||
function getTargetRect(target): any {
|
||||
function getTargetRect(target): ClientRect {
|
||||
return target !== window ?
|
||||
target.getBoundingClientRect() :
|
||||
{ top: 0, left: 0, bottom: 0 };
|
||||
}
|
||||
|
||||
function getOffset(element, target) {
|
||||
function getOffset(element: HTMLElement, target) {
|
||||
const elemRect = element.getBoundingClientRect();
|
||||
const targetRect = getTargetRect(target);
|
||||
|
||||
@@ -31,6 +31,13 @@ function getOffset(element, target) {
|
||||
};
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ?
|
||||
window : null;
|
||||
};
|
||||
|
||||
// Affix
|
||||
export interface AffixProps {
|
||||
/**
|
||||
@@ -42,7 +49,7 @@ export interface AffixProps {
|
||||
offsetBottom?: number;
|
||||
style?: React.CSSProperties;
|
||||
/** 固定状态改变时触发的回调函数 */
|
||||
onChange?: (affixed?: boolean) => any;
|
||||
onChange?: (affixed?: boolean) => void;
|
||||
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
|
||||
target?: () => Window | HTMLElement;
|
||||
prefixCls?: string;
|
||||
@@ -55,19 +62,9 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
target: React.PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
target() {
|
||||
return typeof window !== 'undefined' ?
|
||||
window : null;
|
||||
},
|
||||
onChange() {},
|
||||
prefixCls: 'ant-affix',
|
||||
};
|
||||
|
||||
scrollEvent: any;
|
||||
resizeEvent: any;
|
||||
refs: {
|
||||
[key: string]: any;
|
||||
fixedNode: HTMLElement;
|
||||
};
|
||||
|
||||
@@ -80,7 +77,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
}
|
||||
|
||||
setAffixStyle(e, affixStyle) {
|
||||
const { onChange, target } = this.props;
|
||||
const { onChange = noop, target = getDefaultTarget } = this.props;
|
||||
const originalAffixStyle = this.state.affixStyle;
|
||||
const isWindow = target() === window;
|
||||
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
|
||||
@@ -110,7 +107,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
}
|
||||
|
||||
updatePosition = (e) => {
|
||||
let { offsetTop, offsetBottom, offset, target } = this.props;
|
||||
let { offsetTop, offsetBottom, offset, target = getDefaultTarget } = this.props;
|
||||
const targetNode = target();
|
||||
|
||||
// Backwards support
|
||||
@@ -124,8 +121,8 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
};
|
||||
|
||||
const offsetMode = {
|
||||
top: null as boolean,
|
||||
bottom: null as boolean,
|
||||
top: false,
|
||||
bottom: false,
|
||||
};
|
||||
// Default to `offsetTop=0`.
|
||||
if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') {
|
||||
@@ -174,7 +171,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const target = this.props.target;
|
||||
const target = this.props.target || getDefaultTarget;
|
||||
this.setTargetEventListeners(target);
|
||||
}
|
||||
|
||||
@@ -208,7 +205,7 @@ export default class Affix extends React.Component<AffixProps, any> {
|
||||
|
||||
render() {
|
||||
const className = classNames({
|
||||
[this.props.prefixCls]: this.state.affixStyle,
|
||||
[this.props.prefixCls || 'ant-affix']: this.state.affixStyle,
|
||||
});
|
||||
|
||||
const props = omit(this.props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target']);
|
||||
|
||||
19
components/alert/index.tsx
Normal file → Executable file
19
components/alert/index.tsx
Normal file → Executable file
@@ -4,6 +4,8 @@ import Animate from 'rc-animate';
|
||||
import Icon from '../icon';
|
||||
import classNames from 'classnames';
|
||||
|
||||
function noop() {}
|
||||
|
||||
export interface AlertProps {
|
||||
/**
|
||||
* Type of Alert styles, options:`success`, `info`, `warning`, `error`
|
||||
@@ -18,19 +20,17 @@ export interface AlertProps {
|
||||
/** Additional content of Alert */
|
||||
description?: React.ReactNode;
|
||||
/** Callback when close Alert */
|
||||
onClose?: (event) => void;
|
||||
onClose?: React.MouseEventHandler<any>;
|
||||
/** Whether to show icon */
|
||||
showIcon?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
banner?: boolean;
|
||||
}
|
||||
|
||||
export default class Alert extends React.Component<AlertProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-alert',
|
||||
showIcon: false,
|
||||
onClose() {},
|
||||
type: 'info',
|
||||
};
|
||||
constructor(props) {
|
||||
@@ -51,7 +51,7 @@ export default class Alert extends React.Component<AlertProps, any> {
|
||||
this.setState({
|
||||
closing: false,
|
||||
});
|
||||
this.props.onClose(e);
|
||||
(this.props.onClose || noop)(e);
|
||||
}
|
||||
animationEnd = () => {
|
||||
this.setState({
|
||||
@@ -61,7 +61,8 @@ export default class Alert extends React.Component<AlertProps, any> {
|
||||
}
|
||||
render() {
|
||||
let {
|
||||
closable, description, type, prefixCls, message, closeText, showIcon, banner,
|
||||
closable, description, type, prefixCls = 'ant-alert', message, closeText, showIcon, banner,
|
||||
className = '', style,
|
||||
} = this.props;
|
||||
|
||||
// banner模式默认有 Icon
|
||||
@@ -99,6 +100,7 @@ export default class Alert extends React.Component<AlertProps, any> {
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !showIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[className]: !!className,
|
||||
});
|
||||
|
||||
// closeable when closeText is assigned
|
||||
@@ -107,12 +109,13 @@ export default class Alert extends React.Component<AlertProps, any> {
|
||||
}
|
||||
|
||||
return this.state.closed ? null : (
|
||||
<Animate component=""
|
||||
<Animate
|
||||
component=""
|
||||
showProp="data-show"
|
||||
transitionName={`${prefixCls}-slide-up`}
|
||||
onEnd={this.animationEnd}
|
||||
>
|
||||
<div data-show={this.state.closing} className={alertCls}>
|
||||
<div data-show={this.state.closing} className={alertCls} style={style}>
|
||||
{showIcon ? <Icon className={`${prefixCls}-icon`} type={iconType} /> : null}
|
||||
<span className={`${prefixCls}-message`}>{message}</span>
|
||||
<span className={`${prefixCls}-description`}>{description}</span>
|
||||
|
||||
13
components/auto-complete/index.tsx
Normal file → Executable file
13
components/auto-complete/index.tsx
Normal file → Executable file
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import Select, { Option, OptGroup } from '../select';
|
||||
import Select, { OptionProps, OptGroupProps } from '../select';
|
||||
import { Option, OptGroup } from 'rc-select';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface SelectedValue {
|
||||
@@ -23,13 +24,13 @@ export interface AutoCompleteProps {
|
||||
defaultValue?: string | Array<any> | SelectedValue | Array<SelectedValue>;
|
||||
value?: string | Array<any> | SelectedValue | Array<SelectedValue>;
|
||||
allowClear?: boolean;
|
||||
onChange?: (value) => void;
|
||||
onChange?: (value: string | Array<any> | SelectedValue | Array<SelectedValue>) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default class AutoComplete extends React.Component<AutoCompleteProps, any> {
|
||||
static Option = Option;
|
||||
static OptGroup = OptGroup;
|
||||
static Option = Option as React.ClassicComponentClass<OptionProps>;
|
||||
static OptGroup = OptGroup as React.ClassicComponentClass<OptGroupProps>;
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-select',
|
||||
@@ -45,7 +46,7 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
|
||||
|
||||
render() {
|
||||
let {
|
||||
size, className, notFoundContent, prefixCls, optionLabelProp, dataSource, children,
|
||||
size, className = '', notFoundContent, prefixCls, optionLabelProp, dataSource, children,
|
||||
} = this.props;
|
||||
|
||||
const cls = classNames({
|
||||
@@ -55,7 +56,7 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
|
||||
[`${prefixCls}-show-search`]: true,
|
||||
});
|
||||
|
||||
const options = children || (dataSource ? dataSource.map((item, index) => {
|
||||
const options = children || (dataSource ? dataSource.map((item) => {
|
||||
switch (typeof item) {
|
||||
case 'string':
|
||||
return <Option key={item}>{item}</Option>;
|
||||
|
||||
27
components/back-top/index.tsx
Normal file → Executable file
27
components/back-top/index.tsx
Normal file → Executable file
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import Animate from 'rc-animate';
|
||||
import Icon from '../icon';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'omit.js';
|
||||
import Icon from '../icon';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import getRequestAnimationFrame from '../_util/getRequestAnimationFrame';
|
||||
|
||||
@@ -23,9 +23,16 @@ const easeInOutCubic = (t, b, c, d) => {
|
||||
}
|
||||
};
|
||||
|
||||
function noop() {}
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ?
|
||||
window : null;
|
||||
}
|
||||
|
||||
export interface BackTopProps {
|
||||
visibilityHeight?: number;
|
||||
onClick?: (event) => void;
|
||||
onClick?: React.MouseEventHandler<any>;
|
||||
target?: () => HTMLElement | Window;
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
@@ -34,13 +41,7 @@ export interface BackTopProps {
|
||||
|
||||
export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
static defaultProps = {
|
||||
onClick() {},
|
||||
visibilityHeight: 400,
|
||||
target() {
|
||||
return typeof window !== 'undefined' ?
|
||||
window : null;
|
||||
},
|
||||
prefixCls: 'ant-back-top',
|
||||
};
|
||||
|
||||
scrollEvent: any;
|
||||
@@ -64,11 +65,11 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
}
|
||||
};
|
||||
reqAnimFrame(frameFunc);
|
||||
this.props.onClick(e);
|
||||
(this.props.onClick || noop)(e);
|
||||
}
|
||||
|
||||
setScrollTop(value) {
|
||||
const targetNode = this.props.target();
|
||||
const targetNode = (this.props.target || getDefaultTarget)();
|
||||
if (targetNode === window) {
|
||||
document.body.scrollTop = value;
|
||||
document.documentElement.scrollTop = value;
|
||||
@@ -78,7 +79,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
}
|
||||
|
||||
handleScroll = () => {
|
||||
const { visibilityHeight, target } = this.props;
|
||||
const { visibilityHeight, target = getDefaultTarget } = this.props;
|
||||
const scrollTop = getScroll(target(), true);
|
||||
this.setState({
|
||||
visible: scrollTop > visibilityHeight,
|
||||
@@ -87,7 +88,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
|
||||
componentDidMount() {
|
||||
this.handleScroll();
|
||||
this.scrollEvent = addEventListener(this.props.target(), 'scroll', this.handleScroll);
|
||||
this.scrollEvent = addEventListener((this.props.target || getDefaultTarget)(), 'scroll', this.handleScroll);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -97,7 +98,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, className, children } = this.props;
|
||||
const { prefixCls = 'ant-back-top', className = '', children } = this.props;
|
||||
const classString = classNames({
|
||||
[prefixCls]: true,
|
||||
[className]: !!className,
|
||||
|
||||
@@ -13,26 +13,25 @@ function getNumberArray(num) {
|
||||
.map(i => Number(i)) : [];
|
||||
}
|
||||
|
||||
export default class ScrollNumber extends Component<any, any> {
|
||||
export interface ScrollNumberProps {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
count?: string | number;
|
||||
component?: string;
|
||||
onAnimated?: Function;
|
||||
height?: number;
|
||||
style: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class ScrollNumber extends Component<ScrollNumberProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-scroll-number',
|
||||
count: null,
|
||||
component: 'sup',
|
||||
onAnimated() {
|
||||
},
|
||||
height: 18,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
count: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number,
|
||||
]),
|
||||
component: React.PropTypes.string,
|
||||
onAnimated: React.PropTypes.func,
|
||||
height: React.PropTypes.number,
|
||||
};
|
||||
|
||||
lastCount: any;
|
||||
|
||||
constructor(props) {
|
||||
@@ -85,7 +84,10 @@ export default class ScrollNumber extends Component<any, any> {
|
||||
animateStarted: false,
|
||||
count: nextProps.count,
|
||||
}, () => {
|
||||
this.props.onAnimated();
|
||||
const onAnimated = this.props.onAnimated;
|
||||
if (onAnimated) {
|
||||
onAnimated();
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
});
|
||||
@@ -93,10 +95,10 @@ export default class ScrollNumber extends Component<any, any> {
|
||||
}
|
||||
|
||||
renderNumberList(position) {
|
||||
const childrenToReturn = [];
|
||||
const childrenToReturn: React.ReactElement<any>[] = [];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const currentClassName = (position === i) ? 'current' : null;
|
||||
childrenToReturn.push(<p key={i} className={currentClassName}>{i % 10}</p>);
|
||||
const currentClassName = (position === i) ? 'current' : '';
|
||||
childrenToReturn.push(<p key={i.toString()} className={currentClassName}>{i % 10}</p>);
|
||||
}
|
||||
return childrenToReturn;
|
||||
}
|
||||
@@ -138,7 +140,7 @@ export default class ScrollNumber extends Component<any, any> {
|
||||
className: `${this.props.prefixCls} ${this.props.className}`,
|
||||
});
|
||||
return createElement(
|
||||
this.props.component,
|
||||
this.props.component || 'sup',
|
||||
props,
|
||||
this.renderNumberElement()
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import Animate from 'rc-animate';
|
||||
import ScrollNumber from './ScrollNumber';
|
||||
import classNames from 'classnames';
|
||||
import warning from 'warning';
|
||||
import splitObject from '../_util/splitObject';
|
||||
|
||||
export interface BadgeProps {
|
||||
@@ -64,6 +65,10 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
});
|
||||
|
||||
warning(
|
||||
!(children && status),
|
||||
'`Badge[children]` and `Badge[status]` cannot be used at the same time.'
|
||||
);
|
||||
// <Badge status="success" />
|
||||
if (!children && status) {
|
||||
const statusCls = classNames({
|
||||
@@ -79,7 +84,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={badgeCls} title={count} style={null} {...restProps}>
|
||||
<span {...restProps} className={badgeCls} title={count}>
|
||||
{children}
|
||||
<Animate
|
||||
component=""
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
top: auto;
|
||||
display: block;
|
||||
position: relative;
|
||||
transform: translateX(0);
|
||||
transform: none!important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
components/breadcrumb/Breadcrumb.tsx
Normal file → Executable file
29
components/breadcrumb/Breadcrumb.tsx
Normal file → Executable file
@@ -8,11 +8,11 @@ export interface BreadcrumbProps {
|
||||
routes?: Array<any>;
|
||||
params?: Object;
|
||||
separator?: string | React.ReactNode;
|
||||
itemRender?: (route, params, routes, paths) => React.ReactNode;
|
||||
itemRender?: (route: any, params: any, routes: Array<any>, paths: Array<string>) => React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
};
|
||||
|
||||
function getBreadcrumbName(route, params, routes) {
|
||||
function getBreadcrumbName(route, params) {
|
||||
if (!route.breadcrumbName) {
|
||||
return null;
|
||||
}
|
||||
@@ -24,19 +24,20 @@ function getBreadcrumbName(route, params, routes) {
|
||||
return name;
|
||||
}
|
||||
|
||||
function defaultItemRender(route, params, routes, paths) {
|
||||
const isLastItem = routes.indexOf(route) === routes.length - 1;
|
||||
const name = getBreadcrumbName(route, params);
|
||||
return isLastItem
|
||||
? <span>{name}</span>
|
||||
: <a href={`#/${paths.join('/')}`}>{name}</a>;
|
||||
}
|
||||
|
||||
export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
|
||||
static Item: any;
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-breadcrumb',
|
||||
separator: '/',
|
||||
itemRender: (route, params, routes, paths) => {
|
||||
const isLastItem = routes.indexOf(route) === routes.length - 1;
|
||||
const name = getBreadcrumbName(route, params, routes);
|
||||
return isLastItem
|
||||
? <span>{name}</span>
|
||||
: <a href={`#/${paths.join('/')}`}>{name}</a>;
|
||||
},
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@@ -62,12 +63,12 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
|
||||
|
||||
render() {
|
||||
let crumbs;
|
||||
const { separator, prefixCls, routes, params, children, itemRender } = this.props;
|
||||
const { separator, prefixCls, routes, params = {}, children, itemRender = defaultItemRender } = this.props;
|
||||
if (routes && routes.length > 0) {
|
||||
const paths = [];
|
||||
crumbs = routes.map((route, i) => {
|
||||
const paths: string[] = [];
|
||||
crumbs = routes.map((route) => {
|
||||
route.path = route.path || '';
|
||||
let path = route.path.replace(/^\//, '');
|
||||
let path: string = route.path.replace(/^\//, '');
|
||||
Object.keys(params).forEach(key => {
|
||||
path = path.replace(`:${key}`, params[key]);
|
||||
});
|
||||
@@ -83,7 +84,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
} else if (children) {
|
||||
crumbs = React.Children.map(children, (element: any, index) => {
|
||||
return cloneElement(element, {
|
||||
separator,
|
||||
|
||||
@@ -34,8 +34,8 @@ export interface ButtonProps {
|
||||
icon?: string;
|
||||
shape?: ButtonShape;
|
||||
size?: ButtonSize;
|
||||
onClick?: React.FormEventHandler;
|
||||
onMouseUp?: React.FormEventHandler;
|
||||
onClick?: React.FormEventHandler<any>;
|
||||
onMouseUp?: React.FormEventHandler<any>;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
@@ -48,7 +48,6 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-btn',
|
||||
onClick() {},
|
||||
loading: false,
|
||||
};
|
||||
|
||||
@@ -87,7 +86,10 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => this.clearButton(buttonNode), 500);
|
||||
|
||||
this.props.onClick(e);
|
||||
const onClick = this.props.onClick;
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle auto focus when click button in Chrome
|
||||
|
||||
@@ -77,7 +77,6 @@
|
||||
vertical-align: middle;
|
||||
> .@{btnClassName} {
|
||||
position: relative;
|
||||
float: left;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
@@ -106,7 +105,6 @@
|
||||
margin-bottom: 0;
|
||||
font-weight: @btn-font-weight;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
touch-action: manipulation;
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import { PropTypes } from 'react';
|
||||
import moment from 'moment';
|
||||
import { PREFIX_CLS } from './Constants';
|
||||
import Select from '../select';
|
||||
import { Group, Button } from '../radio';
|
||||
const Option = Select.Option;
|
||||
|
||||
function noop() {}
|
||||
|
||||
export interface HeaderProps {
|
||||
prefixCls?: string;
|
||||
locale?: any;
|
||||
@@ -24,21 +22,6 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
prefixCls: `${PREFIX_CLS}-header`,
|
||||
yearSelectOffset: 10,
|
||||
yearSelectTotal: 20,
|
||||
onValueChange: noop,
|
||||
onTypeChange: noop,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.object,
|
||||
locale: PropTypes.object,
|
||||
yearSelectOffset: PropTypes.number,
|
||||
yearSelectTotal: PropTypes.number,
|
||||
onValueChange: PropTypes.func,
|
||||
onTypeChange: PropTypes.func,
|
||||
prefixCls: PropTypes.string,
|
||||
selectPrefixCls: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
fullscreen: PropTypes.bool,
|
||||
};
|
||||
|
||||
getYearSelectElement(year) {
|
||||
@@ -47,13 +30,13 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
const end = start + yearSelectTotal;
|
||||
const suffix = locale.year === '年' ? '年' : '';
|
||||
|
||||
const options = [];
|
||||
const options: React.ReactElement<any>[] = [];
|
||||
for (let index = start; index < end; index++) {
|
||||
options.push(<Option key={`${index}`}>{index + suffix}</Option>);
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? null : 'small'}
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
dropdownMatchSelectWidth={false}
|
||||
className={`${prefixCls}-year-select`}
|
||||
onChange={this.onYearChange}
|
||||
@@ -64,10 +47,10 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
);
|
||||
}
|
||||
|
||||
getMonthsLocale(value) {
|
||||
getMonthsLocale(value: moment.Moment) {
|
||||
const current = value.clone();
|
||||
const localeData = value.localeData();
|
||||
const months = [];
|
||||
const months: any[] = [];
|
||||
for (let i = 0; i < 12; i++) {
|
||||
current.month(i);
|
||||
months.push(localeData.monthsShort(current));
|
||||
@@ -78,7 +61,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
getMonthSelectElement(month, months) {
|
||||
const props = this.props;
|
||||
const { prefixCls, fullscreen } = props;
|
||||
const options = [];
|
||||
const options: React.ReactElement<any>[] = [];
|
||||
|
||||
for (let index = 0; index < 12; index++) {
|
||||
options.push(<Option key={`${index}`}>{months[index]}</Option>);
|
||||
@@ -86,7 +69,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
|
||||
return (
|
||||
<Select
|
||||
size={fullscreen ? null : 'small'}
|
||||
size={fullscreen ? 'default' : 'small'}
|
||||
dropdownMatchSelectWidth={false}
|
||||
className={`${prefixCls}-month-select`}
|
||||
value={String(month)}
|
||||
@@ -100,17 +83,27 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
onYearChange = (year) => {
|
||||
const newValue = this.props.value.clone();
|
||||
newValue.year(parseInt(year, 10));
|
||||
this.props.onValueChange(newValue);
|
||||
|
||||
const onValueChange = this.props.onValueChange;
|
||||
if (onValueChange) {
|
||||
onValueChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
onMonthChange = (month) => {
|
||||
const newValue = this.props.value.clone();
|
||||
newValue.month(parseInt(month, 10));
|
||||
this.props.onValueChange(newValue);
|
||||
const onValueChange = this.props.onValueChange;
|
||||
if (onValueChange) {
|
||||
onValueChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
onTypeChange = (e) => {
|
||||
this.props.onTypeChange(e.target.value);
|
||||
const onTypeChange = this.props.onTypeChange;
|
||||
if (onTypeChange) {
|
||||
onTypeChange(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -38,12 +38,9 @@ export interface CalendarProps {
|
||||
|
||||
export default class Calendar extends React.Component<CalendarProps, any> {
|
||||
static defaultProps = {
|
||||
monthCellRender: noop,
|
||||
dateCellRender: noop,
|
||||
locale: {},
|
||||
fullscreen: true,
|
||||
prefixCls: PREFIX_CLS,
|
||||
onPanelChange: noop,
|
||||
mode: 'month',
|
||||
};
|
||||
|
||||
@@ -82,28 +79,28 @@ export default class Calendar extends React.Component<CalendarProps, any> {
|
||||
}
|
||||
|
||||
monthCellRender = (value) => {
|
||||
const prefixCls = this.props.prefixCls;
|
||||
const { prefixCls, monthCellRender = noop as Function } = this.props;
|
||||
return (
|
||||
<div className={`${prefixCls}-month`}>
|
||||
<div className={`${prefixCls}-value`}>
|
||||
{value.localeData().monthsShort(value)}
|
||||
</div>
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{this.props.monthCellRender(value)}
|
||||
{monthCellRender(value)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
dateCellRender = (value) => {
|
||||
const prefixCls = this.props.prefixCls;
|
||||
const { prefixCls, dateCellRender = noop as Function } = this.props;
|
||||
return (
|
||||
<div className={`${prefixCls}-date`}>
|
||||
<div className={`${prefixCls}-value`}>
|
||||
{zerofixed(value.date())}
|
||||
</div>
|
||||
<div className={`${prefixCls}-content`}>
|
||||
{this.props.dateCellRender(value)}
|
||||
{dateCellRender(value)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -113,14 +110,20 @@ export default class Calendar extends React.Component<CalendarProps, any> {
|
||||
if (!('value' in this.props) && this.state.value !== value) {
|
||||
this.setState({ value });
|
||||
}
|
||||
this.props.onPanelChange(value, this.state.mode);
|
||||
const onPanelChange = this.props.onPanelChange;
|
||||
if (onPanelChange) {
|
||||
onPanelChange(value, this.state.mode);
|
||||
}
|
||||
}
|
||||
|
||||
setType = (type) => {
|
||||
const mode = (type === 'date') ? 'month' : 'year';
|
||||
if (this.state.mode !== mode) {
|
||||
this.setState({ mode });
|
||||
this.props.onPanelChange(this.state.value, mode);
|
||||
const onPanelChange = this.props.onPanelChange;
|
||||
if (onPanelChange) {
|
||||
onPanelChange(this.state.value, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +133,7 @@ export default class Calendar extends React.Component<CalendarProps, any> {
|
||||
const { prefixCls, style, className, fullscreen } = props;
|
||||
const type = (mode === 'year') ? 'month' : 'date';
|
||||
const locale = getLocale(
|
||||
this.props, this.context, 'Calendar',
|
||||
props, this.context, 'Calendar',
|
||||
() => require('./locale/zh_CN')
|
||||
);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface CardProps {
|
||||
loading?: boolean;
|
||||
children?: any;
|
||||
id?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default (props: CardProps) => {
|
||||
|
||||
@@ -38,6 +38,7 @@ Cascade selection box.
|
||||
| changeOnSelect | change value on each selection if set to true, see above demo for details | boolean | false |
|
||||
| showSearch | Whether show search input in single mode. | Boolean or Object | false |
|
||||
| notFoundContent | Specify content to show when no result matches. | String | 'Not Found' |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative.[example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body |
|
||||
|
||||
Fields in `showSearch`:
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ function defaultSortFilteredOption(a, b, inputValue) {
|
||||
return a.findIndex(callback) - b.findIndex(callback);
|
||||
}
|
||||
|
||||
const defaultDisplayRender = label => label.join(' / ');
|
||||
|
||||
export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-cascader',
|
||||
@@ -96,14 +98,11 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
placeholder: 'Please select',
|
||||
transitionName: 'slide-up',
|
||||
popupPlacement: 'bottomLeft',
|
||||
onChange() {},
|
||||
options: [],
|
||||
displayRender: label => label.join(' / '),
|
||||
disabled: false,
|
||||
allowClear: true,
|
||||
showSearch: false,
|
||||
notFoundContent: 'Not Found',
|
||||
onPopupVisibleChange() {},
|
||||
};
|
||||
|
||||
cachedOptions: CascaderOptionType[];
|
||||
@@ -117,7 +116,7 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: props.value || props.defautValue || [],
|
||||
value: props.value || props.defaultValue || [],
|
||||
inputValue: '',
|
||||
inputFocused: false,
|
||||
popupVisible: false,
|
||||
@@ -146,7 +145,11 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
inputFocused: popupVisible,
|
||||
inputValue: popupVisible ? this.state.inputValue : '',
|
||||
});
|
||||
this.props.onPopupVisibleChange(popupVisible);
|
||||
|
||||
const onPopupVisibleChange = this.props.onPopupVisibleChange;
|
||||
if (onPopupVisibleChange) {
|
||||
onPopupVisibleChange(popupVisible);
|
||||
}
|
||||
}
|
||||
|
||||
handleInputBlur = () => {
|
||||
@@ -173,11 +176,14 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
this.props.onChange(value, selectedOptions);
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange(value, selectedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
const { options, displayRender } = this.props;
|
||||
const { options, displayRender = defaultDisplayRender as Function } = this.props;
|
||||
const value = this.state.value;
|
||||
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
|
||||
const selectedOptions = arrayTreeFilter(options, (o, level) => o.value === unwrappedValue[level]);
|
||||
@@ -197,7 +203,7 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
}
|
||||
|
||||
flattenTree(options, changeOnSelect, ancestor = []) {
|
||||
let flattenOptions = [];
|
||||
let flattenOptions: any = [];
|
||||
options.forEach((option) => {
|
||||
const path = ancestor.concat(option);
|
||||
if (changeOnSelect || !option.children) {
|
||||
@@ -293,10 +299,7 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
this.cachedOptions = options;
|
||||
}
|
||||
|
||||
const dropdownMenuColumnStyle = {
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
};
|
||||
const dropdownMenuColumnStyle: { width?: number, height?: string } = {};
|
||||
const isNotFound = (options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
|
||||
if (isNotFound) {
|
||||
dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
|
||||
@@ -307,7 +310,8 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
dropdownMenuColumnStyle.width = this.refs.input.refs.input.offsetWidth;
|
||||
}
|
||||
return (
|
||||
<RcCascader {...props}
|
||||
<RcCascader
|
||||
{...props}
|
||||
options={options}
|
||||
value={value}
|
||||
popupVisible={state.popupVisible}
|
||||
@@ -320,7 +324,8 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
style={style}
|
||||
className={pickerCls}
|
||||
>
|
||||
<Input {...inputProps}
|
||||
<Input
|
||||
{...inputProps}
|
||||
ref="input"
|
||||
placeholder={value && value.length > 0 ? null : placeholder}
|
||||
className={`${prefixCls}-input ${sizeCls}`}
|
||||
@@ -328,11 +333,13 @@ export default class Cascader extends React.Component<CascaderProps, any> {
|
||||
disabled={disabled}
|
||||
readOnly={!showSearch}
|
||||
autoComplete="off"
|
||||
onClick={showSearch ? this.handleInputClick : null}
|
||||
onBlur={showSearch ? this.handleInputBlur : null}
|
||||
onChange={showSearch ? this.handleInputChange : null}
|
||||
onClick={showSearch ? this.handleInputClick : undefined}
|
||||
onBlur={showSearch ? this.handleInputBlur : undefined}
|
||||
onChange={showSearch ? this.handleInputChange : undefined}
|
||||
/>
|
||||
<span className={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
|
||||
<span className={`${prefixCls}-picker-label`}>
|
||||
{this.getLabel()}
|
||||
</span>
|
||||
{clearIcon}
|
||||
<Icon type="down" className={arrowCls} />
|
||||
</span>
|
||||
|
||||
@@ -39,6 +39,7 @@ subtitle: 级联选择
|
||||
| changeOnSelect | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | Boolean | false |
|
||||
| showSearch | 在选择框中显示搜索框 | Boolean | false |
|
||||
| notFoundContent | 当下拉列表为空时显示的内容 | String | 'Not Found' |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body |
|
||||
|
||||
`showSearch` 为对象时,其中的字段:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Checkbox from './index';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
|
||||
export interface CheckboxOptionType {
|
||||
label: string;
|
||||
@@ -29,7 +29,6 @@ export interface CheckboxGroupState {
|
||||
export default class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupState> {
|
||||
static defaultProps = {
|
||||
options: [],
|
||||
onChange() {},
|
||||
prefixCls: 'ant-checkbox-group',
|
||||
};
|
||||
static propTypes = {
|
||||
@@ -78,7 +77,10 @@ export default class CheckboxGroup extends React.Component<CheckboxGroupProps, C
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
this.props.onChange(value);
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { prefixCls } = this.props;
|
||||
|
||||
@@ -2,7 +2,7 @@ import RcCheckbox from 'rc-checkbox';
|
||||
import React from 'react';
|
||||
import CheckboxGroup from './Group';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
import splitObject from '../_util/splitObject';
|
||||
|
||||
export interface CheckboxProps {
|
||||
@@ -13,7 +13,7 @@ export interface CheckboxProps {
|
||||
/** indeterminate 状态,只负责样式控制 */
|
||||
indeterminate?: boolean;
|
||||
/** 变化时回调函数 */
|
||||
onChange?: React.FormEventHandler;
|
||||
onChange?: React.FormEventHandler<any>;
|
||||
style?: React.CSSProperties;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
|
||||
@@ -8,6 +8,7 @@ import Icon from '../icon';
|
||||
export default class RangePicker extends React.Component<any, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-calendar',
|
||||
allowClear: true,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -45,7 +46,7 @@ export default class RangePicker extends React.Component<any, any> {
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const { disabledDate, showTime, prefixCls, popupStyle, style, onOk, locale } = props;
|
||||
const { disabledDate, disabledTime, showTime, prefixCls, popupStyle, style, onOk, locale } = props;
|
||||
const state = this.state;
|
||||
|
||||
const calendarClassName = classNames({
|
||||
@@ -77,6 +78,7 @@ export default class RangePicker extends React.Component<any, any> {
|
||||
className={calendarClassName}
|
||||
timePicker={props.timePicker}
|
||||
disabledDate={disabledDate}
|
||||
disabledTime={disabledTime}
|
||||
dateInputPlaceholder={[startPlaceholder, endPlaceholder]}
|
||||
locale={locale.lang}
|
||||
onOk={onOk}
|
||||
@@ -84,7 +86,7 @@ export default class RangePicker extends React.Component<any, any> {
|
||||
/>
|
||||
);
|
||||
|
||||
const clearIcon = (!props.disabled && state.value && (state.value[0] || state.value[1]))
|
||||
const clearIcon = (!props.disabled && props.allowClear && state.value && (state.value[0] || state.value[1]))
|
||||
? <Icon
|
||||
type="cross-circle"
|
||||
className={`${prefixCls}-picker-clear`}
|
||||
|
||||
@@ -4,6 +4,7 @@ import MonthCalendar from 'rc-calendar/lib/MonthCalendar';
|
||||
import RcDatePicker from 'rc-calendar/lib/Picker';
|
||||
import classNames from 'classnames';
|
||||
import assign from 'object-assign';
|
||||
import omit from 'omit.js';
|
||||
import Icon from '../icon';
|
||||
|
||||
export interface PickerProps {
|
||||
@@ -13,10 +14,11 @@ export interface PickerProps {
|
||||
|
||||
export default function createPicker(TheCalendar) {
|
||||
// use class typescript error
|
||||
const CalenderWrapper = React.createClass({
|
||||
const CalenderWrapper = React.createClass<any, any>({
|
||||
getDefaultProps() {
|
||||
return {
|
||||
prefixCls: 'ant-calendar',
|
||||
allowClear: true,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -24,6 +26,7 @@ export default function createPicker(TheCalendar) {
|
||||
const props = this.props;
|
||||
return {
|
||||
value: props.value || props.defaultValue,
|
||||
tempValue: undefined,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -50,8 +53,26 @@ export default function createPicker(TheCalendar) {
|
||||
props.onChange(value, (value && value.format(props.format)) || '');
|
||||
},
|
||||
|
||||
handleTempChange(tempValue) {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ tempValue });
|
||||
}
|
||||
},
|
||||
|
||||
// Clear temp value when hide picker panel
|
||||
handleOpenChange(open) {
|
||||
if (!open) {
|
||||
this.setState({
|
||||
tempValue: undefined,
|
||||
});
|
||||
}
|
||||
if (this.props.onOpenChange) {
|
||||
this.props.onOpenChange(open);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const props = omit(this.props, ['onChange']);
|
||||
const prefixCls = props.prefixCls;
|
||||
const locale = props.locale;
|
||||
|
||||
@@ -66,22 +87,24 @@ export default function createPicker(TheCalendar) {
|
||||
});
|
||||
|
||||
// 需要选择时间时,点击 ok 时才触发 onChange
|
||||
let pickerChangeHandler: Object = {
|
||||
onChange: this.handleChange,
|
||||
};
|
||||
let calendarHandler: Object = {
|
||||
onOk: this.handleChange,
|
||||
// fix https://github.com/ant-design/ant-design/issues/1902
|
||||
onSelect: (value, cause) => {
|
||||
if (!('value' in this.props)) {
|
||||
this.setState({ value });
|
||||
}
|
||||
},
|
||||
};
|
||||
let pickerChangeHandler: Object = {};
|
||||
let calendarHandler: Object = {};
|
||||
if (props.showTime) {
|
||||
pickerChangeHandler = {};
|
||||
calendarHandler = {
|
||||
onOk: this.handleChange,
|
||||
// fix https://github.com/ant-design/ant-design/issues/1902
|
||||
onSelect: (value, cause) => {
|
||||
if (cause && cause.source === 'todayButton') {
|
||||
this.handleChange(value);
|
||||
} else {
|
||||
this.handleTempChange(value);
|
||||
}
|
||||
},
|
||||
};
|
||||
} else {
|
||||
calendarHandler = {};
|
||||
pickerChangeHandler = {
|
||||
onChange: this.handleChange,
|
||||
};
|
||||
}
|
||||
|
||||
const calendar = (
|
||||
@@ -101,12 +124,12 @@ export default function createPicker(TheCalendar) {
|
||||
);
|
||||
|
||||
// default width for showTime
|
||||
const pickerStyle = { width: undefined };
|
||||
const pickerStyle: { width?: number } = {};
|
||||
if (props.showTime) {
|
||||
pickerStyle.width = 180;
|
||||
}
|
||||
|
||||
const clearIcon = (!props.disabled && this.state.value) ?
|
||||
const clearIcon = (!props.disabled && props.allowClear && this.state.value) ?
|
||||
<Icon type="cross-circle"
|
||||
className={`${prefixCls}-picker-clear`}
|
||||
onClick={this.clearSelection}
|
||||
@@ -116,8 +139,9 @@ export default function createPicker(TheCalendar) {
|
||||
<RcDatePicker
|
||||
{...props}
|
||||
{...pickerChangeHandler}
|
||||
onOpenChange={this.handleOpenChange}
|
||||
calendar={calendar}
|
||||
value={this.state.value}
|
||||
value={this.state.tempValue || this.state.value}
|
||||
prefixCls={`${prefixCls}-picker-container`}
|
||||
style={props.popupStyle}
|
||||
>
|
||||
|
||||
@@ -8,6 +8,7 @@ title:
|
||||
## zh-CN
|
||||
|
||||
设置 `disabledDate` 方法,来确定不可选时段。
|
||||
设置 `disabledTime` 方法,来确定 showTime 的 RangePicker 的不可选时间段。
|
||||
|
||||
如上例:不可选择今天之后的日期。
|
||||
|
||||
@@ -19,13 +20,54 @@ As in the example above: you can't select a date later than today.
|
||||
|
||||
````jsx
|
||||
import { DatePicker } from 'antd';
|
||||
const RangePicker = DatePicker.RangePicker;
|
||||
|
||||
function newArray(start, end) {
|
||||
const result = [];
|
||||
for (let i = start; i < end; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
const disabledDate = function (current) {
|
||||
// can not select days after today
|
||||
return current && current.valueOf() > Date.now();
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<DatePicker disabledDate={disabledDate} />
|
||||
function disabledTime(time, type) {
|
||||
console.log('disabledTime', time, type);
|
||||
if (type === 'start') {
|
||||
return {
|
||||
disabledHours() {
|
||||
return newArray(0, 60).splice(4, 20);
|
||||
},
|
||||
disabledMinutes() {
|
||||
return newArray(30, 60);
|
||||
},
|
||||
disabledSeconds() {
|
||||
return [55, 56];
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
disabledHours() {
|
||||
return newArray(0, 60).splice(20, 4);
|
||||
},
|
||||
disabledMinutes() {
|
||||
return newArray(0, 31);
|
||||
},
|
||||
disabledSeconds() {
|
||||
return [55, 56];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ReactDOM.render(<div>
|
||||
<DatePicker disabledDate={disabledDate} />
|
||||
<br />
|
||||
<RangePicker showTime disabledDate={disabledDate} disabledTime={disabledTime} />
|
||||
</div>
|
||||
, mountNode);
|
||||
````
|
||||
|
||||
@@ -16,8 +16,9 @@ This property provide an additional time selection. When `showTime` is an Object
|
||||
````jsx
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
function onChange(value) {
|
||||
function onChange(value, dateString) {
|
||||
console.log('Selected Time: ', value);
|
||||
console.log('Formatted Selected Time: ', dateString);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
---
|
||||
order: 4
|
||||
hidden: true
|
||||
title:
|
||||
zh-CN: 日期时间选择二
|
||||
en-US: To select a date, case 2
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
和 <a href="/components/time-picker">时间选择框</a> 配合使用。
|
||||
|
||||
## en-US
|
||||
|
||||
Cooperate with `<a href="/components/time-picker">time-picker</a>`
|
||||
|
||||
````jsx
|
||||
import { DatePicker, TimePicker } from 'antd';
|
||||
|
||||
const DateTimePicker = React.createClass({
|
||||
handleChange(from, value) {
|
||||
this.result = this.result || new Date();
|
||||
if (!value) {
|
||||
if (from === 'date') {
|
||||
this.selectedDate = false;
|
||||
} else {
|
||||
this.selectedTime = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (from === 'date') {
|
||||
this.result.setFullYear(value.getFullYear());
|
||||
this.result.setMonth(value.getMonth());
|
||||
this.result.setDate(value.getDate());
|
||||
this.selectedDate = true;
|
||||
} else {
|
||||
this.result.setHours(value.getHours());
|
||||
this.result.setMinutes(value.getMinutes());
|
||||
this.result.setSeconds(value.getSeconds());
|
||||
this.selectedTime = true;
|
||||
}
|
||||
if (this.selectedDate && this.selectedTime) {
|
||||
this.props.onSelect(this.result);
|
||||
}
|
||||
},
|
||||
handleDateChange(value) {
|
||||
this.handleChange('date', value);
|
||||
},
|
||||
handleTimeChange(value) {
|
||||
this.handleChange('time', value);
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<DatePicker onChange={this.handleDateChange} />
|
||||
<TimePicker onChange={this.handleTimeChange} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
function onSelect(value) {
|
||||
console.log('选择了时间:', value);
|
||||
}
|
||||
|
||||
ReactDOM.render(<DateTimePicker onSelect={onSelect} />
|
||||
, mountNode);
|
||||
````
|
||||
@@ -12,7 +12,7 @@ By clicking the input box, you can select a date from a popup calendar.
|
||||
|
||||
## API
|
||||
|
||||
### DatePicker
|
||||
Note: Part of locale of DatePicker, MonthPicker, RangePicker is read from value. So, please set the locale of moment correctly.
|
||||
|
||||
```jsx
|
||||
import moment from 'moment-timezone/moment-timezone';
|
||||
@@ -27,22 +27,31 @@ moment.tz.setDefault('Asia/Shanghai')
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||
```
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
### Common API
|
||||
|
||||
The following APIs are shared by DatePicker, MonthPicker, RangePicker.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | to set date | [moment](http://momentjs.com/) | - |
|
||||
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
|
||||
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | String | "YYYY-MM-DD" |
|
||||
| disabledDate | to specify the date that cannot be selected | function | - |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
|
||||
| allowClear | Whether to show clear button | bool | true |
|
||||
| disabled | determine whether the DatePicker is disabled | Boolean | false |
|
||||
| style | to customize the style of the input box | Object | {} |
|
||||
| popupStyle | to customize the style of the popup calendar | Object | {} |
|
||||
| size | determine the size of the input box, the height of `large` and `small`, are 32px and 22px respectively, while default size is 28px | String | - |
|
||||
| locale | localization configuration | Object | [default](https://github.com/ant-design/ant-design/issues/424) |
|
||||
| onOk | a callback function, can be executed when OK-button is clicked | function(Date value) | - |
|
||||
| disabledDate | to specify the date that cannot be selected | function | - |
|
||||
| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - |
|
||||
|
||||
### DatePicker
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | to set date | [moment](http://momentjs.com/) | - |
|
||||
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
|
||||
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | String | "YYYY-MM-DD" |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
|
||||
| open | open state of picker | bool | - |
|
||||
| onOpenChange | a callback function, can be executed whether the popup calendar is popped up or closed | function(status) | - |
|
||||
| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - |
|
||||
| showTime | to provide an additional time selection | Object/Boolean | [TimePicker Options](/components/time-picker/#api) |
|
||||
|
||||
### MonthPicker
|
||||
@@ -52,27 +61,18 @@ moment.tz.setDefault('Asia/Shanghai')
|
||||
| value | to set date | [moment](http://momentjs.com/) | - |
|
||||
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
|
||||
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | String | "YYYY-MM" |
|
||||
| disabledDate | to specify the date that cannot be selected | function | - |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
|
||||
| disabled | determine whether the MonthPicker is disabled | Boolean | false |
|
||||
| style | to customize the style of the input box | Object | {} |
|
||||
| popupStyle | to customize the style of the popup calendar | Object | {} |
|
||||
| size | determine the size of the input box, the height of `large` and `small`, are 32px and 22px respectively, while default size is 28px | String | - |
|
||||
| locale | localization configuration | Object | [default](https://github.com/ant-design/ant-design/issues/424) |
|
||||
| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - |
|
||||
|
||||
### RangePicker
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | to set date | [moment, moment] | - |
|
||||
| defaultValue | to set default date | [moment, moment] | - |
|
||||
| value | to set date | [[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
|
||||
| defaultValue | to set default date | [[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
|
||||
| format | to set the date format | String | "YYYY-MM-DD HH:mm:ss" |
|
||||
| onChange | a callback function, can be executed when the selected time is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
|
||||
| showTime | to provide an additional time selection | Object/Boolean | [TimePicker Options](/components/time-picker/#api) |
|
||||
|
||||
The following properties are the same with `DatePicker`: `disabled` `style` `popupStyle` `size` `locale` `showTime` `onOk` `getCalendarContainer`
|
||||
|
||||
| disabledTime | to specify the time that cannot be selected | function(dates: [moment, moment], partial: `'start'|'end'`) | - |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
|
||||
21
components/date-picker/index.tsx
Normal file → Executable file
21
components/date-picker/index.tsx
Normal file → Executable file
@@ -10,15 +10,16 @@ import Calendar from './Calendar';
|
||||
import { TimePickerProps } from '../time-picker';
|
||||
|
||||
export interface PickerProps {
|
||||
prefixCls?: string;
|
||||
inputPrefixCls?: string;
|
||||
format?: string;
|
||||
disabled?: boolean;
|
||||
allowClear?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
popupStyle?: React.CSSProperties;
|
||||
locale?: any;
|
||||
size?: 'large' | 'small' | 'default';
|
||||
getCalendarContainer?: (trigger) => React.ReactNode;
|
||||
prefixCls?: string;
|
||||
inputPrefixCls?: string;
|
||||
getCalendarContainer?: (trigger: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
export interface SinglePickerProps {
|
||||
@@ -32,12 +33,15 @@ export interface DatePickerProps extends PickerProps, SinglePickerProps {
|
||||
showTime?: TimePickerProps | boolean;
|
||||
open?: boolean;
|
||||
toggleOpen?: (e: {open: boolean}) => void;
|
||||
disabledDate?: (current: moment.Moment) => boolean;
|
||||
onOpenChange?: (status: boolean) => void;
|
||||
}
|
||||
const DatePicker = wrapPicker(createPicker(RcCalendar)) as React.ClassicComponentClass<DatePickerProps>;
|
||||
|
||||
export interface MonthPickerProps extends PickerProps, SinglePickerProps {
|
||||
disabledDate?: (current: moment.Moment) => boolean;
|
||||
}
|
||||
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'YYYY-MM') as React.ClassicComponentClass<MonthPickerProps>;
|
||||
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'YYYY-MM');
|
||||
|
||||
export interface RangePickerProps extends PickerProps {
|
||||
value?: [moment.Moment, moment.Moment];
|
||||
@@ -48,9 +52,14 @@ export interface RangePickerProps extends PickerProps {
|
||||
}
|
||||
|
||||
assign(DatePicker, {
|
||||
RangePicker: wrapPicker(RangePicker) as React.ClassicComponentClass<RangePickerProps>,
|
||||
RangePicker: wrapPicker(RangePicker),
|
||||
Calendar,
|
||||
MonthPicker,
|
||||
});
|
||||
|
||||
export default DatePicker;
|
||||
export interface DatePickerDecorator extends React.ClassicComponentClass<DatePickerProps> {
|
||||
RangePicker: React.ClassicComponentClass<RangePickerProps>;
|
||||
MonthPicker: React.ClassicComponentClass<MonthPickerProps>;
|
||||
}
|
||||
|
||||
export default DatePicker as DatePickerDecorator;
|
||||
|
||||
@@ -13,7 +13,7 @@ subtitle: 日期选择框
|
||||
|
||||
## API
|
||||
|
||||
### DatePicker
|
||||
注意:DatePicker、MonthPicker、RangePicker 部分 locale 是从 value 中读取,所以请先正确设置 moment 的 locale。
|
||||
|
||||
```jsx
|
||||
import moment from 'moment-timezone/moment-timezone';
|
||||
@@ -28,50 +28,52 @@ moment.tz.setDefault('Asia/Shanghai')
|
||||
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
|
||||
```
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
### 共同的 API
|
||||
|
||||
以下 API 为 DatePicker、MonthPicker、RangePicker 共享的 API。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | 日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-MM-DD" |
|
||||
| disabledDate | 不可选择的日期 | function | 无 |
|
||||
| onChange | 时间发生变化的回调 | function(date: moment, dateString: string) | 无 |
|
||||
| allowClear | 是否显示清除按钮 | bool | true |
|
||||
| disabled | 禁用 | bool | false |
|
||||
| style | 自定义输入框样式 | object | {} |
|
||||
| popupStyle | 格外的弹出日历样式 | object | {} |
|
||||
| size | 输入框大小,`large` 高度为 32px,`small` 为 22px,默认是 28px | string | 无 |
|
||||
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/issues/424) |
|
||||
| disabledDate | 不可选择的日期 | function | 无 |
|
||||
| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 |
|
||||
|
||||
### DatePicker
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | 日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-MM-DD" |
|
||||
| onChange | 时间发生变化的回调 | function(date: moment, dateString: string) | 无 |
|
||||
| open | 控制弹层是否展开 | bool | - |
|
||||
| onOpenChange | 弹出日历和关闭日历的回调 | function(status) | 无 |
|
||||
| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 |
|
||||
| showTime | 增加时间选择功能 | Object or Boolean | [TimePicker Options](/components/time-picker/#api) |
|
||||
|
||||
### MonthPicker
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | 日期 | moment | 无 |
|
||||
| defaultValue | 默认日期 | moment | 无 |
|
||||
| value | 日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 |
|
||||
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-MM" |
|
||||
| disabledDate | 不可选择的日期 | function | 无 |
|
||||
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: moment, dateString: string) | 无 |
|
||||
| disabled | 禁用 | bool | false |
|
||||
| style | 自定义输入框样式 | object | {} |
|
||||
| popupStyle | 格外的弹出日历样式 | object | {} |
|
||||
| size | 输入框大小,`large` 高度为 32px,`small` 为 22px,默认是 28px | string | 无 |
|
||||
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/issues/424) |
|
||||
| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 |
|
||||
|
||||
### RangePicker
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|--------------|----------------|----------|--------------|
|
||||
| value | 日期 | [moment, moment] | 无 |
|
||||
| defaultValue | 默认日期 | [moment, moment] | 无 |
|
||||
| value | 日期 | [[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | 无 |
|
||||
| defaultValue | 默认日期 | [[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | 无 |
|
||||
| format | 展示的日期格式 | string | "YYYY-MM-DD HH:mm:ss" |
|
||||
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
|
||||
| showTime | 增加时间选择功能 | Object or Boolean | [TimePicker Options](/components/time-picker/#api) |
|
||||
|
||||
`disabled` `style` `popupStyle` `size` `locale` `showTime` `onOk` `getCalendarContainer` 属性与 DatePicker 的一致。
|
||||
| disabledTime | 不可选择的时间 | function(dates: [moment, moment], partial: `'start'|'end'`) | 无 |
|
||||
|
||||
<style>
|
||||
.code-box-demo .ant-calendar-picker {
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function wrapPicker(Picker, defaultFormat?) {
|
||||
});
|
||||
|
||||
const locale = getLocale(
|
||||
this.props, this.context, 'DatePicker',
|
||||
props, this.context, 'DatePicker',
|
||||
() => require('./locale/zh_CN')
|
||||
);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import splitObject from '../_util/splitObject';
|
||||
|
||||
export interface DropdownButtonProps {
|
||||
type?: 'primary' | 'ghost' | 'dash';
|
||||
onClick?: React.FormEventHandler;
|
||||
onClick?: React.FormEventHandler<any>;
|
||||
trigger?: 'click' | 'hover';
|
||||
overlay: React.ReactNode;
|
||||
visible?: boolean;
|
||||
@@ -44,7 +44,7 @@ export default class DropdownButton extends React.Component<DropdownButtonProps,
|
||||
return (
|
||||
<ButtonGroup {...restProps} className={cls}>
|
||||
<Button type={type} onClick={onClick} disabled={disabled}>{children}</Button>
|
||||
<Dropdown align={align} overlay={overlay} trigger={trigger}>
|
||||
<Dropdown align={align} overlay={overlay} trigger={disabled ? [] : trigger}>
|
||||
<Button type={type} disabled={disabled}>
|
||||
<Icon type="down" />
|
||||
</Button>
|
||||
|
||||
42
components/form/Form.tsx
Normal file → Executable file
42
components/form/Form.tsx
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
import omit from 'omit.js';
|
||||
import warning from 'warning';
|
||||
import assign from 'object-assign';
|
||||
@@ -20,7 +20,7 @@ export interface FormProps {
|
||||
inline?: boolean;
|
||||
vertical?: boolean;
|
||||
form?: WrappedFormUtils;
|
||||
onSubmit?: React.FormEventHandler;
|
||||
onSubmit?: React.FormEventHandler<any>;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
prefixCls?: string;
|
||||
@@ -42,11 +42,9 @@ export type WrappedFormUtils = {
|
||||
validateFields(options: Object, callback: (erros: any, values: any) => void): any;
|
||||
validateFields(callback: (erros: any, values: any) => void): any;
|
||||
/** 与 `validateFields` 相似,但校验完后,如果校验不通过的菜单域不在可见范围内,则自动滚动进可见范围 */
|
||||
validateFieldsAndScroll(
|
||||
fieldNames?: Array<string>,
|
||||
options?: Object,
|
||||
callback?: (erros: any, values: any) => void
|
||||
): void;
|
||||
validateFieldsAndScroll(fieldNames?: Array<string>,
|
||||
options?: Object,
|
||||
callback?: (erros: any, values: any) => void): void;
|
||||
/** 获取某个输入控件的 Error */
|
||||
getFieldError(name: string): Object[];
|
||||
/** 判断一个输入控件是否在校验状态*/
|
||||
@@ -62,7 +60,7 @@ export type WrappedFormUtils = {
|
||||
/** 收集子节点的值的时机 */
|
||||
trigger?: string;
|
||||
/** 可以把 onChange 的参数转化为控件的值,例如 DatePicker 可设为:(date, dateString) => dateString */
|
||||
getValueFromEvent?: (...args) => any;
|
||||
getValueFromEvent?: (...args: any[]) => any;
|
||||
/** 校验子节点值的时机 */
|
||||
validateTrigger?: string;
|
||||
/** 校验规则,参见 [async-validator](https://github.com/yiminghe/async-validator) */
|
||||
@@ -83,6 +81,8 @@ export interface ComponentDecorator {
|
||||
<T extends (typeof FormComponent)>(component: T): T;
|
||||
}
|
||||
|
||||
let warnedGetFieldProps = false;
|
||||
|
||||
export default class Form extends React.Component<FormProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-form',
|
||||
@@ -103,8 +103,9 @@ export default class Form extends React.Component<FormProps, any> {
|
||||
static Item = FormItem;
|
||||
|
||||
static create = (options?: FormCreateOption): ComponentDecorator => {
|
||||
const formWrapper = createDOMForm(assign({}, options, {
|
||||
const formWrapper = createDOMForm(assign({
|
||||
fieldNameProp: 'id',
|
||||
}, options, {
|
||||
fieldMetaProp: FIELD_META_PROP,
|
||||
}));
|
||||
|
||||
@@ -121,16 +122,25 @@ export default class Form extends React.Component<FormProps, any> {
|
||||
form: this.props.form,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const getFieldProps = this.props.form.getFieldProps;
|
||||
function deprecatedGetFieldProps(name, option) {
|
||||
componentWillMount() {
|
||||
if (!warnedGetFieldProps) {
|
||||
this.getFieldProps = this.props.form.getFieldProps;
|
||||
}
|
||||
},
|
||||
deprecatedGetFieldProps(name, option) {
|
||||
if (!warnedGetFieldProps) {
|
||||
warnedGetFieldProps = true;
|
||||
warning(
|
||||
false,
|
||||
'`getFieldProps` is deprecated and will be removed in future, please use `getFieldDecorator` instead'
|
||||
'`getFieldProps` is not recommended, please use `getFieldDecorator` instead'
|
||||
);
|
||||
return getFieldProps(name, option);
|
||||
}
|
||||
this.props.form.getFieldProps = deprecatedGetFieldProps;
|
||||
return this.getFieldProps(name, option);
|
||||
},
|
||||
render() {
|
||||
if (!warnedGetFieldProps) {
|
||||
this.props.form.getFieldProps = this.deprecatedGetFieldProps;
|
||||
}
|
||||
|
||||
const withRef: any = {};
|
||||
if (options && options.withRef) {
|
||||
@@ -152,7 +162,7 @@ export default class Form extends React.Component<FormProps, any> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { prefixCls, className, inline, horizontal, vertical } = this.props;
|
||||
const { prefixCls, className = '', inline, horizontal, vertical } = this.props;
|
||||
const formClassName = classNames({
|
||||
[`${prefixCls}`]: true,
|
||||
[`${prefixCls}-horizontal`]: horizontal,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import warning from 'warning';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
import Row from '../row';
|
||||
import Col from '../col';
|
||||
import { WrappedFormUtils } from './Form';
|
||||
@@ -14,7 +15,7 @@ export interface FormItemLabelColOption {
|
||||
export interface FormItemProps {
|
||||
prefixCls?: string;
|
||||
id?: string;
|
||||
label?: string | React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
labelCol?: FormItemLabelColOption;
|
||||
wrapperCol?: FormItemLabelColOption;
|
||||
help?: React.ReactNode;
|
||||
@@ -25,12 +26,14 @@ export interface FormItemProps {
|
||||
required?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
colon?: boolean;
|
||||
children: any;
|
||||
}
|
||||
|
||||
export interface FormItemContext {
|
||||
form: WrappedFormUtils;
|
||||
}
|
||||
|
||||
let autoGenerateWarning = false;
|
||||
export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
static defaultProps = {
|
||||
hasFeedback: false,
|
||||
@@ -58,6 +61,17 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
|
||||
context: FormItemContext;
|
||||
|
||||
componentDidMount() {
|
||||
if (!autoGenerateWarning && (this.getControls(this.props.children, true).length > 1)) {
|
||||
autoGenerateWarning = true;
|
||||
warning(
|
||||
false,
|
||||
'`Form.Item` cannot generate `validateStatus` and `help` automatically, ' +
|
||||
'while there are more than one `getFieldDecorator` in it.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(...args) {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
@@ -72,11 +86,32 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
return props.help;
|
||||
}
|
||||
|
||||
getControls(children, recursively) {
|
||||
let controls: React.ReactElement<any>[] = [];
|
||||
const childrenArray = React.Children.toArray(children);
|
||||
for (let i = 0; i < childrenArray.length; i++) {
|
||||
if (!recursively && controls.length > 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const child = childrenArray[i] as React.ReactElement<any>;
|
||||
if (child.type as any === FormItem) {
|
||||
continue;
|
||||
}
|
||||
if (!child.props) {
|
||||
continue;
|
||||
}
|
||||
if (FIELD_META_PROP in child.props) {
|
||||
controls.push(child);
|
||||
} else if (child.props.children) {
|
||||
controls = controls.concat(this.getControls(child.props.children, recursively));
|
||||
}
|
||||
}
|
||||
return controls;
|
||||
}
|
||||
|
||||
getOnlyControl() {
|
||||
const children = React.Children.toArray(this.props.children);
|
||||
const child = children.filter((c: React.ReactElement<any>) => {
|
||||
return c.props && FIELD_META_PROP in c.props;
|
||||
})[0];
|
||||
const child = this.getControls(this.props.children, false)[0];
|
||||
return child !== undefined ? child : null;
|
||||
}
|
||||
|
||||
@@ -112,15 +147,18 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
|
||||
getValidateStatus() {
|
||||
const { isFieldValidating, getFieldError, getFieldValue } = this.context.form;
|
||||
const field = this.getId();
|
||||
if (!field) {
|
||||
const fieldId = this.getId();
|
||||
if (!fieldId) {
|
||||
return '';
|
||||
}
|
||||
if (isFieldValidating(field)) {
|
||||
if (isFieldValidating(fieldId)) {
|
||||
return 'validating';
|
||||
} else if (!!getFieldError(field)) {
|
||||
}
|
||||
if (!!getFieldError(fieldId)) {
|
||||
return 'error';
|
||||
} else if (getFieldValue(field) !== undefined && getFieldValue(field) !== null) {
|
||||
}
|
||||
const fieldValue = getFieldValue(fieldId);
|
||||
if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
|
||||
return 'success';
|
||||
}
|
||||
return '';
|
||||
@@ -186,7 +224,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
|
||||
|
||||
// remove user input colon
|
||||
let label = props.label;
|
||||
if (typeof label === 'string' && label.trim() !== '') {
|
||||
if (typeof label === 'string' && (label as string).trim() !== '') {
|
||||
label = (props.label as string).replace(/[:|:]\s*$/, '');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
---
|
||||
order: 10
|
||||
title:
|
||||
zh-CN: 高级搜索
|
||||
en-US: Advanced search
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
三列栅格式的表单排列方式,常用于数据表格的高级搜索。
|
||||
|
||||
有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。
|
||||
|
||||
## en-US
|
||||
|
||||
Three columns layout is often used for advanced searching of data table.
|
||||
|
||||
Because the width of label is not fixed, you may need to adjust it by customizing its style.
|
||||
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Row, Col, Button, DatePicker } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
ReactDOM.render(
|
||||
<Form horizontal className="ant-advanced-search-form">
|
||||
<Row gutter={16}>
|
||||
<Col sm={8}>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Long search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<DatePicker size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col sm={8}>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Long search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<DatePicker size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col sm={8}>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Long search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<DatePicker size="default" />
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Search name"
|
||||
labelCol={{ span: 10 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input placeholder="Please input the search name" size="default" />
|
||||
</FormItem>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12} offset={12} style={{ textAlign: 'right' }}>
|
||||
<Button type="primary" htmlType="submit">Search</Button>
|
||||
<Button>Clear</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
/* custom style */
|
||||
|
||||
.ant-advanced-search-form {
|
||||
padding: 16px 8px;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* because the label length is variable, you may need to adjust the left edge to have the form centered */
|
||||
|
||||
.ant-advanced-search-form > .ant-row {
|
||||
position: relative;
|
||||
left: -6px;
|
||||
}
|
||||
|
||||
.ant-advanced-search-form .ant-btn + .ant-btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
````
|
||||
|
||||
<style>
|
||||
#components-form-demo-advanced-search-form .ant-form-horizontal {
|
||||
max-width: none;
|
||||
}
|
||||
</style>
|
||||
128
components/form/demo/advanced-search.md
Normal file
128
components/form/demo/advanced-search.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 高级搜索
|
||||
en-US: Advanced search
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
三列栅格式的表单排列方式,常用于数据表格的高级搜索。
|
||||
|
||||
有部分定制的样式代码,由于输入标签长度不确定,需要根据具体情况自行调整。
|
||||
|
||||
## en-US
|
||||
|
||||
Three columns layout is often used for advanced searching of data table.
|
||||
|
||||
Because the width of label is not fixed, you may need to adjust it by customizing its style.
|
||||
|
||||
|
||||
````jsx
|
||||
import { Form, Row, Col, Input, Button, Icon } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const usualShowedChildren = 2 * 3; // row * col
|
||||
const AdvancedSearchForm = Form.create()(React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
expand: false,
|
||||
};
|
||||
},
|
||||
handleSearch(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values);
|
||||
});
|
||||
},
|
||||
handleReset() {
|
||||
this.props.form.resetFields();
|
||||
},
|
||||
toggle(expand) {
|
||||
this.setState({ expand });
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 19 },
|
||||
};
|
||||
|
||||
// To generate mock Form.Item
|
||||
const children = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
children.push(
|
||||
<Col span={8} key={i}>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label={`Field ${i}`}
|
||||
>
|
||||
{getFieldDecorator(`field-${i}`)(
|
||||
<Input placeholder="placeholder" />
|
||||
)}
|
||||
</FormItem>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
const expand = this.state.expand;
|
||||
const showedChildren = expand ? children.length : usualShowedChildren;
|
||||
return (
|
||||
<Form
|
||||
horizontal
|
||||
className="ant-advanced-search-form"
|
||||
onSubmit={this.handleSearch}
|
||||
>
|
||||
<Row gutter={40}>
|
||||
{children.slice(0, showedChildren)}
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24} style={{ textAlign: 'right' }}>
|
||||
<Button type="primary" htmlType="submit">Search</Button>
|
||||
<Button onClick={this.handleReset}>Clear</Button>
|
||||
{
|
||||
expand ? (
|
||||
<a className="ant-dropdown-link" onClick={() => this.toggle(false)}>
|
||||
Collapse <Icon type="up" />
|
||||
</a>
|
||||
) : (
|
||||
<a className="ant-dropdown-link" onClick={() => this.toggle(true)}>
|
||||
Expand <Icon type="down" />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
ReactDOM.render(<AdvancedSearchForm />, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
#components-form-demo-advanced-search .ant-advanced-search-form {
|
||||
padding: 24px;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#components-form-demo-advanced-search .ant-advanced-search-form .ant-btn + .ant-btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
#components-form-demo-advanced-search .ant-advanced-search-form .ant-dropdown-link {
|
||||
margin-left: 16px;
|
||||
}
|
||||
````
|
||||
|
||||
<style>
|
||||
#components-form-demo-advanced-search .ant-form-horizontal {
|
||||
max-width: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 表单控件
|
||||
en-US: Form controls
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
展示所有支持的表单控件。
|
||||
|
||||
**注**: 输入框:只有正确设置了 type 属性的输入控件才能被赋予正确的样式。
|
||||
|
||||
## en-US
|
||||
|
||||
A list off all the controls that can be used with form.
|
||||
|
||||
**Note**: Input control: Only if set correct type for it, then it will be set correct style.
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Select, Checkbox, Radio } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
const Option = Select.Option;
|
||||
const RadioGroup = Radio.Group;
|
||||
|
||||
function handleSelectChange(value) {
|
||||
console.log(`selected ${value}`);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Form horizontal>
|
||||
<FormItem
|
||||
id="control-input"
|
||||
label="input control"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input id="control-input" placeholder="Please enter..." />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
id="control-textarea"
|
||||
label="text area"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Input type="textarea" id="control-textarea" rows="3" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
id="select"
|
||||
label="Select box"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 14 }}
|
||||
>
|
||||
<Select id="select" size="large" defaultValue="lucy" style={{ width: 200 }} onChange={handleSelectChange}>
|
||||
<Option value="jack">Jack</Option>
|
||||
<Option value="lucy">Lucy</Option>
|
||||
<Option value="disabled" disabled>disabled</Option>
|
||||
<Option value="yiminghe">Yiminghe</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Checkbox"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 18 }}
|
||||
>
|
||||
<Checkbox className="ant-checkbox-vertical">item 1</Checkbox>
|
||||
<Checkbox className="ant-checkbox-vertical">item 2</Checkbox>
|
||||
<Checkbox className="ant-checkbox-vertical" disabled>item 3 (disabled)</Checkbox>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Checkbox"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 18 }}
|
||||
>
|
||||
<Checkbox className="ant-checkbox-inline">item 1</Checkbox>
|
||||
<Checkbox className="ant-checkbox-inline">item 2</Checkbox>
|
||||
<Checkbox className="ant-checkbox-inline">item 3</Checkbox>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Radio"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 18 }}
|
||||
>
|
||||
<RadioGroup defaultValue="b">
|
||||
<Radio value="a">A</Radio>
|
||||
<Radio value="b">B</Radio>
|
||||
<Radio value="c">C</Radio>
|
||||
<Radio value="d">D</Radio>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
</Form>
|
||||
, mountNode);
|
||||
````
|
||||
@@ -1,77 +1,106 @@
|
||||
---
|
||||
order: 14
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 与 Modal 配合使用
|
||||
en-US: With Modal
|
||||
zh-CN: 弹出层中的新建表单
|
||||
en-US: Form in Modal to Create
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
在 Modal 中使用 Form,当点击 Modal 的确定时,调用 `this.props.form.getFieldsValue` 获取表单内的值。
|
||||
当用户访问一个展示了某个列表的页面,想新建一项但又不想跳转页面时,可以用 Modal 弹出一个表单,用户填写必要信息后创建新的项。
|
||||
|
||||
## en-US
|
||||
|
||||
If you use Form in Modal, when you click the Modal, it could invoke `this.props.form.getFieldsValue` to get values of form.
|
||||
When user visit a page with a list of items, and want to create a new item. The page can popup a form in Modal, then let user fills in the form to create an item.
|
||||
|
||||
````jsx
|
||||
import { Button, Form, Input, Modal } from 'antd';
|
||||
const createForm = Form.create;
|
||||
import { Button, Modal, Form, Input, Radio } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
let Demo = React.createClass({
|
||||
const CollectionCreateForm = Form.create()(
|
||||
(props) => {
|
||||
const { visible, onCancel, onCreate, form } = props;
|
||||
const { getFieldDecorator } = form;
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="Create a new collection"
|
||||
okText="Create"
|
||||
onCancel={onCancel}
|
||||
onOk={onCreate}
|
||||
>
|
||||
<Form vertical>
|
||||
<FormItem label="Title">
|
||||
{getFieldDecorator('title', {
|
||||
rules: [{ required: true, message: 'Please input the title of collection!' }],
|
||||
})(
|
||||
<Input />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem label="Description">
|
||||
{getFieldDecorator('description')(<Input type="textarea" />)}
|
||||
</FormItem>
|
||||
<FormItem className="collection-create-form_last-form-item">
|
||||
{getFieldDecorator('modifier', {
|
||||
initialValue: 'public',
|
||||
})(
|
||||
<Radio.Group>
|
||||
<Radio value="public">Public</Radio>
|
||||
<Radio value="private">Private</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CollectionsPage = React.createClass({
|
||||
getInitialState() {
|
||||
return { visible: false };
|
||||
},
|
||||
|
||||
handleSubmit() {
|
||||
console.log(this.props.form.getFieldsValue());
|
||||
this.hideModal();
|
||||
},
|
||||
|
||||
showModal() {
|
||||
this.setState({ visible: true });
|
||||
},
|
||||
|
||||
hideModal() {
|
||||
handleCancel() {
|
||||
this.setState({ visible: false });
|
||||
},
|
||||
handleCreate() {
|
||||
const form = this.form;
|
||||
form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values);
|
||||
form.resetFields();
|
||||
this.setState({ visible: false });
|
||||
});
|
||||
},
|
||||
saveFormRef(form) {
|
||||
this.form = form;
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 20 },
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Button type="primary" onClick={this.showModal}>Surprise</Button>
|
||||
<Modal title="login" visible={this.state.visible} onOk={this.handleSubmit} onCancel={this.hideModal}>
|
||||
<Form horizontal>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="User name"
|
||||
>
|
||||
{getFieldDecorator('username')(
|
||||
<Input type="text" autoComplete="off" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Password"
|
||||
>
|
||||
{getFieldDecorator('password')(
|
||||
<Input type="password" autoComplete="off" />
|
||||
)}
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Button type="primary" onClick={this.showModal}>New Collection</Button>
|
||||
<CollectionCreateForm
|
||||
ref={this.saveFormRef}
|
||||
visible={this.state.visible}
|
||||
onCancel={this.handleCancel}
|
||||
onCreate={this.handleCreate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Demo = createForm()(Demo);
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
ReactDOM.render(<CollectionsPage />, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
.collection-create-form_last-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
````
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 典型表单
|
||||
en-US: Horizontal form
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
示例展示了如何通过使用 `Form.create` 来获取和更新表单提交的数值。
|
||||
|
||||
## en-US
|
||||
|
||||
How to use `Form.create` to get and update values of form.
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Button, Checkbox, Radio, Tooltip, Icon } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
const RadioGroup = Radio.Group;
|
||||
|
||||
let Demo = React.createClass({
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
console.log('Received values of form:', this.props.form.getFieldsValue());
|
||||
},
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
return (
|
||||
<Form horizontal onSubmit={this.handleSubmit}>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="User name"
|
||||
>
|
||||
<p className="ant-form-text" id="userName" name="userName">Big eye minion</p>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Password"
|
||||
>
|
||||
{getFieldDecorator('pass', { initialValue: '' })(
|
||||
<Input type="password" placeholder="Please input the password" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Gender"
|
||||
>
|
||||
{getFieldDecorator('gender', { initialValue: 'female' })(
|
||||
<RadioGroup>
|
||||
<Radio value="male">male</Radio>
|
||||
<Radio value="female">female</Radio>
|
||||
</RadioGroup>
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="remarks"
|
||||
help="Please input something"
|
||||
>
|
||||
{getFieldDecorator('remark', { initialValue: '' })(
|
||||
<Input type="textarea" placeholder="Please input something" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label={<span>Sold myself <Tooltip title="I come for Qiu Xiang"><Icon type="question-circle-o" /></Tooltip></span>}
|
||||
>
|
||||
{getFieldDecorator('agreement', { initialValue: false, valuePropName: 'checked' })(
|
||||
<Checkbox>agree</Checkbox>
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem wrapperCol={{ span: 16, offset: 6 }} style={{ marginTop: 24 }}>
|
||||
<Button type="primary" htmlType="submit">OK</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Demo = Form.create()(Demo);
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
````
|
||||
59
components/form/demo/horizontal-login.md
Normal file
59
components/form/demo/horizontal-login.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 水平登陆栏
|
||||
en-US: Horizontal Login Form
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
水平登陆栏,常用在顶部导航栏中。
|
||||
|
||||
## en-US
|
||||
|
||||
Horizontal login form is often used in navigation bar.
|
||||
|
||||
````jsx
|
||||
import { Form, Icon, Input, Button } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const HorizontalLoginForm = Form.create()(React.createClass({
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values);
|
||||
});
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
return (
|
||||
<Form inline onSubmit={this.handleSubmit}>
|
||||
<FormItem>
|
||||
{getFieldDecorator('userName', {
|
||||
rules: [{ required: true, message: 'Please input your username!' }],
|
||||
})(
|
||||
<Input addonBefore={<Icon type="user" />} placeholder="Username" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please input your Password!' }],
|
||||
})(
|
||||
<Input addonBefore={<Icon type="lock" />} type="password" placeholder="Password" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" htmlType="submit">Log in</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
ReactDOM.render(<HorizontalLoginForm />, mountNode);
|
||||
````
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 平行排列
|
||||
en-US: Inline form
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
行内排列,常用于登录界面。
|
||||
|
||||
## en-US
|
||||
|
||||
Inline form is often used for login.
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Button, Checkbox } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
let Demo = React.createClass({
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
console.log('Received values of form:', this.props.form.getFieldsValue());
|
||||
},
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
return (
|
||||
<Form inline onSubmit={this.handleSubmit}>
|
||||
<FormItem
|
||||
label="Account"
|
||||
>
|
||||
{getFieldDecorator('userName')(
|
||||
<Input placeholder="Please input the account" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="Password"
|
||||
>
|
||||
{getFieldDecorator('password')(
|
||||
<Input type="password" placeholder="Please input the password" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
{getFieldDecorator('agreement')(
|
||||
<Checkbox>Remember me</Checkbox>
|
||||
)}
|
||||
</FormItem>
|
||||
<Button type="primary" htmlType="submit">Submit</Button>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Demo = Form.create()(Demo);
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
````
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 输入框组合
|
||||
en-US: Input group
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
各类输入框的组合展现。
|
||||
|
||||
## en-US
|
||||
|
||||
Input group of different type input controls.
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Select, Col } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
const InputGroup = Input.Group;
|
||||
const Option = Select.Option;
|
||||
|
||||
const selectAfter = (
|
||||
<Select defaultValue=".com" style={{ width: 70 }}>
|
||||
<Option value=".com">.com</Option>
|
||||
<Option value=".jp">.jp</Option>
|
||||
<Option value=".cn">.cn</Option>
|
||||
<Option value=".org">.org</Option>
|
||||
</Select>
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<Form horizontal>
|
||||
<FormItem
|
||||
label="Input control"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<Input addonBefore="Http://" defaultValue="mysite.com" id="site1" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Input control"
|
||||
labelCol={{ span: 6 }}
|
||||
validateStatus="success"
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<Input addonBefore="Http://" addonAfter=".com" defaultValue="mysite" id="site2" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Select input control"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<Input addonAfter={selectAfter} placeholder="www.mysite" id="site4" />
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Identity number"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<InputGroup>
|
||||
<Col span="6">
|
||||
<Input id="certNo1" />
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="certNo2" />
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="certNo3" />
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="certNo4" />
|
||||
</Col>
|
||||
</InputGroup>
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
label="Tel"
|
||||
labelCol={{ span: 6 }}
|
||||
wrapperCol={{ span: 16 }}
|
||||
>
|
||||
<InputGroup>
|
||||
<Col span="4">
|
||||
<Input id="tel1" defaultValue="086" />
|
||||
</Col>
|
||||
<Col span="2">
|
||||
<p className="ant-form-split">--</p>
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="tel1" />
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="tel2" />
|
||||
</Col>
|
||||
<Col span="6">
|
||||
<Input id="tel3" />
|
||||
</Col>
|
||||
</InputGroup>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
, mountNode);
|
||||
````
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 5
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 表单组合
|
||||
en-US: Mix
|
||||
|
||||
81
components/form/demo/normal-login.md
Normal file
81
components/form/demo/normal-login.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 登陆框
|
||||
en-US: Login Form
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
普通的登陆框,可以容纳更多的元素。
|
||||
|
||||
## en-US
|
||||
|
||||
Normal login form which can contain more elements.
|
||||
|
||||
````jsx
|
||||
import { Form, Icon, Input, Button, Checkbox } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
|
||||
const NormalLoginForm = Form.create()(React.createClass({
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values);
|
||||
});
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit} className="login-form">
|
||||
<FormItem>
|
||||
{getFieldDecorator('userName', {
|
||||
rules: [{ required: true, message: 'Please input your username!' }],
|
||||
})(
|
||||
<Input addonBefore={<Icon type="user" />} placeholder="Username" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{ required: true, message: 'Please input your Password!' }],
|
||||
})(
|
||||
<Input addonBefore={<Icon type="lock" />} type="password" placeholder="Password" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
{getFieldDecorator('remember', {
|
||||
valuePropName: 'checked',
|
||||
initialValue: true,
|
||||
})(
|
||||
<Checkbox>Remember me</Checkbox>
|
||||
)}
|
||||
<a className="login-form-forgot">Forgot password</a>
|
||||
<Button type="primary" htmlType="submit" className="login-form-button">
|
||||
Log in
|
||||
</Button>
|
||||
Or <a>register now!</a>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
ReactDOM.render(<NormalLoginForm />, mountNode);
|
||||
````
|
||||
|
||||
```css
|
||||
#components-form-demo-normal-login .login-form {
|
||||
max-width: 300px;
|
||||
}
|
||||
#components-form-demo-normal-login .login-form-forgot {
|
||||
float: right;
|
||||
}
|
||||
#components-form-demo-normal-login .login-form-button {
|
||||
width: 100%;
|
||||
}
|
||||
```
|
||||
224
components/form/demo/register.md
Normal file
224
components/form/demo/register.md
Normal file
@@ -0,0 +1,224 @@
|
||||
---
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 注册新用户
|
||||
en-US: Registration
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
用户填写必须的信息以注册新用户。
|
||||
|
||||
## en-US
|
||||
|
||||
Fill in this form to create a new account for you.
|
||||
|
||||
````jsx
|
||||
import { Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
const Option = Select.Option;
|
||||
|
||||
const residences = [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}];
|
||||
|
||||
const RegistrationForm = Form.create()(React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
passwordDirty: false,
|
||||
};
|
||||
},
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Received values of form: ', values);
|
||||
});
|
||||
},
|
||||
handlePasswordBlur(e) {
|
||||
const value = e.target.value;
|
||||
this.setState({ passwordDirty: this.state.passwordDirty || !!value });
|
||||
},
|
||||
checkPassowrd(rule, value, callback) {
|
||||
const form = this.props.form;
|
||||
if (value && value !== form.getFieldValue('password')) {
|
||||
callback('Two passwords that you enter is inconsistent!');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
checkConfirm(rule, value, callback) {
|
||||
const form = this.props.form;
|
||||
if (value && this.state.passwordDirty) {
|
||||
form.validateFields(['confirm'], { force: true });
|
||||
}
|
||||
|
||||
callback();
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
const prefixSelector = getFieldDecorator('prefix', {
|
||||
initialValue: '86',
|
||||
})(
|
||||
<Select className="icp-selector">
|
||||
<Option value="86">+86</Option>
|
||||
</Select>
|
||||
);
|
||||
return (
|
||||
<Form horizontal onSubmit={this.handleSubmit}>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="E-mail"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('email', {
|
||||
rules: [{
|
||||
type: 'email', message: 'The input is not valid E-mail!',
|
||||
}, {
|
||||
required: true, message: 'Please input your E-mail!',
|
||||
}],
|
||||
})(
|
||||
<Input />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Password"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('password', {
|
||||
rules: [{
|
||||
required: true, message: 'Please input your password!',
|
||||
}, {
|
||||
validator: this.checkConfirm,
|
||||
}],
|
||||
})(
|
||||
<Input type="password" onBlur={this.handlePasswordBlur} />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Confirm Password"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('confirm', {
|
||||
rules: [{
|
||||
required: true, message: 'Please confirm your password!',
|
||||
}, {
|
||||
validator: this.checkPassowrd,
|
||||
}],
|
||||
})(
|
||||
<Input type="password" />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label={(
|
||||
<span>
|
||||
Nickname
|
||||
<Tooltip title="What do you want other to call you?">
|
||||
<Icon type="question-circle-o" />
|
||||
</Tooltip>
|
||||
</span>
|
||||
)}
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('nickname', {
|
||||
rules: [{ required: true, message: 'Please input your nickname!' }],
|
||||
})(
|
||||
<Input />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Habitual Residence"
|
||||
>
|
||||
{getFieldDecorator('residence', {
|
||||
initialValue: ['zhejiang', 'hangzhou', 'xihu'],
|
||||
rules: [{ type: 'array', required: true, message: 'Please select your habitual residence!' }],
|
||||
})(
|
||||
<Cascader options={residences} />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Phone Number"
|
||||
>
|
||||
{getFieldDecorator('phone', {
|
||||
rules: [{ required: true, message: 'Please input your phone number!' }],
|
||||
})(
|
||||
<Input addonBefore={prefixSelector} />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Captcha"
|
||||
>
|
||||
<Row gutter={8}>
|
||||
<Col span={12}>
|
||||
{getFieldDecorator('captcha', {
|
||||
rules: [{ required: true, message: 'Please input the captcha you got!' }],
|
||||
})(
|
||||
<Input size="large" />
|
||||
)}
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Button size="large">Get captcha</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Row>
|
||||
<Col span={14} offset={6}>
|
||||
<p>
|
||||
{getFieldDecorator('agreement', {
|
||||
valuePropName: 'checked',
|
||||
})(
|
||||
<Checkbox>I had read the <a>agreement</a></Checkbox>
|
||||
)}
|
||||
</p>
|
||||
<Button type="primary" htmlType="submit" size="large">Register</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
ReactDOM.render(<RegistrationForm />, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
#components-form-demo-register .icp-selector {
|
||||
width: 60px;
|
||||
}
|
||||
````
|
||||
92
components/form/demo/time-related-controls.md
Normal file
92
components/form/demo/time-related-controls.md
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
order: 5
|
||||
title:
|
||||
zh-CN: 时间类控件
|
||||
en-US: Time-related Controls
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
`antd@2.0` 之后,时间类组件的 `value` 改为 `moment` 类型,所以在提交前需要预处理。
|
||||
|
||||
## en-US
|
||||
|
||||
After `antd@2.0`, the `value` of time-related components had been changed to `moment`. So, we need to pre-process those values.
|
||||
|
||||
````jsx
|
||||
import { Form, DatePicker, TimePicker, Row, Col, Button } from 'antd';
|
||||
const FormItem = Form.Item;
|
||||
const MonthPicker = DatePicker.MonthPicker;
|
||||
const RangePicker = DatePicker.RangePicker;
|
||||
|
||||
const TimeRelatedForm = Form.create()(React.createClass({
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const fieldsValue = this.props.form.getFieldsValue();
|
||||
|
||||
// Should format date value before submit.
|
||||
const rangeValue = fieldsValue['range-picker'];
|
||||
const values = {
|
||||
...fieldsValue,
|
||||
'date-picker': fieldsValue['date-picker'].format('YYYY-MM-DD'),
|
||||
'month-picker': fieldsValue['month-picker'].format('YYYY-MM'),
|
||||
'range-picker': [rangeValue[0].format('YYYY-MM-DD'), rangeValue[1].format('YYYY-MM-DD')],
|
||||
'time-picker': fieldsValue['time-picker'].format('HH:mm:ss'),
|
||||
};
|
||||
console.log('Received values of form: ', values);
|
||||
},
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 6 },
|
||||
wrapperCol: { span: 14 },
|
||||
};
|
||||
return (
|
||||
<Form horizontal onSubmit={this.handleSubmit}>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="DatePicker"
|
||||
>
|
||||
{getFieldDecorator('date-picker')(
|
||||
<DatePicker />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="MonthPicker"
|
||||
>
|
||||
{getFieldDecorator('month-picker')(
|
||||
<MonthPicker />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="RangePicker"
|
||||
>
|
||||
{getFieldDecorator('range-picker')(
|
||||
<RangePicker />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="TimePicker"
|
||||
>
|
||||
{getFieldDecorator('time-picker')(
|
||||
<TimePicker />
|
||||
)}
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Row>
|
||||
<Col span={14} offset={6}>
|
||||
<Button type="primary" htmlType="submit" size="large">Submit</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
ReactDOM.render(<TimeRelatedForm />, mountNode);
|
||||
````
|
||||
@@ -1,183 +0,0 @@
|
||||
---
|
||||
order: 11
|
||||
title:
|
||||
zh-CN: 表单校验
|
||||
en-US: Basic validate
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
基本的表单校验例子。
|
||||
|
||||
## en-US
|
||||
|
||||
Basic validation for form.
|
||||
|
||||
````jsx
|
||||
import { Button, Form, Input } from 'antd';
|
||||
const createForm = Form.create;
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function noop() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let BasicDemo = React.createClass({
|
||||
handleReset(e) {
|
||||
e.preventDefault();
|
||||
this.props.form.resetFields();
|
||||
},
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFields((errors, values) => {
|
||||
if (errors) {
|
||||
console.log('Errors in form!!!');
|
||||
return;
|
||||
}
|
||||
console.log('Submit!!!');
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
|
||||
userExists(rule, value, callback) {
|
||||
if (!value) {
|
||||
callback();
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
if (value === 'Jason Wood') {
|
||||
callback([new Error('Sorry, the user name is already in use.')]);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}, 800);
|
||||
}
|
||||
},
|
||||
|
||||
checkPass(rule, value, callback) {
|
||||
const { validateFields } = this.props.form;
|
||||
if (value) {
|
||||
validateFields(['rePasswd'], { force: true });
|
||||
}
|
||||
callback();
|
||||
},
|
||||
|
||||
checkPass2(rule, value, callback) {
|
||||
const { getFieldValue } = this.props.form;
|
||||
if (value && value !== getFieldValue('passwd')) {
|
||||
callback('The two passwords you enter are inconsistent!');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator, getFieldError, isFieldValidating } = this.props.form;
|
||||
const formItemLayout = {
|
||||
labelCol: { span: 7 },
|
||||
wrapperCol: { span: 12 },
|
||||
};
|
||||
return (
|
||||
<Form horizontal>
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="User name"
|
||||
hasFeedback
|
||||
help={isFieldValidating('name') ? 'validating...' : (getFieldError('name') || []).join(', ')}
|
||||
>
|
||||
{getFieldDecorator('name', {
|
||||
rules: [
|
||||
{ required: true, min: 5, message: 'User name for at least 5 characters' },
|
||||
{ validator: this.userExists },
|
||||
],
|
||||
})(
|
||||
<Input placeholder="Realtime validation, try to input Jason Wood" />
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Email"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('email', {
|
||||
validate: [{
|
||||
rules: [
|
||||
{ required: true },
|
||||
],
|
||||
trigger: 'onBlur',
|
||||
}, {
|
||||
rules: [
|
||||
{ type: 'email', message: 'Please input the correct email' },
|
||||
],
|
||||
trigger: ['onBlur', 'onChange'],
|
||||
}],
|
||||
})(
|
||||
<Input type="email" placeholder="This control uses onBlur and onChange" />
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Password"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('passwd', {
|
||||
rules: [
|
||||
{ required: true, whitespace: true, message: 'Please enter your password' },
|
||||
{ validator: this.checkPass },
|
||||
],
|
||||
})(
|
||||
<Input type="password" autoComplete="off"
|
||||
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
|
||||
/>
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="Confirm password"
|
||||
hasFeedback
|
||||
>
|
||||
{getFieldDecorator('rePasswd', {
|
||||
rules: [{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: 'Please confirm your password',
|
||||
}, {
|
||||
validator: this.checkPass2,
|
||||
}],
|
||||
})(
|
||||
<Input type="password" autoComplete="off" placeholder="Both passwords that you enter must be consistent."
|
||||
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
|
||||
/>
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<FormItem
|
||||
{...formItemLayout}
|
||||
label="remark"
|
||||
>
|
||||
{getFieldDecorator('textarea', {
|
||||
rules: [
|
||||
{ required: true, message: 'Really not supposed to write something?' },
|
||||
],
|
||||
})(
|
||||
<Input type="textarea" placeholder="Please write something" id="textarea" name="textarea" />
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
<FormItem wrapperCol={{ span: 12, offset: 7 }}>
|
||||
<Button type="primary" onClick={this.handleSubmit}>OK</Button>
|
||||
|
||||
<Button type="ghost" onClick={this.handleReset}>Reset</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
BasicDemo = createForm()(BasicDemo);
|
||||
|
||||
ReactDOM.render(<BasicDemo />, mountNode);
|
||||
````
|
||||
@@ -1,239 +0,0 @@
|
||||
---
|
||||
order: 13
|
||||
title:
|
||||
zh-CN: 自定义校验规则
|
||||
en-US: Customized validation
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
密码校验实例。
|
||||
|
||||
这里使用了 `this.props.form.validateFields` 方法,在对第一次输入的密码进行校验时会触发二次密码的校验。
|
||||
|
||||
## en-US
|
||||
|
||||
Customized validation for Password.
|
||||
|
||||
To use `this.props.form.validateFields` method, when validating first password you enter will trigger the second password validation.
|
||||
|
||||
````jsx
|
||||
import { Button, Form, Input, Row, Col } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
const createForm = Form.create;
|
||||
const FormItem = Form.Item;
|
||||
|
||||
function noop() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Demo = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
dirty: false,
|
||||
passBarShow: false, // Whether to display a tooltip of password strength
|
||||
rePassBarShow: false,
|
||||
passStrength: 'L', // Password strength
|
||||
rePassStrength: 'L',
|
||||
};
|
||||
},
|
||||
|
||||
handleSubmit() {
|
||||
this.props.form.validateFields((errors, values) => {
|
||||
if (errors) {
|
||||
console.log('Errors in form!!!');
|
||||
return;
|
||||
}
|
||||
console.log('Submit!!!');
|
||||
console.log(values);
|
||||
});
|
||||
},
|
||||
|
||||
getPassStrenth(value, type) {
|
||||
if (value) {
|
||||
let strength;
|
||||
// Customized the password strength, here is just a simple example
|
||||
if (value.length < 6) {
|
||||
strength = 'L';
|
||||
} else if (value.length <= 9) {
|
||||
strength = 'M';
|
||||
} else {
|
||||
strength = 'H';
|
||||
}
|
||||
this.setState({
|
||||
[`${type}BarShow`]: true,
|
||||
[`${type}Strength`]: strength,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
[`${type}BarShow`]: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
checkPass(rule, value, callback) {
|
||||
const form = this.props.form;
|
||||
this.getPassStrenth(value, 'pass');
|
||||
|
||||
if (form.getFieldValue('pass') && this.state.dirty) {
|
||||
form.validateFields(['rePass'], { force: true });
|
||||
}
|
||||
|
||||
callback();
|
||||
},
|
||||
|
||||
checkPass2(rule, value, callback) {
|
||||
const form = this.props.form;
|
||||
this.getPassStrenth(value, 'rePass');
|
||||
|
||||
if (value && value !== form.getFieldValue('pass')) {
|
||||
callback('Two passwords you enter is inconsistent!');
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
renderPassStrengthBar(type) {
|
||||
const strength = type === 'pass' ? this.state.passStrength : this.state.rePassStrength;
|
||||
const classSet = classNames({
|
||||
'ant-pwd-strength': true,
|
||||
'ant-pwd-strength-low': strength === 'L',
|
||||
'ant-pwd-strength-medium': strength === 'M',
|
||||
'ant-pwd-strength-high': strength === 'H',
|
||||
});
|
||||
const level = {
|
||||
L: 'Low',
|
||||
M: 'Middle',
|
||||
H: 'High',
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ul className={classSet}>
|
||||
<li className="ant-pwd-strength-item ant-pwd-strength-item-1" />
|
||||
<li className="ant-pwd-strength-item ant-pwd-strength-item-2" />
|
||||
<li className="ant-pwd-strength-item ant-pwd-strength-item-3" />
|
||||
<span className="ant-form-text">
|
||||
{level[strength]}
|
||||
</span>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
const { getFieldDecorator } = this.props.form;
|
||||
return (
|
||||
<div>
|
||||
<Form vertical style={{ maxWidth: 600 }}>
|
||||
<Row type="flex" align="middle">
|
||||
<Col span={12}>
|
||||
<FormItem label="Password">
|
||||
{getFieldDecorator('pass', {
|
||||
rules: [
|
||||
{ required: true, whitespace: true, message: 'Please enter your password' },
|
||||
{ validator: this.checkPass },
|
||||
],
|
||||
})(
|
||||
<Input type="password"
|
||||
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
|
||||
autoComplete="off" id="pass"
|
||||
onChange={(e) => {
|
||||
console.log('Your password is stolen in this way', e.target.value);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
const value = e.target.value;
|
||||
this.setState({ dirty: this.state.dirty || !!value });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
{this.state.passBarShow ? this.renderPassStrengthBar('pass') : null}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row type="flex" align="middle">
|
||||
<Col span={12}>
|
||||
<FormItem label="Confirm">
|
||||
{getFieldDecorator('rePass', {
|
||||
rules: [{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: 'Please confirm your password',
|
||||
}, {
|
||||
validator: this.checkPass2,
|
||||
}],
|
||||
})(
|
||||
<Input type="password"
|
||||
onContextMenu={noop} onPaste={noop} onCopy={noop} onCut={noop}
|
||||
autoComplete="off" id="rePass"
|
||||
/>
|
||||
)}
|
||||
</FormItem>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
{this.state.rePassBarShow ? this.renderPassStrengthBar('rePass') : null}
|
||||
</Col>
|
||||
</Row>
|
||||
<FormItem><Button type="primary" onClick={this.handleSubmit}>Submit</Button></FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Demo = createForm()(Demo);
|
||||
|
||||
ReactDOM.render(<Demo />, mountNode);
|
||||
````
|
||||
|
||||
````css
|
||||
.ant-pwd-strength {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ant-pwd-strength-item {
|
||||
float: left;
|
||||
margin-right: 1px;
|
||||
margin-top: 12px;
|
||||
width: 19px;
|
||||
height: 8px;
|
||||
line-height: 8px;
|
||||
list-style: none;
|
||||
background-color: #f3f3f3;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
}
|
||||
|
||||
.ant-pwd-strength-item-1 {
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.ant-pwd-strength-item-2 {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.ant-pwd-strength-item-3 {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ant-pwd-strength-low .ant-pwd-strength-item-1, .ant-pwd-strength-medium .ant-pwd-strength-item-1, .ant-pwd-strength-high .ant-pwd-strength-item-1 {
|
||||
background-color: #FAC450;
|
||||
}
|
||||
|
||||
.ant-pwd-strength-medium .ant-pwd-strength-item-2, .ant-pwd-strength-high .ant-pwd-strength-item-2 {
|
||||
background-color: rgba(135, 208, 104, .6);
|
||||
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#9987D068,endColorstr=#9987D068);
|
||||
}
|
||||
|
||||
.ant-pwd-strength-high .ant-pwd-strength-item-3 {
|
||||
background-color: #87D068;
|
||||
}
|
||||
````
|
||||
@@ -137,20 +137,8 @@ let Demo = React.createClass({
|
||||
{...formItemLayout}
|
||||
label="Hobby"
|
||||
>
|
||||
{getFieldDecorator('eat', {
|
||||
valuePropName: 'checked',
|
||||
})(
|
||||
<Checkbox>eat</Checkbox>
|
||||
)}
|
||||
{getFieldDecorator('sleep', {
|
||||
valuePropName: 'checked',
|
||||
})(
|
||||
<Checkbox>sleeping</Checkbox>
|
||||
)}
|
||||
{getFieldDecorator('beat', {
|
||||
valuePropName: 'checked',
|
||||
})(
|
||||
<Checkbox>dozen doug</Checkbox>
|
||||
{getFieldDecorator('hobby')(
|
||||
<Checkbox.Group options={['eat', 'sleeping', 'dozen doug']} />
|
||||
)}
|
||||
</FormItem>
|
||||
|
||||
|
||||
@@ -76,6 +76,13 @@ If the form has been decorated by `Form.create` then it has `this.props.form` pr
|
||||
|
||||
### this.props.form.getFieldDecorator(id, options)
|
||||
|
||||
After wrapped by `getFieldDecorator`, `value` `onChange` props will be added to form controls,the flow of form data will be handled by Form which will cause:
|
||||
|
||||
|
||||
1. You don't need to use `onChange` and should not add `value` `onChange` to controls. (There are warnings after `antd@2.0`)
|
||||
2. You can not set default value via `defaultValue` prop, you should use `initialValue` in `getFieldDecorator` instead.
|
||||
3. You don't need to call `setState` manully, please use `this.props.form.setFieldsValue` to change value programmatically.
|
||||
|
||||
#### Special attention
|
||||
|
||||
If you use `react@<15.3.0`, then, you can't use `getFieldDecorator` in stateless component: https://github.com/facebook/react/pull/6534
|
||||
|
||||
@@ -78,6 +78,12 @@ CustomizedForm = Form.create({})(CustomizedForm);
|
||||
|
||||
### this.props.form.getFieldDecorator(id, options)
|
||||
|
||||
经过 `getFieldDecorator` 包装的控件,表单控件会自动添加 `value` `onChange` 等属性,数据同步将被 Form 接管,这会导致以下结果:
|
||||
|
||||
1. 你不再需要用 `onChange` 来做同步,也不应该给控件单独添加 `value` `onChange` 等属性。(2.0 后会在控制台进行警告)
|
||||
2. 你不能用控件的 `defaultValue` 属性来设置默认值,请用 `getFieldDecorator` 里的 `initialValue`。
|
||||
3. 你不需要用 `setState`,可以使用 `this.props.form.setFieldsValue` 来动态改变表单值。
|
||||
|
||||
#### 特别注意
|
||||
|
||||
如果使用的是 `react@<15.3.0`,则 `getFieldDecorator` 调用不能位于纯函数组件中: https://github.com/facebook/react/pull/6534
|
||||
|
||||
@@ -62,6 +62,7 @@ input[type="checkbox"] {
|
||||
font-size: @font-size-base;
|
||||
margin-bottom: @form-item-margin-bottom;
|
||||
color: #666;
|
||||
vertical-align: top;
|
||||
|
||||
// nested FormItem
|
||||
& > &,
|
||||
|
||||
3
components/icon/index.tsx
Normal file → Executable file
3
components/icon/index.tsx
Normal file → Executable file
@@ -6,8 +6,9 @@ export interface IconProps {
|
||||
type: string;
|
||||
className?: string;
|
||||
title?: string;
|
||||
onClick?: (e) => void;
|
||||
onClick?: React.MouseEventHandler<any>;
|
||||
spin?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default (props: IconProps) => {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.anticon {
|
||||
&-spin {
|
||||
display: inline-block;
|
||||
animation: loadingCircle 1.6s infinite linear;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1 @@
|
||||
import '../../style/index.less';
|
||||
|
||||
import './index.less';
|
||||
|
||||
@@ -10,14 +10,15 @@ export interface GroupProps {
|
||||
}
|
||||
|
||||
const Group: React.StatelessComponent<GroupProps> = (props) => {
|
||||
const className = classNames({
|
||||
[props.prefixCls]: true,
|
||||
[`${props.prefixCls}-lg`]: props.size === 'large',
|
||||
[`${props.prefixCls}-sm`]: props.size === 'small',
|
||||
[props.className]: !!props.className,
|
||||
const { prefixCls = 'ant-input-group', className = '' } = props;
|
||||
const cls = classNames({
|
||||
[prefixCls]: true,
|
||||
[`${prefixCls}-lg`]: props.size === 'large',
|
||||
[`${prefixCls}-sm`]: props.size === 'small',
|
||||
[className]: !!className,
|
||||
});
|
||||
return (
|
||||
<span className={className} style={props.style}>
|
||||
<span className={cls} style={props.style}>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
@@ -27,8 +28,4 @@ Group.propTypes = {
|
||||
children: React.PropTypes.any,
|
||||
};
|
||||
|
||||
Group.defaultProps = {
|
||||
prefixCls: 'ant-input-group',
|
||||
};
|
||||
|
||||
export default Group;
|
||||
|
||||
@@ -45,11 +45,11 @@ export interface InputProps {
|
||||
readOnly?: boolean;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
onPressEnter?: React.FormEventHandler;
|
||||
onKeyDown?: React.FormEventHandler;
|
||||
onChange?: React.FormEventHandler;
|
||||
onClick?: React.FormEventHandler;
|
||||
onBlur?: React.FormEventHandler;
|
||||
onPressEnter?: React.FormEventHandler<any>;
|
||||
onKeyDown?: React.FormEventHandler<any>;
|
||||
onChange?: React.FormEventHandler<any>;
|
||||
onClick?: React.FormEventHandler<any>;
|
||||
onBlur?: React.FormEventHandler<any>;
|
||||
autosize?: boolean | AutoSizeType;
|
||||
autoComplete?: 'on' | 'off';
|
||||
style?: React.CSSProperties;
|
||||
@@ -61,9 +61,6 @@ export default class Input extends Component<InputProps, any> {
|
||||
disabled: false,
|
||||
prefixCls: 'ant-input',
|
||||
type: 'text',
|
||||
onPressEnter() {},
|
||||
onKeyDown() {},
|
||||
onChange() {},
|
||||
autosize: false,
|
||||
};
|
||||
|
||||
@@ -114,17 +111,23 @@ export default class Input extends Component<InputProps, any> {
|
||||
}
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.props.onPressEnter(e);
|
||||
const { onPressEnter, onKeyDown } = this.props;
|
||||
if (e.keyCode === 13 && onPressEnter) {
|
||||
onPressEnter(e);
|
||||
}
|
||||
if (onKeyDown) {
|
||||
onKeyDown(e);
|
||||
}
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
|
||||
handleTextareaChange = (e) => {
|
||||
if (!('value' in this.props)) {
|
||||
this.resizeTextarea();
|
||||
}
|
||||
this.props.onChange(e);
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
}
|
||||
|
||||
resizeTextarea = () => {
|
||||
|
||||
@@ -87,8 +87,8 @@ function calculateNodeStyling(node, useCache = false) {
|
||||
export default function calculateNodeHeight(
|
||||
uiTextNode,
|
||||
useCache = false,
|
||||
minRows = null,
|
||||
maxRows = null
|
||||
minRows: number | null = null,
|
||||
maxRows: number | null = null
|
||||
) {
|
||||
if (!hiddenTextarea) {
|
||||
hiddenTextarea = document.createElement('textarea');
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
|
||||
&-addon,
|
||||
&-wrap {
|
||||
width: 1%;
|
||||
width: 1px; // To make addon/wrap as small as possible
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
&:hover {
|
||||
border-color: @border-color-base;
|
||||
}
|
||||
form & {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
||||
&&-focus .@{ant-prefix}-search-btn-noempty,
|
||||
&:hover .@{ant-prefix}-search-btn-noempty {
|
||||
|
||||
@@ -30,52 +30,53 @@ export interface ColProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Col: React.StatelessComponent<ColProps> = (props) => {
|
||||
const [{ span, order, offset, push, pull, className, children, prefixCls = 'ant-col' }, others] = splitObject(props,
|
||||
['span', 'order', 'offset', 'push', 'pull', 'className', 'children', 'prefixCls']);
|
||||
let sizeClassObj = {};
|
||||
['xs', 'sm', 'md', 'lg'].forEach(size => {
|
||||
let sizeProps: ColSize = {};
|
||||
if (typeof props[size] === 'number') {
|
||||
sizeProps.span = props[size];
|
||||
} else if (typeof props[size] === 'object') {
|
||||
sizeProps = props[size] || {};
|
||||
}
|
||||
export default class Col extends React.Component<ColProps, any> {
|
||||
static propTypes = {
|
||||
span: stringOrNumber,
|
||||
order: stringOrNumber,
|
||||
offset: stringOrNumber,
|
||||
push: stringOrNumber,
|
||||
pull: stringOrNumber,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
xs: objectOrNumber,
|
||||
sm: objectOrNumber,
|
||||
md: objectOrNumber,
|
||||
lg: objectOrNumber,
|
||||
};
|
||||
|
||||
delete others[size];
|
||||
render() {
|
||||
const props = this.props;
|
||||
const [{ span, order, offset, push, pull, className, children, prefixCls = 'ant-col' }, others] = splitObject(props,
|
||||
['span', 'order', 'offset', 'push', 'pull', 'className', 'children', 'prefixCls']);
|
||||
let sizeClassObj = {};
|
||||
['xs', 'sm', 'md', 'lg'].forEach(size => {
|
||||
let sizeProps: ColSize = {};
|
||||
if (typeof props[size] === 'number') {
|
||||
sizeProps.span = props[size];
|
||||
} else if (typeof props[size] === 'object') {
|
||||
sizeProps = props[size] || {};
|
||||
}
|
||||
|
||||
sizeClassObj = assign({}, sizeClassObj, {
|
||||
[`${prefixCls}-${size}-${sizeProps.span}`]: sizeProps.span !== undefined,
|
||||
[`${prefixCls}-${size}-order-${sizeProps.order}`]: sizeProps.order,
|
||||
[`${prefixCls}-${size}-offset-${sizeProps.offset}`]: sizeProps.offset,
|
||||
[`${prefixCls}-${size}-push-${sizeProps.push}`]: sizeProps.push,
|
||||
[`${prefixCls}-${size}-pull-${sizeProps.pull}`]: sizeProps.pull,
|
||||
delete others[size];
|
||||
|
||||
sizeClassObj = assign({}, sizeClassObj, {
|
||||
[`${prefixCls}-${size}-${sizeProps.span}`]: sizeProps.span !== undefined,
|
||||
[`${prefixCls}-${size}-order-${sizeProps.order}`]: sizeProps.order,
|
||||
[`${prefixCls}-${size}-offset-${sizeProps.offset}`]: sizeProps.offset,
|
||||
[`${prefixCls}-${size}-push-${sizeProps.push}`]: sizeProps.push,
|
||||
[`${prefixCls}-${size}-pull-${sizeProps.pull}`]: sizeProps.pull,
|
||||
});
|
||||
});
|
||||
});
|
||||
const classes = classNames(assign({}, {
|
||||
[`${prefixCls}-${span}`]: span !== undefined,
|
||||
[`${prefixCls}-order-${order}`]: order,
|
||||
[`${prefixCls}-offset-${offset}`]: offset,
|
||||
[`${prefixCls}-push-${push}`]: push,
|
||||
[`${prefixCls}-pull-${pull}`]: pull,
|
||||
[className]: !!className,
|
||||
}, sizeClassObj));
|
||||
const classes = classNames(assign({}, {
|
||||
[`${prefixCls}-${span}`]: span !== undefined,
|
||||
[`${prefixCls}-order-${order}`]: order,
|
||||
[`${prefixCls}-offset-${offset}`]: offset,
|
||||
[`${prefixCls}-push-${push}`]: push,
|
||||
[`${prefixCls}-pull-${pull}`]: pull,
|
||||
[className]: !!className,
|
||||
}, sizeClassObj));
|
||||
|
||||
return <div {...others} className={classes}>{children}</div>;
|
||||
};
|
||||
|
||||
Col.propTypes = {
|
||||
span: stringOrNumber,
|
||||
order: stringOrNumber,
|
||||
offset: stringOrNumber,
|
||||
push: stringOrNumber,
|
||||
pull: stringOrNumber,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
xs: objectOrNumber,
|
||||
sm: objectOrNumber,
|
||||
md: objectOrNumber,
|
||||
lg: objectOrNumber,
|
||||
};
|
||||
|
||||
export default Col;
|
||||
return <div {...others} className={classes}>{children}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface LocaleProviderProps {
|
||||
Transfer?: Object,
|
||||
Select?: Object,
|
||||
};
|
||||
children: any;
|
||||
}
|
||||
|
||||
export default class LocaleProvider extends React.Component<LocaleProviderProps, any> {
|
||||
|
||||
41
components/mention/demo/controllder-simple.md
Normal file
41
components/mention/demo/controllder-simple.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 受控模式
|
||||
en-US: Controlled
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
受控模式.
|
||||
|
||||
## en-US
|
||||
|
||||
Controlled mode.
|
||||
|
||||
````jsx
|
||||
import { Mention } from 'antd';
|
||||
const { toEditorState } = Mention;
|
||||
|
||||
const App = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
value: toEditorState('@afc163'),
|
||||
};
|
||||
},
|
||||
handleChange(editorState) {
|
||||
this.setState({
|
||||
value: editorState,
|
||||
});
|
||||
},
|
||||
render() {
|
||||
return (<Mention
|
||||
suggestions={['afc163', 'benjycui', 'yiminghe', 'RaoHai', '中文', 'にほんご']}
|
||||
value={this.state.value}
|
||||
onChange={this.handleChange}
|
||||
/>);
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.render(<App />, mountNode);
|
||||
````
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
order: 3
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 受控模式
|
||||
en-US: Controlled
|
||||
zh-CN: 配合 Form 使用
|
||||
en-US: With Form
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 4
|
||||
order: 5
|
||||
title:
|
||||
zh-CN: 多行
|
||||
en-US: Multi-lines Mode
|
||||
|
||||
@@ -31,7 +31,6 @@ export default class Mention extends React.Component<MentionProps, MentionState>
|
||||
static getMentions = getMentions;
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-mention',
|
||||
suggestions: [],
|
||||
notFoundContent: '无匹配结果,轻敲空格完成输入',
|
||||
loading: false,
|
||||
multiLines: false,
|
||||
@@ -65,7 +64,7 @@ export default class Mention extends React.Component<MentionProps, MentionState>
|
||||
|
||||
defaultSearchChange(value: String): void {
|
||||
const searchValue = value.toLowerCase();
|
||||
const filteredSuggestions = this.props.suggestions.filter(
|
||||
const filteredSuggestions = (this.props.suggestions || []).filter(
|
||||
suggestion => suggestion.toLowerCase().indexOf(searchValue) !== -1
|
||||
);
|
||||
this.setState({
|
||||
@@ -74,7 +73,7 @@ export default class Mention extends React.Component<MentionProps, MentionState>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, prefixCls, style, multiLines, defaultValue } = this.props;
|
||||
const { className = '', prefixCls, style, multiLines, defaultValue } = this.props;
|
||||
let { notFoundContent } = this.props;
|
||||
|
||||
const { suggestions, focus } = this.state;
|
||||
|
||||
@@ -37,7 +37,7 @@ const Sider = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Switch onChange={this.changeTheme} checkedChildren={<Icon type="eye" />} unCheckedChildren={<Icon type="eye-o" />} />
|
||||
<Switch onChange={this.changeTheme} checkedChildren="Dark" unCheckedChildren="Light" />
|
||||
<br />
|
||||
<br />
|
||||
<Menu theme={this.state.theme}
|
||||
|
||||
@@ -3,9 +3,6 @@ import RcMenu, { Item, Divider, SubMenu, ItemGroup } from 'rc-menu';
|
||||
import animation from '../_util/openAnimation';
|
||||
import warning from 'warning';
|
||||
|
||||
function noop() {
|
||||
}
|
||||
|
||||
export interface SelectParam {
|
||||
key: string;
|
||||
keyPath: Array<string>;
|
||||
@@ -61,8 +58,6 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
static ItemGroup = ItemGroup;
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-menu',
|
||||
onClick: noop,
|
||||
onOpenChange: noop,
|
||||
className: '',
|
||||
theme: 'light', // or dark
|
||||
};
|
||||
@@ -90,11 +85,19 @@ export default class Menu extends React.Component<MenuProps, any> {
|
||||
}
|
||||
handleClick = (e) => {
|
||||
this.setOpenKeys([]);
|
||||
this.props.onClick(e);
|
||||
|
||||
const onClick = this.props.onClick;
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
}
|
||||
}
|
||||
handleOpenChange = (openKeys: string[]) => {
|
||||
this.setOpenKeys(openKeys);
|
||||
this.props.onOpenChange(openKeys);
|
||||
|
||||
const onOpenChange = this.props.onOpenChange;
|
||||
if (onOpenChange) {
|
||||
onOpenChange(openKeys);
|
||||
}
|
||||
}
|
||||
setOpenKeys(openKeys) {
|
||||
if (!('openKeys' in this.props)) {
|
||||
|
||||
@@ -244,14 +244,12 @@
|
||||
top: 1px;
|
||||
float: left;
|
||||
border-bottom: 2px solid transparent;
|
||||
z-index: 2;
|
||||
|
||||
&:hover,
|
||||
&-active,
|
||||
&-selected {
|
||||
border-bottom: 2px solid @primary-color;
|
||||
color: @primary-color;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
> a {
|
||||
|
||||
10
components/message/index.tsx
Normal file → Executable file
10
components/message/index.tsx
Normal file → Executable file
@@ -23,7 +23,7 @@ function notice(
|
||||
content: React.ReactNode,
|
||||
duration: number = defaultDuration,
|
||||
type: NoticeType,
|
||||
onClose: () => void) {
|
||||
onClose?: () => void) {
|
||||
let iconType = ({
|
||||
info: 'info-circle',
|
||||
success: 'check-circle',
|
||||
@@ -55,7 +55,7 @@ function notice(
|
||||
|
||||
type ConfigContent = React.ReactNode | string;
|
||||
type ConfigDuration = number;
|
||||
type ConfigOnClose = () => void;
|
||||
export type ConfigOnClose = () => void;
|
||||
|
||||
export interface ConfigOptions {
|
||||
top?: number;
|
||||
@@ -85,13 +85,13 @@ export default {
|
||||
},
|
||||
|
||||
config(options: ConfigOptions) {
|
||||
if ('top' in options) {
|
||||
if (options.top !== undefined) {
|
||||
defaultTop = options.top;
|
||||
}
|
||||
if ('duration' in options) {
|
||||
if (options.duration !== undefined) {
|
||||
defaultDuration = options.duration;
|
||||
}
|
||||
if ('prefixCls' in options) {
|
||||
if (options.prefixCls !== undefined) {
|
||||
prefixCls = options.prefixCls;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@ import Dialog from 'rc-dialog';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import Button from '../button';
|
||||
|
||||
function noop() {}
|
||||
|
||||
let mousePosition;
|
||||
let mousePositionEventBinded;
|
||||
|
||||
@@ -21,7 +19,7 @@ export interface ModalProps {
|
||||
/** 点击确定回调*/
|
||||
onOk?: () => void;
|
||||
/** 点击模态框右上角叉、取消按钮、Props.maskClosable 值为 true 时的遮罩层或键盘按下 Esc 时的回调*/
|
||||
onCancel?: (e: React.MouseEvent) => void;
|
||||
onCancel?: (e: React.MouseEvent<any>) => void;
|
||||
/** 宽度*/
|
||||
width?: string | number;
|
||||
/** 底部内容*/
|
||||
@@ -55,8 +53,6 @@ export default class Modal extends React.Component<ModalProps, any> {
|
||||
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-modal',
|
||||
onOk: noop,
|
||||
onCancel: noop,
|
||||
width: 520,
|
||||
transitionName: 'zoom',
|
||||
maskTransitionName: 'fade',
|
||||
@@ -86,11 +82,17 @@ export default class Modal extends React.Component<ModalProps, any> {
|
||||
context: ModalContext;
|
||||
|
||||
handleCancel = (e) => {
|
||||
this.props.onCancel(e);
|
||||
const onCancel = this.props.onCancel;
|
||||
if (onCancel) {
|
||||
onCancel(e);
|
||||
}
|
||||
}
|
||||
|
||||
handleOk = () => {
|
||||
this.props.onOk();
|
||||
const onOk = this.props.onOk;
|
||||
if (onOk) {
|
||||
onOk();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
@@ -101,7 +101,7 @@ export default function confirm(config) {
|
||||
</div>
|
||||
);
|
||||
|
||||
let footer = null;
|
||||
let footer: React.ReactElement<any> | null = null;
|
||||
if (props.okCancel) {
|
||||
footer = (
|
||||
<div className={`${prefixCls}-btns`}>
|
||||
|
||||
19
components/notification/index.tsx
Normal file → Executable file
19
components/notification/index.tsx
Normal file → Executable file
@@ -92,8 +92,19 @@ function notice(args) {
|
||||
});
|
||||
}
|
||||
|
||||
const api = {
|
||||
open(args) {
|
||||
const api: {
|
||||
success?(args: ArgsProps): void;
|
||||
error?(args: ArgsProps): void;
|
||||
info?(args: ArgsProps): void;
|
||||
warn?(args: ArgsProps): void;
|
||||
warning?(args: ArgsProps): void;
|
||||
|
||||
open(args: ArgsProps): void;
|
||||
close(key: string): void;
|
||||
config(options: ConfigProps): void;
|
||||
destroy(): void;
|
||||
} = {
|
||||
open(args: ArgsProps) {
|
||||
notice(args);
|
||||
},
|
||||
close(key) {
|
||||
@@ -102,10 +113,10 @@ const api = {
|
||||
}
|
||||
},
|
||||
config(options: ConfigProps) {
|
||||
if ('top' in options) {
|
||||
if (options.top !== undefined) {
|
||||
defaultTop = options.top;
|
||||
}
|
||||
if ('duration' in options) {
|
||||
if (options.duration !== undefined) {
|
||||
defaultDuration = options.duration;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@ import Icon from '../icon';
|
||||
import Button from '../button';
|
||||
import splitObject from '../_util/splitObject';
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export interface PopconfirmProps {
|
||||
/**
|
||||
* Position of popup-container, options:`top`, `left`, `right`, `bottom`
|
||||
@@ -46,9 +44,6 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
|
||||
transitionName: 'zoom-big',
|
||||
placement: 'top',
|
||||
trigger: 'click',
|
||||
onConfirm: noop,
|
||||
onCancel: noop,
|
||||
onVisibleChange: noop,
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -72,12 +67,20 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
|
||||
|
||||
confirm = () => {
|
||||
this.setVisible(false);
|
||||
this.props.onConfirm.call(this);
|
||||
|
||||
const onConfirm = this.props.onConfirm;
|
||||
if (onConfirm) {
|
||||
onConfirm.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
cancel = () => {
|
||||
this.setVisible(false);
|
||||
this.props.onCancel.call(this);
|
||||
|
||||
const onCancel = this.props.onCancel;
|
||||
if (onCancel) {
|
||||
onCancel.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
onVisibleChange = (visible) => {
|
||||
@@ -88,7 +91,11 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
|
||||
if (!('visible' in this.props)) {
|
||||
this.setState({ visible });
|
||||
}
|
||||
this.props.onVisibleChange(visible);
|
||||
|
||||
const onVisibleChange = this.props.onVisibleChange;
|
||||
if (onVisibleChange) {
|
||||
onVisibleChange(visible);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import assign from 'object-assign';
|
||||
import Tooltip from '../tooltip';
|
||||
|
||||
export interface PopoverProps {
|
||||
@@ -11,7 +12,7 @@ export interface PopoverProps {
|
||||
placement?: 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' |
|
||||
'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom';
|
||||
/** title of popup-container */
|
||||
title?: React.ReactNode | string;
|
||||
title?: React.ReactNode;
|
||||
/** classname of popup-container */
|
||||
overlayClassName?: string;
|
||||
/** Style of overlay */
|
||||
@@ -43,10 +44,12 @@ export default class Popover extends React.Component<PopoverProps, any> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const props = assign({}, this.props);
|
||||
delete props.title;
|
||||
return (
|
||||
<Tooltip
|
||||
ref="tooltip"
|
||||
{...this.props}
|
||||
{...props}
|
||||
overlay={this.getOverlay()}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -52,7 +52,6 @@
|
||||
}
|
||||
|
||||
&-inner {
|
||||
min-width: @popover-min-width;
|
||||
background-color: @popover-bg;
|
||||
background-clip: padding-box;
|
||||
border-radius: @border-radius-base;
|
||||
@@ -60,6 +59,7 @@
|
||||
}
|
||||
|
||||
&-title {
|
||||
min-width: @popover-min-width;
|
||||
margin: 0; // reset heading margin
|
||||
padding: 0 16px;
|
||||
line-height: 32px;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Radio from './radio';
|
||||
import RadioButton from './radioButton';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
import assign from 'object-assign';
|
||||
|
||||
function getCheckedValue(children) {
|
||||
@@ -19,7 +19,7 @@ function getCheckedValue(children) {
|
||||
|
||||
export interface RadioGroupProps {
|
||||
/** 选项变化时的回调函数*/
|
||||
onChange?: React.FormEventHandler;
|
||||
onChange?: React.FormEventHandler<any>;
|
||||
/** 用于设置当前选中的值*/
|
||||
value?: string | number;
|
||||
/** 默认选中的值*/
|
||||
@@ -33,10 +33,7 @@ export interface RadioGroupProps {
|
||||
|
||||
export default class RadioGroup extends React.Component<RadioGroupProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-radio-group',
|
||||
disabled: false,
|
||||
onChange() {
|
||||
},
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -76,11 +73,15 @@ export default class RadioGroup extends React.Component<RadioGroupProps, any> {
|
||||
value: ev.target.value,
|
||||
});
|
||||
}
|
||||
this.props.onChange(ev);
|
||||
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange(ev);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const props = this.props;
|
||||
const children = React.Children.map(props.children, (radio: any) => {
|
||||
const children = React.Children.map((props.children || {}), (radio: any) => {
|
||||
if (radio && (radio.type === Radio || radio.type === RadioButton) && radio.props) {
|
||||
const keyProps = {};
|
||||
if (!('key' in radio) && typeof radio.props.value === 'string') {
|
||||
@@ -94,9 +95,10 @@ export default class RadioGroup extends React.Component<RadioGroupProps, any> {
|
||||
}
|
||||
return radio;
|
||||
});
|
||||
const prefixCls = props.prefixCls || 'ant-radio-group';
|
||||
const classString = classNames({
|
||||
[props.prefixCls]: true,
|
||||
[`${props.prefixCls}-${props.size}`]: props.size,
|
||||
[prefixCls]: true,
|
||||
[`${prefixCls}-${props.size}`]: props.size,
|
||||
});
|
||||
return <div className={classString} style={props.style}>{children}</div>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import RcRadio from 'rc-radio';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
|
||||
|
||||
export interface RadioProps {
|
||||
/** 指定当前是否选中*/
|
||||
@@ -28,7 +28,7 @@ export default class Radio extends React.Component<RadioProps, any> {
|
||||
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
|
||||
}
|
||||
render() {
|
||||
const { prefixCls, children, checked, disabled, className, style } = this.props;
|
||||
const { prefixCls, children, checked, disabled, className = '', style } = this.props;
|
||||
const wrapperClassString = classNames({
|
||||
[`${prefixCls}-wrapper`]: true,
|
||||
[`${prefixCls}-wrapper-checked`]: checked,
|
||||
|
||||
@@ -181,7 +181,8 @@ span.@{radio-prefix-cls} + * {
|
||||
}
|
||||
|
||||
.@{radio-prefix-cls}-inner,
|
||||
input {
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
.opacity(0);
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
@@ -42,7 +42,10 @@ A Selector similar to Select2.
|
||||
| size | Size of Select input. `large` `small` | String | default |
|
||||
| showSearch | Whether show search input in single mode.| boolean | false |
|
||||
| disabled | Whether disabled select | boolean | false |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative.[example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body |
|
||||
| dropdownStyle | style of dropdown menu | object | - |
|
||||
| dropdownClassName | className of dropdown menu | string | - |
|
||||
| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative.[example](http://codepen.io/anon/pen/xVBOVQ?editors=001) | function(triggerNode) | () => document.body |
|
||||
| labelInValue | whether to embed label in value | boolean | false |
|
||||
|
||||
### Option props
|
||||
|
||||
|
||||
9
components/select/index.tsx
Normal file → Executable file
9
components/select/index.tsx
Normal file → Executable file
@@ -13,7 +13,7 @@ export interface SelectProps {
|
||||
defaultValue?: SelectValue;
|
||||
size?: 'default' | 'large' | 'small';
|
||||
combobox?: boolean;
|
||||
notFoundContent?: React.ReactNode | string;
|
||||
notFoundContent?: React.ReactNode | null;
|
||||
showSearch?: boolean;
|
||||
transitionName?: string;
|
||||
choiceTransitionName?: string;
|
||||
@@ -35,7 +35,7 @@ export interface SelectProps {
|
||||
style?: React.CSSProperties;
|
||||
dropdownStyle?: React.CSSProperties;
|
||||
dropdownMenuStyle?: React.CSSProperties;
|
||||
onChange?: (value) => void;
|
||||
onChange?: (value: SelectValue) => void;
|
||||
}
|
||||
|
||||
export interface OptionProps {
|
||||
@@ -53,7 +53,8 @@ export interface SelectContext {
|
||||
};
|
||||
}
|
||||
|
||||
export { Option, OptGroup };
|
||||
// => It is needless to export the declaration of below two inner components.
|
||||
// export { Option, OptGroup };
|
||||
|
||||
export default class Select extends React.Component<SelectProps, any> {
|
||||
static Option = Option as React.ClassicComponentClass<OptionProps>;
|
||||
@@ -83,7 +84,7 @@ export default class Select extends React.Component<SelectProps, any> {
|
||||
render() {
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
className = '',
|
||||
size,
|
||||
combobox,
|
||||
showSearch,
|
||||
|
||||
@@ -44,6 +44,8 @@ title: Select
|
||||
| showSearch | 在选择框中显示搜索框 | boolean | false |
|
||||
| disabled | 是否禁用 | boolean | false |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true
|
||||
| dropdownStyle | 下拉菜单的 style 属性 | object | - |
|
||||
| dropdownClassName | 下拉菜单的 className 属性 | string | - |
|
||||
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](http://codepen.io/anon/pen/xVBOVQ?editors=001) | Function(triggerNode) | () => document.body |
|
||||
| labelInValue | 是否把每个选项的 label 包装到 value 中,决定 Select 的 value 类型。 | boolean | false |
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
color: #666;
|
||||
font-size: @font-size-base;
|
||||
|
||||
@@ -96,6 +95,15 @@
|
||||
&:hover &__clear {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&-selected-value {
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-disabled {
|
||||
@@ -132,33 +140,31 @@
|
||||
height: 28px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
height: 26px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-left: 8px;
|
||||
padding-right: 24px;
|
||||
line-height: 26px;
|
||||
}
|
||||
}
|
||||
|
||||
&-open&-show-search &-selection--single &-selection-selected-value {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 0;
|
||||
&-selection__rendered {
|
||||
display: block;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
line-height: 26px;
|
||||
// https://github.com/ant-design/ant-design/issues/3481#issuecomment-254721026
|
||||
&:after {
|
||||
content: '.';
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-lg {
|
||||
.@{select-prefix-cls}-selection--single {
|
||||
height: 32px;
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
line-height: 30px;
|
||||
}
|
||||
.@{select-prefix-cls}-selection--multiple {
|
||||
min-height: 32px;
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
@@ -176,9 +182,9 @@
|
||||
}
|
||||
.@{select-prefix-cls}-selection--single {
|
||||
height: 22px;
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
line-height: 20px;
|
||||
}
|
||||
.@{select-prefix-cls}-selection--multiple {
|
||||
min-height: 22px;
|
||||
@@ -208,7 +214,7 @@
|
||||
&-search__field__placeholder { // for TreeSelect compatibility
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 9px;
|
||||
left: 0;
|
||||
right: 9px;
|
||||
color: #ccc;
|
||||
line-height: 20px;
|
||||
@@ -222,7 +228,6 @@
|
||||
|
||||
&-search--inline {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.@{select-prefix-cls}-selection--multiple & {
|
||||
@@ -239,6 +244,7 @@
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
outline: 0;
|
||||
border-radius: @border-radius-base;
|
||||
@@ -247,7 +253,7 @@
|
||||
.@{select-prefix-cls}-search__field__mirror {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 9999px;
|
||||
left: -9999px;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -260,24 +266,26 @@
|
||||
&-selection--multiple {
|
||||
min-height: 28px;
|
||||
cursor: text;
|
||||
padding-bottom: 3px;
|
||||
.clearfix;
|
||||
|
||||
.@{select-prefix-cls}-search--inline {
|
||||
width: auto;
|
||||
padding: 0;
|
||||
.@{select-prefix-cls}-search__field {
|
||||
width: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-left: 6px;
|
||||
padding-bottom: 4px;
|
||||
margin-left: 5px;
|
||||
margin-bottom: -3px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
> ul > li,
|
||||
.@{select-prefix-cls}-selection__rendered > ul > li { // for tree-select
|
||||
margin-top: 4px;
|
||||
margin-top: 3px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
@@ -348,6 +356,7 @@
|
||||
}
|
||||
.@{select-prefix-cls}-search--inline {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
.@{select-prefix-cls}-search__field__wrap {
|
||||
@@ -355,7 +364,6 @@
|
||||
height: 100%;
|
||||
}
|
||||
.@{select-prefix-cls}-search__field {
|
||||
padding: 0 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
@@ -364,7 +372,7 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
.@{select-prefix-cls}-selection__rendered {
|
||||
padding: 0 24px 0 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -489,6 +497,8 @@
|
||||
.iconfont-size-under-12px(10px);
|
||||
transition: all 0.2s ease;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: 16px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0.1px 0, 0.1px 0 0, 0 -0.1px 0, -0.1px 0;
|
||||
|
||||
@@ -54,10 +54,12 @@
|
||||
border: solid 2px tint(@primary-color, 50%);
|
||||
background-color: #fff;
|
||||
z-index: 2;
|
||||
transition: border-color 0.3s ease;
|
||||
transition: border-color 0.3s ease, transform .3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||||
|
||||
&:hover {
|
||||
border-color: tint(@primary-color, 20%);
|
||||
transform: scale(1.2);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
&:active {
|
||||
|
||||
@@ -182,8 +182,10 @@
|
||||
.@{iconfont-css-prefix}-hdd:before { content: "\e69a"; }
|
||||
.@{iconfont-css-prefix}-ie:before { content: "\e69b"; }
|
||||
.@{iconfont-css-prefix}-file-jpg:before { content: "\e69c"; }
|
||||
.@{iconfont-css-prefix}-like:before { content: "\e69d"; }
|
||||
.@{iconfont-css-prefix}-dislike:before { content: "\e69e"; }
|
||||
.@{iconfont-css-prefix}-like:before { content: "\e64c"; }
|
||||
.@{iconfont-css-prefix}-like-o:before { content: "\e69d"; }
|
||||
.@{iconfont-css-prefix}-dislike:before { content: "\e64b"; }
|
||||
.@{iconfont-css-prefix}-dislike-o:before { content: "\e69e"; }
|
||||
.@{iconfont-css-prefix}-delete:before { content: "\e69f"; }
|
||||
.@{iconfont-css-prefix}-enter:before { content: "\e6a0"; }
|
||||
.@{iconfont-css-prefix}-pushpin-o:before { content: "\e6a1"; }
|
||||
@@ -232,3 +234,10 @@
|
||||
.@{iconfont-css-prefix}-down-square-o:before { content: "\e6ce"; }
|
||||
.@{iconfont-css-prefix}-up-square-o:before { content: "\e6cf"; }
|
||||
.@{iconfont-css-prefix}-loading:before { content: "\e6ae"; }
|
||||
.@{iconfont-css-prefix}-bulb:before { content: "\e649"; }
|
||||
.@{iconfont-css-prefix}-select:before { content: "\e64a"; }
|
||||
|
||||
.@{iconfont-css-prefix}-spin {
|
||||
display: inline-block;
|
||||
animation: loadingCircle 1.6s infinite linear;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
// ICONFONT
|
||||
@iconfont-css-prefix : anticon;
|
||||
@icon-url : "https://at.alicdn.com/t/font_1473840929_824008";
|
||||
@icon-url : "https://at.alicdn.com/t/font_pldm8phsjkathuxr";
|
||||
|
||||
// LINK
|
||||
@link-color : #2db7f5;
|
||||
|
||||
@@ -29,6 +29,5 @@ ReactDOM.render(
|
||||
<style>
|
||||
.ant-switch {
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
<style>
|
||||
|
||||
@@ -31,6 +31,7 @@ const Test = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<Switch disabled={this.state.disabled} />
|
||||
<br />
|
||||
<Button type="primary" onClick={this.toggle}>Toggle disabled</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ import { Switch } from 'antd';
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<Switch />
|
||||
<br />
|
||||
<Switch size="small" />
|
||||
</div>
|
||||
, mountNode);
|
||||
|
||||
@@ -18,7 +18,9 @@ import { Switch, Icon } from 'antd';
|
||||
|
||||
ReactDOM.render(<div>
|
||||
<Switch checkedChildren={'开'} unCheckedChildren={'关'} />
|
||||
<br />
|
||||
<Switch checkedChildren="1" unCheckedChildren="0" />
|
||||
<br />
|
||||
<Switch checkedChildren={<Icon type="check" />} unCheckedChildren={<Icon type="cross" />} />
|
||||
</div>, mountNode);
|
||||
````
|
||||
|
||||
3
components/switch/index.tsx
Normal file → Executable file
3
components/switch/index.tsx
Normal file → Executable file
@@ -12,6 +12,7 @@ export interface SwitchProps {
|
||||
onChange?: (checked: boolean) => any;
|
||||
checkedChildren?: React.ReactNode;
|
||||
unCheckedChildren?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default class Switch extends React.Component<SwitchProps, any> {
|
||||
@@ -27,7 +28,7 @@ export default class Switch extends React.Component<SwitchProps, any> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { prefixCls, size, className } = this.props;
|
||||
const { prefixCls, size, className = '' } = this.props;
|
||||
const classes = classNames({
|
||||
[className]: !!className,
|
||||
[`${prefixCls}-small`]: size === 'small',
|
||||
|
||||
@@ -8,22 +8,23 @@
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
width: 44px;
|
||||
height: 22px;
|
||||
min-width: 44px;
|
||||
line-height: 20px;
|
||||
vertical-align: middle;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #ccc;
|
||||
cursor: pointer;
|
||||
transition: all @switch-duration @ease-in-out-circ;
|
||||
transition: all @switch-duration;
|
||||
user-select: none;
|
||||
|
||||
&-inner {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
margin-left: 24px;
|
||||
margin-right: 6px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:after {
|
||||
@@ -32,11 +33,12 @@
|
||||
height: 18px;
|
||||
left: 2px;
|
||||
top: 1px;
|
||||
|
||||
border-radius: 18px;
|
||||
background-color: #fff;
|
||||
content: " ";
|
||||
cursor: pointer;
|
||||
transition: left @switch-duration @ease-in-out-circ, width @switch-duration @ease-in-out-circ;
|
||||
transition: all @switch-duration, width @switch-duration;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
@@ -54,23 +56,40 @@
|
||||
|
||||
&-small {
|
||||
height: 14px;
|
||||
min-width: 28px;
|
||||
line-height: 12px;
|
||||
width: 28px;
|
||||
|
||||
.@{switch-prefix-cls}-inner {
|
||||
margin-left: 18px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&-small&-checked:after {
|
||||
left: 14px;
|
||||
&-small&-checked {
|
||||
&:after {
|
||||
left: 100%;
|
||||
margin-left: -12px;
|
||||
}
|
||||
|
||||
.@{switch-prefix-cls}-inner {
|
||||
margin-left: 3px;
|
||||
margin-right: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
&-small:active&-checked:after {
|
||||
left: 10px;
|
||||
}
|
||||
@@ -80,11 +99,13 @@
|
||||
background-color: @primary-color;
|
||||
|
||||
.@{switch-prefix-cls}-inner {
|
||||
left: 6px;
|
||||
margin-left: 6px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
left: 22px;
|
||||
left: 100%;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
&:active:after {
|
||||
|
||||
13
components/table/FilterDropdownMenuWrapper.tsx
Normal file
13
components/table/FilterDropdownMenuWrapper.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface FilterDropdownMenuWrapperProps {
|
||||
onClick?: React.MouseEventHandler<any>;
|
||||
children?: any;
|
||||
className?: string;
|
||||
}
|
||||
export default class FilterDropdownMenuWrapper extends React.Component<FilterDropdownMenuWrapperProps, any> {
|
||||
render() {
|
||||
const { onClick, children, className } = this.props;
|
||||
return <div className={className} onClick={onClick}>{children}</div>;
|
||||
}
|
||||
}
|
||||
101
components/table/Table.tsx
Normal file → Executable file
101
components/table/Table.tsx
Normal file → Executable file
@@ -34,22 +34,22 @@ const defaultPagination = {
|
||||
onShowSizeChange: noop,
|
||||
};
|
||||
|
||||
export interface TableRowSelection {
|
||||
export interface TableRowSelection<T> {
|
||||
type?: 'checkbox' | 'radio';
|
||||
selectedRowKeys?: string[];
|
||||
onChange?: (selectedRowKeys: string[], selectedRows: Object[]) => any;
|
||||
getCheckboxProps?: (record: Object) => Object;
|
||||
onSelect?: (record: Object, selected: boolean, selectedRows: Object[]) => any;
|
||||
getCheckboxProps?: (record: T) => Object;
|
||||
onSelect?: (record: T, selected: boolean, selectedRows: Object[]) => any;
|
||||
onSelectAll?: (selected: boolean, selectedRows: Object[], changeRows: Object[]) => any;
|
||||
}
|
||||
|
||||
export interface TableColumnConfig {
|
||||
export interface TableColumnConfig<T> {
|
||||
title?: React.ReactNode;
|
||||
key?: string;
|
||||
dataIndex?: string;
|
||||
render?: (text: any, record: Object, index: number) => React.ReactNode;
|
||||
render?: (text: any, record: T, index: number) => React.ReactNode;
|
||||
filters?: { text: string; value: string }[];
|
||||
onFilter?: (value: any, record: Object) => boolean;
|
||||
onFilter?: (value: any, record: T) => boolean;
|
||||
filterMultiple?: boolean;
|
||||
filterDropdown?: React.ReactNode;
|
||||
sorter?: boolean | ((a: any, b: any) => number);
|
||||
@@ -61,16 +61,16 @@ export interface TableColumnConfig {
|
||||
sortOrder?: boolean | ('ascend' | 'descend');
|
||||
}
|
||||
|
||||
export interface TableProps {
|
||||
export interface TableProps<T> {
|
||||
prefixCls?: string;
|
||||
dropdownPrefixCls?: string;
|
||||
rowSelection?: TableRowSelection;
|
||||
rowSelection?: TableRowSelection<T>;
|
||||
pagination?: PaginationProps | boolean;
|
||||
size?: 'default' | 'small';
|
||||
dataSource?: Object[];
|
||||
columns?: TableColumnConfig[];
|
||||
rowKey?: string | ((record: Object, index: number) => string);
|
||||
rowClassName?: (record: Object, index: number) => string;
|
||||
dataSource?: T[];
|
||||
columns: TableColumnConfig<T>[];
|
||||
rowKey?: string | ((record: T, index: number) => string);
|
||||
rowClassName?: (record: T, index: number) => string;
|
||||
expandedRowRender?: any;
|
||||
defaultExpandedRowKeys?: string[];
|
||||
expandedRowKeys?: string[];
|
||||
@@ -80,14 +80,14 @@ export interface TableProps {
|
||||
loading?: boolean;
|
||||
locale?: Object;
|
||||
indentSize?: number;
|
||||
onRowClick?: (record: Object, index: number) => any;
|
||||
onRowClick?: (record: T, index: number) => any;
|
||||
useFixedHeader?: boolean;
|
||||
bordered?: boolean;
|
||||
showHeader?: boolean;
|
||||
footer?: (currentPageData: Object[]) => React.ReactNode;
|
||||
title?: (currentPageData: Object[]) => React.ReactNode;
|
||||
scroll?: { x?: boolean | number, y?: boolean | number};
|
||||
childrenColumnName?: 'string';
|
||||
childrenColumnName?: string;
|
||||
bodyStyle?: React.CSSProperties;
|
||||
className?: string;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export interface TableContext {
|
||||
};
|
||||
}
|
||||
|
||||
export default class Table extends React.Component<TableProps, any> {
|
||||
export default class Table<T> extends React.Component<TableProps<T>, any> {
|
||||
static propTypes = {
|
||||
dataSource: React.PropTypes.array,
|
||||
columns: React.PropTypes.array.isRequired,
|
||||
@@ -117,7 +117,6 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
static defaultProps = {
|
||||
dataSource: [],
|
||||
prefixCls: 'ant-table',
|
||||
dropdownPrefixCls: 'ant-dropdown',
|
||||
useFixedHeader: false,
|
||||
rowSelection: null,
|
||||
className: '',
|
||||
@@ -125,10 +124,8 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
loading: false,
|
||||
bordered: false,
|
||||
indentSize: 20,
|
||||
onChange: noop,
|
||||
locale: {},
|
||||
rowKey: 'key',
|
||||
childrenColumnName: 'children',
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
@@ -361,7 +358,10 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
this.props.onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, newState)));
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, newState)));
|
||||
}
|
||||
}
|
||||
|
||||
handleFilter = (column, nextFilters) => {
|
||||
@@ -409,11 +409,14 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
}
|
||||
|
||||
this.setState(newState, () => {
|
||||
props.onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
selectionDirty: false,
|
||||
filters,
|
||||
pagination,
|
||||
})));
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
selectionDirty: false,
|
||||
filters,
|
||||
pagination,
|
||||
})));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -463,7 +466,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
.map((item, i) => this.getRecordKey(item, i));
|
||||
|
||||
// 记录变化的列
|
||||
const changeRowKeys = [];
|
||||
const changeRowKeys: string[] = [];
|
||||
if (checked) {
|
||||
changableRowKeys.forEach(key => {
|
||||
if (selectedRowKeys.indexOf(key) < 0) {
|
||||
@@ -511,13 +514,16 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
}
|
||||
this.setState(newState);
|
||||
|
||||
this.props.onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
selectionDirty: false,
|
||||
pagination,
|
||||
})));
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
selectionDirty: false,
|
||||
pagination,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
renderSelectionRadio = (value, record, index) => {
|
||||
renderSelectionRadio = (_, record, index) => {
|
||||
let rowIndex = this.getRecordKey(record, index); // 从 1 开始
|
||||
const props = this.getCheckboxPropsByItem(record);
|
||||
let checked;
|
||||
@@ -537,7 +543,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
);
|
||||
}
|
||||
|
||||
renderSelectionCheckBox = (value, record, index) => {
|
||||
renderSelectionCheckBox = (_, record, index) => {
|
||||
let rowIndex = this.getRecordKey(record, index); // 从 1 开始
|
||||
let checked;
|
||||
if (this.state.selectionDirty) {
|
||||
@@ -558,7 +564,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
);
|
||||
}
|
||||
|
||||
getRecordKey(record, index?) {
|
||||
getRecordKey(record, index?): string {
|
||||
const rowKey = this.props.rowKey;
|
||||
if (typeof rowKey === 'function') {
|
||||
return rowKey(record, index);
|
||||
@@ -584,11 +590,11 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
}
|
||||
|
||||
renderRowSelection() {
|
||||
const prefixCls = this.props.prefixCls;
|
||||
const { prefixCls, rowSelection } = this.props;
|
||||
const columns = this.props.columns.concat();
|
||||
if (this.props.rowSelection) {
|
||||
if (rowSelection) {
|
||||
const data = this.getFlatCurrentPageData().filter((item) => {
|
||||
if (this.props.rowSelection.getCheckboxProps) {
|
||||
if (rowSelection.getCheckboxProps) {
|
||||
return !this.getCheckboxPropsByItem(item).disabled;
|
||||
}
|
||||
return true;
|
||||
@@ -617,7 +623,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
);
|
||||
}
|
||||
let selectionColumn;
|
||||
if (this.props.rowSelection.type === 'radio') {
|
||||
if (rowSelection.type === 'radio') {
|
||||
selectionColumn = {
|
||||
key: 'selection-column',
|
||||
render: this.renderSelectionRadio,
|
||||
@@ -689,7 +695,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
selectedKeys={colFilters}
|
||||
confirmFilter={this.handleFilter}
|
||||
prefixCls={`${prefixCls}-filter`}
|
||||
dropdownPrefixCls={dropdownPrefixCls}
|
||||
dropdownPrefixCls={dropdownPrefixCls || 'ant-dropdown'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -736,9 +742,13 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
pagination.onShowSizeChange(current, pageSize);
|
||||
const nextPagination = assign({}, pagination, { pageSize, current });
|
||||
this.setState({ pagination: nextPagination });
|
||||
this.props.onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
pagination: nextPagination,
|
||||
})));
|
||||
|
||||
const onChange = this.props.onChange;
|
||||
if (onChange) {
|
||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||
pagination: nextPagination,
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
renderPagination() {
|
||||
@@ -750,7 +760,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
const { pagination } = this.state;
|
||||
if (pagination.size) {
|
||||
size = pagination.size;
|
||||
} else if (this.props.size === 'middle' || this.props.size === 'small') {
|
||||
} else if (this.props.size as string === 'middle' || this.props.size === 'small') {
|
||||
size = 'small';
|
||||
}
|
||||
let total = pagination.total || this.getLocalData().length;
|
||||
@@ -803,7 +813,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
// 当数据量少于等于每页数量时,直接设置数据
|
||||
// 否则进行读取分页数据
|
||||
if (data.length > pageSize || pageSize === Number.MAX_VALUE) {
|
||||
data = data.filter((item, i) => {
|
||||
data = data.filter((_, i) => {
|
||||
return i >= (current - 1) * pageSize && i < current * pageSize;
|
||||
});
|
||||
}
|
||||
@@ -819,7 +829,7 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
}
|
||||
|
||||
recursiveSort(data, sorterFn) {
|
||||
const { childrenColumnName } = this.props;
|
||||
const { childrenColumnName = 'children' } = this.props;
|
||||
return data.sort(sorterFn).map(item => (item[childrenColumnName] ? assign(
|
||||
{},
|
||||
item, {
|
||||
@@ -849,8 +859,9 @@ export default class Table extends React.Component<TableProps, any> {
|
||||
if (values.length === 0) {
|
||||
return;
|
||||
}
|
||||
data = col.onFilter ? data.filter(record => {
|
||||
return values.some(v => col.onFilter(v, record));
|
||||
const onFilter = col.onFilter;
|
||||
data = onFilter ? data.filter(record => {
|
||||
return values.some(v => onFilter(v, record));
|
||||
}) : data;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ title:
|
||||
|
||||
> 若列头与内容不对齐,请指定每列宽度 `width`。
|
||||
|
||||
> 建议指定 scroll.x 为固定宽度。
|
||||
> 建议指定 `scroll.x` 为固定宽度。注意,非固定列宽度之和不要超过 `scroll.x`。
|
||||
|
||||
## en-US
|
||||
|
||||
@@ -19,7 +19,7 @@ Suitable for large amounts of data with long columns.
|
||||
|
||||
> Specify the width of each column if header and cell do not align properly.
|
||||
|
||||
> A fixed width for `scroll.x` is recommended.
|
||||
> A fixed width for `scroll.x` is recommended. The sum of unfixed columns should not greater than `scroll.x`.
|
||||
|
||||
````jsx
|
||||
import { Table } from 'antd';
|
||||
@@ -34,7 +34,7 @@ const columns = [
|
||||
{ title: 'Column 5', dataIndex: 'address', key: '5', width: 150 },
|
||||
{ title: 'Column 6', dataIndex: 'address', key: '6', width: 150 },
|
||||
{ title: 'Column 7', dataIndex: 'address', key: '7', width: 150 },
|
||||
{ title: 'Column 8', dataIndex: 'address', key: '8', width: 150 },
|
||||
{ title: 'Column 8', dataIndex: 'address', key: '8' },
|
||||
{
|
||||
title: 'Action',
|
||||
key: 'operation',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user