Compare commits

..

154 Commits

Author SHA1 Message Date
zombiej
e6fd745c26 bump 3.11.5 2018-12-24 16:48:40 +08:00
afc163
e047bb166f site: make document side bar sticky
close #3800
2018-12-24 15:59:59 +08:00
ztplz
4bd107f9d7 Correct doc 2018-12-23 23:16:42 +08:00
afc163
9eaac3f568 🆙 ant-design-palettes => @ant-design/colors 2018-12-23 21:57:36 +08:00
afc163
0218bee810 📝 update badges 2018-12-23 18:08:59 +08:00
afc163
ee27e87cf0 📝 update 3.11.4 changelog 2018-12-23 17:55:16 +08:00
afc163
b3b39fcfd5 💄 use chalk in console.log 2018-12-23 17:55:16 +08:00
afc163
03c10c9a05 🎉 3.11.4 2018-12-23 17:55:16 +08:00
afc163
89574017be 🆙 upgrade devDeps 2018-12-23 16:46:22 +08:00
j3l11234
95cf9f3e48 Update rc-form requirement from ~2.2.7 to ~2.4.0 (#13788)
& and preserve option
2018-12-23 00:07:43 +08:00
afc163
a318c2ed77 📝 update badges 2018-12-22 22:26:50 +08:00
ztplz
8fe3c113b6 Fix typo (#13786) 2018-12-22 20:01:39 +08:00
ztplz
bce044bac2 Fix typo in changelog (#13784) 2018-12-22 19:46:38 +08:00
afc163
cdfdae51ed 💄 fix lgtm alert 2018-12-22 19:45:38 +08:00
afc163
88615024b1 Fix test snapshots 2018-12-22 19:36:46 +08:00
afc163
b828741dc0 🔨 Refactor Tag 🔨 2018-12-22 19:20:38 +08:00
afc163
fe47f01796 📝 Fix documentation 2018-12-22 18:13:30 +08:00
afc163
4e68a53d27 📝 update changelog 2018-12-22 17:45:19 +08:00
afc163
a383df8870 🌐 Add English changelog of 3.11.3 2018-12-22 17:42:12 +08:00
afc163
7d3d271aea 📝 fix changelog 2018-12-22 17:42:12 +08:00
afc163
5977d31590 🎉 release 3.11.3 2018-12-22 17:42:12 +08:00
afc163
3a25ef8d14 💄 Fix lgtm alerts
https://lgtm.com/projects/g/ant-design/ant-design/alerts/?mode=list
2018-12-22 17:40:25 +08:00
afc163
4378454495 🆙 upgrade pagination, close #13720 2018-12-22 16:52:32 +08:00
afc163
1350c87ab1 🎨 Improve form demo code style 2018-12-22 16:48:30 +08:00
afc163
d78d0de36c 💄 Fix extra margin of nested form item
close #13748
2018-12-22 16:44:10 +08:00
afc163
a7597819a2 💄 use disabled cursor on disabled tab, close #13709 2018-12-21 22:38:06 +08:00
wangshuai
cb0a92e13b use T[] insteadof Array<T> 2018-12-21 21:34:19 +08:00
wangshuai
8c95732bfb fix(Table):fix arguments and type definition in TableRowSelection 2018-12-21 21:34:19 +08:00
HarlanLuo
476ace8aae Transfer: fix search-not-found block sometimes not show in edge 2018-12-21 21:33:58 +08:00
杨哲迪
42cd542677 fix(tabs): fix tabs.tabpane disabled style error 2018-12-21 21:33:31 +08:00
黄俊亮
96766d4018 refactor: add alert-close-hover-color 2018-12-21 19:06:36 +08:00
vthinkxie
f99b8a1792 feat(table): support table border-radius define in theme 2018-12-21 11:50:46 +08:00
Alfred Qiu
b24d5a65a5 add form-tutorial.md 2018-12-21 11:03:54 +08:00
Sam Lanning
1817052194 📝 Put gitter flags in badges in README (#13744) 2018-12-21 08:04:18 +08:00
zombiej
7473f9b871 update deps of rc-select 2018-12-20 23:25:04 +08:00
zombiej
0d438610ff skip show-tooltip.md test when is dist test 2018-12-20 23:11:32 +08:00
afc163
69d2656ab2 💄 update slider demo 2018-12-20 20:39:40 +08:00
Sam Lanning
97ee28ee7a Fix a regular expression
Pipe (|) is not supposed to be used as a delimiter in character classes, though
it seems like that's what someone's trying to do here, given it's trying to
extract numbers from a css transform property.
2018-12-20 20:30:18 +08:00
Sam Lanning
0f547e31d5 Improvements to badges in README (#13743)
* 📝 Use flat badges where possible

* 📝 Add LGTM.com badge in README
2018-12-20 20:00:53 +08:00
afc163
ecf83c0438 💄 Fix one tslint problem 2018-12-20 17:34:30 +08:00
stevenyuysy
0929a8b2c5 🐛 fix status normal 2018-12-20 17:30:28 +08:00
Sam Lanning
8f830ec366 fix inconsistent component state updates
React component state updates using setState may
asynchronously update this.props and this.state, thus it is
not safe to use either of the two when calculating the new
state passed to setState, and instead the callback-based
variant should be used instead.
2018-12-20 17:29:02 +08:00
徐新航
e0c74501f4 feat: The <Affix> run update-position when first mounted, to get the actual styles. 2018-12-20 17:27:32 +08:00
afc163
1ac6b5782d 🐛 Correct Drawer style
close #11504
2018-12-20 17:26:34 +08:00
ztplz
1c02a65593 format mention 2018-12-20 17:19:44 +08:00
偏右
a127ba6789 Update index.tsx 2018-12-20 17:19:10 +08:00
sylvanasGone
b71301b65f fix: add missing parameter 2018-12-20 17:19:10 +08:00
yibu.wang
839d0dcb09 fix 2018-12-20 17:18:12 +08:00
Wang yb
f8fbb33c73 Update interface.tsx 2018-12-20 17:18:12 +08:00
ztplz
1f730faf2d Set menu mode to vertical by default 2018-12-20 17:17:50 +08:00
小哈husky
f5bf79d2d8 修复当值为0时样式不对问题
修复当值为0时样式不对问题 ,bug demo.https://codesandbox.io/s/0146v1lxkl
2018-12-20 17:17:05 +08:00
Sam Lanning
af8a025aeb [chore] remove additional unused variable 2018-12-20 17:16:00 +08:00
Sam Lanning
e56e1a71c3 [chore] fix useless assignments to local variables
This commit addresses all the instances of useless assignments to
local variables, as found by LGTM.com
2018-12-20 17:16:00 +08:00
Sam Lanning
bfc75f5872 [chore] remove unneeded checks
args will always be defined, and an array, additionally Array.some()
will always return false if the array is empty. So these conditions
are uneeded.
2018-12-20 17:16:00 +08:00
Sam Lanning
2f0580d08b [chore] simplify condition to remove useless conditional 2018-12-20 17:16:00 +08:00
Sam Lanning
843056e58e [chore] fix off-by-one error in loop
This fixes an alert that was found on LGTM.com
2018-12-20 17:15:17 +08:00
Vu Hoang Minh
69f0601d13 Update show-tooltip.md 2018-12-20 17:13:27 +08:00
Vu Hoang Minh
f7bf62de65 Update show-tooltip.md 2018-12-20 17:13:27 +08:00
zy410419243
2bfc23b6f1 fix: render correctly when set childrenColumnName 2018-12-20 17:05:27 +08:00
kenve
6106e459a3 fix: demos can‘t be opened correctly in CodeSandbox (#13734) 2018-12-20 16:47:10 +08:00
Rahul Gurung
422b20993b #12115 - fixed dropdown hover flickering 2018-12-20 12:32:37 +08:00
Rahul Gurung
f04e6886b6 #13533 - fixed popover arrow
inherit arrow color from parent so that it doesn't
appears to be pointing 
upward when user changes
color of inner content, user
can further add desired color
to downward pointing arrow.
2018-12-20 12:28:23 +08:00
黄俊亮
1272ec0e04 refactor: add @tabs-horizontal-padding-{sm,lg} 2018-12-20 11:54:56 +08:00
ycjcl868
57a3d96c3b 🐛 fix Comment action padding 2018-12-19 17:26:00 +08:00
afc163
51e3012dc6 💄 Fix card loading edge when height is specified and small 2018-12-19 16:37:25 +08:00
afc163
3aeca7c10e 🐛 remove right padding in dropdown, close ant-design/ant-design-pro#3161 2018-12-19 16:04:50 +08:00
afc163
f706e2554f 🎅 🎅 🎅 2018-12-19 14:14:09 +08:00
afc163
28f6c4891a 📝 fix demo order 2018-12-19 12:07:32 +08:00
Rakshit Kumar
2ac67ddc25 Fixes #10576 (#13703)
FIxes #10576 by removing the extra padding that causes the card title to go out of line with action props
2018-12-19 11:04:32 +08:00
huishiyi
dd30678033 doc: update form & layout docs (#13701)
* doc: remove duplicate setFields in form doc-US

* doc: fix layout doc collapsedWidth default
2018-12-19 10:56:44 +08:00
zombiej
d23a996b35 update doc
close #11802
2018-12-18 23:31:50 +08:00
afc163
1fb7bac8ca 🐛 Fix Badge offset not working when count is ReactNode
close #13694
2018-12-18 18:31:12 +08:00
afc163
e1d68061d1 📝 Add react-highlight-words in recomendation 2018-12-18 18:19:27 +08:00
afc163
7abf528b96 💄 Optimize custom filter demo of Table 2018-12-18 18:19:27 +08:00
Teng YANG
c2d1d72675 Fix #13529 input group compact border color issues (#13550)
fix #13529
2018-12-18 16:53:45 +08:00
Julia Passynkova
7b643d2aaa Added CheckBox.Group to Form demo (#13680) 2018-12-18 13:33:57 +08:00
Lyndon001
4b0bcee220 Update values.zh-CN.md 2018-12-18 11:52:00 +08:00
zombiej
79f25d1107 fix spin style 2018-12-18 11:02:09 +08:00
zombiej
9a386e7336 update test case, OK button under calendar should not clickable 2018-12-18 10:36:51 +08:00
afc163
2e4d2b497a 💄 remove text-align right of card extra 2018-12-18 10:34:43 +08:00
zombiej
c9aeb29b43 update snapshot 2018-12-18 10:32:33 +08:00
偏右
14fff83a4a Merge pull request #13674 from iugo/patch-2
fix date-picker doc type
2018-12-18 10:09:15 +08:00
偏右
92e10eb015 Merge pull request #13650 from iugo/patch-1
fix RangePickerProps onOk type
2018-12-18 10:08:53 +08:00
Justin Reich
d0d5481c17 Updated CommentProps type for author 2018-12-18 10:08:38 +08:00
iugo
30a6d38898 Update index.zh-CN.md 2018-12-18 09:32:02 +08:00
iugo
21b0f329b9 fix date-picker en doc type 2018-12-18 09:30:59 +08:00
iugo
a98e42784e fix date-picker doc type 2018-12-18 09:28:25 +08:00
afc163
2c7112be7b Improve spin perfermance and UI detail 2018-12-17 23:27:24 +08:00
zombieJ
68ad468938 move sorter logic from div to th cell (#13669)
fix #13467
2018-12-17 22:42:16 +08:00
afc163
26936de2e8 🐛 Fix WeekPicker width, close #13629 2018-12-17 20:34:34 +08:00
afc163
1b0d37d1f3 🐛 remove word-break for consistent ui, close #13624 2018-12-17 20:14:39 +08:00
zombiej
d4715e1783 pass props to fix IE <= 10 not support constructor
fix #13540
2018-12-17 16:13:44 +08:00
陈帅
8b8c133984 fix test ci (#13659) 2018-12-17 16:10:58 +08:00
Damian Green
a140533a26 Fix lifecycle method for radio (#13239) 2018-12-17 15:01:55 +08:00
iugo
5681d6381a fix RangePickerProps onOk type 2018-12-17 11:39:47 +08:00
Gianfrancø Palumbo
219500ee7a Fix cursor for radio button disabled state
Add a style for `ant-radio-inner` span element.
2018-12-16 20:43:52 +08:00
陈帅
243a5b6097 Migrate jest new configuration 2018-12-16 11:26:36 +08:00
zombiej
cf43c19a80 use new lifecycle method of Cascader
ref #9792
2018-12-14 11:40:47 +08:00
ztplz
933d175f5b Merge pull request #13625 from killhtf/documentation-fix
Set apostrophes to uniform style in documentation
2018-12-14 03:55:16 +08:00
Igor
43f47e0f4a Set apostrophes to uniform style
Some apostrophes use more width than they need and all symbols should be in uniform style.
2018-12-13 21:20:34 +03:00
afc163
3b8dd8152d fix back-top test 2018-12-14 00:49:28 +08:00
afc163
8280c6da80 fix back-top test 2018-12-14 00:23:39 +08:00
afc163
d1650e31e9 fix key warning in tabs demo 2018-12-13 22:58:14 +08:00
afc163
860809f173 fix invalid title in div 2018-12-13 22:51:53 +08:00
afc163
9c777231e3 fix locale provider snapshot 2018-12-13 22:47:30 +08:00
陈帅
2c79d2a7c1 Revert "Use less @plugin" (#13613) 2018-12-13 18:07:49 +08:00
陈帅
377062bc9b Merge pull request #13595 from ant-design/lessPlugin
Use less @plugin
2018-12-13 12:22:57 +08:00
zombiej
2d5d71bf85 clean rgb converter 2018-12-13 11:54:34 +08:00
zombiej
06403f92f3 clean convert 2018-12-13 11:16:33 +08:00
zombiej
c5462e4d45 clean _applyModification 2018-12-13 11:12:08 +08:00
zombiej
0574c759cf clean readable 2018-12-13 11:06:42 +08:00
zombiej
2546a30485 remove toXXX 2018-12-13 10:53:14 +08:00
ztplz
05cf557ee1 Format menu 2018-12-13 09:38:55 +08:00
zombiej
fa007942b2 need latest less 2018-12-12 22:43:37 +08:00
zombiej
a5df939c2c use less plugin 2018-12-12 20:46:44 +08:00
Wei Zhu
2a66546433 docs: document Steps[className] 2018-12-12 17:13:39 +08:00
zombiej
a7c1aca7f9 clean tinyColor 2018-12-12 16:34:16 +08:00
zombiej
75df1e762e del bezierEasing 2018-12-12 16:12:57 +08:00
zombiej
154e6781ec Merge branch 'master' into lessPlugin 2018-12-12 16:04:11 +08:00
afc163
1c9ed454b1 🆙 upgrade devDeps 2018-12-12 15:04:02 +08:00
afc163
d644da4a5d 🆙 upgrade antd-tools 2018-12-12 11:54:48 +08:00
zombiej
fc8a21d03c update antd-tools of 6.1.3 2018-12-12 11:08:56 +08:00
zombiej
aba8f7e585 update antd-tools dep 2018-12-12 10:51:42 +08:00
zombiej
67ad6262e5 update bisheng-plugin-react 2018-12-12 09:48:09 +08:00
zombiej
1f2945e88e update ignore 2018-12-12 01:11:02 +08:00
zombiej
87efad2da7 clean color-palette 2018-12-12 01:09:58 +08:00
zombiej
62fe5dda35 add color palette 2018-12-12 01:01:34 +08:00
zombiej
41df6cead4 add missing color 2018-12-12 00:18:42 +08:00
zombiej
2029762297 replace components colors 2018-12-11 22:45:03 +08:00
zombiej
ee128020e7 replace colors 2018-12-11 22:17:19 +08:00
Sergey Volynkin
d554fa82a4 snapshots update 2018-12-11 19:25:26 +08:00
Sergey Volynkin
cc9b09df13 improved description on Chinese, small refactor at the map func 2018-12-11 19:25:26 +08:00
Sergey Volynkin
0d246d38bc Add "Hide Already Selected" example for Select component 2018-12-11 19:25:26 +08:00
zombiej
e2ec0c2b48 update antd-tools & bisheng version 2018-12-11 17:43:05 +08:00
陈帅
6b6f4625ab Merge pull request #13565 from ant-design/prettier
auto prettier and auto git add
2018-12-11 17:08:32 +08:00
陈帅
e7b48e92eb auto prettier and auto git add 2018-12-11 16:55:38 +08:00
lilun
4f5355675b doc: add antd-table-infinity in en-US 2018-12-11 15:44:54 +08:00
lilun
988a84ab15 doc: add antd-table-infinity 2018-12-11 15:44:54 +08:00
afc163
7e911637e2 📝 detect recommendation links click data 2018-12-11 12:33:23 +08:00
afc163
6ee4c1a290 🐛 Fix Table filter dropdown button not clickable 2018-12-11 11:08:18 +08:00
zy410419243
ce2469667d pref: fix typos 2018-12-11 10:38:35 +08:00
afc163
4e4c737e9b fix demo lint 2018-12-10 21:42:38 +08:00
afc163
cde6dbe7c3 💄 improve Drawer demo and add snapshots 2018-12-10 18:59:37 +08:00
afc163
327f78adbb 📝 Add more instrunctions about antd 3 migration 2018-12-10 17:07:21 +08:00
ztplz
c7452f3a9d Fix type error 2018-12-10 16:23:59 +08:00
afc163
ca56828df7 🚑 3.11.2 2018-12-10 16:19:31 +08:00
陈广亮
1e0400704b fix getColumnTitle bug
title: <div>
           <span>title</span>
           null
        </div>
2018-12-10 16:19:22 +08:00
wangxingkang
13593a9629 feat: Improve Dropdown component item definition (#13536) 2018-12-10 13:25:51 +08:00
afc163
77cd0c1849 🆙 upgrade typescript version 2018-12-10 11:39:21 +08:00
afc163
5a1c9066f9 📝 Add issuehunt badge in README 2018-12-10 11:36:22 +08:00
Teng YANG
6b94bd0bb4 Fix customize border-width problem in input group compact mode (#13534) 2018-12-10 10:42:23 +08:00
afc163
8d2a42fa91 📝 fix outdated link 2018-12-10 00:23:50 +08:00
Ali Zhdanov
4c6244bd0c Outdated url to file input accept prop (#13531)
Looks like outdated URL to file input `accept` prop. I've added the proper one.
2018-12-10 00:21:48 +08:00
bukas
814980d9c6 fix: upload blob preview && upload beforeUpload doc (#13528)
* fix: upload not preview blob

* fix: test && readme

* fix: upload promise
2018-12-10 00:16:51 +08:00
150 changed files with 2773 additions and 1686 deletions

View File

@@ -7,25 +7,10 @@ const transformIgnorePatterns = [
module.exports = {
verbose: true,
setupFiles: [
'./tests/setup.js',
],
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
'md',
],
modulePathIgnorePatterns: [
'/_site/',
],
testPathIgnorePatterns: [
'/node_modules/',
'dekko',
'node',
],
setupFiles: ['./tests/setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],
modulePathIgnorePatterns: ['/_site/'],
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
transform: {
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
@@ -41,12 +26,10 @@ module.exports = {
'!components/**/*/interface.{ts,tsx}',
],
transformIgnorePatterns,
snapshotSerializers: [
'enzyme-to-json/serializer',
],
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',
tsConfig: './tsconfig.test.json',
},
},
testURL: 'http://localhost',

View File

@@ -1,14 +1,7 @@
// jest config for server render environment
module.exports = {
setupFiles: [
'./tests/setup.js',
],
moduleFileExtensions: [
'ts',
'tsx',
'js',
'md',
],
setupFiles: ['./tests/setup.js'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
transform: {
'\\.tsx?$': './node_modules/antd-tools/lib/jest/codePreprocessor',
'\\.js$': './node_modules/antd-tools/lib/jest/codePreprocessor',
@@ -16,9 +9,7 @@ module.exports = {
},
testRegex: 'demo\\.test\\.js$',
testEnvironment: 'node',
snapshotSerializers: [
'enzyme-to-json/serializer',
],
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: {
'ts-jest': {
tsConfigFile: './tsconfig.test.json',

View File

@@ -15,13 +15,75 @@ timeline: true
---
## 3.11.5
`2018-12-24`
- 🐞 Fixed `lib` missing css file match. [#13791](https://github.com/ant-design/ant-design/issues/13803)
## 3.11.4
`2018-12-23`
- 🐞 Fixed DependencyNotFoundError `Could not find dependency: '@babel/runtime'`. [#13791](https://github.com/ant-design/ant-design/issues/13791)
- ⚡️ Refactor Tag component with less code and better performance. [b828741](https://github.com/ant-design/ant-design/commit/b828741dc06eaa69ff3f8c76024fd5527ed6d74f)
## 3.11.3 🎅🏻
`2018-12-22`
- ⚡️ Upgrade our toolchains (babel and webpack) to latest version and prettier all codes!
- Table
- 🐞 **Fixed that dropdown menu action not clickable**. [#13563](https://github.com/ant-design/ant-design/issues/13563)
- 🐞 Fixed hovering components on Table sortable column. [#13467](https://github.com/ant-design/ant-design/issues/13467)
- 🐞 Fixed crash issue of selection Table under IE9/10. [#13540](https://github.com/ant-design/ant-design/issues/13540)
- 🐞 Fixed check-all checkbox state when Table `childrenColumnName` is specified. [#13710](https://github.com/ant-design/ant-design/issues/13710)
- 💄 Remove work break styles in table cell for consistent behavior. [#13624](https://github.com/ant-design/ant-design/issues/13624)
- 💄 Rewrote the custom filter demo of Table. [Link](https://ant.design/components/table-cn/#components-table-demo-custom-filter-panel)
- 🐞 Fixed padding of Button which children is `0`. [#13596](https://github.com/ant-design/ant-design/pull/13596) [@951565664](https://github.com/951565664)
- 💄 Chore Card header and loading UI.
- 💄 Optimized Spin wrapper styles and improve performance slightly. [2c7112b](https://github.com/ant-design/ant-design/commit/2c7112be7bf32c6e8362334b86b0799cc3a4a6c4)
- 🐞 Fixed border color of validated Input.Group. [#13529](https://github.com/ant-design/ant-design/issues/13529) [@morenyang](https://github.com/morenyang)
- 🐞 Fixed submenu animation of vertical-type Menu. [#13597](https://github.com/ant-design/ant-design/issues/13597)
- 🐞 Fixed width of WeekPicker. [#13629](https://github.com/ant-design/ant-design/issues/13629)
- 🐞 Fixed cursor style of disabled Radio.Button. [#13642](https://github.com/ant-design/ant-design/pull/13642) [@gianpaj](https://github.com/gianpaj)
- Dropdown
- 🐞 Fixed slight shift when menu is popped in Chrome. [#12115](https://github.com/ant-design/ant-design/issues/12115) [@gurungrahul2](https://github.com/gurungrahul2)
- 🐞 Fixed unexpected scrollbar caused by dropdown placed at screen edge. [00564dd](https://github.com/ant-design/ant-design/commit/3aeca7c10ec6ee3441f024fe7fdb5ae9e00564dd)
- 🐞 Fixed `offset` props when Badge `count` is specified as a ReactNode. [#13694](https://github.com/ant-design/ant-design/issues/13694)
- 🐞 Remove nested Form.Item negative margin. [#13748](https://github.com/ant-design/ant-design/issues/13748)
- 📝 Added a Select demo of [Hide Already Selected](https://ant.design/components/select/#components-select-demo-hide-selected). [#13552](https://github.com/ant-design/ant-design/pull/13552) [@SergeyVolynkin](https://github.com/SergeyVolynkin)
- 🐞 Fixed padding of Comment actions. [#13713](https://github.com/ant-design/ant-design/issues/13713)
- 🐞 Fixed broken arrow style when customize Popover's background color. [#13533](https://github.com/ant-design/ant-design/issues/13533) [@gurungrahul2](https://github.com/gurungrahul2)
- 🐞 Corrected Drawer `style` prop to outside wrapper. [#11504](https://github.com/ant-design/ant-design/issues/11504)
- 🐞 Fixed one problem of incorrect state when Affix first mounted. [#13737](https://github.com/ant-design/ant-design/pull/13737) [@xuxinhang](https://github.com/xuxinhang)
- 🐞 Fixed Tabs cursor style of disabled tab. [#13709](https://github.com/ant-design/ant-design/issues/13709)
- 🌟 Added some less variables of [Tabs](https://github.com/ant-design/ant-design/pull/13727), [Table](https://github.com/ant-design/ant-design/pull/13754), [Alert](https://github.com/ant-design/ant-design/pull/13768).
- TypeScript
- ⚡️ Enhanced Table `ColumnProps` types about `dataIndex`. [#13605](https://github.com/ant-design/ant-design/pull/13605) [@bondBo](https://github.com/bondBo)
- ⚡️ Enhanced Table `TableRowSelection.onChange` arguments with generic types.[#13761](https://github.com/ant-design/ant-design/issues/13761) [@hahabazinga](https://github.com/hahabazinga)
- 🐞 Fixed type of LocaleProvider's `children`. [#12974](https://github.com/ant-design/ant-design/issues/12974)
- 🐞 Fixed type of RangePicker `onOk`'s arguments. [#13650](https://github.com/ant-design/ant-design/pull/13650) [@iugo](https://github.com/iugo)
- 🐞 Fixed Comment `author` type from string to ReactNode。[#13670](https://github.com/ant-design/ant-design/pull/13670) [@reichjustin](https://github.com/reichjustin)
- 🐞 Fixed type of Select `dropdownProps`'s arguments. [#13617](https://github.com/ant-design/ant-design/pull/13617) [@SylvanasGone](https://github.com/SylvanasGone)
## 3.11.2
`2018-12-10`
- 🐞 Fixed Table `Cannot read property 'children' of undefined` error when customize `column.title` as ReactNode. [#13542](https://github.com/ant-design/ant-design/issues/13542) [@geraldchen890806](https://github.com/geraldchen890806)
- 🐞 Fixed another border problem of Button when customized less variable `@border-width-base`. [#13534](https://github.com/ant-design/ant-design/issues/13534) [@morenyang](https://github.com/morenyang)
- 🐞 Fixed Upload don't support resolve `Blob` object when `beforeUpload` returns a Promise. [#13528](https://github.com/ant-design/ant-design/pull/13528/) [@huanz](https://github.com/huanz)
- https://github.com/ant-design/ant-design/pull/13536
- 🐞 Fixed two props of Dropdown TypeScript definitions. [#13536](https://github.com/ant-design/ant-design/pull/13536) [@wangxingkang](https://github.com/wangxingkang)
## 3.11.1
`2018-12-08`
- 🐞 Fixed the issue where the Avatar icon could not be centered vertically. [#13408](https://github.com/ant-design/ant-design/issues/13408)
- 🐞 Fixed the border problem of Button when setting the less variable `@border-width-base` to `1`. [#13413](https://github.com/ant-design/ant-design/issues/13413) [@morenyang](https://github.com/morenyang)
- 🐞 Fixed Commnet does not correctly display line breaks. [#13429](https://github.com/ant-design/ant-design/issues/13429)
- 🐞 Fixed the border problem of Button when customized less variable `@border-width-base`. [#13413](https://github.com/ant-design/ant-design/issues/13413) [@morenyang](https://github.com/morenyang)
- 🐞 Fixed Comment does not correctly display line breaks. [#13429](https://github.com/ant-design/ant-design/issues/13429)
- 🐞 Fixed the issue that when the Alert is in `closable`, the icon will be covered by the text. [#13440](https://github.com/ant-design/ant-design/issues/13440)
- Button
- 🐞 Fixed the issue that when the `href` property is `undefined`, the Button will also be rendered as a anchor. [#13337](https://github.com/ant-design/ant-design/issues/13337)
@@ -1101,12 +1163,15 @@ Learn more in the [Ant Design 3.0 announcement post](https://medium.com/ant-desi
- New [Divider](https://ant.design/components/divider/) component.
- 30 New [icons](https://ant.design/components/icon/).
### ⚠️ Read it before migration
- We suggest you upgrade to latest 3.x version directly.
- Some APIs may be deprecated in other 3.x versions which don't described below, please pay attention to warning in browser console and upgrade them.
- We strongly suggest upgrade react to 16 or newest for better support and performance, which could be conduct via [React 16 upgrading](https://reactjs.org/blog/2017/09/26/react-v16.0.html#upgrading).
- Then you can migrate to antd@3 by following `Breaking Changes` section.
### Breaking Changes
> We suggest you upgrade to latest 3.x version directly.
> Some APIs may be deprecated in other 3.x versions which don't described below, please pay attention to warning in browser console and upgrade them.
We provide a [migration tool](https://github.com/ant-design/antd-migration-helper) to help you find deprecated usages in your codebase.
- Card's `noHovering` has been renamed to `hoverable`, and its default value now is `true`.

View File

@@ -15,12 +15,74 @@ timeline: true
---
## 3.11.5
`2018-12-24`
- 🐞 修复 `lib` 下样式文件路径问题。[#13791](https://github.com/ant-design/ant-design/issues/13803)
## 3.11.4
`2018-12-23`
- 🐞 修复 `Could not find dependency: '@babel/runtime'` 的问题。[#13791](https://github.com/ant-design/ant-design/issues/13791)
- ⚡️ 重构 Tag 组件,简化代码并提升性能。[b828741](https://github.com/ant-design/ant-design/commit/b828741dc06eaa69ff3f8c76024fd5527ed6d74f)
## 3.11.3 🎅🏻
`2018-12-22`
- ⚡️ 升级内部依赖到 babel@7 和 webpack@4,并使用 prettier 格式化了所有代码。
- Table
- 🐞 **修复 Table 列筛选菜单按钮不可点击的问题**。[#13563](https://github.com/ant-design/ant-design/issues/13563)
- 🐞 修复 Table 列设置排序后影响列头自定义浮出组件的展现问题。[#13467](https://github.com/ant-design/ant-design/issues/13467)
- 🐞 修复 Table 选择时在 IE9/10 下崩溃的问题。[#13540](https://github.com/ant-design/ant-design/issues/13540)
- 🐞 修复 Table 指定 `childrenColumnName` 时,全选框无法自动勾选的问题。[#13710](https://github.com/ant-design/ant-design/issues/13710)
- 💄 移除 Table 下英文单词断行的样式。[#13624](https://github.com/ant-design/ant-design/issues/13624)
- 💄 优化了 Table 自定义列搜索例子的实现和 UI。[演示](https://ant.design/components/table-cn/#components-table-demo-custom-filter-panel)
- 🐞 修复 Button 内容为 `0` 时的样式。[#13596](https://github.com/ant-design/ant-design/pull/13596) [@951565664](https://github.com/951565664)
- 💄 微调 Card 头部和加载中的样式细节。
- 💄 优化 Spin 样式并略微提升了切换状态的性能。[2c7112b](https://github.com/ant-design/ant-design/commit/2c7112be7bf32c6e8362334b86b0799cc3a4a6c4)
- 🐞 修复一个 Input.Group 使用 compact 时校验状态边框样式的问题。[#13529](https://github.com/ant-design/ant-design/issues/13529) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Menu 在 vertical 模式下的展开收起动画。[#13597](https://github.com/ant-design/ant-design/issues/13597)
- 🐞 修复 WeekPicker 的宽度样式异常。[#13629](https://github.com/ant-design/ant-design/issues/13629)
- 🐞 修复 Radio.Button 失效状态下的鼠标手势。[#13642](https://github.com/ant-design/ant-design/pull/13642) [@gianpaj](https://github.com/gianpaj)
- Dropdown
- 🐞 修复 Chrome 下菜单弹出时有轻微移动的问题。[#12115](https://github.com/ant-design/ant-design/issues/12115) [@gurungrahul2](https://github.com/gurungrahul2)
- 🐞 修复一个屏幕边缘的 Dropdown 菜单引起的浏览器滚动条异常出现的问题。[00564dd](https://github.com/ant-design/ant-design/commit/3aeca7c10ec6ee3441f024fe7fdb5ae9e00564dd)
- 🐞 修复 Badge 的 `count` 是自定义 ReactNode 时 `offset` 属性失效的问题。[#13694](https://github.com/ant-design/ant-design/issues/13694)
- 🐞 去掉 Form.Item 内嵌负边距,改用其他的方式实现单行多个表单项。[#13748](https://github.com/ant-design/ant-design/issues/13748)
- 📝 补充了一个 Select 选择后隐藏选项的[例子](https://ant.design/components/select-cn/#components-select-demo-hide-selected)。[#13552](https://github.com/ant-design/ant-design/pull/13552) [@SergeyVolynkin](https://github.com/SergeyVolynkin)
- 🐞 修复 Comment 的操作链接边距样式。[#13713](https://github.com/ant-design/ant-design/issues/13713)
- 🐞 修复自定义 Popover 背景色时箭头样式突兀的问题。[#13533](https://github.com/ant-design/ant-design/issues/13533) [@gurungrahul2](https://github.com/gurungrahul2)
- 🐞 修正 Drawer 的 `style` 属性到最外层容器上。[#11504](https://github.com/ant-design/ant-design/issues/11504)
- 🐞 修复一个 Affix 初始化时固定状态不正确的问题。[#13737](https://github.com/ant-design/ant-design/pull/13737) [@xuxinhang](https://github.com/xuxinhang)
- 🐞 修复 Tabs 失效页签的鼠标手型。[#13709](https://github.com/ant-design/ant-design/issues/13709)
- 🌟 补充 [Tabs](https://github.com/ant-design/ant-design/pull/13727)、[Table](https://github.com/ant-design/ant-design/pull/13754)、[Alert](https://github.com/ant-design/ant-design/pull/13768) 组件的一些样式变量。
- TypeScript
- ⚡️ 完善 Table 的 `ColumnProps` 定义,增强对 `dataIndex` 的校验。[#13605](https://github.com/ant-design/ant-design/pull/13605) [@bondBo](https://github.com/bondBo)
- ⚡️ 完善 Table 的 `TableRowSelection.onChange` 参数泛型定义。[#13761](https://github.com/ant-design/ant-design/issues/13761) [@hahabazinga](https://github.com/hahabazinga)
- 🐞 修复 LocaleProvider 的 `children` 类型。 [#12974](https://github.com/ant-design/ant-design/issues/12974)
- 🐞 修复 RangePicker 的 `onOk` 的参数类型。[#13650](https://github.com/ant-design/ant-design/pull/13650) [@iugo](https://github.com/iugo)
- 🐞 修正 Comment `author` 属性的类型为 ReactNode。[#13670](https://github.com/ant-design/ant-design/pull/13670) [@reichjustin](https://github.com/reichjustin)
- 🐞 修复 Select `dropdownProps` 的参数定义。[#13617](https://github.com/ant-design/ant-design/pull/13617) [@SylvanasGone](https://github.com/SylvanasGone)
## 3.11.2
`2018-12-10`
- 🐞 修复 Table 使用自定义列头时报 `Cannot read property 'children' of undefined` 的问题。[#13542](https://github.com/ant-design/ant-design/issues/13542) [@geraldchen890806](https://github.com/geraldchen890806)
- 🐞 修复另一个 Input 在自定义了 less 变量 `@border-width-base` 时的边框问题。[#13534](https://github.com/ant-design/ant-design/pull/13534) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Upload 的 `beforeUpload` 方法返回 Promise 时不支持 resolve `Blob` 对象的问题。[#13528](https://github.com/ant-design/ant-design/pull/13528/) [@huanz](https://github.com/huanz)
- https://github.com/ant-design/ant-design/pull/13536
- 🐞 修复 Dropdown 两个属性的 TypeScript 定义。[#13536](https://github.com/ant-design/ant-design/pull/13536) [@wangxingkang](https://github.com/wangxingkang)
## 3.11.1
`2018-12-08`
- 🐞 修复 Avatar 图标不能垂直居中的问题。[#13408](https://github.com/ant-design/ant-design/issues/13408)
- 🐞 修复 Input 在设置 less 变量 `@border-width-base``1` 时的边框问题。[#13413](https://github.com/ant-design/ant-design/issues/13413) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Input 在自定义了 less 变量 `@border-width-base` 时的边框问题。[#13413](https://github.com/ant-design/ant-design/issues/13413) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Commnet 组件不能正确显示换行的问题。[#13429](https://github.com/ant-design/ant-design/issues/13429)
- 🐞 修复 Alert 在 `closable` 时,关闭图标会被文字遮挡的问题。[#13440](https://github.com/ant-design/ant-design/issues/13440)
- Button
@@ -1104,12 +1166,15 @@ timeline: true
- 新的 [Divider](https://ant.design/components/divider-cn/) 组件。
- 新增 30 个[图标](https://ant.design/components/icon-cn/)。
### ⚠️ 升级必读
- 如果你从 2.x 升级到 3.x建议直接升级到 3.x 的最新版本。
- 3.x 后续的版本可能已经废弃了一些下面没有提到的改动,请参考控制台的警告提示相应升级。
- 建议同时升级 React 到 16 或更新版本,以获得更好的性能和更完善的支持,升级方式见 [官方发布文档](https://reactjs.org/blog/2017/09/26/react-v16.0.html#upgrading)。
- 最后请参照下面的不兼容改动进行升级。
### 不兼容改动
> 如果你从 2.x 升级到 3.x建议直接升级到 3.x 的最新版本。
> 3.x 后续的版本可能已经废弃了一些下面没有提到的改动,请参考控制台的警告提示相应升级。
此版本有部分不兼容的改动,升级时确保修改相应的使用代码。另外由于人肉查找代码中的废弃用法过于低效,所以我们提供了 [antd-migration-helper](https://github.com/ant-design/antd-migration-helper) 用于扫描代码中的废弃用法。
- Card 的 `noHovering` 属性重命名为 `hoverable`,且默认值改为 `true`

View File

@@ -12,14 +12,16 @@
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design)
[![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg)](https://david-dm.org/ant-design/ant-design)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg)](https://david-dm.org/ant-design/ant-design?type=dev)
[![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/ant-design/ant-design.svg)](http://isitmaintained.com/project/ant-design/ant-design "Percentage of issues still open")
[![Gitter](https://badges.gitter.im/ant-design/ant-design-english.svg)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)(🇺🇸)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)(🇨🇳)
[![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev)
[![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/)
[![Language grade: JavaScript](https://flat.badgen.net/lgtm/grade/javascript/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/context:javascript)
[![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
</div>
@@ -109,6 +111,8 @@ $ npm start
> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)
## 社区互助
如果您在使用的过程中碰到问题,可以通过下面几个途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。

View File

@@ -8,18 +8,20 @@
<div align="center">
An enterprise-class UI design language and React-based implementation.
An enterprise-class UI design language and React implementation.
[![CircleCI branch](https://img.shields.io/circleci/project/github/ant-design/ant-design/master.svg?style=flat-square)](https://circleci.com/gh/ant-design/ant-design)
[![Codecov](https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square)](https://codecov.io/gh/ant-design/ant-design/branch/master)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg)](https://david-dm.org/ant-design/ant-design)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg)](https://david-dm.org/ant-design/ant-design?type=dev)
[![npm package](https://img.shields.io/npm/v/antd.svg?style=flat-square)](https://www.npmjs.org/package/antd)
[![NPM downloads](http://img.shields.io/npm/dm/antd.svg?style=flat-square)](http://npmjs.com/antd)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/ant-design/ant-design.svg)](http://isitmaintained.com/project/ant-design/ant-design "Percentage of issues still open")
[![Gitter](https://badges.gitter.im/ant-design/ant-design-english.svg)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)(🇺🇸)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)(🇨🇳)
[![Issues need help](https://flat.badgen.net/github/label-issues/ant-design/ant-design/help%20wanted/open)](https://github.com/ant-design/ant-design/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
[![Dependencies](https://img.shields.io/david/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design)
[![DevDependencies](https://img.shields.io/david/dev/ant-design/ant-design.svg?style=flat-square)](https://david-dm.org/ant-design/ant-design?type=dev)
[![Total alerts](https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/alerts/)
[![Language grade: JavaScript](https://flat.badgen.net/lgtm/grade/javascript/g/ant-design/ant-design)](https://lgtm.com/projects/g/ant-design/ant-design/context:javascript)
[![Gitter](https://img.shields.io/gitter/room/ant-design/ant-design-english.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyMzUiIGhlaWdodD0iNjUwIiB2aWV3Qm94PSIwIDAgNzQxMCAzOTAwIj4NCjxyZWN0IHdpZHRoPSI3NDEwIiBoZWlnaHQ9IjM5MDAiIGZpbGw9IiNiMjIyMzQiLz4NCjxwYXRoIGQ9Ik0wLDQ1MEg3NDEwbTAsNjAwSDBtMCw2MDBINzQxMG0wLDYwMEgwbTAsNjAwSDc0MTBtMCw2MDBIMCIgc3Ryb2tlPSIjZmZmIiBzdHJva2Utd2lkdGg9IjMwMCIvPg0KPHJlY3Qgd2lkdGg9IjI5NjQiIGhlaWdodD0iMjEwMCIgZmlsbD0iIzNjM2I2ZSIvPg0KPGcgZmlsbD0iI2ZmZiI%2BDQo8ZyBpZD0iczE4Ij4NCjxnIGlkPSJzOSI%2BDQo8ZyBpZD0iczUiPg0KPGcgaWQ9InM0Ij4NCjxwYXRoIGlkPSJzIiBkPSJNMjQ3LDkwIDMxNy41MzQyMzAsMzA3LjA4MjAzOSAxMzIuODczMjE4LDE3Mi45MTc5NjFIMzYxLjEyNjc4MkwxNzYuNDY1NzcwLDMwNy4wODIwMzl6Ii8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB5PSI0MjAiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHk9Ijg0MCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTI2MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjcyIgeT0iMTY4MCIvPg0KPC9nPg0KPHVzZSB4bGluazpocmVmPSIjczQiIHg9IjI0NyIgeT0iMjEwIi8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzOSIgeD0iNDk0Ii8%2BDQo8L2c%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzMTgiIHg9Ijk4OCIvPg0KPHVzZSB4bGluazpocmVmPSIjczkiIHg9IjE5NzYiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3M1IiB4PSIyNDcwIi8%2BDQo8L2c%2BDQo8L3N2Zz4%3D)](https://gitter.im/ant-design/ant-design-english?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Join the chat at https://gitter.im/ant-design/ant-design](https://img.shields.io/gitter/room/ant-design/ant-design.svg?style=flat-square&logoWidth=20&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjkwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAzMCAyMCI%2BDQo8ZGVmcz4NCjxwYXRoIGlkPSJzIiBkPSJNMCwtMSAwLjU4Nzc4NSwwLjgwOTAxNyAtMC45NTEwNTcsLTAuMzA5MDE3SDAuOTUxMDU3TC0wLjU4Nzc4NSwwLjgwOTAxN3oiIGZpbGw9IiNmZmRlMDAiLz4NCjwvZGVmcz4NCjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIyMCIgZmlsbD0iI2RlMjkxMCIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNSw1KSBzY2FsZSgzKSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsMikgcm90YXRlKDIzLjAzNjI0MykiLz4NCjx1c2UgeGxpbms6aHJlZj0iI3MiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDQpIHJvdGF0ZSg0NS44Njk4OTgpIi8%2BDQo8dXNlIHhsaW5rOmhyZWY9IiNzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiw3KSByb3RhdGUoNjkuOTQ1Mzk2KSIvPg0KPHVzZSB4bGluazpocmVmPSIjcyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAsOSkgcm90YXRlKDIwLjY1OTgwOCkiLz4NCjwvc3ZnPg%3D%3D)](https://gitter.im/ant-design/ant-design?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
</div>
@@ -107,3 +109,5 @@ Open your browser and visit http://127.0.0.1:8001 , see more at [Development](ht
Read our [contributing guide](https://ant.design/docs/react/contributing) and let's build a better antd together.
We welcome all contributions. Please read our [CONTRIBUTING.md](https://github.com/ant-design/ant-design/blob/master/.github/CONTRIBUTING.md) first. You can submit any ideas as [pull requests](https://github.com/ant-design/ant-design/pulls) or as [GitHub issues](https://github.com/ant-design/ant-design/issues). If you'd like to improve code, check out the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and have a good time! :)
[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)

View File

@@ -218,6 +218,8 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
// Wait for parent component ref has its value
this.timeout = setTimeout(() => {
this.setTargetEventListeners(target);
// Mock Event object.
this.updatePosition({} as Event);
});
}

View File

@@ -6,6 +6,7 @@
@alert-message-color: @heading-color;
@alert-text-color: @text-color;
@alert-close-color: @text-color-secondary;
@alert-close-hover-color: #404040;
.@{alert-prefix-cls} {
.reset-component;
@@ -78,7 +79,7 @@
color: @alert-close-color;
transition: color 0.3s;
&:hover {
color: #404040;
color: @alert-close-hover-color;
}
}
}

View File

@@ -3,22 +3,14 @@ import { mount } from 'enzyme';
import BackTop from '..';
describe('BackTop', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
it('should scroll to top after click it', () => {
it('should scroll to top after click it', async () => {
const wrapper = mount(<BackTop visibilityHeight={-1} />);
document.documentElement.scrollTop = 400;
// trigger scroll manually
wrapper.instance().handleScroll();
jest.runAllTimers();
await new Promise(resolve => setTimeout(resolve, 0));
wrapper.find('.ant-back-top').simulate('click');
jest.runAllTimers();
await new Promise(resolve => setTimeout(resolve, 1000));
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
});
});

View File

@@ -164,7 +164,10 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
}
if (displayComponent) {
return React.cloneElement(displayComponent, {
className: `${prefixCls}-custom-component`,
className: classNames(
`${prefixCls}-custom-component`,
displayComponent.props && displayComponent.props.className,
),
});
}
return createElement(component as any, newProps, this.renderNumberElement());

View File

@@ -2185,3 +2185,18 @@ exports[`Badge should render when count is changed 5`] = `
</span>
</Badge>
`;
exports[`Badge should support offset when count is a ReactNode 1`] = `
<span
class="ant-badge"
>
<a
class="head-example"
href="#"
/>
<span
class="ant-scroll-number-custom-component custom"
style="right:-10px;margin-top:20px;color:#f5222d"
/>
</span>
`;

View File

@@ -72,4 +72,14 @@ describe('Badge', () => {
);
expect(wrapper).toMatchSnapshot();
});
// https://github.com/ant-design/ant-design/issues/13694
it('should support offset when count is a ReactNode', () => {
const wrapper = render(
<Badge count={<span className="custom" style={{ color: '#f5222d' }} />} offset={[10, 20]}>
<a href="#" className="head-example" />
</Badge>,
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@@ -112,7 +112,16 @@ export default class Badge extends React.Component<BadgeProps, any> {
renderDispayComponent() {
const { count } = this.props;
return count && typeof count === 'object' ? (count as React.ReactElement<any>) : undefined;
const customNode = count as React.ReactElement<any>;
if (!customNode || typeof customNode !== 'object') {
return undefined;
}
return React.cloneElement(customNode, {
style: {
...this.getStyleWithOffset(),
...(customNode.props && customNode.props.style),
},
});
}
renderBadgeNumber() {
@@ -130,8 +139,6 @@ export default class Badge extends React.Component<BadgeProps, any> {
[`${prefixCls}-status-${status}`]: !!status,
});
const styleWithOffset = this.getStyleWithOffset();
return hidden ? null : (
<ScrollNumber
prefixCls={scrollNumberPrefixCls}
@@ -140,7 +147,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
count={displayCount}
displayComponent={this.renderDispayComponent()} // <Badge status="success" count={<Icon type="xxx" />}></Badge>
title={this.getScollNumberTitle()}
style={styleWithOffset}
style={this.getStyleWithOffset()}
key="scrollNumber"
/>
);
@@ -172,12 +179,10 @@ export default class Badge extends React.Component<BadgeProps, any> {
[`${prefixCls}-status-${status}`]: !!status,
});
const styleWithOffset = this.getStyleWithOffset();
// <Badge status="success" />
if (!children && status) {
return (
<span {...restProps} className={this.getBadgeClassName()} style={styleWithOffset}>
<span {...restProps} className={this.getBadgeClassName()} style={this.getStyleWithOffset()}>
<span className={statusCls} />
<span className={`${prefixCls}-status-text`}>{text}</span>
</span>

View File

@@ -207,7 +207,7 @@ export default class Button extends React.Component<ButtonProps, any> {
[`${prefixCls}-${type}`]: type,
[`${prefixCls}-${shape}`]: shape,
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-icon-only`]: !children && icon,
[`${prefixCls}-icon-only`]: !children && children !== 0 && icon,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-background-ghost`]: ghost,
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar,

View File

@@ -66,11 +66,10 @@
&-extra {
float: right;
padding: @card-head-padding + 1.5px 0;
padding: @card-head-padding 0;
font-size: @font-size-base;
color: @text-color;
font-weight: normal;
text-align: right;
// https://stackoverflow.com/a/22429853/3040605
margin-left: auto;
}
@@ -232,6 +231,21 @@
}
}
&-loading {
overflow: hidden;
position: relative;
&:after {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: @card-padding-base;
background: @component-background;
content: '';
}
}
&-loading &-body {
user-select: none;
}

View File

@@ -4,6 +4,7 @@ import arrayTreeFilter from 'array-tree-filter';
import classNames from 'classnames';
import omit from 'omit.js';
import KeyCode from 'rc-util/lib/KeyCode';
import { polyfill } from 'react-lifecycles-compat';
import Input from '../input';
import Icon from '../icon';
import { ConfigConsumer, ConfigProviderProps } from '../config-provider';
@@ -103,6 +104,7 @@ export interface CascaderState {
value: string[];
popupVisible: boolean | undefined;
flattenOptions: CascaderOptionType[][] | undefined;
prevProps: CascaderProps;
}
interface CascaderLocale {
@@ -180,9 +182,29 @@ function getFilledFieldNames(props: CascaderProps) {
return names;
}
function flattenTree(
options: CascaderOptionType[],
props: CascaderProps,
ancestor: CascaderOptionType[] = [],
) {
const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = [] as CascaderOptionType[][];
const childrenName = names.children;
options.forEach(option => {
const path = ancestor.concat(option);
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path);
}
if (option[childrenName]) {
flattenOptions = flattenOptions.concat(flattenTree(option[childrenName], props, path));
}
});
return flattenOptions;
}
const defaultDisplayRender = (label: string[]) => label.join(' / ');
export default class Cascader extends React.Component<CascaderProps, CascaderState> {
class Cascader extends React.Component<CascaderProps, CascaderState> {
static defaultProps = {
prefixCls: 'ant-cascader',
inputPrefixCls: 'ant-input',
@@ -194,6 +216,24 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
notFoundContent: 'Not Found',
};
static getDerivedStateFromProps(nextProps: CascaderProps, { prevProps }: CascaderState) {
const newState: Partial<CascaderState> = {
prevProps: nextProps,
};
if ('value' in nextProps) {
newState.value = nextProps.value || [];
}
if ('popupVisible' in nextProps) {
newState.popupVisible = nextProps.popupVisible;
}
if (nextProps.showSearch && prevProps.options !== nextProps.options) {
newState.flattenOptions = flattenTree(nextProps.options, nextProps);
}
return newState;
}
cachedOptions: CascaderOptionType[];
private input: Input;
@@ -205,24 +245,11 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
inputValue: '',
inputFocused: false,
popupVisible: props.popupVisible,
flattenOptions: props.showSearch ? this.flattenTree(props.options, props) : undefined,
flattenOptions: props.showSearch ? flattenTree(props.options, props) : undefined,
prevProps: props,
};
}
componentWillReceiveProps(nextProps: CascaderProps) {
if ('value' in nextProps) {
this.setState({ value: nextProps.value || [] });
}
if ('popupVisible' in nextProps) {
this.setState({ popupVisible: nextProps.popupVisible });
}
if (nextProps.showSearch && this.props.options !== nextProps.options) {
this.setState({
flattenOptions: this.flattenTree(nextProps.options, nextProps),
});
}
}
handleChange = (value: any, selectedOptions: CascaderOptionType[]) => {
this.setState({ inputValue: '' });
if (selectedOptions[0].__IS_FILTERED_OPTION) {
@@ -236,11 +263,11 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
handlePopupVisibleChange = (popupVisible: boolean) => {
if (!('popupVisible' in this.props)) {
this.setState({
this.setState(state => ({
popupVisible,
inputFocused: popupVisible,
inputValue: popupVisible ? this.state.inputValue : '',
});
inputValue: popupVisible ? state.inputValue : '',
}));
}
const onPopupVisibleChange = this.props.onPopupVisibleChange;
@@ -312,26 +339,6 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
}
};
flattenTree(
options: CascaderOptionType[],
props: CascaderProps,
ancestor: CascaderOptionType[] = [],
) {
const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = [] as CascaderOptionType[][];
const childrenName = names.children;
options.forEach(option => {
const path = ancestor.concat(option);
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path);
}
if (option[childrenName]) {
flattenOptions = flattenOptions.concat(this.flattenTree(option[childrenName], props, path));
}
});
return flattenOptions;
}
generateFilteredOptions(prefixCls: string | undefined) {
const { showSearch, notFoundContent } = this.props;
const names: FilledFieldNamesType = getFilledFieldNames(this.props);
@@ -564,3 +571,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
);
}
}
polyfill(Cascader);
export default Cascader;

View File

@@ -5,7 +5,7 @@ export interface CommentProps {
/** List of action items rendered below the comment content */
actions?: Array<React.ReactNode>;
/** The element to display as the comment author. */
author?: string;
author?: React.ReactNode;
/** The element to display as the comment avatar - generally an antd Avatar */
avatar?: React.ReactNode;
/** className of comment */

View File

@@ -69,7 +69,7 @@
&-actions {
margin-top: 12px;
padding-left: 0;
> li {
display: inline-block;
color: @comment-action-color;

View File

@@ -173,24 +173,22 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
<span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
const input = ({ value }: { value: moment.Moment | undefined }) => {
return (
<span style={{ display: 'inline-block' }}>
<input
ref={this.saveInput}
disabled={disabled}
readOnly
value={(value && value.format(format)) || ''}
placeholder={placeholder}
className={pickerInputClass}
onFocus={onFocus}
onBlur={onBlur}
/>
{clearIcon}
{inputIcon}
</span>
);
};
const input = ({ value }: { value: moment.Moment | undefined }) => (
<span style={{ display: 'inline-block', width: '100%' }}>
<input
ref={this.saveInput}
disabled={disabled}
readOnly
value={(value && value.format(format)) || ''}
placeholder={placeholder}
className={pickerInputClass}
onFocus={onFocus}
onBlur={onBlur}
/>
{clearIcon}
{inputIcon}
</span>
);
return (
<span className={classNames(className, pickerClass)} style={style} id={id}>
<RcDatePicker

View File

@@ -2995,7 +2995,6 @@ exports[`RangePicker switch to corresponding month panel when click presetted ra
>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
My Birthday
</div>

View File

@@ -6,7 +6,7 @@ exports[`WeekPicker should support style prop 1`] = `
style="width: 400px;"
>
<span
style="display: inline-block;"
style="display: inline-block; width: 100%;"
>
<input
class="ant-calendar-picker-input ant-input"

View File

@@ -112,7 +112,7 @@ exports[`renders ./components/date-picker/demo/basic.md correctly 1`] = `
class="ant-calendar-picker"
>
<span
style="display:inline-block"
style="display:inline-block;width:100%"
>
<input
class="ant-calendar-picker-input ant-input"
@@ -1146,7 +1146,7 @@ exports[`renders ./components/date-picker/demo/size.md correctly 1`] = `
class="ant-calendar-picker ant-calendar-picker-default"
>
<span
style="display:inline-block"
style="display:inline-block;width:100%"
>
<input
class="ant-calendar-picker-input ant-input"
@@ -1353,7 +1353,7 @@ exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
class="ant-calendar-picker"
>
<span
style="display:inline-block"
style="display:inline-block;width:100%"
>
<input
class="ant-calendar-picker-input ant-input"
@@ -1455,7 +1455,7 @@ exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
class="ant-calendar-picker"
>
<span
style="display:inline-block"
style="display:inline-block;width:100%"
>
<input
class="ant-calendar-picker-input ant-input"

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import moment from 'moment';
import DatePicker from '..';
const { RangePicker } = DatePicker;
@@ -38,6 +39,7 @@ describe('DatePicker with showTime', () => {
onChange={onChangeFn}
onOk={onOkFn}
onOpenChange={onOpenChangeFn}
defaultValue={moment()}
/>,
);

View File

@@ -117,16 +117,16 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
| -------- | ----------- | ---- | ------- |
| defaultValue | to set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
| defaultPickerValue | to set default picker date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)\] | - |
| disabledTime | to specify the time that cannot be selected | function(dates: [moment, moment], partial: `'start'|'end'`) | - |
| disabledTime | to specify the time that cannot be selected | function(dates: \[moment, moment], partial: `'start'|'end'`) | - |
| format | to set the date format | string | "YYYY-MM-DD HH:mm:ss" |
| ranges | preseted ranges for quick selection | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | - |
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - |
| showTime | to provide an additional time selection | object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | to set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
| showTime.defaultValue | to set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | \[moment(), moment()] |
| value | to set date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
| onChange | a callback function, can be executed when the selected time is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
| onOk | callback when click ok button | function() | - |
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - |
| onChange | a callback function, can be executed when the selected time is changing | function(dates: \[moment, moment], dateStrings: \[string, string]) | - |
| onOk | callback when click ok button | function(dates: [moment](http://momentjs.com/)\[]) | - |
<style>
.code-box-demo .ant-calendar-picker {

View File

@@ -119,16 +119,16 @@ moment.locale('zh-cn');
| --- | --- | --- | --- |
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | 无 |
| defaultPickerValue | 默认面板日期 | [moment](http://momentjs.com/)\[] | 无 |
| disabledTime | 不可选择的时间 | function(dates: [moment, moment], partial: `'start'|'end'`) | 无 |
| disabledTime | 不可选择的时间 | function(dates: \[moment, moment\], partial: `'start'|'end'`) | 无 |
| format | 展示的日期格式 | string | "YYYY-MM-DD HH:mm:ss" |
| ranges       | 预设时间范围快捷选择 | { \[range: string]: [moment](http://momentjs.com/)\[] } \| { \[range: string]: () => [moment](http://momentjs.com/)\[] } | 无 |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - |
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | \[moment(), moment()] |
| value | 日期 | [moment](http://momentjs.com/)\[] | 无 |
| onCalendarChange | 待选日期发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
| onChange | 日期范围发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
| onOk | 点击确定按钮的回调 | function() | - |
| onCalendarChange | 待选日期发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 |
| onChange | 日期范围发生变化的回调 | function(dates: \[moment, moment\], dateStrings: \[string, string\]) | 无 |
| onOk | 点击确定按钮的回调 | function(dates: [moment](http://momentjs.com/)\[]) | - |
<style>
.code-box-demo .ant-calendar-picker {

View File

@@ -67,7 +67,7 @@ export interface RangePickerProps extends PickerProps {
defaultPickerValue?: RangePickerValue;
onChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
onCalendarChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
onOk?: (selectedTime: moment.Moment) => void;
onOk?: (selectedTime: moment.Moment[]) => void;
showTime?: TimePickerProps | boolean;
ranges?: {
[range: string]: RangePickerPresetRange;

View File

@@ -0,0 +1,261 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/drawer/demo/basic-right.md correctly 1`] = `
<div>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open
</span>
</button>
</div>
`;
exports[`renders ./components/drawer/demo/form-in-drawer.md correctly 1`] = `
<div>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<i
class="anticon anticon-plus"
>
<svg
aria-hidden="true"
class=""
data-icon="plus"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M848 474H550V152h-76v322H176c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h298v322h76V550h298c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
<span>
New account
</span>
</button>
</div>
`;
exports[`renders ./components/drawer/demo/multi-level-drawer.md correctly 1`] = `
<div>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open drawer
</span>
</button>
</div>
`;
exports[`renders ./components/drawer/demo/placement.md correctly 1`] = `
<div>
<div
class="ant-radio-group ant-radio-group-outline"
style="margin-right:8px"
>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="top"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
top
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="right"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
right
</span>
</label>
<label
class="ant-radio-wrapper"
>
<span
class="ant-radio"
>
<input
class="ant-radio-input"
type="radio"
value="bottom"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
bottom
</span>
</label>
<label
class="ant-radio-wrapper ant-radio-wrapper-checked"
>
<span
class="ant-radio ant-radio-checked"
>
<input
checked=""
class="ant-radio-input"
type="radio"
value="left"
/>
<span
class="ant-radio-inner"
/>
</span>
<span>
left
</span>
</label>
</div>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open
</span>
</button>
</div>
`;
exports[`renders ./components/drawer/demo/user-profile.md correctly 1`] = `
<div>
<div
class="ant-list ant-list-split ant-list-bordered"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-list-item"
>
<div
class="ant-list-item-meta"
>
<div
class="ant-list-item-meta-avatar"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
/>
</span>
</div>
<div
class="ant-list-item-meta-content"
>
<h4
class="ant-list-item-meta-title"
>
<a
href="https://ant.design/index-cn"
>
Lily
</a>
</h4>
<div
class="ant-list-item-meta-description"
>
Progresser AFX
</div>
</div>
</div>
<ul
class="ant-list-item-action"
>
<li>
<a>
View Profile
</a>
</li>
</ul>
</div>
<div
class="ant-list-item"
>
<div
class="ant-list-item-meta"
>
<div
class="ant-list-item-meta-avatar"
>
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png"
/>
</span>
</div>
<div
class="ant-list-item-meta-content"
>
<h4
class="ant-list-item-meta-title"
>
<a
href="https://ant.design/index-cn"
>
Lily
</a>
</h4>
<div
class="ant-list-item-meta-description"
>
Progresser AFX
</div>
</div>
</div>
<ul
class="ant-list-item-action"
>
<li>
<a>
View Profile
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
`;

View File

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

View File

@@ -1,21 +1,21 @@
---
order: 3
title:
zh-CN: 对象编辑
en-US: Edit item in drawer
zh-CN: 抽屉表单
en-US: Submit form in drawer
---
## zh-CN
用于承载编辑相关操作,需要点击关闭按钮关闭
在抽屉中使用表单
## en-US
A drawer containing an editable form which needs to be collapsed by clicking the close button.
Use form in drawer with submit button.
```jsx
import {
Drawer, Form, Button, Col, Row, Input, Select, DatePicker,
Drawer, Form, Button, Col, Row, Input, Select, DatePicker, Icon,
} from 'antd';
const { Option } = Select;
@@ -40,19 +40,17 @@ class DrawerForm extends React.Component {
return (
<div>
<Button type="primary" onClick={this.showDrawer}>
Create
<Icon type="plus" /> New account
</Button>
<Drawer
title="Create"
title="Create a new account"
width={720}
placement="right"
onClose={this.onClose}
maskClosable={false}
visible={this.state.visible}
style={{
height: 'calc(100% - 55px)',
overflow: 'auto',
paddingBottom: 53,
height: 'calc(100% - 108px)',
paddingBottom: '108px',
}}
>
<Form layout="vertical" hideRequiredMark>
@@ -60,20 +58,20 @@ class DrawerForm extends React.Component {
<Col span={12}>
<Form.Item label="Name">
{getFieldDecorator('name', {
rules: [{ required: true, message: 'please enter user name' }],
})(<Input placeholder="please enter user name" />)}
rules: [{ required: true, message: 'Please enter user name' }],
})(<Input placeholder="Please enter user name" />)}
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="Url">
{getFieldDecorator('url', {
rules: [{ required: true, message: 'please enter url' }],
rules: [{ required: true, message: 'Please enter url' }],
})(
<Input
style={{ width: '100%' }}
addonBefore="http://"
addonAfter=".com"
placeholder="please enter url"
placeholder="Please enter url"
/>
)}
</Form.Item>
@@ -149,25 +147,21 @@ class DrawerForm extends React.Component {
<div
style={{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e8e8e8',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
textAlign: 'right',
left: 0,
background: '#fff',
borderRadius: '0 0 4px 4px',
textAlign: 'right',
}}
>
<Button
style={{
marginRight: 8,
}}
onClick={this.onClose}
>
<Button onClick={this.onClose} style={{ marginRight: 8 }}>
Cancel
</Button>
<Button onClick={this.onClose} type="primary">Submit</Button>
<Button onClick={this.onClose} type="primary">
Submit
</Button>
</div>
</Drawer>
</div>

View File

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

View File

@@ -196,25 +196,20 @@ export default class Drawer extends React.Component<DrawerProps, IDrawerState> {
>
{header}
{closer}
<div className={`${prefixCls}-body`} style={this.props.style}>
{this.props.children}
</div>
<div className={`${prefixCls}-body`}>{this.props.children}</div>
</div>
);
};
getRcDrawerStyle = () => {
const { zIndex, placement, maskStyle } = this.props;
return this.state.push
? {
...maskStyle,
zIndex,
transform: this.getPushTransform(placement),
}
: {
...maskStyle,
zIndex,
};
const { zIndex, placement, maskStyle, style } = this.props;
const { push } = this.state;
return {
...maskStyle,
zIndex,
transform: push ? this.getPushTransform(placement) : undefined,
style,
};
};
// render Provider for Multi-level drawe

View File

@@ -20,7 +20,7 @@ title: Drawer
| --- | --- | --- | --- |
| closable | 是否显示右上角的关闭按钮 | boolean | true |
| destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false |
| getContainer | 指定 Drawer 挂载的 HTML 节点 | HTMLElement \| `() => HTMLElement` \| selectors  | 'body' |
| getContainer | 指定 Drawer 挂载的 HTML 节点 | HTMLElement \| `() => HTMLElement` \| Selectors | 'body' |
| maskClosable | 点击蒙层是否允许关闭 | boolean | true |
| mask | 是否展示遮罩 | Boolean | true |
| maskStyle | 遮罩样式 | object | {} |

View File

@@ -18,6 +18,8 @@ export interface DropDownProps {
className?: string;
transitionName?: string;
placement?: 'topLeft' | 'topCenter' | 'topRight' | 'bottomLeft' | 'bottomCenter' | 'bottomRight';
overlayClassName?: string;
overlayStyle?: React.CSSProperties;
forceRender?: boolean;
mouseEnterDelay?: number;
mouseLeaveDelay?: number;

View File

@@ -15,7 +15,7 @@
position: absolute;
top: -7px;
left: -7px;
right: -7px;
right: 0;
bottom: -7px;
content: ' ';
opacity: 0.0001;
@@ -55,6 +55,7 @@
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
background-clip: padding-box;
-webkit-transform: translate3d(0, 0, 0);
&-item-group-title {
color: @text-color-secondary;

View File

@@ -83,6 +83,8 @@ export type GetFieldDecoratorOptions = {
normalize?: (value: any, prevValue: any, allValues: any) => any;
/** Whether stop validate on first rule of error for this field. */
validateFirst?: boolean;
/** 是否一直保留子节点的信息 */
preserve?: boolean;
};
// function create

View File

@@ -3118,6 +3118,163 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
<div
class="ant-col-6 ant-form-item-label"
>
<label
class=""
for="checkbox-group"
title="Checkbox.Group"
>
Checkbox.Group
</label>
</div>
<div
class="ant-col-14 ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control has-success"
>
<span
class="ant-form-item-children"
>
<div
class="ant-checkbox-group"
data-__field="[object Object]"
data-__meta="[object Object]"
id="checkbox-group"
style="width:100%"
>
<div
class="ant-row"
>
<div
class="ant-col-8"
>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked"
>
<span
class="ant-checkbox ant-checkbox-checked"
>
<input
checked=""
class="ant-checkbox-input"
type="checkbox"
value="A"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
A
</span>
</label>
</div>
<div
class="ant-col-8"
>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled"
>
<span
class="ant-checkbox ant-checkbox-checked ant-checkbox-disabled"
>
<input
checked=""
class="ant-checkbox-input"
disabled=""
type="checkbox"
value="B"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
B
</span>
</label>
</div>
<div
class="ant-col-8"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
value="C"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
C
</span>
</label>
</div>
<div
class="ant-col-8"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
value="D"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
D
</span>
</label>
</div>
<div
class="ant-col-8"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
value="E"
/>
<span
class="ant-checkbox-inner"
/>
</span>
<span>
E
</span>
</label>
</div>
</div>
</div>
</span>
</div>
</div>
</div>
<div
class="ant-row ant-form-item"
>
@@ -4171,6 +4328,7 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
</div>
<div
class="ant-row ant-form-item"
style="margin-bottom:0"
>
<div
class="ant-form-item-label ant-col-xs-24 ant-col-sm-5"
@@ -4192,114 +4350,104 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
class="ant-form-item-children"
>
<div
class="ant-col-11"
class="ant-row ant-form-item ant-form-item-with-help"
style="display:inline-block;width:calc(50% - 12px)"
>
<div
class="ant-row ant-form-item ant-form-item-with-help"
class="ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control-wrapper"
class="ant-form-item-control has-error"
>
<div
class="ant-form-item-control has-error"
<span
class="ant-form-item-children"
>
<span
class="ant-form-item-children"
class="ant-calendar-picker"
>
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<i
class="anticon anticon-calendar ant-calendar-picker-icon"
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<i
class="anticon anticon-calendar ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="calendar"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="calendar"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</i>
</div>
</span>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</i>
</div>
</span>
<div
class="ant-form-explain"
>
Please select the correct date
</div>
</span>
<div
class="ant-form-explain"
>
Please select the correct date
</div>
</div>
</div>
</div>
<div
class="ant-col-2"
<span
style="display:inline-block;width:24px;text-align:center"
>
<span
style="display:inline-block;width:100%;text-align:center"
>
-
</span>
</div>
-
</span>
<div
class="ant-col-11"
class="ant-row ant-form-item"
style="display:inline-block;width:calc(50% - 12px)"
>
<div
class="ant-row ant-form-item"
class="ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control-wrapper"
class="ant-form-item-control"
>
<div
class="ant-form-item-control"
<span
class="ant-form-item-children"
>
<span
class="ant-form-item-children"
class="ant-calendar-picker"
>
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<i
class="anticon anticon-calendar ant-calendar-picker-icon"
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<i
class="anticon anticon-calendar ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="calendar"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="calendar"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</i>
</div>
</span>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</i>
</div>
</span>
</div>
</span>
</div>
</div>
</div>

View File

@@ -23,8 +23,6 @@ import {
Form, Row, Col, Input, Button, Icon,
} from 'antd';
const FormItem = Form.Item;
class AdvancedSearchForm extends React.Component {
state = {
expand: false,
@@ -38,7 +36,7 @@ class AdvancedSearchForm extends React.Component {
for (let i = 0; i < 10; i++) {
children.push(
<Col span={8} key={i} style={{ display: i < count ? 'block' : 'none' }}>
<FormItem label={`Field ${i}`}>
<Form.Item label={`Field ${i}`}>
{getFieldDecorator(`field-${i}`, {
rules: [{
required: true,
@@ -47,7 +45,7 @@ class AdvancedSearchForm extends React.Component {
})(
<Input placeholder="placeholder" />
)}
</FormItem>
</Form.Item>
</Col>
);
}

View File

@@ -18,8 +18,7 @@ import {
Form, Select, Input, Button,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const { Option } = Select;
class App extends React.Component {
handleSubmit = (e) => {
@@ -42,7 +41,7 @@ class App extends React.Component {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
<Form.Item
label="Note"
labelCol={{ span: 5 }}
wrapperCol={{ span: 12 }}
@@ -52,8 +51,8 @@ class App extends React.Component {
})(
<Input />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
label="Gender"
labelCol={{ span: 5 }}
wrapperCol={{ span: 12 }}
@@ -69,14 +68,14 @@ class App extends React.Component {
<Option value="female">female</Option>
</Select>
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
wrapperCol={{ span: 12, offset: 5 }}
>
<Button type="primary" htmlType="submit">
Submit
</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -24,8 +24,7 @@ import {
Form, Input, Select, Button,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const { Option } = Select;
class PriceInput extends React.Component {
static getDerivedStateFromProps(nextProps) {
@@ -122,15 +121,15 @@ class Demo extends React.Component {
const { getFieldDecorator } = this.props.form;
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem label="Price">
<Form.Item label="Price">
{getFieldDecorator('price', {
initialValue: { number: 0, currency: 'rmb' },
rules: [{ validator: this.checkPrice }],
})(<PriceInput />)}
</FormItem>
<FormItem>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -18,8 +18,6 @@ import {
Form, Input, Icon, Button,
} from 'antd';
const FormItem = Form.Item;
let id = 0;
class DynamicFieldSet extends React.Component {
@@ -80,7 +78,7 @@ class DynamicFieldSet extends React.Component {
getFieldDecorator('keys', { initialValue: [] });
const keys = getFieldValue('keys');
const formItems = keys.map((k, index) => (
<FormItem
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
label={index === 0 ? 'Passengers' : ''}
required={false}
@@ -104,19 +102,19 @@ class DynamicFieldSet extends React.Component {
onClick={() => this.remove(k)}
/>
) : null}
</FormItem>
</Form.Item>
));
return (
<Form onSubmit={this.handleSubmit}>
{formItems}
<FormItem {...formItemLayoutWithOutLabel}>
<Form.Item {...formItemLayoutWithOutLabel}>
<Button type="dashed" onClick={this.add} style={{ width: '60%' }}>
<Icon type="plus" /> Add field
</Button>
</FormItem>
<FormItem {...formItemLayoutWithOutLabel}>
</Form.Item>
<Form.Item {...formItemLayoutWithOutLabel}>
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -18,8 +18,6 @@ import {
Form, Input, Button, Checkbox,
} from 'antd';
const FormItem = Form.Item;
const formItemLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 8 },
@@ -55,7 +53,7 @@ class DynamicRule extends React.Component {
const { getFieldDecorator } = this.props.form;
return (
<div>
<FormItem {...formItemLayout} label="Name">
<Form.Item {...formItemLayout} label="Name">
{getFieldDecorator('username', {
rules: [{
required: true,
@@ -64,8 +62,8 @@ class DynamicRule extends React.Component {
})(
<Input placeholder="Please input your name" />
)}
</FormItem>
<FormItem {...formItemLayout} label="Nickname">
</Form.Item>
<Form.Item {...formItemLayout} label="Nickname">
{getFieldDecorator('nickname', {
rules: [{
required: this.state.checkNick,
@@ -74,20 +72,20 @@ class DynamicRule extends React.Component {
})(
<Input placeholder="Please input your nickname" />
)}
</FormItem>
<FormItem {...formTailLayout}>
</Form.Item>
<Form.Item {...formTailLayout}>
<Checkbox
checked={this.state.checkNick}
onChange={this.handleChange}
>
Nickname is required
</Checkbox>
</FormItem>
<FormItem {...formTailLayout}>
</Form.Item>
<Form.Item {...formTailLayout}>
<Button type="primary" onClick={this.check}>
Check
</Button>
</FormItem>
</Form.Item>
</div>
);
}

View File

@@ -18,8 +18,6 @@ import {
Button, Modal, Form, Input, Radio,
} from 'antd';
const FormItem = Form.Item;
const CollectionCreateForm = Form.create()(
// eslint-disable-next-line
class extends React.Component {
@@ -37,17 +35,17 @@ const CollectionCreateForm = Form.create()(
onOk={onCreate}
>
<Form layout="vertical">
<FormItem label="Title">
<Form.Item label="Title">
{getFieldDecorator('title', {
rules: [{ required: true, message: 'Please input the title of collection!' }],
})(
<Input />
)}
</FormItem>
<FormItem label="Description">
</Form.Item>
<Form.Item label="Description">
{getFieldDecorator('description')(<Input type="textarea" />)}
</FormItem>
<FormItem className="collection-create-form_last-form-item">
</Form.Item>
<Form.Item className="collection-create-form_last-form-item">
{getFieldDecorator('modifier', {
initialValue: 'public',
})(
@@ -56,7 +54,7 @@ const CollectionCreateForm = Form.create()(
<Radio value="private">Private</Radio>
</Radio.Group>
)}
</FormItem>
</Form.Item>
</Form>
</Modal>
);

View File

@@ -20,8 +20,6 @@ We can store form data into upper component or [Redux](https://github.com/reactj
````jsx
import { Form, Input } from 'antd';
const FormItem = Form.Item;
const CustomizedForm = Form.create({
onFieldsChange(props, changedFields) {
props.onChange(changedFields);
@@ -41,11 +39,11 @@ const CustomizedForm = Form.create({
const { getFieldDecorator } = props.form;
return (
<Form layout="inline">
<FormItem label="Username">
<Form.Item label="Username">
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Username is required!' }],
})(<Input />)}
</FormItem>
</Form.Item>
</Form>
);
});

View File

@@ -18,8 +18,6 @@ import {
Form, Icon, Input, Button,
} from 'antd';
const FormItem = Form.Item;
function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
@@ -49,7 +47,7 @@ class HorizontalLoginForm extends React.Component {
const passwordError = isFieldTouched('password') && getFieldError('password');
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<FormItem
<Form.Item
validateStatus={userNameError ? 'error' : ''}
help={userNameError || ''}
>
@@ -58,8 +56,8 @@ class HorizontalLoginForm extends React.Component {
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
validateStatus={passwordError ? 'error' : ''}
help={passwordError || ''}
>
@@ -68,8 +66,8 @@ class HorizontalLoginForm extends React.Component {
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
@@ -77,7 +75,7 @@ class HorizontalLoginForm extends React.Component {
>
Log in
</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -18,8 +18,6 @@ import {
Form, Input, Button, Radio,
} from 'antd';
const FormItem = Form.Item;
class FormLayoutDemo extends React.Component {
constructor() {
super();
@@ -44,7 +42,7 @@ class FormLayoutDemo extends React.Component {
return (
<div>
<Form layout={formLayout}>
<FormItem
<Form.Item
label="Form Layout"
{...formItemLayout}
>
@@ -53,22 +51,22 @@ class FormLayoutDemo extends React.Component {
<Radio.Button value="vertical">Vertical</Radio.Button>
<Radio.Button value="inline">Inline</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem
</Form.Item>
<Form.Item
label="Field A"
{...formItemLayout}
>
<Input placeholder="input placeholder" />
</FormItem>
<FormItem
</Form.Item>
<Form.Item
label="Field B"
{...formItemLayout}
>
<Input placeholder="input placeholder" />
</FormItem>
<FormItem {...buttonItemLayout}>
</Form.Item>
<Form.Item {...buttonItemLayout}>
<Button type="primary">Submit</Button>
</FormItem>
</Form.Item>
</Form>
</div>
);

View File

@@ -18,8 +18,6 @@ import {
Form, Icon, Input, Button, Checkbox,
} from 'antd';
const FormItem = Form.Item;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
@@ -34,21 +32,21 @@ class NormalLoginForm extends React.Component {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
<Form.Item>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
</Form.Item>
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
@@ -60,7 +58,7 @@ class NormalLoginForm extends React.Component {
Log in
</Button>
Or <a href="">register now!</a>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -18,8 +18,7 @@ import {
Form, Input, Tooltip, Icon, Cascader, Select, Row, Col, Checkbox, Button, AutoComplete,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const { Option } = Select;
const AutoCompleteOption = AutoComplete.Option;
const residences = [{
@@ -134,7 +133,7 @@ class RegistrationForm extends React.Component {
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
<Form.Item
{...formItemLayout}
label="E-mail"
>
@@ -147,8 +146,8 @@ class RegistrationForm extends React.Component {
})(
<Input />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Password"
>
@@ -161,8 +160,8 @@ class RegistrationForm extends React.Component {
})(
<Input type="password" />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Confirm Password"
>
@@ -175,8 +174,8 @@ class RegistrationForm extends React.Component {
})(
<Input type="password" onBlur={this.handleConfirmBlur} />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label={(
<span>
@@ -192,8 +191,8 @@ class RegistrationForm extends React.Component {
})(
<Input />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Habitual Residence"
>
@@ -203,8 +202,8 @@ class RegistrationForm extends React.Component {
})(
<Cascader options={residences} />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Phone Number"
>
@@ -213,8 +212,8 @@ class RegistrationForm extends React.Component {
})(
<Input addonBefore={prefixSelector} style={{ width: '100%' }} />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Website"
>
@@ -229,8 +228,8 @@ class RegistrationForm extends React.Component {
<Input />
</AutoComplete>
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Captcha"
extra="We must make sure that your are a human."
@@ -247,17 +246,17 @@ class RegistrationForm extends React.Component {
<Button>Get captcha</Button>
</Col>
</Row>
</FormItem>
<FormItem {...tailFormItemLayout}>
</Form.Item>
<Form.Item {...tailFormItemLayout}>
{getFieldDecorator('agreement', {
valuePropName: 'checked',
})(
<Checkbox>I have read the <a href="">agreement</a></Checkbox>
)}
</FormItem>
<FormItem {...tailFormItemLayout}>
</Form.Item>
<Form.Item {...tailFormItemLayout}>
<Button type="primary" htmlType="submit">Register</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -18,7 +18,6 @@ import {
Form, DatePicker, TimePicker, Button,
} from 'antd';
const FormItem = Form.Item;
const { MonthPicker, RangePicker } = DatePicker;
class TimeRelatedForm extends React.Component {
@@ -69,62 +68,62 @@ class TimeRelatedForm extends React.Component {
};
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
<Form.Item
{...formItemLayout}
label="DatePicker"
>
{getFieldDecorator('date-picker', config)(
<DatePicker />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="DatePicker[showTime]"
>
{getFieldDecorator('date-time-picker', config)(
<DatePicker showTime format="YYYY-MM-DD HH:mm:ss" />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="MonthPicker"
>
{getFieldDecorator('month-picker', config)(
<MonthPicker />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="RangePicker"
>
{getFieldDecorator('range-picker', rangeConfig)(
<RangePicker />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="RangePicker[showTime]"
>
{getFieldDecorator('range-time-picker', rangeConfig)(
<RangePicker showTime format="YYYY-MM-DD HH:mm:ss" />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="TimePicker"
>
{getFieldDecorator('time-picker', config)(
<TimePicker />
)}
</FormItem>
<FormItem
</Form.Item>
<Form.Item
wrapperCol={{
xs: { span: 24, offset: 0 },
sm: { span: 16, offset: 8 },
}}
>
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -16,13 +16,11 @@ Demostration for validataion configuration for form controls which are not show
````jsx
import {
Form, Select, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate,
Slider, Button, Upload, Icon, Rate, Checkbox,
Row, Col,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;
const { Option } = Select;
class Demo extends React.Component {
handleSubmit = (e) => {
@@ -50,13 +48,13 @@ class Demo extends React.Component {
};
return (
<Form onSubmit={this.handleSubmit}>
<FormItem
<Form.Item
{...formItemLayout}
label="Plain Text"
>
<span className="ant-form-text">China</span>
</FormItem>
<FormItem
</Form.Item>
<Form.Item
{...formItemLayout}
label="Select"
hasFeedback
@@ -71,9 +69,9 @@ class Demo extends React.Component {
<Option value="usa">U.S.A</Option>
</Select>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Select[multiple]"
>
@@ -88,9 +86,9 @@ class Demo extends React.Component {
<Option value="blue">Blue</Option>
</Select>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="InputNumber"
>
@@ -98,18 +96,18 @@ class Demo extends React.Component {
<InputNumber min={1} max={10} />
)}
<span className="ant-form-text"> machines</span>
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Switch"
>
{getFieldDecorator('switch', { valuePropName: 'checked' })(
<Switch />
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Slider"
>
@@ -119,35 +117,54 @@ class Demo extends React.Component {
}}
/>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Radio.Group"
>
{getFieldDecorator('radio-group')(
<RadioGroup>
<Radio.Group>
<Radio value="a">item 1</Radio>
<Radio value="b">item 2</Radio>
<Radio value="c">item 3</Radio>
</RadioGroup>
</Radio.Group>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Radio.Button"
>
{getFieldDecorator('radio-button')(
<RadioGroup>
<RadioButton value="a">item 1</RadioButton>
<RadioButton value="b">item 2</RadioButton>
<RadioButton value="c">item 3</RadioButton>
</RadioGroup>
<Radio.Group>
<Radio.Button value="a">item 1</Radio.Button>
<Radio.Button value="b">item 2</Radio.Button>
<Radio.Button value="c">item 3</Radio.Button>
</Radio.Group>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Checkbox.Group"
>
{getFieldDecorator("checkbox-group", {
initialValue: ["A", "B"],
})(
<Checkbox.Group style={{ width: "100%" }}>
<Row>
<Col span={8}><Checkbox value="A">A</Checkbox></Col>
<Col span={8}><Checkbox disabled value="B">B</Checkbox></Col>
<Col span={8}><Checkbox value="C">C</Checkbox></Col>
<Col span={8}><Checkbox value="D">D</Checkbox></Col>
<Col span={8}><Checkbox value="E">E</Checkbox></Col>
</Row>
</Checkbox.Group>
)}
</Form.Item>
<Form.Item
{...formItemLayout}
label="Rate"
>
@@ -156,9 +173,9 @@ class Demo extends React.Component {
})(
<Rate />
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Upload"
extra="longgggggggggggggggggggggggggggggggggg"
@@ -173,9 +190,9 @@ class Demo extends React.Component {
</Button>
</Upload>
)}
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Dragger"
>
@@ -193,13 +210,13 @@ class Demo extends React.Component {
</Upload.Dragger>
)}
</div>
</FormItem>
</Form.Item>
<FormItem
<Form.Item
wrapperCol={{ span: 12, offset: 6 }}
>
<Button type="primary" htmlType="submit">Submit</Button>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -23,11 +23,10 @@ We provide properties like `validateStatus` `help` `hasFeedback` to customize yo
````jsx
import {
Form, Input, DatePicker, Col, TimePicker, Select, Cascader, InputNumber,
Form, Input, DatePicker, TimePicker, Select, Cascader, InputNumber,
} from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
const { Option } = Select;
const formItemLayout = {
labelCol: {
@@ -42,24 +41,24 @@ const formItemLayout = {
ReactDOM.render(
<Form>
<FormItem
<Form.Item
{...formItemLayout}
label="Fail"
validateStatus="error"
help="Should be combination of numbers & alphabets"
>
<Input placeholder="unavailable choice" id="error" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Warning"
validateStatus="warning"
>
<Input placeholder="Warning" id="warning" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Validating"
hasFeedback
@@ -67,27 +66,27 @@ ReactDOM.render(
help="The information is being validated..."
>
<Input placeholder="I'm the content is being validated" id="validating" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Success"
hasFeedback
validateStatus="success"
>
<Input placeholder="I'm the content" id="success" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Warning"
hasFeedback
validateStatus="warning"
>
<Input placeholder="Warning" id="warning" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Fail"
hasFeedback
@@ -95,27 +94,27 @@ ReactDOM.render(
help="Should be combination of numbers & alphabets"
>
<Input placeholder="unavailable choice" id="error" />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Success"
hasFeedback
validateStatus="success"
>
<DatePicker style={{ width: '100%' }} />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Warning"
hasFeedback
validateStatus="warning"
>
<TimePicker style={{ width: '100%' }} />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Error"
hasFeedback
@@ -126,9 +125,9 @@ ReactDOM.render(
<Option value="2">Option 2</Option>
<Option value="3">Option 3</Option>
</Select>
</FormItem>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Validating"
hasFeedback
@@ -136,37 +135,36 @@ ReactDOM.render(
help="The information is being validated..."
>
<Cascader defaultValue={['1']} options={[]} />
</FormItem>
</Form.Item>
<FormItem
<Form.Item
label="inline"
{...formItemLayout}
style={{ marginBottom: 0 }}
>
<Col span={11}>
<FormItem validateStatus="error" help="Please select the correct date">
<DatePicker />
</FormItem>
</Col>
<Col span={2}>
<span style={{ display: 'inline-block', width: '100%', textAlign: 'center' }}>
-
</span>
</Col>
<Col span={11}>
<FormItem>
<DatePicker />
</FormItem>
</Col>
</FormItem>
<Form.Item
validateStatus="error"
help="Please select the correct date"
style={{ display: 'inline-block', width: 'calc(50% - 12px)' }}
>
<DatePicker />
</Form.Item>
<span style={{ display: 'inline-block', width: '24px', textAlign: 'center' }}>
-
</span>
<Form.Item style={{ display: 'inline-block', width: 'calc(50% - 12px)' }}>
<DatePicker />
</Form.Item>
</Form.Item>
<FormItem
<Form.Item
{...formItemLayout}
label="Success"
hasFeedback
validateStatus="success"
>
<InputNumber style={{ width: '100%' }} />
</FormItem>
</Form.Item>
</Form>,
mountNode
);

View File

@@ -16,8 +16,6 @@ title:
````jsx
import { Form, InputNumber } from 'antd';
const FormItem = Form.Item;
function validatePrimeNumber(number) {
if (number === 11) {
return {
@@ -56,7 +54,7 @@ class RawForm extends React.Component {
const tips = 'A prime is a natural number greater than 1 that has no positive divisors other than 1 and itself.';
return (
<Form>
<FormItem
<Form.Item
{...formItemLayout}
label="Prime between 8 & 12"
validateStatus={number.validateStatus}
@@ -68,7 +66,7 @@ class RawForm extends React.Component {
value={number.value}
onChange={this.handleNumberChange}
/>
</FormItem>
</Form.Item>
</Form>
);
}

View File

@@ -0,0 +1,99 @@
# 表单实现原理
---
antd 中的 [Form](https://github.com/ant-design/ant-design/blob/master/components/form/index.zh-CN.md) 组件基于 [rc-form](https://github.com/react-component/form) 实现。本文第一部分将介绍 rc-form 库;第二部分再介绍 antd 中的 Form 组件;第三部分将结合表单组件的使用,回顾前两部分的内容。
---
### rc-form
常规收集表单数据并作校验,只需以 store 实时记录表单数据,校验后重绘表单。这样的思路以业务代码为例,就是,以数据模型 model 集成数据处理操作,再通过 setState 将 model 中的实时数据注入组件中,并驱动组件重绘(除了 setState 方法以外,也可以使用 forceUpdate 方法重绘组件,并在 render 阶段重新访问 model 中的实时数据)。
#### FieldsStore
在 rc-form 中,上述数据模型的具体实现为 FieldsStore 类。如前所述FieldsStore 实例与视图层的交互逻辑为,在用户行为驱动字段变更时,即时存储字段的值及校验信息(本文以校验信息代指校验文案和校验状态),继而调用表单组件实例的 forceUpdate 方法强制重绘;在绘制过程中,再从 FieldsStore 实例读取实时数据和校验信息。
建模方面FieldsStore 实例以 fields 属性存储表单的实时数据,即由用户行为或开发者显式更新的数据。本文把实时数据已存入 fields 中的字段称为已收集字段;反之,称为未收集字段。以下是 fields\[name\] 中成员属性的意义name 为字段名,下同)。
- value 字段的值。
- errors 校验文案,数组形式。
- validating 校验状态。
- dirty 脏值标识。真值时意味着字段的值已作变更,但未作校验。
- touched 用户行为标识。通常情况下,真值时意味着用户行为已促使字段的值发生了变更。
FieldsStore 实例又以 fieldsMeta 属性存储字段的元数据。元数据作为配置项,通常是指定后不再变更的数据,用于操控表单数据转换等行为。元数据通过下文中 getFieldProps, getFieldDecorator 方法的次参 fieldOption 配置。以下是 fieldsMeta\[name\] 中部分成员属性的意义(如不作特殊说明,该成员属性即 fieldOption 中的同名属性)。
- validate 校验规则和触发事件,用于约定在何种事件下以何种方式校验字段的值,\[{ rules, trigger }\] 形式。通过fieldOption.validate, fieldOption.rules, fieldOption.validateTrigger 配置。
- hidden 设置为 true 时validateFields 将不会校验该字段需要开发者手动校验并且getFieldsValue, getFieldsError 等方法也将无法获取该字段的数据及校验信息等实时数据。适用场景如勾选协议复选框,当其已勾选时,提交按钮才可点击,该复选框的值不会出现在全量表单数据中。本文假定配置了 hidden 为 true 的字段为虚拟隐藏项。
- getValueFromEvent(event) 用于从 event 对象中获取字段的值,适用场景如处理 input, radio 等原生组件。特别的,当字段组件输出的首参不是 event 对象时getValueFromEvent 将对首参进行数据转化,以满足特定的场景,比如由开发者指定具体首参内容的自定义字段组件。
- initialValue 字段的初始值。当字段的值未作收集时,初始值用于作为字段的值。
- valuePropName 约定字段的值以何种 props 属性注入字段组件中,适用场景如将字段的值以 props.checked 属性注入 radio 组件中。注入字段组件的值数据 props 由 getFieldValuePropValue 方法生成,下同。
- getValueProps(value) 用于转化字段的值,输出 props 以注入字段组件中。适用场景如当 get, post 请求的数据结构不同时,且字段组件以 post 请求的数据结构输出字段的值、以 get 请求的数据结构接受字段的值,那就可以使用 getValueProps 将 post 请求的数据结构转换为 get 请求的数据结构。
- normalize(newValue, oldValue, values) 用于转化存入 FieldsStore 实例的字段的值。使用案例如[全选按钮](https://codepen.io/afc163/pen/JJVXzG?editors=001)。
在此基础上FieldsStore 提供了一些便捷的访问器操作。需要说明的是rc-form 借助 [lodash](https://github.com/lodash/lodash),支持以嵌套结构定义字段名,即使用 '.', '\[|\]' 作为分割符,如 'a.b' 意味着 a 对象下的 b 属性;'c\[0\]' 意味着 c 数组的首项。本文约定匹配字段指所有以指定字符串起始或等值的字段列表。为此FieldsStore 提供 isValidNestedFieldName 用于校验字段名不能作为表单中另一个字段名的成员flattenRegisteredFields 用于传参数据作扁平化处理getValidFieldsFullName 用于获取匹配字段列表但不包含虚拟隐藏项。除此以外FieldsStore 提供了对实时数据和元数据的读取操作,特别的,部分 api 可用于获取匹配字段的实时数据,参见[文档](https://ant.design/components/form-cn/)。
#### BaseForm
与业务 model 不同的是FieldsStore 仅作为表单实时数据和元数据的存储器,校验数据等方法由 BaseForm 提供。BaseForm既作为 HOC 容器,能为开发者自定义表单组件(下文用自定义表单替代)注入表单操作函数集,通常是 props.form又用于装饰字段组件或其 props以收集字段的元数据、通过绑定函数收集或校验字段的实时数据。因此可以部分认为BaseForm 即 FieldsStore 和视图层桥接的控制器。其机制为:
首先,通过 createBaseForm(option, mixins) 创建装饰函数。装饰函数可以为自定义表单包裹上 BaseForm 容器。参数 option 用于配置表单层面的绑定函数、校验文案以及部分字段组件的 props 属性名mixins 将作为实例方法混入 BaseForm特别的createDOMForm 函数即通过这一机制混入了 validateFieldsAndScroll 方法。
其次,在 BaseForm 的 getInitialState 阶段,将创建 FieldsStore 实例,并初始化 clearedFieldMetaCache 等缓存。这些缓存的意义包含缓存字段组件实例,缓存内置的 ref 引用及绑定函数缓存渲染状态等。特别的clearedFieldMetaCache 用于在 ref 引用函数执行时缓存字段的实时数据和元数据,以便于在字段重绘过程中的第两次执行 ref 引用时,恢复 FieldsStore 存储的实时数据和元数据(参见[源码](https://github.com/react-component/form/blob/master/src/createBaseForm.js)中的 saveRef 方法)。不然,元数据的丢失将导致数据校验无法正常工作。
其次,执行 render 方法,将表单操作函数集通过 props 注入自定义表单。介于此,在自定义表单中,开发者可以获取到表单的实时数据,或者更新表单数据,或者校验表单,以完成特定的处理逻辑。
其次,由 BaseForm 提供的 getFieldProps 或 getFieldDecorator 实例方法完成字段组件的渲染。以下是 getFieldProps, getFieldDecorator 的意义。
- getFieldProps(name, fieldOption) 用于为 FieldsStore 实例收集字段的元数据,如经转化后的校验规则等;指定字段组件的 ref 引用函数;为字段组件绑定 onCollect, onCollectValidate 实例方法,以在指定事件触发时收集或校验字段的值;最终将输出注入字段组件的 props包含全量的元数据和实时数据、以及字段的值。
- getFieldDecorator(name, fieldOption) 基于 getFieldProps 方法,直接装饰字段组件,这样就可以操控添加在字段组件上的 ref 引用函数及 props.onChange 等绑定函数。
其次,当用户行为促使字段的值发生变更时,将执行 BaseForm 实例的 onCollect, onCollectValidate 方法以收集或校验该字段的实时数据并重绘表单。其中rc-form 中的数据校验通过 [async-validate](https://github.com/yiminghe/async-validator) 库实现,具体实现为 BaseForm 实例的 validateFieldsInternal 方法。校验字段时,默认将沿用上一次的校验信息;当设置 force 为 true 时,将强制重新校验。
详细的工作流程参见下方的时序图:
<img class="preview-img no-padding" src="http://xzfyu.com/2018/11/04/ant%20design/antd-Form%20%E7%BB%84%E4%BB%B6/rc-form%E6%97%B6%E5%BA%8F%E5%9B%BE.png">
### Form 表单
#### 表单
Form 组件本身并不承载逻辑,而是通过 props.className, props.prefixCls, props.layout, props.hideRequiredMark, props.onSubmit 设定注入 form 原生节点的样式类及绑定函数以影响表单内部节点渲染时的样式。同时Form 组件将为子组件传入 context.vertical 以区分是水平布局,还是垂直布局。
#### 表单域
FormItem 组件用于设定表单项的布局。如同受控组件和非受控组件FormItem 组件提供两种使用方式:其一,当未设定校验信息相关的 props 属性时FormItem 组件将自动根据内部字段组件实例的状况渲染校验文案及校验状态;其二,当设定校验信息相关的 props 属性时FormItem 组件将根据开发者传入的 props 渲染校验文案及校验状态。在第一种使用方式下FormItem 组件只可以包含一个字段组件在第二种使用方式下FormItem 组件中可以包含多个字段组件,布局也更为灵活。这里说的相关 props 属性包含:校验文案 help, 校验状态 validateStatus用于绘制反馈图标, 必填标识 required, 字段名 id影响点击 label 时聚焦哪个字段元素)。
那么FormItem 又是怎样自动收集字段组件的校验数据呢?因为在 BaseForm 组件提供的 getFieldProp 方法,字段名、元数据和实时数据都将作为特殊的 props 属性传入到字段组件中,所以作为字段组件容器的 FormItem就可以通过这些特殊的 props 属性判断子组件实例是不是一个字段组件实例。当其为字段组件实例时,进一步收集实时的校验信息,从校验规则中获取是否必填标识,以完成表单域的渲染。
此外FormItem 可以使用 props.labelCol, props.wrapperCol 属性栅格化布局标签组件和字段组件,其实现借助于 antd 提供的 [Row, Col 组件](https://ant.design/components/grid-cn/)。当点击标签 label 时FormItem 提供的绑定函数也能自动为字段组件获得焦点。这里不再多加介绍。
### 使用与回顾
#### 创建 HOC 容器
结合上文antd 使用 Form.create(options)(CustomizedForm) 形式为用户自定义表单包裹上 BaseForm 高阶组件,以此植入 props.form 表单操作函数集。
高阶组件影响对自定义表单设置 ref 引用。默认可使用 BaseForm 实例的 refs.wrappedComponent 属性访问 CustomizedForm 实例,其次也可以通过 props.wrappedComponentRef 为 CustomizedForm 实例配置 ref 引用(参考案例 —— 弹出层中的新建表单)。当自定义表单可切换或者需要对外交互时,设置 ref 引用通常是不可避免的。
高阶组件影响 props 传递。antd 既支持使用 options.mapPropsToFields 将 BaseForm 实例获得的 props 转化成表单的实时数据(需要结合 Form.createFormField 方法),又支持在 CustomizedForm 实例中调用 props.form.setFields 方法更新实时数据。当 options.mapPropsToFields, options.onFieldsChange 方法结合使用时,可用于完成自定义表单和上层组件、或者 view 层和 store 层的交互(参考案例 —— 表单数据存储于上层组件)。
一般认为,对整张表单的控制,需要借助于 Form.create 方法的首参 options 配置实现。
#### 操作函数集
通常情况下CustomizedForm 实例可通过 props.form 获取到表单操作函数集。当然,开发者也可以通过 options.formPropName 指定操作函数集的 props 属性名。
操作函数集包含 getFieldProps, getFieldDecorator, getFieldInstance, setFields, setFieldsValue, setFieldsInitialValue, resetFields, validateFields, getFieldsValue, getFieldValue, getFieldsError, getFieldError, isFieldsValidating, isFieldValidating, isFieldsTouched, isFieldTouched 方法,即用于装饰字段组件(或其 props、获取字段组件实例、设置或获取实时数据、重置或校验字段。
如上文所说getFieldProps, getFieldDecorator 方法即用于自动为字段组件绑定监听函数,这样就可以在指定事件触发时,收集和校验字段的值。同时,可以通过这两个方法将视图中未加显示的字段存入 FieldsStore 中(这时,可以将 FieldsStore 视为一个内置于表单组件的状态管理器)。比如在包含其他选项的单选框场景中,就可以使用 getFieldDecorator 创建虚拟字段,通过绑定函数将单选框和输入框的值赋值到该虚拟字段中,并使用该虚拟字段的校验信息绘制 FormItem。参考案例 —— 动态增减表单项。
上述单选框场景,也可以使用自定义字段组件 CustomizedField 实现。参考案例 —— 自定义字段组件。当使用自定义字段组件时,通过 getFieldInstance 获取 CustomizedField 的实例可能是必不可少的,这样可以把略显复杂的数据校验方法集成在 CustomizedField 中。此外,在字段组件中,既可以通过 props.value 属性获得字段的值,也可以通过 props\['data-__meta'\], props\['data-__field'\] 获得字段的全量元数据和实时数据。
在 CustomizedForm 中,使用 getFieldValue 可以取得字段的实时更新值,这样就能根据指定字段的值控制另一个字段的显隐。此外,通过在字段组件的 onChange 绑定函数中调用 setFieldValue也能对另一个字段组件加以赋值这样就可以实现表单的联动效果参考案例 —— 表单联动。若在字段组件的 onChange 绑定函数中调用 validateFields 方法,就可以实现关联字段的校验,比如表单中存在设置最大最小值的两个输入框,参考案例 —— 动态校验规则。
使用 getFieldError, isFieldValidating 获取到的校验信息可用于直接绘制 FormItem这样就能更加细微地操控 FormItem 下字段组件的布局,比如放置多个字段组件。当然,对于多个字段组件公用校验信息的场景,也可以使用包含多个字段的 CustomizedField 实现。isFieldTouched 可判断用户是否对字段组件有触发数据收集和校验的行为,参考案例 —— 水平登录栏。介于 BaseForm 默认在 onChange 事件中收集并校验字段的值,在这种情形下,也可以通过 isFieldTouched 判断字段的值是否已作更新,而不需要在 CustomizedForm 中设置特殊的更新标识。
#### 其他
当不使用 Form.create 为字段组件自动绑定校验方法时,可以使用 Form, FormItem 组件设定表单的布局、校验信息的绘制,参考案例 —— 表单布局、自行处理表单数据、自定义校验。

View File

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

View File

@@ -74,12 +74,6 @@ input[type='checkbox'] {
margin-bottom: @form-item-margin-bottom;
vertical-align: top;
// nested FormItem
&-control > &:last-child,
& [class^='@{ant-prefix}-col-'] > &:only-child {
margin-bottom: -@form-item-margin-bottom;
}
&-control {
line-height: @form-component-max-height - 0.0001px; // https://github.com/ant-design/ant-design/issues/8220
position: relative;
@@ -455,6 +449,9 @@ form {
.@{ant-prefix}-select {
&-selection {
border-color: @warning-color;
&:hover {
border-color: @warning-color;
}
}
&-open .@{ant-prefix}-select-selection,
&-focused .@{ant-prefix}-select-selection {
@@ -501,6 +498,9 @@ form {
.@{ant-prefix}-select {
&-selection {
border-color: @error-color;
&:hover {
border-color: @error-color;
}
}
&-open .@{ant-prefix}-select-selection,
&-focused .@{ant-prefix}-select-selection {

View File

@@ -25,6 +25,24 @@
.active(@border-color);
}
// Input prefix
.@{ant-prefix}-input-affix-wrapper {
.@{ant-prefix}-input {
&,
&:hover {
border-color: @border-color;
}
&:focus {
.active(@border-color);
}
}
&:hover .@{ant-prefix}-input:not(.@{ant-prefix}-input-disabled) {
border-color: @border-color;
}
}
.@{ant-prefix}-input-prefix {
color: @text-color;
}

View File

@@ -102,7 +102,7 @@ export default class Row extends React.Component<RowProps, RowState> {
getGutter(): number | undefined {
const { gutter } = this.props;
if (typeof gutter === 'object') {
for (let i = 0; i <= responsiveArray.length; i++) {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
if (this.state.screens[breakpoint] && gutter[breakpoint] !== undefined) {
return gutter[breakpoint];

View File

@@ -138,7 +138,7 @@ const Icon: IconComponent<IconProps> = props => {
);
}
computedType = withThemeSuffix(
removeTypeTheme(alias(type)),
removeTypeTheme(alias(computedType)),
dangerousTheme || theme || defaultTheme,
);
innerNode = (

View File

@@ -267,28 +267,26 @@
&-wrap,
> .@{inputClass} {
&:not(:first-child):not(:last-child) {
border-right-width: 1px;
border-right-color: transparent;
border-right-width: @border-width-base;
&:hover {
.hover();
z-index: 1;
}
&:focus {
.active();
z-index: 1;
}
}
}
& > * {
border-radius: 0;
border-right-width: 0;
vertical-align: top; // https://github.com/ant-design/ant-design-pro/issues/139
float: none;
display: inline-block;
}
// https://github.com/ant-design/ant-design/issues/11863
& > span:not(:last-child) > .@{inputClass} {
border-right-width: 0;
& > *:not(:last-child) {
border-right-width: @border-width-base;
margin-right: -@border-width-base;
}
// Undo float for .ant-input-group .ant-input
@@ -304,13 +302,12 @@
& > .@{ant-prefix}-mention-wrapper .@{ant-prefix}-mention-editor,
& > .@{ant-prefix}-time-picker .@{ant-prefix}-time-picker-input {
border-radius: 0;
border-right-width: 1px;
border-right-color: transparent;
border-right-width: @border-width-base;
&:hover {
.hover();
z-index: 1;
}
&:focus {
.active();
z-index: 1;
}
}
@@ -335,17 +332,7 @@
& > .@{ant-prefix}-time-picker:last-child .@{ant-prefix}-time-picker-input {
border-top-right-radius: @border-radius-base;
border-bottom-right-radius: @border-radius-base;
border-right-width: 1px;
border-right-color: @input-border-color;
&:hover {
.hover();
}
&:focus {
.active();
.@{ant-prefix}-cascader-input {
.active();
}
}
border-right-width: @border-width-base;
}
// https://github.com/ant-design/ant-design/issues/12493
@@ -365,13 +352,14 @@
}
.@{inputClass} {
position: static;
position: relative;
}
.@{inputClass}-prefix,
.@{inputClass}-suffix {
position: absolute;
top: 50%;
z-index: 2;
transform: translateY(-50%);
line-height: 0;
color: @input-color;

View File

@@ -90,7 +90,7 @@ The sidebar.
| breakpoint | [breakpoints](/components/grid#api) of the responsive layout | Enum { 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' } | - |
| className | container className | string | - |
| collapsed | to set the current status | boolean | - |
| collapsedWidth | width of the collapsed sidebar, by setting to `0` a special trigger will appear | number | 64 |
| collapsedWidth | width of the collapsed sidebar, by setting to `0` a special trigger will appear | number | 80 |
| collapsible | whether can be collapsed | boolean | false |
| defaultCollapsed | to set the initial status | boolean | false |
| reverseArrow | reverse direction of arrow, for a sider that expands from the right | boolean | false |

View File

@@ -91,7 +91,7 @@ title: Layout
| breakpoint | 触发响应式布局的[断点](/components/grid#api) | Enum { 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' } | - |
| className | 容器 className | string | - |
| collapsed | 当前收起状态 | boolean | - |
| collapsedWidth | 收缩宽度,设置为 0 会出现特殊 trigger | number | 64 |
| collapsedWidth | 收缩宽度,设置为 0 会出现特殊 trigger | number | 80 |
| collapsible | 是否可收起 | boolean | false |
| defaultCollapsed | 是否默认收起 | boolean | false |
| reverseArrow | 翻转折叠提示箭头的方向,当 Sider 在右边时可以使用 | boolean | false |

View File

@@ -35,7 +35,11 @@ class Basic extends React.Component<BasicProps, any> {
}
}
class BasicLayout extends React.Component<BasicProps, any> {
interface BasicLayoutState {
siders: string[];
}
class BasicLayout extends React.Component<BasicProps, BasicLayoutState> {
static childContextTypes = {
siderHook: PropTypes.object,
};
@@ -45,14 +49,14 @@ class BasicLayout extends React.Component<BasicProps, any> {
return {
siderHook: {
addSider: (id: string) => {
this.setState({
siders: [...this.state.siders, id],
});
this.setState(state => ({
siders: [...state.siders, id],
}));
},
removeSider: (id: string) => {
this.setState({
siders: this.state.siders.filter(currentId => currentId !== id),
});
this.setState(state => ({
siders: state.siders.filter(currentId => currentId !== id),
}));
},
},
};

View File

@@ -8,7 +8,9 @@ export interface LocaleReceiverProps {
children: (locale: object, localeCode?: string) => React.ReactElement<any>;
}
interface LocaleInterface { [key: string]: any }
interface LocaleInterface {
[key: string]: any;
}
export interface LocaleReceiverContext {
antLocale?: LocaleInterface;
@@ -27,7 +29,8 @@ export default class LocaleReceiver extends React.Component<LocaleReceiverProps>
getLocale() {
const { componentName, defaultLocale } = this.props;
const locale: object | Function = defaultLocale || (defaultLocaleData as LocaleInterface)[componentName || 'global'];
const locale: object | Function =
defaultLocale || (defaultLocaleData as LocaleInterface)[componentName || 'global'];
const { antLocale } = this.context;
const localeFromContext = componentName && antLocale ? antLocale[componentName] : {};
return {

View File

@@ -20,7 +20,7 @@ export interface Locale {
export interface LocaleProviderProps {
locale: Locale;
children?: React.ReactElement<any>;
children?: React.ReactNode;
}
function setMomentLocale(locale: Locale) {

View File

@@ -47,7 +47,7 @@ When need to mention someone or something.
| value | core state of mention | ContentState | null |
| onBlur | Callback function called when mention component blur | function(e) | null |
| onChange | Callback function called when content of input changes | function(contentState: ContentState) | null |
| onFocus | Callback function called when mention component get focus | functione) | null |
| onFocus | Callback function called when mention component get focus | function | null |
| onSearchChange | Callback function called when search content changes | function(value:string, trigger: string) | \[] |
| onSelect | Callback function called when select from suggestions | function(suggestion: string, data?: any) | null |

View File

@@ -103,6 +103,7 @@ class Mention extends React.Component<MentionProps, MentionState> {
this.props.onFocus(ev);
}
};
onBlur = (ev: React.FocusEvent<HTMLElement>) => {
this.setState({
focus: false,
@@ -111,12 +112,15 @@ class Mention extends React.Component<MentionProps, MentionState> {
this.props.onBlur(ev);
}
};
focus = () => {
this.mentionEle._editor.focusEditor();
};
mentionRef = (ele: any) => {
this.mentionEle = ele;
};
render() {
const { className = '', prefixCls, loading, placement } = this.props;
const { suggestions, focus } = this.state;
@@ -124,7 +128,6 @@ class Mention extends React.Component<MentionProps, MentionState> {
[`${prefixCls}-active`]: focus,
[`${prefixCls}-placement-top`]: placement === 'top',
});
const notFoundContent = loading ? <Icon type="loading" /> : this.props.notFoundContent;
return (

View File

@@ -21,12 +21,15 @@ class MenuItem extends React.Component<MenuItemProps, any> {
static isMenuItem = 1;
context: any;
private menuItem: any;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {
this.menuItem.onKeyDown(e);
};
saveMenuItem = (menuItem: any) => {
this.menuItem = menuItem;
};
render() {
const { inlineCollapsed } = this.context;
const { level, children, rootPrefixCls } = this.props;

View File

@@ -11,12 +11,15 @@ class SubMenu extends React.Component<any, any> {
static isSubMenu = 1;
context: any;
private subMenu: any;
onKeyDown = (e: React.MouseEvent<HTMLElement>) => {
this.subMenu.onKeyDown(e);
};
saveSubMenu = (subMenu: any) => {
this.subMenu = subMenu;
};
render() {
const { rootPrefixCls, className } = this.props;
const theme = this.context.antdMenuTheme;

View File

@@ -78,9 +78,11 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
siderCollapsed: PropTypes.bool,
collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};
context: any;
switchingModeFromInline: boolean;
inlineOpenKeys: string[] = [];
constructor(props: MenuProps) {
super(props);
@@ -106,20 +108,24 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
openKeys: openKeys || [],
};
}
getChildContext() {
return {
inlineCollapsed: this.getInlineCollapsed(),
antdMenuTheme: this.props.theme,
};
}
componentWillReceiveProps(nextProps: MenuProps, nextContext: SiderContext) {
if (this.props.mode === 'inline' && nextProps.mode !== 'inline') {
this.switchingModeFromInline = true;
}
if ('openKeys' in nextProps) {
this.setState({ openKeys: nextProps.openKeys! });
return;
}
if (
(nextProps.inlineCollapsed && !this.props.inlineCollapsed) ||
(nextContext.siderCollapsed && !this.context.siderCollapsed)
@@ -128,6 +134,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
this.inlineOpenKeys = this.state.openKeys;
this.setState({ openKeys: [] });
}
if (
(!nextProps.inlineCollapsed && this.props.inlineCollapsed) ||
(!nextContext.siderCollapsed && this.context.siderCollapsed)
@@ -136,12 +143,14 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
this.inlineOpenKeys = [];
}
}
restoreModeVerticalFromInline() {
if (this.switchingModeFromInline) {
this.switchingModeFromInline = false;
this.setState({});
}
}
// Restore vertical mode when menu is collapsed responsively when mounted
// https://github.com/ant-design/ant-design/issues/13104
// TODO: not a perfect solution, looking a new way to avoid setting switchingModeFromInline in this situation
@@ -152,6 +161,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
onMouseEnter(e);
}
};
handleTransitionEnd = (e: TransitionEvent) => {
// when inlineCollapsed menu width animation finished
// https://github.com/ant-design/ant-design/issues/12864
@@ -164,6 +174,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
this.restoreModeVerticalFromInline();
}
};
handleClick = (e: ClickParam) => {
this.handleOpenChange([]);
@@ -172,6 +183,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
onClick(e);
}
};
handleOpenChange = (openKeys: string[]) => {
this.setOpenKeys(openKeys);
@@ -180,11 +192,13 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
onOpenChange(openKeys);
}
};
setOpenKeys(openKeys: string[]) {
if (!('openKeys' in this.props)) {
this.setState({ openKeys });
}
}
getRealMenuMode() {
const inlineCollapsed = this.getInlineCollapsed();
if (this.switchingModeFromInline && inlineCollapsed) {
@@ -193,6 +207,7 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
const { mode } = this.props;
return inlineCollapsed ? 'vertical' : mode;
}
getInlineCollapsed() {
const { inlineCollapsed } = this.props;
if (this.context.siderCollapsed !== undefined) {
@@ -200,30 +215,24 @@ export default class Menu extends React.Component<MenuProps, MenuState> {
}
return inlineCollapsed;
}
getMenuOpenAnimation(menuMode: MenuMode) {
const { openAnimation, openTransitionName } = this.props;
let menuOpenAnimation = openAnimation || openTransitionName;
if (openAnimation === undefined && openTransitionName === undefined) {
switch (menuMode) {
case 'horizontal':
menuOpenAnimation = 'slide-up';
break;
case 'vertical':
case 'vertical-left':
case 'vertical-right':
// When mode switch from inline
// submenu should hide without animation
if (this.switchingModeFromInline) {
menuOpenAnimation = '';
this.switchingModeFromInline = false;
} else {
menuOpenAnimation = 'zoom-big';
}
break;
case 'inline':
menuOpenAnimation = animation;
break;
default:
if (menuMode === 'horizontal') {
menuOpenAnimation = 'slide-up';
} else if (menuMode === 'inline') {
menuOpenAnimation = animation;
} else {
// When mode switch from inline
// submenu should hide without animation
if (this.switchingModeFromInline) {
menuOpenAnimation = '';
this.switchingModeFromInline = false;
} else {
menuOpenAnimation = 'zoom-big';
}
}
}
return menuOpenAnimation;

View File

@@ -136,7 +136,7 @@ export default function confirm(config: ModalFuncProps) {
if (unmountResult && div.parentNode) {
div.parentNode.removeChild(div);
}
const triggerCancel = args && args.length && args.some(param => param && param.triggerCancel);
const triggerCancel = args.some(param => param && param.triggerCancel);
if (config.onCancel && triggerCancel) {
config.onCancel(...args);
}

View File

@@ -100,10 +100,6 @@ export default class Pagination extends React.Component<PaginationProps, {}> {
};
render() {
return (
<LocaleReceiver componentName="Pagination">
{this.renderPagination}
</LocaleReceiver>
);
return <LocaleReceiver componentName="Pagination">{this.renderPagination}</LocaleReceiver>;
}
}

View File

@@ -101,6 +101,7 @@
&-arrow {
background: @popover-bg;
background-color: inherit;
width: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
height: sqrt(@popover-arrow-width * @popover-arrow-width * 2);
transform: rotate(45deg);

View File

@@ -89,6 +89,33 @@ exports[`Progress render negetive successPercent 1`] = `
</div>
`;
exports[`Progress render normal progress 1`] = `
<div
class="ant-progress ant-progress-line ant-progress-status-normal ant-progress-show-info ant-progress-default"
>
<div>
<div
class="ant-progress-outer"
>
<div
class="ant-progress-inner"
>
<div
class="ant-progress-bg"
style="width: 0%; height: 8px; border-radius: 100px;"
/>
</div>
</div>
<span
class="ant-progress-text"
title="0%"
>
0%
</span>
</div>
</div>
`;
exports[`Progress render out-of-range progress 1`] = `
<div
class="ant-progress ant-progress-line ant-progress-status-success ant-progress-show-info ant-progress-default"

View File

@@ -46,4 +46,9 @@ describe('Progress', () => {
const wrapper = mount(<Progress type="circle" percent={50} strokeColor="red" />);
expect(wrapper.render()).toMatchSnapshot();
});
it('render normal progress', () => {
const wrapper = mount(<Progress status="normal" />);
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@@ -22,7 +22,7 @@ If it will take a long time to complete an operation, you can use `Progress` to
| gapPosition `(type=circle)` | the gap position, options: `top` `bottom` `left` `right` | string | `top` |
| percent | to set the completion percentage | number | 0 |
| showInfo | whether to display the progress value and the status icon | boolean | true |
| status | to set the status of the Progress, options: `success` `exception` `active` | string | - |
| status | to set the status of the Progress, options: `success` `exception` `active` `normal` | string | - |
| strokeWidth `(type=line)` | to set the width of the progress bar, unit: `px` | number | 10 |
| strokeWidth `(type=circle)` | to set the width of the circular progress bar, unit: percentage of the canvas width | number | 6 |
| strokeLinecap | to set the style of the progress linecap | Enum{ 'round', 'square' } | `round` |

View File

@@ -23,7 +23,7 @@ title: Progress
| gapPosition `(type=circle)` | 圆形进度条缺口位置 | Enum{ 'top', 'bottom', 'left', 'right' } | `top` |
| percent | 百分比 | number | 0 |
| showInfo | 是否显示进度数值或状态图标 | boolean | true |
| status | 状态,可选:`success` `exception` `active` | string | - |
| status | 状态,可选:`success` `exception` `active` `normal` | string | - |
| strokeWidth `(type=line)` | 进度条线的宽度,单位 px | number | 10 |
| strokeWidth `(type=circle)` | 圆形进度条线的宽度,单位是进度条画布宽度的百分比 | number | 6 |
| strokeLinecap | | Enum{ 'round', 'square' } | `round` |

View File

@@ -20,7 +20,7 @@ export interface ProgressProps {
percent?: number;
successPercent?: number;
format?: (percent?: number, successPercent?: number) => React.ReactNode;
status?: 'success' | 'active' | 'exception';
status?: 'success' | 'active' | 'exception' | 'normal';
showInfo?: boolean;
strokeWidth?: number;
strokeLinecap?: string;

View File

@@ -2,6 +2,7 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import shallowEqual from 'shallowequal';
import { polyfill } from 'react-lifecycles-compat';
import Radio from './radio';
import {
RadioGroupProps,
@@ -22,7 +23,7 @@ function getCheckedValue(children: React.ReactNode) {
return matched ? { value } : undefined;
}
export default class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
class RadioGroup extends React.Component<RadioGroupProps, RadioGroupState> {
static defaultProps = {
disabled: false,
prefixCls: 'ant-radio',
@@ -33,6 +34,22 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
radioGroup: PropTypes.any,
};
static getDerivedStateFromProps(nextProps: RadioGroupProps) {
if ('value' in nextProps) {
return {
value: nextProps.value,
};
} else {
const checkedValue = getCheckedValue(nextProps.children);
if (checkedValue) {
return {
value: checkedValue.value,
};
}
}
return null;
}
constructor(props: RadioGroupProps) {
super(props);
let value;
@@ -60,21 +77,6 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
};
}
componentWillReceiveProps(nextProps: RadioGroupProps) {
if ('value' in nextProps) {
this.setState({
value: nextProps.value,
});
} else {
const checkedValue = getCheckedValue(nextProps.children);
if (checkedValue) {
this.setState({
value: checkedValue.value,
});
}
}
}
shouldComponentUpdate(nextProps: RadioGroupProps, nextState: RadioGroupState) {
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
}
@@ -157,3 +159,6 @@ export default class RadioGroup extends React.Component<RadioGroupProps, RadioGr
);
}
}
polyfill(RadioGroup);
export default RadioGroup;

View File

@@ -116,6 +116,7 @@
.@{radio-inner-prefix-cls} {
border-color: @border-color-base !important;
background-color: @input-disabled-bg;
cursor: not-allowed;
&:after {
background-color: #ccc;
}

View File

@@ -355,6 +355,55 @@ exports[`renders ./components/select/demo/custom-dropdown-menu.md correctly 1`]
</div>
`;
exports[`renders ./components/select/demo/hide-selected.md correctly 1`] = `
<div
class="ant-select ant-select-enabled"
style="width:100%"
>
<div
aria-autocomplete="list"
aria-controls="test-uuid"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--multiple"
role="combobox"
>
<div
class="ant-select-selection__rendered"
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
>
Inserted are removed
</div>
<ul>
<li
class="ant-select-search ant-select-search--inline"
>
<div
class="ant-select-search__field__wrap"
>
<input
autocomplete="off"
class="ant-select-search__field"
value=""
/>
<span
class="ant-select-search__field__mirror"
>
 
</span>
</div>
</li>
</ul>
</div>
</div>
</div>
`;
exports[`renders ./components/select/demo/label-in-value.md correctly 1`] = `
<div
class="ant-select ant-select-enabled"

View File

@@ -0,0 +1,52 @@
---
order: 22
title:
zh-CN: 隐藏已选择选项
en-US: Hide Already Selected
---
## zh-CN
隐藏下拉列表中已选择的选项。
## en-US
Hide already selected options in the dropdown.
````jsx
import { Select } from 'antd';
const OPTIONS = ['Apples', 'Nails', 'Bananas', 'Helicopters'];
class SelectWithHiddenSelectedOptions extends React.Component {
state = {
selectedItems: [],
};
handleChange = selectedItems => {
this.setState({ selectedItems });
};
render() {
const { selectedItems } = this.state;
const filteredOptions = OPTIONS.filter(o => !selectedItems.includes(o));
return (
<Select
mode="multiple"
placeholder="Inserted are removed"
value={selectedItems}
onChange={this.handleChange}
style={{ width: '100%' }}
>
{filteredOptions.map(item => (
<Select.Option key={item} value={item}>
{item}
</Select.Option>
))}
</Select>
);
}
}
ReactDOM.render(<SelectWithHiddenSelectedOptions />, mountNode);
````

View File

@@ -37,7 +37,7 @@ export interface AbstractSelectProps {
open?: boolean;
onDropdownVisibleChange?: (open: boolean) => void;
autoClearSearchValue?: boolean;
dropdownRender?: (menu: React.ReactNode) => React.ReactNode;
dropdownRender?: (menu?: React.ReactNode, props?: SelectProps) => React.ReactNode;
loading?: boolean;
}
@@ -131,8 +131,8 @@ export default class Select<T = SelectValue> extends React.Component<SelectProps
warning(
props.mode !== 'combobox',
'The combobox mode of Select is deprecated,' +
'it will be removed in next major version,' +
'The combobox mode of Select is deprecated, ' +
'it will be removed in next major version, ' +
'please use AutoComplete instead',
);
}

View File

@@ -1,3 +1,3 @@
import demoTest from '../../../tests/shared/demoTest';
demoTest('slider');
demoTest('slider', { skip: process.env.LIB_DIR === 'dist' && ['show-tooltip.md'] });

View File

@@ -5,19 +5,16 @@ title:
en-US: Control visible of ToolTip
---
## zh-CN
## zh-CN
`tooltipVisible``true`将始终显示ToolTip反之则始终不显示即使在拖动、移入时也是如此。
`tooltipVisible``true`将始终显示ToolTip反之则始终不显示即使在拖动、移入时也是如此。
## en-US
## en-US
When `tooltipVisible` is `true`, ToolTip will show always, or ToolTip will not show anyway, even if dragging or hovering.
When `tooltipVisible` is `true`, ToolTip will show always, or ToolTip will not show anyway, even if dragging or hovering.
````jsx
import { Slider } from 'antd';
````jsx
import Slider from '..';
ReactDOM.render(<Slider defaultValue={30} tooltipVisible />, mountNode);
````
ReactDOM.render(<Slider defaultValue={30} tooltipVisible />, mountNode);
````

View File

@@ -81,12 +81,7 @@ export default class Slider extends React.Component<SliderProps, SliderState> {
const { tooltipPrefixCls, tipFormatter, tooltipVisible } = this.props;
const { visibles } = this.state;
const isTipFormatter = tipFormatter ? visibles[index] || dragging : false;
let visible;
if (tooltipVisible) {
visible = tooltipVisible || isTipFormatter;
} else if (tooltipVisible === undefined) {
visible = isTipFormatter;
}
const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter);
return (
<Tooltip
prefixCls={tooltipPrefixCls}

View File

@@ -14,7 +14,7 @@ describe('Spin', () => {
.find('.ant-spin-nested-loading')
.at(0)
.prop('style'),
).toBe(null);
).toBeFalsy();
expect(
wrapper
.find('.ant-spin')

View File

@@ -1,7 +1,6 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import Animate from 'rc-animate';
import omit from 'omit.js';
export type SpinSize = 'small' | 'default' | 'large';
@@ -93,13 +92,6 @@ class Spin extends React.Component<SpinProps, SpinState> {
return !!(this.props && this.props.children);
}
componentDidMount() {
const { spinning, delay } = this.props;
if (shouldDelay(spinning, delay)) {
this.delayTimeout = window.setTimeout(this.delayUpdateSpinning, delay);
}
}
componentWillUnmount() {
if (this.debounceTimeout) {
clearTimeout(this.debounceTimeout);
@@ -145,7 +137,7 @@ class Spin extends React.Component<SpinProps, SpinState> {
};
render() {
const { className, size, prefixCls, tip, wrapperClassName, ...restProps } = this.props;
const { className, size, prefixCls, tip, wrapperClassName, style, ...restProps } = this.props;
const { spinning } = this.state;
const spinClassName = classNames(
@@ -163,33 +155,22 @@ class Spin extends React.Component<SpinProps, SpinState> {
const divProps = omit(restProps, ['spinning', 'delay', 'indicator']);
const spinElement = (
<div {...divProps} className={spinClassName}>
<div {...divProps} style={style} className={spinClassName}>
{renderIndicator(this.props)}
{tip ? <div className={`${prefixCls}-text`}>{tip}</div> : null}
</div>
);
if (this.isNestedPattern()) {
let animateClassName = prefixCls + '-nested-loading';
if (wrapperClassName) {
animateClassName += ' ' + wrapperClassName;
}
const containerClassName = classNames({
[`${prefixCls}-container`]: true,
const containerClassName = classNames(`${prefixCls}-container`, {
[`${prefixCls}-blur`]: spinning,
});
return (
<Animate
{...divProps}
component="div"
className={animateClassName}
style={null}
transitionName="fade"
>
<div {...divProps} className={classNames(`${prefixCls}-nested-loading`, wrapperClassName)}>
{spinning && <div key="loading">{spinElement}</div>}
<div className={containerClassName} key="container">
{this.props.children}
</div>
</Animate>
</div>
);
}
return spinElement;

View File

@@ -26,7 +26,7 @@
display: block;
position: absolute;
height: 100%;
max-height: 360px;
max-height: 400px;
width: 100%;
z-index: 4;
.@{spin-prefix-cls}-dot {
@@ -75,19 +75,6 @@
&-container {
position: relative;
transition: opacity 0.3s;
.clearfix;
}
&-blur {
pointer-events: none;
user-select: none;
overflow: hidden;
opacity: 0.5;
-webkit-filter: blur(0.5px);
filter: blur(0.5px);
/* autoprefixer: off */
filter: ~'progid\:DXImageTransform\.Microsoft\.Blur(PixelRadius\=1, MakeShadow\=false)';
&:after {
content: '';
@@ -97,12 +84,27 @@
top: 0;
bottom: 0;
background: #fff;
opacity: 0.3;
opacity: 0;
pointer-events: none;
transition: all 0.3s;
height: 100%;
width: 100%;
z-index: 10;
}
}
&-blur {
pointer-events: none;
user-select: none;
overflow: hidden;
opacity: 0.5;
&:after {
opacity: 0.4;
pointer-events: auto;
}
}
// tip
// ------------------------------
&-tip {

View File

@@ -27,6 +27,7 @@ The whole of the step bar.
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| className | additional class to Steps | string | - |
| current | to set the current step, counting from 0. You can overwrite this state by using `status` of `Step` | number | 0 |
| direction | to specify the direction of the step bar, `horizontal` or `vertical` | string | `horizontal` |
| labelPlacement | place title and description with `horizontal` or `vertical` direction | string | `horizontal` |

View File

@@ -6,6 +6,7 @@ import Icon from '../icon';
export interface StepsProps {
prefixCls?: string;
iconPrefix?: string;
className?: string;
current?: number;
initial?: number;
labelPlacement?: 'horizontal' | 'vertical';

View File

@@ -28,6 +28,7 @@ title: Steps
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| className | 步骤条类名 | string | - |
| current | 指定当前步骤,从 0 开始记数。在子 Step 元素中,可以通过 `status` 属性覆盖状态 | number | 0 |
| direction | 指定步骤条方向。目前支持水平(`horizontal`)和竖直(`vertical`)两种方向 | string | horizontal |
| labelPlacement | 指定标签放置位置,默认水平放图标右侧,可选 `vertical` 放图标下方 | string | `horizontal` |

View File

@@ -356,6 +356,7 @@
@table-expanded-row-bg: #fbfbfb;
@table-padding-vertical: 16px;
@table-padding-horizontal: 16px;
@table-border-radius-base : @border-radius-base;
// Tag
// --
@@ -420,6 +421,8 @@
@tabs-bar-margin: 0 0 16px 0;
@tabs-horizontal-margin: 0 32px 0 0;
@tabs-horizontal-padding: 12px 16px;
@tabs-horizontal-padding-lg: 16px;
@tabs-horizontal-padding-sm: 8px 16px;
@tabs-vertical-padding: 8px 24px;
@tabs-vertical-margin: 0 0 16px 0;
@tabs-scrolling-size: 32px;

View File

@@ -58,8 +58,13 @@ export default class SelectionCheckboxAll<T> extends React.Component<
});
}
checkSelection(data: T[], type: string, byDefaultChecked: boolean) {
const { store, getCheckboxPropsByItem, getRecordKey } = this.props;
checkSelection(
props: SelectionCheckboxAllProps<T>,
data: T[],
type: string,
byDefaultChecked: boolean,
) {
const { store, getCheckboxPropsByItem, getRecordKey } = props || this.props;
// type should be 'every' | 'some'
if (type === 'every' || type === 'some') {
return byDefaultChecked
@@ -93,8 +98,9 @@ export default class SelectionCheckboxAll<T> extends React.Component<
checked = false;
} else {
checked = store.getState().selectionDirty
? this.checkSelection(data, 'every', false)
: this.checkSelection(data, 'every', false) || this.checkSelection(data, 'every', true);
? this.checkSelection(props, data, 'every', false)
: this.checkSelection(props, data, 'every', false) ||
this.checkSelection(props, data, 'every', true);
}
return checked;
}
@@ -106,15 +112,17 @@ export default class SelectionCheckboxAll<T> extends React.Component<
indeterminate = false;
} else {
indeterminate = store.getState().selectionDirty
? this.checkSelection(data, 'some', false) && !this.checkSelection(data, 'every', false)
: (this.checkSelection(data, 'some', false) &&
!this.checkSelection(data, 'every', false)) ||
(this.checkSelection(data, 'some', true) && !this.checkSelection(data, 'every', true));
? this.checkSelection(props, data, 'some', false) &&
!this.checkSelection(props, data, 'every', false)
: (this.checkSelection(props, data, 'some', false) &&
!this.checkSelection(props, data, 'every', false)) ||
(this.checkSelection(props, data, 'some', true) &&
!this.checkSelection(props, data, 'every', true));
}
return indeterminate;
}
handleSelectAllChagne = (e: CheckboxChangeEvent) => {
handleSelectAllChange = (e: CheckboxChangeEvent) => {
const checked = e.target.checked;
this.props.onSelect(checked ? 'all' : 'removeAll', 0, null);
};
@@ -171,7 +179,7 @@ export default class SelectionCheckboxAll<T> extends React.Component<
checked={checked}
indeterminate={indeterminate}
disabled={disabled}
onChange={this.handleSelectAllChagne}
onChange={this.handleSelectAllChange}
/>
{customSelections}
</div>

View File

@@ -26,6 +26,7 @@ import {
TableComponents,
RowSelectionType,
TableLocale,
AdditionalCellProps,
ColumnProps,
CompareFn,
TableStateFilters,
@@ -488,7 +489,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
const key = this.getRecordKey(record, rowIndex);
const { pivot } = this.state;
const rows = this.getFlatCurrentPageData();
const rows = this.getFlatCurrentPageData(this.props.childrenColumnName);
let realIndex = rowIndex;
if (this.props.expandedRowRender) {
realIndex = rows.findIndex(row => this.getRecordKey(row, rowIndex) === key);
@@ -553,10 +554,8 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
handleRadioSelect = (record: T, rowIndex: number, e: RadioChangeEvent) => {
const checked = e.target.checked;
const nativeEvent = e.nativeEvent;
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
const key = this.getRecordKey(record, rowIndex);
selectedRowKeys = [key];
const selectedRowKeys = [key];
this.store.setState({
selectionDirty: true,
});
@@ -570,7 +569,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
};
handleSelectRow = (selectionKey: string, index: number, onSelectFunc: SelectionItemSelectFn) => {
const data = this.getFlatCurrentPageData();
const data = this.getFlatCurrentPageData(this.props.childrenColumnName);
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
const changeableRowKeys = data
@@ -722,10 +721,10 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
};
renderRowSelection(locale: TableLocale) {
const { prefixCls, rowSelection } = this.props;
const { prefixCls, rowSelection, childrenColumnName } = this.props;
const columns = this.columns.concat();
if (rowSelection) {
const data = this.getFlatCurrentPageData().filter((item, index) => {
const data = this.getFlatCurrentPageData(childrenColumnName).filter((item, index) => {
if (rowSelection.getCheckboxProps) {
return !this.getCheckboxPropsByItem(item, index).disabled;
}
@@ -777,7 +776,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
getColumnKey(column: ColumnProps<T>, index?: number) {
return column.key || column.dataIndex || index;
return column.key || (column.dataIndex as string) || index;
}
getMaxCurrent(total: number) {
@@ -805,6 +804,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
const key = this.getColumnKey(column, i) as string;
let filterDropdown;
let sortButton;
let onHeaderCell = column.onHeaderCell;
const sortTitle = this.getColumnTitle(column.title, {}) || locale.sortTitle;
const isSortColumn = this.isSortColumn(column);
if ((column.filters && column.filters.length > 0) || column.filterDropdown) {
@@ -839,7 +839,27 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
/>
</div>
);
onHeaderCell = (col: Column<T>) => {
let colProps: AdditionalCellProps = {};
// Get original first
if (column.onHeaderCell) {
colProps = {
...column.onHeaderCell(col),
};
}
// Add sorter logic
const onHeaderCellClick = colProps.onClick;
colProps.onClick = (...args) => {
this.toggleSortOrder(column);
if (onHeaderCellClick) {
onHeaderCellClick(...args);
}
};
return colProps;
};
}
const sortTitleString = sortButton && typeof sortTitle === 'string' ? sortTitle : undefined;
return {
...column,
className: classNames(column.className, {
@@ -851,15 +871,15 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
title: [
<div
key="title"
title={sortButton ? sortTitle : undefined}
title={sortTitleString}
className={sortButton ? `${prefixCls}-column-sorters` : undefined}
onClick={() => this.toggleSortOrder(column)}
>
{this.renderColumnTitle(column.title)}
{sortButton}
</div>,
filterDropdown,
],
onHeaderCell,
};
});
}
@@ -882,8 +902,8 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
if (!(title instanceof Function) && typeof title !== 'string' && typeof title !== 'number') {
const props = title.props;
const { children } = props;
if (props && props.children) {
const { children } = props;
return this.getColumnTitle(children, props);
}
} else {
@@ -1003,8 +1023,8 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
return flatArray(this.getLocalData(null, false));
}
getFlatCurrentPageData() {
return flatArray(this.getCurrentPageData());
getFlatCurrentPageData(childrenColumnName: string | undefined) {
return flatArray(this.getCurrentPageData(), childrenColumnName);
}
recursiveSort(data: T[], sorterFn: (a: any, b: any) => number): T[] {

View File

@@ -615,4 +615,40 @@ describe('Table.rowSelection', () => {
expect(onChange.mock.calls[1][0].length).toBe(2);
expect(onChange.mock.calls[1][1].length).toBe(2);
});
it('render correctly when set childrenColumnName', () => {
const newDatas = [
{
key: 1,
name: 'Jack',
children: [
{
key: 11,
name: 'John Brown',
},
],
},
{
key: 2,
name: 'Lucy',
children: [
{
key: 21,
name: 'Lucy Brown',
},
],
},
];
const wrapper = mount(
<Table columns={columns} dataSource={newDatas} childrenColumnName="test" rowSelection={{}} />,
);
const checkboxes = wrapper.find('input');
const checkboxAll = wrapper.find('SelectionCheckboxAll');
checkboxes.at(1).simulate('change', { target: { checked: true } });
expect(checkboxAll.instance().state).toEqual({ indeterminate: true, checked: false });
checkboxes.at(2).simulate('change', { target: { checked: true } });
expect(checkboxAll.instance().state).toEqual({ indeterminate: false, checked: true });
});
});

View File

@@ -240,13 +240,11 @@ exports[`renders ./components/table/demo/basic.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
nice
</div>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
developer
</div>
@@ -305,7 +303,6 @@ exports[`renders ./components/table/demo/basic.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
loser
</div>
@@ -364,13 +361,11 @@ exports[`renders ./components/table/demo/basic.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
cool
</div>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
teacher
</div>
@@ -1038,8 +1033,12 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
class=""
>
<colgroup>
<col />
<col />
<col
style="width:30%;min-width:30%"
/>
<col
style="width:20%;min-width:20%"
/>
<col />
</colgroup>
<thead
@@ -1053,31 +1052,48 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
Name
</div>
<i
class="anticon anticon-smile-o ant-table-filter-icon ant-dropdown-trigger"
style="color:#aaa"
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
title="Filter menu"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
data-icon="search"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</i>
</th>
<th
class=""
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<div>
Age
</div>
<i
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
title="Filter menu"
>
<svg
aria-hidden="true"
class=""
data-icon="search"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</i>
</th>
<th
class="ant-table-column-has-actions ant-table-column-has-filters"
@@ -1086,20 +1102,20 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
Address
</div>
<i
class="anticon anticon-filter ant-dropdown-trigger"
class="anticon anticon-search ant-table-filter-icon ant-dropdown-trigger"
title="Filter menu"
>
<svg
aria-hidden="true"
class=""
data-icon="filter"
data-icon="search"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196zm531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z"
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</i>
@@ -1120,17 +1136,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
John Brown
</td>
<td
class=""
>
32
<span>
<span
class=""
>
John Brown
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
New York No. 1 Lake Park
<span>
<span
class=""
>
32
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<span
class=""
>
New York No. 1 Lake Park
</span>
</span>
</td>
</tr>
<tr
@@ -1144,17 +1178,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
Joe Black
</td>
<td
class=""
>
42
<span>
<span
class=""
>
Joe Black
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
London No. 1 Lake Park
<span>
<span
class=""
>
42
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<span
class=""
>
London No. 1 Lake Park
</span>
</span>
</td>
</tr>
<tr
@@ -1168,17 +1220,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
Jim Green
</td>
<td
class=""
>
32
<span>
<span
class=""
>
Jim Green
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
Sidney No. 1 Lake Park
<span>
<span
class=""
>
32
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<span
class=""
>
Sidney No. 1 Lake Park
</span>
</span>
</td>
</tr>
<tr
@@ -1192,17 +1262,35 @@ exports[`renders ./components/table/demo/custom-filter-panel.md correctly 1`] =
class="ant-table-row-indent indent-level-0"
style="padding-left:0px"
/>
Jim Red
</td>
<td
class=""
>
32
<span>
<span
class=""
>
Jim Red
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
London No. 2 Lake Park
<span>
<span
class=""
>
32
</span>
</span>
</td>
<td
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<span
class=""
>
London No. 2 Lake Park
</span>
</span>
</td>
</tr>
</tbody>
@@ -9556,13 +9644,11 @@ exports[`renders ./components/table/demo/jsx.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
nice
</div>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
developer
</div>
@@ -9622,7 +9708,6 @@ exports[`renders ./components/table/demo/jsx.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
loser
</div>
@@ -9682,13 +9767,11 @@ exports[`renders ./components/table/demo/jsx.md correctly 1`] = `
<span>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
cool
</div>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
teacher
</div>

View File

@@ -17,6 +17,7 @@ Implement a customized column search example via `filterDropdown`.
import {
Table, Input, Button, Icon,
} from 'antd';
import Highlighter from 'react-highlight-words';
const data = [{
key: '1',
@@ -45,12 +46,60 @@ class App extends React.Component {
searchText: '',
};
handleSearch = (selectedKeys, confirm) => () => {
getColumnSearchProps = (dataIndex) => ({
filterDropdown: ({
setSelectedKeys, selectedKeys, confirm, clearFilters,
}) => (
<div className="custom-filter-dropdown">
<Input
ref={node => { this.searchInput = node; }}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm)}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm)}
icon="search"
size="small"
style={{ width: 90, marginRight: 8 }}
>
Search
</Button>
<Button
onClick={() => this.handleReset(clearFilters)}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</div>
),
filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) => record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => this.searchInput.select());
}
},
render: (text) => (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text.toString()}
/>
),
})
handleSearch = (selectedKeys, confirm) => {
confirm();
this.setState({ searchText: selectedKeys[0] });
}
handleReset = clearFilters => () => {
handleReset = (clearFilters) => {
clearFilters();
this.setState({ searchText: '' });
}
@@ -60,57 +109,19 @@ class App extends React.Component {
title: 'Name',
dataIndex: 'name',
key: 'name',
filterDropdown: ({
setSelectedKeys, selectedKeys, confirm, clearFilters,
}) => (
<div className="custom-filter-dropdown">
<Input
ref={ele => this.searchInput = ele}
placeholder="Search name"
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={this.handleSearch(selectedKeys, confirm)}
/>
<Button type="primary" onClick={this.handleSearch(selectedKeys, confirm)}>Search</Button>
<Button onClick={this.handleReset(clearFilters)}>Reset</Button>
</div>
),
filterIcon: filtered => <Icon type="smile-o" style={{ color: filtered ? '#108ee9' : '#aaa' }} />,
onFilter: (value, record) => record.name.toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => {
this.searchInput.focus();
});
}
},
render: (text) => {
const { searchText } = this.state;
return searchText ? (
<span>
{text.split(new RegExp(`(${searchText})`, 'gi')).map((fragment, i) => (
fragment.toLowerCase() === searchText.toLowerCase()
? <span key={i} className="highlight">{fragment}</span> : fragment // eslint-disable-line
))}
</span>
) : text;
},
width: '30%',
...this.getColumnSearchProps('name'),
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '20%',
...this.getColumnSearchProps('age'),
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
filters: [{
text: 'London',
value: 'London',
}, {
text: 'New York',
value: 'New York',
}],
onFilter: (value, record) => record.address.indexOf(value) === 0,
...this.getColumnSearchProps('address'),
}];
return <Table columns={columns} dataSource={data} />;
}
@@ -122,21 +133,8 @@ ReactDOM.render(<App />, mountNode);
````css
.custom-filter-dropdown {
padding: 8px;
border-radius: 6px;
border-radius: 4px;
background: #fff;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
}
.custom-filter-dropdown input {
width: 130px;
margin-right: 8px;
}
.custom-filter-dropdown button {
margin-right: 8px;
}
.highlight {
color: #f50;
box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
}
````

View File

@@ -1,5 +1,5 @@
---
order: 7
order: 6
title:
en-US: Filter and sorter
zh-CN: 筛选和排序

View File

@@ -61,6 +61,7 @@ const columns = [{
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - |
| expandedRowKeys | Current expanded row keys | string\[] | - |
| expandedRowRender | Expanded container render for each row | Function(record, index, indent, expanded):ReactNode | - |
| expandIcon | Customize row expand Icon. Ref [example](http://react-component.github.io/table/examples/expandIcon.html) | Function(props):ReactNode | - |
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | `false` |
| footer | Table footer renderer | Function(currentPageData) | |
| indentSize | Indent size in pixels of tree data | number | 15 |

View File

@@ -66,6 +66,7 @@ const columns = [{
| defaultExpandedRowKeys | 默认展开的行 | string\[] | - |
| expandedRowKeys | 展开的行,控制属性 | string\[] | - |
| expandedRowRender | 额外的展开行 | Function(record, index, indent, expanded):ReactNode | - |
| expandIcon | 自定义展开图标,参考[示例](http://react-component.github.io/table/examples/expandIcon.html) | Function(props):ReactNode | - |
| expandRowByClick | 通过点击行来展开子行 | boolean | `false` |
| footer | 表格尾部 | Function(currentPageData) | |
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 |

Some files were not shown because too many files have changed in this diff Show More